forked from gxwebsoft/websoft-cms
26 changed files with 1108 additions and 832 deletions
@ -0,0 +1,61 @@ |
|||
<template> |
|||
<el-upload action="#" list-type="picture-card" :auto-upload="false"> |
|||
<el-icon><Plus /></el-icon> |
|||
|
|||
<template #file="{ file }"> |
|||
<div> |
|||
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> |
|||
<span class="el-upload-list__item-actions"> |
|||
<span |
|||
class="el-upload-list__item-preview" |
|||
@click="handlePictureCardPreview(file)" |
|||
> |
|||
<el-icon><zoom-in /></el-icon> |
|||
</span> |
|||
<span |
|||
v-if="!disabled" |
|||
class="el-upload-list__item-delete" |
|||
@click="handleDownload(file)" |
|||
> |
|||
<el-icon><Download /></el-icon> |
|||
</span> |
|||
<span |
|||
v-if="!disabled" |
|||
class="el-upload-list__item-delete" |
|||
@click="handleRemove(file)" |
|||
> |
|||
<el-icon><Delete /></el-icon> |
|||
</span> |
|||
</span> |
|||
</div> |
|||
</template> |
|||
</el-upload> |
|||
|
|||
<el-dialog v-model="dialogVisible"> |
|||
<img :src="dialogImageUrl" alt="Preview Image" /> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { ref } from 'vue' |
|||
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue' |
|||
|
|||
import type { UploadFile } from 'element-plus' |
|||
|
|||
const dialogImageUrl = ref('') |
|||
const dialogVisible = ref(false) |
|||
const disabled = ref(false) |
|||
|
|||
const handleRemove = (file: UploadFile) => { |
|||
console.log(file) |
|||
} |
|||
|
|||
const handlePictureCardPreview = (file: UploadFile) => { |
|||
dialogImageUrl.value = file.url! |
|||
dialogVisible.value = true |
|||
} |
|||
|
|||
const handleDownload = (file: UploadFile) => { |
|||
console.log(file) |
|||
} |
|||
</script> |
@ -0,0 +1,58 @@ |
|||
<template> |
|||
<el-menu |
|||
class="full-width-menu" |
|||
style="border: none; width: 100%" |
|||
:default-active="activeIndex" |
|||
@select="handleSelect" |
|||
> |
|||
<el-menu-item v-for="(item,index) in activities" :index="`${item.path}`" :key="index" class="sm:w-[170px] w-full" style="border-bottom: 1px solid #f3f3f3;"> |
|||
<span class="text-center w-full" @click="navigateTo(item.path)">{{ item.name }}</span> |
|||
</el-menu-item> |
|||
</el-menu> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import {navigateTo} from "#imports"; |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
layout?: any; |
|||
activeIndex?: string; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done', index: string): void; |
|||
(e: 'update:visible', visible: boolean): void; |
|||
}>(); |
|||
|
|||
const activities = [ |
|||
{ |
|||
name: '账号信息', |
|||
path: '/user' |
|||
}, |
|||
{ |
|||
name: '密码修改', |
|||
path: '/user/password' |
|||
}, |
|||
{ |
|||
name: '实名认证', |
|||
path: '/user/auth' |
|||
}, |
|||
{ |
|||
name: '订单列表', |
|||
path: '/user/order' |
|||
}, |
|||
{ |
|||
name: '退出登录', |
|||
path: '/user/logout' |
|||
}, |
|||
] |
|||
|
|||
|
|||
const handleSelect = (index: string) => { |
|||
emit('done', index) |
|||
} |
|||
|
|||
</script> |
@ -1,79 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :data="form" /> |
|||
|
|||
<!-- 容器 --> |
|||
<div class="container md:w-screen-xl m-auto"> |
|||
<div class="flex flex-col"> |
|||
<Breadcrumb :data="form" /> |
|||
<div :class="form.design?.style" class="page-main w-full bg-white rounded-lg"> |
|||
<div class="p-4 leading-7" v-html="form.design?.content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="mt-[60px]" v-if="!form.design"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdBySpm} from "~/utils/common"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const website = useWebsite(); |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const form = useForm(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
|
|||
// 存在spm(优先级高) |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5)) |
|||
if (nav.value?.data) { |
|||
form.value = nav.value.data |
|||
} |
|||
|
|||
// 不存在spm的情况 |
|||
if(!form.value.navigationId){ |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: route.path}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
} |
|||
|
|||
// seo |
|||
useHead({ |
|||
title: `${form.value.title} - ${website.value.websiteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: `console.log(${JSON.stringify(form.value)})`, |
|||
}, |
|||
], |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form.value |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
if(path){ |
|||
reload(); |
|||
} |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -1,53 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :data="form" /> |
|||
|
|||
<div v-if="form" class="flex flex-col w-full md:w-screen-xl m-auto my-3"> |
|||
|
|||
<Breadcrumb :data="form" title="文章详情" /> |
|||
|
|||
<div class="m-3 bg-white p-3 mt-4 flex flex-col gap-xl rounded-lg"> |
|||
<div class="article-title-box p-4"> |
|||
<div class="sm:text-3xl text-xl sm:text-left text-center">{{ form.title }}</div> |
|||
<div class="text-sm pt-2 text-gray-4 flex gap-xl sm:text-left sm:justify-start justify-center"> |
|||
<span>{{ form.createTime }}</span> |
|||
<span>浏览:{{ form.actualViews }}</span> |
|||
</div> |
|||
</div> |
|||
<el-divider style="height: 1px " /> |
|||
<div class="content leading-8 sm:text-xl px-3 text-gray-700" v-html="form.content"></div> |
|||
</div> |
|||
</div> |
|||
<div v-if="!form"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {Goods} from "~/api/shop/goods/model"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import {getIdByParam} from "~/utils/common"; |
|||
|
|||
const route = useRoute(); |
|||
const { params } = route; |
|||
const { id } = params; |
|||
|
|||
// 页面信息 |
|||
const form = ref<Article | any>(); |
|||
|
|||
// 请求数据 |
|||
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/article/' + getIdByParam(id)) |
|||
form.value = info.value?.data; |
|||
form.value.num = 1; |
|||
form.value.radio = '0' |
|||
|
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.content *{ |
|||
max-width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
</style> |
@ -1,97 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :data="form" /> |
|||
{{ form }}-- |
|||
<!-- 容器 --> |
|||
<div class="container md:w-screen-xl m-auto" v-if="form"> |
|||
<div class="flex flex-col"> |
|||
<Breadcrumb :data="form" /> |
|||
<div :class="form.design?.styles" class="page-main w-full bg-white rounded-lg"> |
|||
<div class="p-4 leading-7" v-html="form.design?.content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div v-if="!form"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const website = useWebsite(); |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const form = useForm(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
// 都不存在 |
|||
if(!form.value.navigationId && !form.value.path){ |
|||
return ; |
|||
} |
|||
|
|||
// 存在spm(优先级高) |
|||
if(form.value.navigationId){ |
|||
console.log('11111') |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + form.value.navigationId) |
|||
if (nav.value?.data) { |
|||
form.value = nav.value.data |
|||
} |
|||
} |
|||
|
|||
// 不存在spm的情况 |
|||
if(!form.value.navigationId){ |
|||
console.log('2222') |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: form.value.path}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
} |
|||
|
|||
// seo |
|||
useHead({ |
|||
title: `${form.value.title} - ${website.value.websiteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: `console.log(${JSON.stringify(form.value)})`, |
|||
}, |
|||
], |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form.value |
|||
} |
|||
|
|||
watch( |
|||
() => route.query.spm, |
|||
(spm) => { |
|||
// TODO 方案一(优先级高):从spm参数提取商品ID |
|||
if(spm){ |
|||
const spmValue = String(spm).split('.') |
|||
if(spmValue[5]){ |
|||
form.value.navigationId = Number(spmValue[5]) |
|||
} |
|||
return reload(); |
|||
} |
|||
|
|||
// TODO 方案二:从params获取商品ID |
|||
const { custom } = route.params |
|||
form.value.path = `/${custom}`; |
|||
|
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -1,274 +0,0 @@ |
|||
<template> |
|||
<!-- 首页幻灯片 --> |
|||
<div class="banner sm:pt-[60px] pt-[46px]" v-if="banner"> |
|||
<el-carousel :interval="5000" arrow="always" :height="screenWidth <= 768 ? `170px` : `750px`"> |
|||
<el-carousel-item v-for="item in banner.data" :key="item.uid"> |
|||
<nuxt-link v-if="item.path" :to="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</nuxt-link> |
|||
<a v-else :href="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</a> |
|||
</el-carousel-item> |
|||
</el-carousel> |
|||
</div> |
|||
|
|||
<!-- 首页新闻栏目 --> |
|||
<div class="category md:w-screen-xl m-auto my-3 p-3 flex flex-col gap-xl"> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">栏目名称</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2 flex items-center"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">友情链接</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import type {AdItem} from "~/api/cms/ad/model"; |
|||
import type {Link} from "~/api/cms/link/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import {getPath} from "~/utils/common"; |
|||
|
|||
const website = useWebsite(); |
|||
const banner = ref<any>(); |
|||
const links = ref<any>(); |
|||
const movieList = ref<any>(); |
|||
const screenWidth = window.innerWidth; |
|||
const screenHeight = window.innerHeight; |
|||
|
|||
// 获取数据 |
|||
const reload = async () => { |
|||
await nextTick() |
|||
const form = useServerRequest('/cms/navigation/getNavigationByPath',{query: {path: getPath()}}) |
|||
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side'); |
|||
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接'); |
|||
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]); |
|||
console.log(slide.value) |
|||
banner.value = slide.value?.data; |
|||
links.value = link.value?.data; |
|||
} |
|||
reload(); |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.index { |
|||
padding-top: 20px; |
|||
|
|||
.banner { |
|||
.el-carousel__container { |
|||
height: 380px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 380px; |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.el-carousel__container { |
|||
height: 200px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 200px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.friendly-link { |
|||
border-bottom: #eee solid 1px; |
|||
padding: 10px; |
|||
font-size: 18px; |
|||
|
|||
> img { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
&__content { |
|||
padding: 20px; |
|||
|
|||
a { |
|||
padding-right: 15px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.demonstration { |
|||
color: var(--el-text-color-secondary); |
|||
} |
|||
|
|||
.col-pd { |
|||
li { |
|||
a { |
|||
font-size: 14px; |
|||
padding: 10px 0 10px; |
|||
border-bottom: dotted 1px #eeeeee; |
|||
|
|||
.badge { |
|||
display: inline-block; |
|||
margin-right: 10px; |
|||
width: 18px; |
|||
height: 18px; |
|||
text-align: center; |
|||
line-height: 18px; |
|||
border-radius: 2px; |
|||
font-size: 12px; |
|||
background-color: #eee; |
|||
color: #333; |
|||
} |
|||
|
|||
.text-muted { |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(1) { |
|||
.badge { |
|||
background-color: #ff4a4a; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(2) { |
|||
.badge { |
|||
background-color: #ff7701; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(3) { |
|||
.badge { |
|||
background-color: #ffb400; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.panel_hd { |
|||
border-bottom: #eeeeee solid 1px; |
|||
height: 46px; |
|||
margin-bottom: 15px; |
|||
|
|||
.title { |
|||
font-size: 18px; |
|||
line-height: 24px; |
|||
|
|||
img { |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
&__right { |
|||
li { |
|||
position: relative; |
|||
|
|||
&::before { |
|||
content: ''; |
|||
display: block; |
|||
width: 1px; |
|||
height: 10px; |
|||
background: #eee; |
|||
position: absolute; |
|||
top: 50%; |
|||
transform: translateY(-30%); |
|||
right: 0; |
|||
} |
|||
|
|||
&:last-child::before { |
|||
display: none; |
|||
} |
|||
|
|||
a { |
|||
padding: 0 10px; |
|||
color: #999; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.video-list { |
|||
&__block { |
|||
padding: 10px 0; |
|||
|
|||
&__img { |
|||
width: 100%; |
|||
height: 218px; |
|||
} |
|||
|
|||
.img-box { |
|||
position: relative; |
|||
height: 218px; |
|||
display: block; |
|||
|
|||
span { |
|||
position: absolute; |
|||
bottom: 0; |
|||
width: 100%; |
|||
height: 30px; |
|||
line-height: 30px; |
|||
left: 0; |
|||
display: inline-block; |
|||
background-image: linear-gradient(transparent, rgba(0, 0, 0, .5)); |
|||
color: #fff; |
|||
font-size: 12px; |
|||
text-align: right; |
|||
padding-right: 10px; |
|||
box-sizing: border-box; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&__detail { |
|||
.title { |
|||
font-size: 14px; |
|||
color: #333; |
|||
padding-top: 10px; |
|||
} |
|||
|
|||
p { |
|||
min-height: 19px; |
|||
font-size: 12px; |
|||
margin-bottom: 0; |
|||
margin-top: 5px; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media only screen and (max-width: 991px) { |
|||
.video-list { |
|||
&__block { |
|||
&__img, .img-box { |
|||
height: 170px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -1,80 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :data="form" /> |
|||
|
|||
<!-- 容器 --> |
|||
<div class="container md:w-screen-xl m-auto" v-if="form"> |
|||
<div class="flex flex-col"> |
|||
<Breadcrumb :data="form" /> |
|||
<div :class="form.design?.styles" class="page-main w-full bg-white rounded-lg"> |
|||
<div class="p-4 leading-7" v-html="form.design?.content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div v-if="!form"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useConfigInfo, useForm, useToken} from "~/composables/configState"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const form = useForm(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + form.value.navigationId) |
|||
if (nav.value?.data) { |
|||
form.value = nav.value.data |
|||
// seo |
|||
useHead({ |
|||
title: `${form.value.title} - ${config.value.siteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: "console.log('Hello World====')", |
|||
}, |
|||
], |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form.value |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
() => route.query.spm, |
|||
(spm) => { |
|||
console.log(spm,'spm>>') |
|||
// TODO 方案一:从spm参数提取商品ID |
|||
const spmValue = String(spm).split('.') |
|||
if(spmValue[5]){ |
|||
form.value.navigationId = Number(spmValue[5]) |
|||
} |
|||
|
|||
// TODO 方案二(优先级更高):从params获取商品ID |
|||
console.log(route.params.id,'sdfsd'); |
|||
// const str = String(custom).replaceAll(".html", ""); |
|||
// const split = str.split('-'); |
|||
// if(split[1]){ |
|||
// console.log('方案二:从params获取ID=' + split[1]) |
|||
// form.value.navigationId = Number(split[1]) |
|||
// } |
|||
|
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -1,67 +0,0 @@ |
|||
<template> |
|||
<el-card shadow="hover"> |
|||
<template #header> |
|||
<div class="flex items-center gap-xs"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<span>推荐文章</span> |
|||
</div> |
|||
</template> |
|||
<div class="flex flex-wrap p-2"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
|
|||
<el-card shadow="hover"> |
|||
<template #header> |
|||
<div class="flex items-center gap-xs"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<span>友情链接</span> |
|||
</div> |
|||
</template> |
|||
<div class="flex flex-wrap p-2"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
|
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import type {AdItem} from "~/api/cms/ad/model"; |
|||
import type {Link} from "~/api/cms/link/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
|
|||
const banner = ref<any>(); |
|||
const links = ref<any>(); |
|||
const movieList = ref<any>(); |
|||
const screenWidth = window.innerWidth; |
|||
const screenHeight = window.innerHeight; |
|||
|
|||
const route = useRoute(); |
|||
const { params, query } = route; |
|||
const { spm } = query; |
|||
|
|||
console.log(spm,'spm=') |
|||
|
|||
// 获取数据 |
|||
const reload = async () => { |
|||
await nextTick() |
|||
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side'); |
|||
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接'); |
|||
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]); |
|||
console.log(slide.value) |
|||
banner.value = slide.value?.data; |
|||
links.value = link.value?.data; |
|||
} |
|||
reload(); |
|||
</script> |
@ -0,0 +1,184 @@ |
|||
<template> |
|||
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full"> |
|||
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3"> |
|||
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full"> |
|||
<div class="flex justify-center pb-4 flex-col justify-center items-center"> |
|||
<el-avatar :src="userInfo?.avatar" :size="70" /> |
|||
<text class="text-gray-400 py-1 text-sm">更换头像</text> |
|||
</div> |
|||
<!-- 用户菜单 --> |
|||
<UserMenu :activeIndex="activeIndex" /> |
|||
</el-card> |
|||
<el-tabs type="border-card" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0"> |
|||
<el-tab-pane label="个人认证"> |
|||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2"> |
|||
<el-form-item label="真实姓名"> |
|||
<el-input v-model="form.realName" placeholder="请输入真实姓名" /> |
|||
</el-form-item> |
|||
<el-form-item label="证件号码"> |
|||
<el-input v-model="form.idCard" placeholder="请输入证件号码" /> |
|||
</el-form-item> |
|||
<el-form-item label="身份证(正面)"> |
|||
<Upload /> |
|||
</el-form-item> |
|||
<el-form-item label="身份证(反面)"> |
|||
<Upload /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="企业认证"> |
|||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2"> |
|||
<el-form-item label="企业名称"> |
|||
<el-input v-model="form.companyName" placeholder="请输入企业名称" /> |
|||
</el-form-item> |
|||
<el-form-item label="社会信用代码"> |
|||
<el-input v-model="form.idCard" placeholder="请输入社会信用代码" /> |
|||
</el-form-item> |
|||
<el-form-item label="营业执照"> |
|||
<Upload /> |
|||
</el-form-item> |
|||
<el-form-item label="所属行业"> |
|||
<el-select v-model="form.city" placeholder="请选择所属行业"> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="网站信息"> |
|||
<el-input v-model="form.idCard" placeholder="请输入网站信息" /> |
|||
</el-form-item> |
|||
<el-form-item label="您的身份"> |
|||
<el-radio-group v-model="form.sex"> |
|||
<el-radio value="1">法定代表人</el-radio> |
|||
<el-radio value="2">被授权人</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="用户协议"> |
|||
<el-checkbox v-model="form.status">请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。</el-checkbox> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="个人认证成功"> |
|||
<el-result |
|||
icon="success" |
|||
title="个人认证成功" |
|||
sub-title="认证完成时间 2024-09-30" |
|||
> |
|||
<template #extra> |
|||
<el-button type="text">修改认证信息</el-button> |
|||
</template> |
|||
</el-result> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="企业认证成功"> |
|||
<el-result |
|||
icon="success" |
|||
title="企业认证成功" |
|||
sub-title="认证完成时间 2024-09-30" |
|||
> |
|||
<template #extra> |
|||
<el-button type="text">修改认证信息</el-button> |
|||
</template> |
|||
</el-result> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState"; |
|||
import useFormData from '@/utils/use-form-data'; |
|||
import type { User } from '@/api/system/user/model'; |
|||
import { ref } from 'vue' |
|||
import { Plus } from '@element-plus/icons-vue' |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
import UserMenu from "~/components/UserMenu.vue"; |
|||
|
|||
// 配置信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const route = useRoute(); |
|||
const activeIndex = ref(''); |
|||
const website = useWebsite() |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const userInfo = ref<User>(); |
|||
|
|||
// 配置信息 |
|||
const { form, assignFields } = useFormData<User>({ |
|||
userId: undefined, |
|||
nickname: '', |
|||
username: '', |
|||
phone: '', |
|||
mobile: '', |
|||
sex: '', |
|||
sexName: '', |
|||
email: '', |
|||
password: '', |
|||
code: '', |
|||
smsCode: '', |
|||
comments: '', |
|||
remember: true, |
|||
isAdmin: true |
|||
}); |
|||
|
|||
useHead({ |
|||
title: `用户中心 - ${config.value?.siteName}`, |
|||
meta: [{ name: website.value.keywords, content: website.value.comments }] |
|||
}); |
|||
|
|||
const onSubmit = async () => { |
|||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{ |
|||
baseURL: runtimeConfig.public.apiServer, |
|||
method: 'put', |
|||
body: form |
|||
}) |
|||
if(modify.value?.code == 0){ |
|||
ElMessage.success('修改成功') |
|||
} |
|||
} |
|||
|
|||
const handleAvatarSuccess: UploadProps['onSuccess'] = ( |
|||
response, |
|||
uploadFile |
|||
) => { |
|||
imageUrl.value = URL.createObjectURL(uploadFile.raw!) |
|||
} |
|||
|
|||
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => { |
|||
if (rawFile.type !== 'image/jpeg') { |
|||
ElMessage.error('Avatar picture must be JPG format!') |
|||
return false |
|||
} else if (rawFile.size / 1024 / 1024 > 2) { |
|||
ElMessage.error('Avatar picture size can not exceed 2MB!') |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer}) |
|||
if(response.value?.data){ |
|||
userInfo.value = response.value?.data; |
|||
assignFields(response.value?.data); |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
activeIndex.value = path; |
|||
console.log(path,'=>Path') |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
|||
<style lang="less"> |
|||
body{ |
|||
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png"); |
|||
background-size: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,13 @@ |
|||
<template> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useToken} from "~/composables/configState"; |
|||
|
|||
const token = useToken(); |
|||
token.value = ''; |
|||
localStorage.clear(); |
|||
setTimeout(() => { |
|||
navigateTo('/') |
|||
return; |
|||
}, 1000) |
|||
</script> |
@ -0,0 +1,104 @@ |
|||
<template> |
|||
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full"> |
|||
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3"> |
|||
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full"> |
|||
<div class="flex justify-center pb-4 flex-col justify-center items-center"> |
|||
<el-avatar :src="userInfo?.avatar" :size="70" /> |
|||
<text class="text-gray-400 py-1 text-sm">更换头像</text> |
|||
</div> |
|||
<!-- 用户菜单 --> |
|||
<UserMenu :activeIndex="activeIndex" /> |
|||
</el-card> |
|||
<el-card shadow="hover" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0"> |
|||
<template #header>订单列表</template> |
|||
<el-table :data="tableData" style="width: 100%"> |
|||
<el-table-column prop="date" label="下单日期" width="180" /> |
|||
<el-table-column prop="name" label="产品名称" width="180" /> |
|||
<el-table-column prop="address" label="订单信息" /> |
|||
</el-table> |
|||
</el-card> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState"; |
|||
import useFormData from '@/utils/use-form-data'; |
|||
import type { User } from '@/api/system/user/model'; |
|||
import { ref } from 'vue' |
|||
import {getCaptcha} from "~/api/passport/login"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
import type {Website} from "~/api/cms/website/model"; |
|||
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model"; |
|||
import {getPath, navigateTo} from "#imports"; |
|||
import UserMenu from "~/components/UserMenu.vue"; |
|||
|
|||
// 配置信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const route = useRoute(); |
|||
const website = useWebsite() |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const userInfo = ref<User>(); |
|||
const activeIndex = ref(''); |
|||
|
|||
// 配置信息 |
|||
const { form, assignFields } = useFormData<User>({ |
|||
userId: undefined, |
|||
nickname: '', |
|||
username: '', |
|||
phone: '', |
|||
mobile: '', |
|||
sex: '', |
|||
sexName: '', |
|||
email: '', |
|||
password: '', |
|||
code: '', |
|||
smsCode: '', |
|||
comments: '', |
|||
remember: true, |
|||
isAdmin: true |
|||
}); |
|||
|
|||
const tableData = ref<any[]>(); |
|||
|
|||
useHead({ |
|||
title: `用户中心 - ${config.value?.siteName}`, |
|||
meta: [{ name: website.value.keywords, content: website.value.comments }] |
|||
}); |
|||
|
|||
const onSubmit = async () => { |
|||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{ |
|||
baseURL: runtimeConfig.public.apiServer, |
|||
method: 'put', |
|||
body: form |
|||
}) |
|||
if(modify.value?.code == 0){ |
|||
ElMessage.success('修改成功') |
|||
} |
|||
} |
|||
|
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer}) |
|||
if(response.value?.data){ |
|||
userInfo.value = response.value?.data; |
|||
assignFields(response.value?.data); |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
activeIndex.value = path; |
|||
console.log(path,'=>Path') |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
|||
<style lang="less"> |
|||
body{ |
|||
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png"); |
|||
background-size: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,161 @@ |
|||
<template> |
|||
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full"> |
|||
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3"> |
|||
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full"> |
|||
<div class="flex justify-center pb-4 flex-col justify-center items-center"> |
|||
<el-avatar :src="userInfo?.avatar" :size="70" /> |
|||
<text class="text-gray-400 py-1 text-sm">更换头像</text> |
|||
</div> |
|||
<!-- 用户菜单 --> |
|||
<UserMenu :activeIndex="activeIndex" /> |
|||
</el-card> |
|||
<el-card shadow="hover" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0"> |
|||
<template #header>修改密码</template> |
|||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2"> |
|||
<!-- <el-form-item label="手机号码">--> |
|||
<!-- <el-input disabled v-model="form.phone" />--> |
|||
<!-- </el-form-item>--> |
|||
<!-- <el-form-item label="短信验证码">--> |
|||
<!-- <el-space class="flex w-full">--> |
|||
<!-- <el-input size="large" placeholder="短信验证码" class="w-full" v-model="form.code" />--> |
|||
<!-- <el-button size="large" class="w-full" :disabled="!!countdownTime" @click="sendCode">--> |
|||
<!-- <span v-if="!countdownTime">发送验证码</span>--> |
|||
<!-- <span v-else>已发送 {{ countdownTime }} s</span>--> |
|||
<!-- </el-button>--> |
|||
<!-- </el-space>--> |
|||
<!-- </el-form-item>--> |
|||
<el-form-item label="旧密码"> |
|||
<el-input v-model="form.oldPassword" placeholder="请输入旧密码" /> |
|||
</el-form-item> |
|||
<el-form-item label="新密码"> |
|||
<el-input v-model="form.password" type="password" placeholder="请输入新密码" /> |
|||
</el-form-item> |
|||
<el-form-item label="确认密码"> |
|||
<el-input v-model="form.password2" type="password" placeholder="请确认新密码" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" size="large" @click="onSubmit">保存</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState"; |
|||
import useFormData from '@/utils/use-form-data'; |
|||
import type { User } from '@/api/system/user/model'; |
|||
import { ref } from 'vue' |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
import UserMenu from "~/components/UserMenu.vue"; |
|||
import type {CaptchaResult} from "~/api/passport/login/model"; |
|||
|
|||
// 配置信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const route = useRoute(); |
|||
const website = useWebsite() |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const userInfo = ref<User>(); |
|||
const activeIndex = ref(''); |
|||
|
|||
// 验证码 base64 数据 |
|||
const captcha = ref(''); |
|||
// 验证码内容, 实际项目去掉 |
|||
const text = ref(''); |
|||
// 图形验证码 |
|||
const imgCode = ref(''); |
|||
// 发送验证码按钮loading |
|||
const codeLoading = ref(false); |
|||
// 验证码倒计时时间 |
|||
const countdownTime = ref(0); |
|||
// 验证码倒计时定时器 |
|||
let countdownTimer: number | null = null; |
|||
|
|||
// 配置信息 |
|||
const { form, assignFields } = useFormData<User>({ |
|||
userId: undefined, |
|||
nickname: '', |
|||
username: '', |
|||
phone: '', |
|||
mobile: '', |
|||
sex: '', |
|||
sexName: '', |
|||
email: '', |
|||
oldPassword: '', |
|||
password: '', |
|||
password2: '', |
|||
code: '', |
|||
smsCode: '', |
|||
comments: '', |
|||
remember: true, |
|||
isAdmin: true |
|||
}); |
|||
|
|||
useHead({ |
|||
title: `用户中心 - ${config.value?.siteName}`, |
|||
meta: [{ name: website.value.keywords, content: website.value.comments }] |
|||
}); |
|||
|
|||
|
|||
/* 发送短信验证码 */ |
|||
const sendCode = async () => { |
|||
if (!form.phone) { |
|||
ElMessage.error('请输入手机号码'); |
|||
return; |
|||
} |
|||
imgCode.value = text.value; |
|||
codeLoading.value = true; |
|||
|
|||
const {data: smsCode } = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha',{baseURL: runtimeConfig.public.apiServer,method: "post",body: { |
|||
phone: form.phone |
|||
}}); |
|||
if(smsCode.value){ |
|||
codeLoading.value = false; |
|||
countdownTime.value = 30; |
|||
// 开始对按钮进行倒计时 |
|||
countdownTimer = window.setInterval(() => { |
|||
if (countdownTime.value <= 1) { |
|||
countdownTimer && clearInterval(countdownTimer); |
|||
countdownTimer = null; |
|||
} |
|||
countdownTime.value--; |
|||
}, 1000); |
|||
} |
|||
}; |
|||
|
|||
const onSubmit = async () => { |
|||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/password',{ |
|||
baseURL: runtimeConfig.public.apiServer, |
|||
method: 'put', |
|||
body: form |
|||
}) |
|||
if(modify.value?.code == 0){ |
|||
ElMessage.success('修改成功') |
|||
} |
|||
} |
|||
|
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer}) |
|||
if(response.value?.data){ |
|||
userInfo.value = response.value?.data; |
|||
assignFields(response.value?.data); |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
activeIndex.value = path; |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
|||
<style lang="less"> |
|||
body{ |
|||
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png"); |
|||
background-size: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,6 @@ |
|||
import Vue from 'vue' |
|||
import ElementUI from 'element-ui' |
|||
import 'element-ui/lib/theme-chalk/index.css' |
|||
import locale from 'element-ui/lib/locale/lang/zh-CN' |
|||
|
|||
Vue.use(ElementUI, { locale }) |
Loading…
Reference in new issue