新版本官网优化完成
This commit is contained in:
19
pages/[...404].vue
Normal file
19
pages/[...404].vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
path: '/404'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card class="m-5 w-screen-sm mt-[300px] mb-[200px] m-auto">
|
||||
<!-- 异常状态 -->
|
||||
<el-result
|
||||
icon="warning"
|
||||
:title="`404 页面不存在`"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 简单模式(常规单页面) -->
|
||||
<PageContainer :form="form" :layout="layout" />
|
||||
|
||||
<!-- 高级模式(自定义组件) -->
|
||||
<div class="flex flex-col" v-if="layout?.showLayout">
|
||||
{{ layout }}
|
||||
</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 type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
import PageContainer from "~/components/PageContainer.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const layout = ref<any>();
|
||||
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/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
}else{
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
console.log(path,'=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :title="`${form?.title}`" :desc="`${form?.comments}`" />
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {ArticleCategory} from "~/api/cms/category/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<ArticleCategory>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,limit: 8,categoryId: route.params.categoryId, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `文章列表 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
141
pages/article/[id].vue
Normal file
141
pages/article/[id].vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> {{ page.title }} </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="flex items-center">
|
||||
<el-input v-model="where.keywords" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="reload"/>
|
||||
</div>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" class="left">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer" @click="navigateTo(`/detail/${item.articleId}.html`)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="cover"
|
||||
:lazy="true" class="w-full md:h-[166px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex min-h-[56px]">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3 text-sm">
|
||||
<el-space class="flex items-end">
|
||||
<div class="text-gray-400 gap-1 flex items-center"><el-icon><View /></el-icon><span>{{ getViews(item) }}</span></div>
|
||||
</el-space>
|
||||
<div class="text-gray-400">
|
||||
<el-avatar v-if="item.avatar" size="small" :src="`${item.avatar}`" />
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
<Pagination :total="total" @done="search" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {getNavIdByParamsId, getViews, paramsId} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navId = ref();
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref<number>(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
getCmsNavigation(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
console.log(categoryData)
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
// window.history.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
</script>
|
||||
@@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="line-clamp-2 font-semibold text-gray-700 dark:text-white text-base gap-1.5 text-xl">
|
||||
<el-icon v-if="item.permission > 0"><Lock class="text-gray-400 pr-1"/></el-icon>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-3">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,211 +0,0 @@
|
||||
<template>
|
||||
<UserCard :form="form" title="社区" desc="分享研发成果 交流技术经验" :avatar="user?.avatar"/>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import LikeArticle from "~/pages/detail/components/LikeArticle.vue";
|
||||
import CardList from "~/pages/ask/components/CardList.vue";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import type {User} from "~/api/system/user/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const user = useUser();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const previousArticle = ref<Article>();
|
||||
const nextArticle = ref<Article>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const showContent = ref<boolean>();
|
||||
const showPassword = ref<boolean>();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Article>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
parentName: undefined,
|
||||
parentPath: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined
|
||||
});
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-article/checkArticlePassword', {
|
||||
query: {
|
||||
password: form?.password,
|
||||
password2: form.password2
|
||||
}
|
||||
})
|
||||
if (response.value?.code === 0) {
|
||||
showPassword.value = false;
|
||||
showContent.value = true;
|
||||
} else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
// const reload = async (path: string) => {
|
||||
//
|
||||
// if (response.value?.data) {
|
||||
// if (list.value.length < response.value?.data.count) {
|
||||
// disabled.value = false;
|
||||
// if (response.value?.data.list) {
|
||||
// list.value = list.value.concat(response.value?.data.list);
|
||||
// }
|
||||
// } else {
|
||||
// disabled.value = true;
|
||||
// }
|
||||
// if (response.value.data.count == 0) {
|
||||
// resultText.value = '暂无相关结果'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: route.params.userId,
|
||||
keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: userInfo } = await useServerRequest<ApiResult<User>>(`/system/user/getByUserId/${getIdBySpm(5)}`)
|
||||
console.log(userInfo.value)
|
||||
// if (response.value?.data) {
|
||||
// assignFields(response.value.data)
|
||||
// if (form.permission === 1) {
|
||||
// console.log('登录可见')
|
||||
// return;
|
||||
// }
|
||||
// if (form.permission === 2) {
|
||||
// console.log('需要密码')
|
||||
// showPassword.value = true;
|
||||
// return;
|
||||
// }
|
||||
// form.parentPath = getSpmUrl(`/category`, form, form.articleId);
|
||||
// console.log(form.parentPath)
|
||||
// }
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.title} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<!-- <div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">-->
|
||||
<!-- <div class="flex items-center text-xl cursor-pointer max-w-md line-clamp-2"><el-icon-->
|
||||
<!-- v-if="item.permission > 0"><Lock class="text-gray-400 pr-1"/></el-icon>{{ item.title }}</div>-->
|
||||
<!-- </div>-->
|
||||
<div class="line-clamp-2 font-semibold text-gray-700 dark:text-white text-base gap-1.5 text-xl">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.logo" :size="20" @click.stop="openSpmUrl(`/ask/${item.userId}`,item,item.userId)"/>
|
||||
<span>{{ item.tenantName }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
<el-icon v-if="item.permission > 0" :size="24"><Lock class="text-gray-400 px-1"/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {Lock} from '@element-plus/icons-vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if (!props.disabled) {
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="社区" desc="分享研发成果 交流技术经验" />
|
||||
<el-tabs v-model="activeName" class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative pb-2" @tab-click="handleClick">
|
||||
<el-tab-pane label="轻松一刻" name="happy"></el-tab-pane>
|
||||
<el-tab-pane label="企业官网" name="website"></el-tab-pane>
|
||||
<el-tab-pane label="企业商城" name="weShop"></el-tab-pane>
|
||||
<el-tab-pane label="内容管理" name="cms"></el-tab-pane>
|
||||
<el-tab-pane label="点餐外卖" name="food"></el-tab-pane>
|
||||
<el-tab-pane label="派单系统" name="task"></el-tab-pane>
|
||||
<el-tab-pane label="办公OA" name="oa"></el-tab-pane>
|
||||
<el-tab-pane label="问答" name="ask"></el-tab-pane>
|
||||
<el-tab-pane label="分享" name="share"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
tenantId: getIdBySpm(1),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `社区 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
193
pages/case/[id].vue
Normal file
193
pages/case/[id].vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> {{ page.title }} </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-space class="flex items-center">
|
||||
<el-select v-model="value" clearable placeholder="筛选" style="width: 200px">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-model="where.keywords" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="reload"/>
|
||||
</el-space>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" class="left">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer" @click="navigateTo(`/show/${item.articleId}.html`)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="cover"
|
||||
:lazy="true" class="w-full md:h-[166px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3 text-sm">
|
||||
<el-space class="flex items-end">
|
||||
<div class="text-gray-400 gap-1 flex items-center"><el-icon><View /></el-icon><span>{{ getViews(item) }}</span></div>
|
||||
</el-space>
|
||||
<div class="text-gray-400">
|
||||
<el-avatar v-if="item.avatar" size="small" :src="`${item.avatar}`" />
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import { ArrowLeft,View, Search } from '@element-plus/icons-vue'
|
||||
import { ElNotification as notify } from 'element-plus'
|
||||
import {useConfigInfo, useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import { h } from 'vue'
|
||||
import dayjs from "dayjs";
|
||||
import {detail, getNavIdByParamsId, navTo, paramsId} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navId = ref();
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
const activeName = ref('2839');
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data;
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(navigation => {
|
||||
category.value = navigation;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = page.value.navigationId;
|
||||
}else {
|
||||
where.categoryId = page.value.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response.count;
|
||||
list.value = response.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
const value = ref('')
|
||||
const options = [
|
||||
{
|
||||
value: 'Option1',
|
||||
label: 'Option1',
|
||||
},
|
||||
{
|
||||
value: 'Option2',
|
||||
label: 'Option2',
|
||||
},
|
||||
{
|
||||
value: 'Option3',
|
||||
label: 'Option3',
|
||||
},
|
||||
{
|
||||
value: 'Option4',
|
||||
label: 'Option4',
|
||||
},
|
||||
{
|
||||
value: 'Option5',
|
||||
label: 'Option5',
|
||||
},
|
||||
]
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.right .content img{
|
||||
width: auto !important;
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo" />
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<el-button @click.stop="loginByToken(item)">
|
||||
控制台
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginByToken, openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: Company[];
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto md:p-0 px-4">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span v-if="layout.title">{{ layout.title }}</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
<div class="flex justify-between w-full items-center mt-4">
|
||||
<el-space>
|
||||
<div class="w-[500px]">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button
|
||||
size="large"
|
||||
@click="openSpmUrl(`/passport/login`)"
|
||||
>
|
||||
创建
|
||||
</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const config = useConfigInfo();
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Company[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/pageAll',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,91 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :title="`${form?.title}`" :desc="`${form?.comments}`" />
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {ArticleCategory} from "~/api/cms/category/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<ArticleCategory>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,limit: 8,categoryId: getIdBySpm(4), keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(4)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
console.log(form);
|
||||
}
|
||||
// 页面布局
|
||||
// if(form.value?.layout){
|
||||
// layout.value = JSON.parse(form.value?.layout)
|
||||
// }
|
||||
|
||||
useHead({
|
||||
title: `文章列表 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,104 +0,0 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="cover" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">
|
||||
<span class="flex-1 text-xl cursor-pointer max-h-[57px] overflow-hidden">{{ item.title }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="p-4 text-center text-gray-500">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const disabled = ref(false);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const load = () => {
|
||||
if (!props.disabled) {
|
||||
page.value++;
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, limit: 8, keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if (response.value.data.count == 0) {
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reload();
|
||||
</script>
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span>WEBSOFT</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
|
||||
<el-space class="mt-4">
|
||||
<el-button
|
||||
:icon="ElIconView"
|
||||
size="large"
|
||||
v-if="layout.demoUrl"
|
||||
@click="openSpmUrl(layout.demoUrl)"
|
||||
>
|
||||
演示地址
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="layout.buyUrl"
|
||||
:icon="ElIconBottom"
|
||||
size="large"
|
||||
@click="openSpmUrl(layout.buyUrl)"
|
||||
>
|
||||
下载模版
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="ElIconMemo"
|
||||
size="large"
|
||||
v-if="layout.docUrl"
|
||||
@click="openSpmUrl(layout.docUrl)"
|
||||
>
|
||||
帮助文档
|
||||
</el-button>
|
||||
</el-space>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
const config = useConfigInfo();
|
||||
|
||||
</script>
|
||||
@@ -1,87 +0,0 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center pb-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`"
|
||||
fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
<div class="text-red-500">¥{{ item.price }}</div>
|
||||
</div>
|
||||
<div v-if="item.price && item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl('/item', item,item.goodsId,true)">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button type="primary" size="large" v-if="item.price && item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买
|
||||
</el-button>
|
||||
<el-button v-else class="w-full" size="large" :icon="ElIconShoppingCart">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
limit: 8
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
79
pages/components/Carousel.vue
Normal file
79
pages/components/Carousel.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="w-full bg-white mb-3 hidden-sm-and-down" v-if="ad">
|
||||
<el-carousel :height="'750px'">
|
||||
<el-carousel-item v-for="(item,index) in ad?.imageList" :key="index">
|
||||
<div class="item relative flex justify-center items-center">
|
||||
<el-image :src="item.url" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
<!-- 移动端 -->
|
||||
<div class="sm:hidden w-full bg-white mt-[48px] mb-3 hidden-sm-and-up" v-if="ad">
|
||||
<el-carousel indicator-position="none" height="150">
|
||||
<el-carousel-item v-for="(item,index) in ad?.imageList" :key="index">
|
||||
<el-image :src="item.url" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {CmsAd} from "~/api/cms/cmsAd/model";
|
||||
import {pageCmsAd} from "~/api/cms/cmsAd";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config?: any;
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const ad = ref<CmsAd>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
pageCmsAd({
|
||||
type: 1,
|
||||
lang: getLang()
|
||||
}).then(res => {
|
||||
console.log(res)
|
||||
if(res){
|
||||
ad.value = res.list[0];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.config,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hidden-sm-and-up .el-carousel{
|
||||
height: 160px;
|
||||
}
|
||||
</style>
|
||||
@@ -19,8 +19,8 @@
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div class="py-24 sm:py-32 md:py-20 relative dark:bg-gradient-to-b from-gray-950 to-gray-900 md:pb-36">
|
||||
<svg data-v-835f5c7a="" width="100%" height="869" viewBox="0 0 1440 869" fill="none"
|
||||
<div class="mt-10 py-24 sm:py-32 md:py-20 relative dark:bg-gradient-to-b from-gray-950 to-gray-900 md:pb-36">
|
||||
<svg data-v-835f5c7a="" width="100%" height="700" viewBox="0 0 1440 869" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg" class="absolute top-0 inset-x-0 w-full hidden lg:block">
|
||||
<g clip-path="url(#clip0_184_92145)" data-v-835f5c7a="">
|
||||
<path
|
||||
@@ -85,23 +85,45 @@
|
||||
<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl gap-16 sm:gap-y-24 flex flex-col ">
|
||||
<div class="text-center relative z-[1]">
|
||||
<div class="mb-8 cursor-pointer">
|
||||
<el-tag type="warning" round @click="openSpmUrl(`/detail`, {},730,true)">v3.0 版本发布
|
||||
<el-tag type="warning" round @click="navTo(`/detail`)">v3.0 版本发布
|
||||
</el-tag>
|
||||
</div>
|
||||
<h1 class="text-5xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-7xl">
|
||||
<span>构建现代Web应用</span><br/>
|
||||
<span class="text-primary block lg:inline-block text-green-500">Vue 框架</span>
|
||||
<!-- <span class="text-primary block lg:inline-block text-green-500">Vue 框架</span>-->
|
||||
</h1>
|
||||
<div class="mt-6 text-lg tracking-tight text-gray-600 dark:text-gray-300"> WEBSOFT是一个基于Vue和Nuxt构建的web框架,使web开发更直观而强大<br>
|
||||
<div class="mt-6 text-lg tracking-tight text-gray-400 dark:text-gray-300"> 基于Java SpringBoot构建的SaaS软件平台、支持Vue、React前端框架<br>
|
||||
自信地创建高性能和生产级的全栈web应用程序和网站
|
||||
</div>
|
||||
<div class="mt-10 flex flex-wrap gap-x-6 gap-y-3 justify-center">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex items-center">
|
||||
<el-button size="large" type="primary" :icon="ElIconArrowRight" @click="openSpmUrl(`/passport/login`)">立即开始</el-button>
|
||||
<!-- <el-button size="large" type="primary" v-else :icon="ElIconArrowRight" @click="loginAdminByToken">进入控制台</el-button>-->
|
||||
<el-button size="large" type="success" :icon="ElIconDownload" @click="openSpmUrl(`http://git.gxwebsoft.com/free`, {},0,true)">源码下载</el-button>
|
||||
<el-button size="large" type="danger" @click="openSpmUrl(`https://website.websoft.top`, {},123)">商业版演示</el-button>
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- <el-button size="large" type="danger" :icon="ElIconUser" @click="openUrl(`https://site.websoft.top`)">立即登录</el-button>-->
|
||||
<!-- <el-button size="large" type="primary" :icon="ElIconArrowRight" @click="openUrl(`https://site.websoft.top/register`)">免费注册</el-button>-->
|
||||
|
||||
<div class="flex flex-col justify-center cursor-pointer text-white items-center bg-gray-400 hover:bg-gray-500 w-[100px] h-[100px] rounded-full">
|
||||
<img alt="Windows" src="data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E %3Cg fill='%23FFF' fill-rule='evenodd'%3E %3Cpath d='M6.316 21.826v9.025l10.99 1.539V21.826zm12.045 0v10.712l14.463 2.026V21.826zM6.316 11.767v9.004h10.99V10.205zM32.824 8l-14.463 2.055v10.716h14.463z' opacity='1'/%3E %3Cpath fill='none' d='M0 0h40v40H0z'/%3E %3C/g%3E%3C/svg%3E" />
|
||||
<span class="text-sm">Windows</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center cursor-pointer text-white items-center bg-gray-400 hover:bg-green-600 w-[100px] h-[100px] rounded-full" @click="openUrl(`https://oss.wsdns.cn/download/%E7%BD%91%E5%AE%BF%E8%BD%AF%E4%BB%B6%28OA%29-3.0.0-arm64.dmg`)">
|
||||
<img alt="macOs" src="data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='40' height='40' viewBox='0 0 40 40'%3E %3Cdefs%3E %3Cpath id='47f1323c-993c-4d45-aab1-bebb1206c2bb-a' d='M.001.592h26.926v24.039H.001z'/%3E %3C/defs%3E %3Cg fill='none' fill-rule='evenodd'%3E %3Cpath d='M0 0h40v40H0z'/%3E %3Cg opacity='1' transform='translate(6.316 2.737)'%3E %3Cg transform='translate(0 7.045)'%3E %3Cmask id='47f1323c-993c-4d45-aab1-bebb1206c2bb-b' fill='%23fff'%3E %3Cuse xlink:href='%2347f1323c-993c-4d45-aab1-bebb1206c2bb-a'/%3E %3C/mask%3E %3Cpath fill='%23FFF' d='M20.005.608c-2.722-.193-5.031 1.456-6.32 1.456-1.308 0-3.324-1.415-5.461-1.376-2.806.04-5.393 1.564-6.84 3.972C-1.531 9.506.64 16.687 3.479 20.62c1.389 1.922 3.045 4.088 5.221 4.009 2.094-.08 2.887-1.298 5.418-1.298 2.53 0 3.243 1.298 5.458 1.259 2.254-.04 3.683-1.962 5.061-3.894 1.596-2.23 2.251-4.39 2.29-4.505-.05-.018-4.394-1.614-4.437-6.408-.04-4.008 3.415-5.935 3.573-6.028C24.102.989 21.073.684 20.005.608' mask='url(%2347f1323c-993c-4d45-aab1-bebb1206c2bb-b)'/%3E %3C/g%3E %3Cpath fill='%23FFF' d='M18.325 5.057C19.482 3.718 20.26 1.854 20.045 0c-1.663.063-3.676 1.062-4.87 2.4-1.07 1.185-2.008 3.083-1.753 4.899 1.855.137 3.748-.902 4.903-2.242'/%3E %3C/g%3E %3C/g%3E%3C/svg%3E" />
|
||||
<span class="text-sm">macOs</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center cursor-pointer text-white items-center bg-gray-400 hover:bg-gray-500 w-[100px] h-[100px] rounded-full">
|
||||
<img alt="iOS" src="data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='40' height='40' viewBox='0 0 40 40'%3E %3Cdefs%3E %3Cpath id='f46e5a6d-7d66-4f5f-8628-7f4ceb95844a-a' d='M0 0h27v24.104H0z'/%3E %3C/defs%3E %3Cg fill='none' fill-rule='evenodd' transform='translate(7 4)'%3E %3Cg transform='translate(0 7.658)'%3E %3Cmask id='f46e5a6d-7d66-4f5f-8628-7f4ceb95844a-b' fill='%23fff'%3E %3Cuse xlink:href='%23f46e5a6d-7d66-4f5f-8628-7f4ceb95844a-a'/%3E %3C/mask%3E %3Cpath fill='%23FFF' d='M20.06.016c-2.73-.194-5.046 1.46-6.339 1.46-1.311 0-3.332-1.418-5.475-1.38-2.814.04-5.408 1.57-6.86 3.983-2.922 4.86-.745 12.06 2.102 16.004 1.392 1.927 3.053 4.098 5.235 4.02 2.1-.08 2.895-1.302 5.433-1.302 2.536 0 3.252 1.301 5.473 1.262 2.26-.04 3.692-1.967 5.075-3.904 1.6-2.237 2.257-4.403 2.296-4.517-.05-.018-4.406-1.619-4.45-6.426-.039-4.019 3.425-5.951 3.583-6.044C24.167.398 21.129.092 20.059.016z' mask='url(%23f46e5a6d-7d66-4f5f-8628-7f4ceb95844a-b)'/%3E %3C/g%3E %3Cpath fill='%23FFF' d='M18.375 5.07c1.16-1.342 1.94-3.21 1.725-5.07-1.669.063-3.687 1.065-4.884 2.406-1.072 1.188-2.013 3.091-1.757 4.912 1.86.138 3.758-.904 4.916-2.247z'/%3E %3C/g%3E%3C/svg%3E">
|
||||
<span class="text-sm">iOS</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center cursor-pointer text-white items-center bg-gray-400 hover:bg-gray-500 w-[100px] h-[100px] rounded-full">
|
||||
<img alt="Android" src="data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='40' height='40' viewBox='0 0 40 40'%3E %3Cdefs%3E %3Cpath id='a688108f-309f-4e18-a0dc-ed026741fa12-a' d='M0 0h33.684v18.947H0z'/%3E %3C/defs%3E %3Cg fill='none' fill-rule='evenodd'%3E %3Cpath d='M0 0h40v40H0z'/%3E %3Cg opacity='1' transform='translate(3.158 11.158)'%3E %3Cmask id='a688108f-309f-4e18-a0dc-ed026741fa12-b' fill='%23fff'%3E %3Cuse xlink:href='%23a688108f-309f-4e18-a0dc-ed026741fa12-a'/%3E %3C/mask%3E %3Cpath fill='%23FFF' d='M24.594 14.156a1.402 1.402 0 1 1-.003-2.803 1.402 1.402 0 0 1 .003 2.803m-15.504 0a1.402 1.402 0 1 1-.002-2.803 1.402 1.402 0 0 1 .002 2.803M25.097 5.72L27.9.874a.584.584 0 0 0-1.01-.583L24.05 5.2c-2.17-.989-4.608-1.54-7.21-1.54-2.6 0-5.038.552-7.209 1.54L6.794.291a.584.584 0 0 0-1.01.582l2.803 4.848C3.774 8.335.482 13.2 0 18.947h33.684C33.202 13.2 29.91 8.335 25.097 5.721' mask='url(%23a688108f-309f-4e18-a0dc-ed026741fa12-b)'/%3E %3C/g%3E %3C/g%3E%3C/svg%3E">
|
||||
<span class="text-sm">Android</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- <el-button size="large" :icon="ElIconDownload" @click="openUrl(`https://file.gxwebsoft.com/download/website-3.0.1-arm64.dmg`)">macOs</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,7 +134,8 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useToken} from "~/composables/configState";
|
||||
import {loginAdminByToken, openSpmUrl} from "~/utils/common";
|
||||
import {loginAdminByToken, navTo, openUrl} from "~/utils/common";
|
||||
import {navigateTo} from "#imports";
|
||||
const token = useToken();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
插件市场
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
安装插件,几秒钟内即可启动并运行
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
<div class="text-red-500">¥{{ item.price }}</div>
|
||||
</div>
|
||||
<div v-if="item.price && item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl(`/item`, item,item.productId,true)">查看详情</el-button>
|
||||
<el-button type="primary" size="large" v-if="item.price && item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买
|
||||
</el-button>
|
||||
<el-button v-else class="w-full" size="large" :icon="ElIconShoppingCart">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Plug} from "~/api/system/plug/model";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
type?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
limit: 8,
|
||||
type: props.type,
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-12">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.productId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="cover" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.title }}
|
||||
<el-tag v-if="item.tag == '1'" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
<el-tag v-if="item.tag == '2'" size="small" type="success" class="text-white ml-2" effect="dark">开源</el-tag>
|
||||
<el-tag v-if="item.tag == '3'" size="small" type="danger" class="text-white ml-2" effect="dark">付费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-3">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<div v-if="item.price && item.price > 0" class="text-red-500 text-xl">¥{{ item.price }} </div>
|
||||
<el-button v-if="item.price && item.price > 0" @click.stop="openSpmUrl(`/product/create`,item,item.productId,true)">立即开通
|
||||
</el-button>
|
||||
<el-button v-else>下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
type?: number;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
type: props.type,
|
||||
limit: 8,
|
||||
status: 0
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
226
pages/detail/[id].vue
Normal file
226
pages/detail/[id].vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="font-600 mr-3"> 文章详情 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="flex items-center">
|
||||
<el-input v-model="where.keywords" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="reload"/>
|
||||
</div>
|
||||
</template>
|
||||
<el-card shadow="hover" class=" my-5">
|
||||
|
||||
<!-- 新闻详细 -->
|
||||
<div class="news_detail bg-white">
|
||||
<h1 class="pt-5 text-2xl">{{ form.title }}</h1>
|
||||
<div class="clearfix">
|
||||
<h3 class=" text-gray-400">
|
||||
{{ $t('createTime') }}:<span>{{ dayjs(form.createTime).format('YYYY-MM-DD') }}</span>
|
||||
{{ $t('author') }}:<span>{{ form.nickname }}</span>
|
||||
{{ $t('click') }}:<span>{{ getViews(form) }}</span>
|
||||
</h3>
|
||||
<div class="share">
|
||||
<!-- Baidu Button BEGIN -->
|
||||
<div class="bdsharebuttonbox">
|
||||
<a href="#" class="bds_more" data-cmd="more"></a>
|
||||
<a href="#" class="bds_qzone" data-cmd="qzone"></a>
|
||||
<a href="#" class="bds_tsina" data-cmd="tsina"></a>
|
||||
<a href="#" class="bds_tqq" data-cmd="tqq"></a>
|
||||
<a href="#" class="bds_renren" data-cmd="renren"></a>
|
||||
<a href="#" class="bds_weixin" data-cmd="weixin"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 内容组件 -->
|
||||
<Content class="content" :data="form.content" />
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle :articleId="articleId" />
|
||||
</div>
|
||||
|
||||
<!-- 最近浏览 -->
|
||||
<CmsArticleRecently :data="form" type="article" />
|
||||
<!-- 相关产品和相关新闻 -->
|
||||
<div class="relate_list">
|
||||
<CmsProductRelated :data="form" />
|
||||
<CmsArticleRelated :data="form" />
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
</el-page-header>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {getNavIdByParamsId, getViews, locationUrl, navTo, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import dayjs from "dayjs";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {
|
||||
getCmsArticle
|
||||
} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import CmsArticleRecently from "~/components/CmsRecently.vue";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
import Content from "~/components/Content.vue";
|
||||
import CmsArticleList from "~/components/CmsArticleList.vue";
|
||||
import PageBanner from "~/pages/item/components/PageBanner.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const layout = ref<Layout>({});
|
||||
const articleId = ref();
|
||||
const i18n = useI18n();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const showPassword = ref<boolean>();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 标签
|
||||
tags: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户ID
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
// window.history.back();
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(articleId.value).then(data => {
|
||||
assignFields(data)
|
||||
layout.value.banner = data?.banner;
|
||||
// 二级栏目分类
|
||||
if (data.parentId) {
|
||||
listCmsNavigation({parentId: data.parentId}).then(list => {
|
||||
category.value = list;
|
||||
})
|
||||
}
|
||||
|
||||
if(form.permission === 1){
|
||||
console.log('登录可见')
|
||||
return;
|
||||
}
|
||||
if(form.permission === 2){
|
||||
console.log('需要密码')
|
||||
showPassword.value = true;
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form?.comments,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
articleId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,64 +0,0 @@
|
||||
<template>
|
||||
<el-affix class="lg:p-0 p-3" v-if="form" :offset="offset" @change="onAffix">
|
||||
<el-button type="text" color="gray">
|
||||
<div class="text-xl text-gray-600 ">你可能还喜欢</div>
|
||||
<el-icon class="el-icon--right text-gray-600" color="#999999">
|
||||
<ArrowRightBold/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer mt-3"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">
|
||||
<span class="flex-1 text-xl cursor-pointer max-h-[57px] overflow-hidden">{{ item.title }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</el-affix>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowRightBold } from '@element-plus/icons-vue'
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
form?: Article;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const list = ref<Article[]>([]);
|
||||
const isAffix = ref<any>();
|
||||
const offset = ref(70)
|
||||
|
||||
const onAffix = (e: any) => {
|
||||
isAffix.value = e;
|
||||
}
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
params: {
|
||||
categoryId: getIdBySpm(4),
|
||||
limit: 3
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
list.value = response.value.data.list;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl">
|
||||
<span v-if="form.title">{{ form.title }}</span>
|
||||
</h1>
|
||||
<el-space class="mt-4 text-gray-500 dark:text-gray-400">
|
||||
<el-avatar size="small" :src="form.logo" />
|
||||
<span class="cursor-pointer text-gray-500" @click="openSpmUrl(`/ask/${form.userId}`)">{{ form.tenantName }}</span> · {{ dayjs(form.createTime).format('YYYY-MM-DD hh:mm') }}
|
||||
</el-space>
|
||||
|
||||
<!-- <el-space class="mt-4">-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconView"-->
|
||||
<!-- size="large"-->
|
||||
<!-- v-if="form.demoUrl"-->
|
||||
<!-- @click="openSpmUrl(form.demoUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 演示地址-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- v-if="form.buyUrl"-->
|
||||
<!-- :icon="ElIconBottom"-->
|
||||
<!-- size="large"-->
|
||||
<!-- @click="openSpmUrl(form.buyUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 下载模版-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconMemo"-->
|
||||
<!-- size="large"-->
|
||||
<!-- v-if="form.docUrl"-->
|
||||
<!-- @click="openSpmUrl(form.docUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 帮助文档-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-space>-->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
@@ -1,232 +0,0 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<PageBanner :form="form"/>
|
||||
<div class="page-main md:w-screen-xl m-auto md:px-3" ref="container">
|
||||
<el-row :gutter="24">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="18" class="min-w-xs">
|
||||
<template v-if="form.permission === 0 || showContent">
|
||||
<div class="p-6 leading-7 bg-white md:rounded-lg text-lg line-height-loose text-gray-600 content">
|
||||
<p v-html="form?.content"></p>
|
||||
<template v-for="(item,index) in form.fileList" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="form.fileList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.permission === 1">
|
||||
<el-result
|
||||
v-if="!showContent && form.permission === 1"
|
||||
icon="warning"
|
||||
title="无访问权限"
|
||||
>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-if="form.permission === 2">
|
||||
<el-result
|
||||
v-if="!showContent && form.permission === 2"
|
||||
icon="warning"
|
||||
title="请输入访问密码"
|
||||
>
|
||||
<template #extra>
|
||||
<el-space>
|
||||
<el-input type="password" v-model="form.password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
|
||||
<el-button type="primary" @click="checkPassword">确定</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<div class="page md:gap-xl gap-xs md:mt-4 m-auto md:p-0 p-4 flex justify-between">
|
||||
<div v-if="previousArticle" class="bg-white text-gray-600 hover:shadow hover:text-gray-800 hover:font-bold rounded-lg p-4 cursor-pointer w-[50%]"
|
||||
@click="openSpmUrl(`/detail`,previousArticle,previousArticle?.articleId)">
|
||||
<span>上一篇:{{ previousArticle?.title }}</span>
|
||||
</div>
|
||||
<div v-if="nextArticle" class="bg-white text-gray-600 hover:shadow hover:text-gray-800 hover:font-bold rounded-lg p-4 cursor-pointer w-[50%]"
|
||||
@click="openSpmUrl(`/detail`,nextArticle,nextArticle?.articleId)">
|
||||
<span>下一篇:{{ nextArticle?.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- <el-col :xs="24" :sm="6" :md="4" :lg="3" :xl="6">-->
|
||||
<el-col :xs="24" :sm="6" :md="6" :lg="6">
|
||||
<!-- 你可能感兴趣 -->
|
||||
<LikeArticle v-if="form" :form="form" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import LikeArticle from "./components/LikeArticle.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const previousArticle = ref<Article>();
|
||||
const nextArticle = ref<Article>();
|
||||
const showContent = ref<boolean>();
|
||||
const showPassword = ref<boolean>();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Article>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
parentName: undefined,
|
||||
parentPath: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-article/checkArticlePassword', {
|
||||
query: {
|
||||
password: form?.password,
|
||||
password2: form.password2
|
||||
}
|
||||
})
|
||||
if(response.value?.code === 0){
|
||||
showPassword.value = false;
|
||||
showContent.value = true;
|
||||
}else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
assignFields(nav.value.data)
|
||||
if(form.permission === 1){
|
||||
console.log('登录可见')
|
||||
return;
|
||||
}
|
||||
if(form.permission === 2){
|
||||
console.log('需要密码')
|
||||
showPassword.value = true;
|
||||
return;
|
||||
}
|
||||
form.parentPath = getSpmUrl(`/category`,form,form.articleId);
|
||||
console.log(form.parentPath)
|
||||
}
|
||||
|
||||
// 上一篇
|
||||
const {data: previous} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/getPrevious/' + getIdBySpm(5))
|
||||
if (previous.value?.data) {
|
||||
previousArticle.value = previous.value.data
|
||||
}
|
||||
|
||||
// 下一篇
|
||||
const {data: next} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/getNext/' + getIdBySpm(5))
|
||||
if (next.value?.data) {
|
||||
nextArticle.value = next.value.data
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.title} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="入驻成为开发者" @done="onDone" />
|
||||
<div v-if="showContent" class="md:w-screen-xl md:px-0 px-4 m-auto relative sm:flex bg-white rounded-lg py-4 ">
|
||||
1231232
|
||||
<el-tabs class="px-4 bg-white" v-if="form">
|
||||
<el-tab-pane label="个人开发者认证">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full 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-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-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full 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-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>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import type {ShopMerchant} from "~/api/shop/shopMerchant/model";
|
||||
|
||||
const route = useRoute();
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const showContent = ref<boolean>(false);
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onDone = (index: boolean) => {
|
||||
showContent.value = index;
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: 0,
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchant>>('/shop/shop-merchant-apply/getByUserId')
|
||||
if (response.value?.data) {
|
||||
if (response.value.data.status == 2) {
|
||||
loginDeveloperCenterByToken();
|
||||
}
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `开发者中心 - ${website.value?.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,149 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" />
|
||||
<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">
|
||||
<div class="flash ml-8 bg-white rounded-lg px-7 py-4 w-full">
|
||||
<el-tabs class="flash bg-white 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: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-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-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full 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-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>
|
||||
</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";
|
||||
|
||||
// 配置信息
|
||||
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
|
||||
});
|
||||
|
||||
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>
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="文档" />
|
||||
<el-tabs v-model="activeName" class="md:w-screen-xl m-auto relative sm:flex pb-2" @tab-click="handleClick">
|
||||
<el-tab-pane label="企业官网" name="website"></el-tab-pane>
|
||||
<el-tab-pane label="企业商城" name="weShop"></el-tab-pane>
|
||||
<el-tab-pane label="内容管理" name="cms"></el-tab-pane>
|
||||
<el-tab-pane label="点餐外卖" name="food"></el-tab-pane>
|
||||
<el-tab-pane label="派单系统" name="task"></el-tab-pane>
|
||||
<el-tab-pane label="办公OA" name="oa"></el-tab-pane>
|
||||
<el-tab-pane label="常见问题" name="ask"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: 0,
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
console.log(nav.value?.data)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
console.log(form.value,'form...')
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `文档 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image :src="`${item.image}`" fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl(`/item`, item, item.productId,true)">在线体验</el-button>
|
||||
<el-button type="primary" size="large" class="w-full" :icon="ElIconShoppingCart">下载源码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: CmsProduct[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`演示中心`"
|
||||
:desc="`拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!`"/>
|
||||
<!-- <div class="text-3xl py-5">{{ count }}</div>-->
|
||||
<!-- <el-button @click="increment">点击</el-button>-->
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch"/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<CmsProduct[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if (!disabled.value) {
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
const count = useToken()
|
||||
|
||||
function increment() {
|
||||
count.value = uuidv4()
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsProduct>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
price: 0,
|
||||
keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if (response.value.data.count == 0) {
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {query: {path: route.path}})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `产品 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
466
pages/index.vue
466
pages/index.vue
@@ -1,68 +1,442 @@
|
||||
|
||||
<template>
|
||||
|
||||
<Flash/>
|
||||
|
||||
|
||||
<CompanyList :param="{official: true,recommend: true,limit: 4}" :fit="`cover`" />
|
||||
|
||||
<!-- <ProductList :param="{type:0, official: true,limit: 4}" :fit="`cover`" title="产品服务" comments="拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!"/>-->
|
||||
|
||||
<!-- <ArticleList title="开发者社区" comments="分享研发成果 交流技术经验"/>-->
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import type {FormRules, FormInstance} from 'element-plus'
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import type {CmsOrder} from "~/api/cms/cmsOrder/model";
|
||||
import type {CmsLink} from "~/api/cms/cmsLink/model";
|
||||
import {pageCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {pageCmsLink} from "~/api/cms/cmsLink";
|
||||
import {addCmsOrder} from "~/api/cms/cmsOrder";
|
||||
import {getCaptcha} from "~/api/passport/login";
|
||||
import Flash from './components/Flash.vue';
|
||||
import ArticleList from './components/ArticleList.vue';
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = ref<any>();
|
||||
const token = useToken();
|
||||
const form = useForm();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const i18n = useI18n();
|
||||
const config = useConfigInfo();
|
||||
const productList = ref<CmsNavigation[]>([]);
|
||||
const caseList = ref<CmsArticle[]>([]);
|
||||
const topNews = ref<CmsArticle[]>([]);
|
||||
const topNewsImage = ref<string>('');
|
||||
const hotNews = ref<CmsArticle[]>([]);
|
||||
const links = ref<CmsLink[]>([]);
|
||||
const formRef = ref<FormInstance>()
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
const text = ref<string>('');
|
||||
|
||||
const scrollTop = ref(0)
|
||||
window.onscroll = e => {
|
||||
scrollTop.value = window.document.documentElement.scrollTop
|
||||
}
|
||||
|
||||
|
||||
const {form, assignFields, resetFields} = useFormData<CmsOrder>({
|
||||
// 订单号
|
||||
orderId: undefined,
|
||||
// 模型名称
|
||||
model: 'order',
|
||||
// 订单标题
|
||||
title: '-',
|
||||
// 订单编号
|
||||
orderNo: undefined,
|
||||
// 订单类型,0商城 1询价 2留言
|
||||
type: undefined,
|
||||
// 关联项目ID,配合订单类型使用
|
||||
articleId: undefined,
|
||||
// 真实姓名
|
||||
realName: undefined,
|
||||
// 手机号码
|
||||
phone: undefined,
|
||||
// 电子邮箱
|
||||
email: undefined,
|
||||
// 收货地址
|
||||
address: undefined,
|
||||
// 订单内容
|
||||
content: undefined,
|
||||
// 订单总额
|
||||
totalPrice: '0.00',
|
||||
// 实际付款
|
||||
payPrice: '0.00',
|
||||
// 报价询价
|
||||
price: '0.00',
|
||||
// 购买数量
|
||||
totalNum: undefined,
|
||||
// 二维码地址,保存订单号,支付成功后才生成
|
||||
qrcode: undefined,
|
||||
// 下单渠道,0网站 1小程序 2其他
|
||||
channel: undefined,
|
||||
// 过期时间
|
||||
expirationTime: undefined,
|
||||
// 订单是否已结算(0未结算 1已结算)
|
||||
isSettled: undefined,
|
||||
// 用户id
|
||||
userId: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 图像验证码
|
||||
code: '',
|
||||
})
|
||||
const rules = reactive<FormRules<CmsOrder>>({
|
||||
title: [
|
||||
{required: true, message: '请输入产品名称', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
realName: [
|
||||
{required: true, message: '请输入联系人姓名', trigger: 'blur'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入留言内容', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (form.code !== text.value) {
|
||||
await reload();
|
||||
ElMessage.error('验证码不正确!');
|
||||
return false;
|
||||
}
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
addCmsOrder(form).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
resetFields();
|
||||
reload();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
getCaptcha().then(captchaData => {
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
})
|
||||
};
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
// 读取产品系列
|
||||
pageCmsNavigation({
|
||||
limit: 8,
|
||||
parentId: i18n.locale.value == 'en' ? 1073 : 998,
|
||||
lang: i18n.locale.value
|
||||
}).then(data => {
|
||||
if (data) {
|
||||
productList.value = data?.list;
|
||||
}
|
||||
})
|
||||
|
||||
// 未登录状态(是否强制登录)
|
||||
if (!token.value || token.value == '') {
|
||||
// if (config.value.MustLogin) {
|
||||
// navigateTo('/passport/login');
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
// 读取案例
|
||||
pageCmsArticle({
|
||||
limit: 6,
|
||||
status: 0,
|
||||
recommend: 1,
|
||||
parentId: i18n.locale.value == 'en' ? 1074 : 999,
|
||||
lang: i18n.locale.value
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
caseList.value = res?.list;
|
||||
caseMaxScroll = caseList.value.length * 303 - 1200;
|
||||
setCaseScrollTimer()
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `构建现代WEB应用 · WEBSOFT`,
|
||||
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
|
||||
// 新闻头条
|
||||
pageCmsArticle({
|
||||
limit: 1,
|
||||
status: 0,
|
||||
recommend: 1,
|
||||
categoryId: i18n.locale.value == 'en' ? 1080 : 1005,
|
||||
lang: i18n.locale.value
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
topNews.value = res?.list;
|
||||
topNewsImage.value = res?.list[0]?.image;
|
||||
}
|
||||
})
|
||||
|
||||
// 热门推荐
|
||||
pageCmsArticle({
|
||||
limit: 2,
|
||||
status: 0,
|
||||
recommend: 1,
|
||||
categoryId: i18n.locale.value == 'en' ? 1081 : 1006,
|
||||
lang: i18n.locale.value
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
hotNews.value = res?.list;
|
||||
}
|
||||
})
|
||||
|
||||
// 品牌展示
|
||||
pageCmsLink({
|
||||
limit: 50,
|
||||
lang: i18n.locale.value,
|
||||
categoryId: 1067,
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
links.value = res?.list;
|
||||
linksMaxScroll = links.value.length * 148 - 1200;
|
||||
setLinksScroll()
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
setRegionNum()
|
||||
setCountryNum()
|
||||
setOrgNum()
|
||||
setProjectNum()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
const regionNum = ref(0);
|
||||
const countryNum = ref(0);
|
||||
const orgNum = ref(480);
|
||||
const projectNum = ref(970);
|
||||
|
||||
const setRegionNum = () => {
|
||||
if (regionNum.value < 20) {
|
||||
setTimeout(() => {
|
||||
regionNum.value += 1;
|
||||
setRegionNum();
|
||||
}, 70)
|
||||
}
|
||||
}
|
||||
|
||||
const setCountryNum = () => {
|
||||
if (countryNum.value < 15) {
|
||||
setTimeout(() => {
|
||||
countryNum.value += 1;
|
||||
setCountryNum();
|
||||
}, 70)
|
||||
}
|
||||
}
|
||||
|
||||
const setOrgNum = () => {
|
||||
if (orgNum.value < 500) {
|
||||
setTimeout(() => {
|
||||
orgNum.value += 1;
|
||||
setOrgNum();
|
||||
}, 70)
|
||||
}
|
||||
}
|
||||
|
||||
const setProjectNum = () => {
|
||||
if (projectNum.value < 999) {
|
||||
setTimeout(() => {
|
||||
projectNum.value += 1;
|
||||
setProjectNum();
|
||||
}, 70)
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = (e: any) => {
|
||||
currentScrollLeft = e.scrollLeft
|
||||
if (currentScrollLeft === 0) currentScrollLeftEnd = 0
|
||||
// console.log(currentScrollLeft)
|
||||
}
|
||||
|
||||
let currentScrollLeft = 0
|
||||
let currentScrollLeftEnd = 0
|
||||
let currentScroll = 0
|
||||
let scrollNum = 0
|
||||
const scrollbarRef = ref()
|
||||
const scroll = (dir: string) => {
|
||||
const maxScrollLeft = 309 * 5 - 1120 - 20
|
||||
if (dir === 'left') {
|
||||
if (currentScrollLeft === 0) scrollNum = maxScrollLeft
|
||||
else if (currentScrollLeft < 309) {
|
||||
scrollNum = -currentScrollLeft
|
||||
} else scrollNum = -309
|
||||
} else {
|
||||
if (currentScrollLeftEnd === maxScrollLeft) scrollNum = -maxScrollLeft
|
||||
else if (maxScrollLeft - currentScrollLeftEnd < 309 && maxScrollLeft - currentScrollLeftEnd > 0) {
|
||||
scrollNum = maxScrollLeft - currentScrollLeftEnd
|
||||
} else if ((maxScrollLeft + 10) === currentScrollLeft || (maxScrollLeft - 10) === currentScrollLeft) {
|
||||
scrollNum = -maxScrollLeft
|
||||
} else scrollNum = 309
|
||||
}
|
||||
setScroll()
|
||||
}
|
||||
|
||||
const setScroll = () => {
|
||||
if (scrollNum > 0) {
|
||||
if (currentScroll < scrollNum) {
|
||||
setTimeout(() => {
|
||||
currentScroll += 5
|
||||
scrollbarRef.value?.setScrollLeft(currentScrollLeftEnd + currentScroll)
|
||||
setScroll()
|
||||
}, 5)
|
||||
} else {
|
||||
currentScroll = 0
|
||||
currentScrollLeftEnd += scrollNum
|
||||
}
|
||||
} else {
|
||||
if (currentScroll > scrollNum) {
|
||||
setTimeout(() => {
|
||||
currentScroll -= 5
|
||||
scrollbarRef.value?.setScrollLeft(currentScrollLeftEnd + currentScroll)
|
||||
setScroll()
|
||||
}, 5)
|
||||
} else {
|
||||
currentScroll = 0
|
||||
currentScrollLeftEnd += scrollNum
|
||||
}
|
||||
}
|
||||
// console.log(currentScrollLeftEnd, scrollNum)
|
||||
}
|
||||
|
||||
const setScrollTimer = () => {
|
||||
setInterval(() => {
|
||||
scroll('right')
|
||||
}, 2000)
|
||||
}
|
||||
setScrollTimer()
|
||||
|
||||
const linksScrollbarRef = ref()
|
||||
let linksCurrentScroll = 0
|
||||
const onLinksScroll = (e: any) => {
|
||||
linksCurrentScroll = e.scrollLeft
|
||||
}
|
||||
|
||||
let linksMaxScroll = 0
|
||||
const setLinksScroll = () => {
|
||||
setTimeout(() => {
|
||||
if (linksCurrentScroll < linksMaxScroll) {
|
||||
linksCurrentScroll += 1
|
||||
} else {
|
||||
linksCurrentScroll = 0
|
||||
}
|
||||
// console.log(linksCurrentScroll, linksMaxScroll)
|
||||
linksScrollbarRef.value?.setScrollLeft(linksCurrentScroll)
|
||||
setLinksScroll()
|
||||
}, 15)
|
||||
|
||||
}
|
||||
|
||||
const caseScrollbarRef = ref()
|
||||
let caseCurrentScroll = 0
|
||||
const onCaseScroll = (e: any) => {
|
||||
caseCurrentScroll = e.scrollLeft
|
||||
}
|
||||
|
||||
let caseMaxScroll = 0
|
||||
let caseScrollNum = 0
|
||||
let caseCurrentScrollLeftEnd = 0
|
||||
const setCaseScroll = () => {
|
||||
if (caseCurrentScrollLeftEnd === caseMaxScroll) {
|
||||
caseScrollNum = 0
|
||||
caseCurrentScrollLeftEnd = 0
|
||||
} else if (caseMaxScroll - caseCurrentScrollLeftEnd < 303 && caseMaxScroll - caseCurrentScrollLeftEnd > 0) {
|
||||
caseScrollNum = caseMaxScroll - caseCurrentScrollLeftEnd
|
||||
} else if ((caseMaxScroll + 20) === caseCurrentScroll || (caseMaxScroll - 20) === caseCurrentScroll) {
|
||||
scrollNum = 0
|
||||
} else caseScrollNum = 303
|
||||
console.log(caseCurrentScrollLeftEnd, caseMaxScroll, caseScrollNum)
|
||||
setCaseScrollJob()
|
||||
}
|
||||
|
||||
const setCaseScrollJob = () => {
|
||||
if (caseScrollNum > 0) {
|
||||
if (caseCurrentScroll < caseScrollNum) {
|
||||
setTimeout(() => {
|
||||
caseCurrentScroll += 5
|
||||
caseScrollbarRef.value?.setScrollLeft(caseCurrentScrollLeftEnd + caseCurrentScroll)
|
||||
setCaseScrollJob()
|
||||
}, 5)
|
||||
} else {
|
||||
caseCurrentScroll = 0
|
||||
caseCurrentScrollLeftEnd += caseScrollNum
|
||||
}
|
||||
} else {
|
||||
if (caseCurrentScroll > caseScrollNum) {
|
||||
setTimeout(() => {
|
||||
caseCurrentScroll -= 5
|
||||
caseScrollbarRef.value?.setScrollLeft(caseCurrentScrollLeftEnd + caseCurrentScroll)
|
||||
setCaseScrollJob()
|
||||
}, 5)
|
||||
} else {
|
||||
caseCurrentScroll = 0
|
||||
caseCurrentScrollLeftEnd += caseScrollNum
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let caseScrollTimer = null
|
||||
const setCaseScrollTimer = () => {
|
||||
if (caseScrollTimer) clearInterval(caseScrollTimer)
|
||||
caseScrollTimer = setInterval(() => {
|
||||
setCaseScroll()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
changeCaptcha();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
|
||||
//.imgbig{
|
||||
// width: 289px;
|
||||
// height: 200px;
|
||||
// overflow: hidden;
|
||||
//}
|
||||
.product-image {
|
||||
width: 289px;
|
||||
height: 425px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrollbar-flex-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.scrollbar-demo-item {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10px;
|
||||
width: 289px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
278
pages/item/[id].vue
Normal file
278
pages/item/[id].vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<PageBanner :form="page" @done="reload"/>
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card shadow="hover" class="mb-5">
|
||||
<el-descriptions title="参数信息" :column="2" border>
|
||||
<el-descriptions-item :span="2" label="产品名称">{{page.title}}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="租户ID"><span class="text-orange-500">{{form.title}}</span></el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="插件ID"><span class="text-orange-500">{{form.menuId || '-'}}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="控制台"><a class="cursor-pointer" @click="openUrl(`https://${form.domain}`)">{{form.domain}}</a></el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in form.parameters" :key="index" :label="item.name">{{ item.value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template v-if="form.accounts && form.accounts.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="登录账号" :column="1" border>
|
||||
<template v-for="(item,index) in form.accounts" :key="index">
|
||||
<el-descriptions-item :label="item.type" v-if="item.account">
|
||||
还没有账号? <el-button type="text" @click="openSpmUrl(`/passport/regis`)">立即注册</el-button>
|
||||
</el-descriptions-item>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.gits && form.gits.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="代码仓库" :column="1" border>
|
||||
<el-descriptions-item v-for="(item,index) in form.gits" :key="index" :label="item.title">
|
||||
<el-input v-model="item.domain" readonly />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.files && form.files.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="图文详情" />
|
||||
<div v-for="(item,index) in JSON.parse(form.files)" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.content">
|
||||
<p v-html="form.content" class="content"></p>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- 产品评论 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card shadow="hover" class="mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>推荐产品</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <el-space class="flex items-center">-->
|
||||
<!-- <div class="avatar">-->
|
||||
<!-- <el-avatar :size="55" :src="form.image"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="flex flex-col">-->
|
||||
<!-- <span class="font-bold text-lg text-gray-600">{{ form.title }}</span>-->
|
||||
<!-- <span class="text-gray-400 pb-1 line-clamp-2">{{ form.comments }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-space>-->
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Cpu,Download,Star,Coin,Tickets} from '@element-plus/icons-vue'
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useLayout, usePage, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import {getIdBySpm, getNavIdByParamsId, openUrl} from "~/utils/common";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import Comments from './components/Comments.vue';
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {getCmsArticle, pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const i18n = useI18n();
|
||||
const total = ref(0);
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const comments = ref<CompanyComment[]>([]);
|
||||
const commentsTotal = ref(0);
|
||||
const commentsPage = ref(1);
|
||||
const navId = ref();
|
||||
const activeName = ref();
|
||||
const url =
|
||||
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
|
||||
const srcList = ref<any[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 标签
|
||||
tags: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户ID
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const doComments = async (page: any) => {
|
||||
commentsPage.value = page;
|
||||
await reloadComments();
|
||||
}
|
||||
|
||||
|
||||
// 加载评论
|
||||
const reloadComments = async () => {
|
||||
const {data: commentsResponse} = await useServerRequest<ApiResult<PageResult<CompanyComment>>>('/system/company-comment/page', {
|
||||
params: {
|
||||
companyId: getIdBySpm(5),
|
||||
page: commentsPage.value,
|
||||
// status: 1
|
||||
}
|
||||
})
|
||||
if(commentsResponse.value && commentsResponse.value?.data){
|
||||
comments.value = commentsResponse.value?.data?.list
|
||||
commentsTotal.value = commentsResponse.value?.data?.count;
|
||||
}
|
||||
}
|
||||
|
||||
// 读取导航详情
|
||||
const reload = async () => {
|
||||
getCmsArticle(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
assignFields(data)
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
// listCmsNavigation({
|
||||
// parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
// }).then(categoryData => {
|
||||
// category.value = categoryData;
|
||||
// // 加载文章列表
|
||||
// if(data.parentId == 0 && category.value.length > 0){
|
||||
// where.parentId = data.navigationId;
|
||||
// }else {
|
||||
// where.categoryId = data.navigationId;
|
||||
// }
|
||||
// pageCmsArticle(where).then(response => {
|
||||
// if(response){
|
||||
// total.value = response?.count;
|
||||
// list.value = response?.list;
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-card shadow="hover" v-if="comments" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<el-card shadow="hover" v-if="comments" class="mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl flex justify-between">
|
||||
<span>评分和评价</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<div class="banner m-auto relative sm:flex mt-15">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
@@ -20,23 +20,23 @@
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto">
|
||||
<Breadcrumb :data="form"/>
|
||||
<Breadcrumb :data="form" :categoryName="form?.categoryName"/>
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full sm:px-0 px-4">
|
||||
<div class="flex flex-1">
|
||||
<template v-if="form.companyLogo">
|
||||
<el-avatar :src="form.companyLogo" shape="square" :size="180"
|
||||
class="hidden-sm-and-down rounded-avatar shadow-sm hover:shadow mr-4"/>
|
||||
<el-avatar :src="form.companyLogo" shape="square" :size="80"
|
||||
class="hidden-sm-and-up rounded-avatar-xs shadow-sm hover:shadow mr-4"/>
|
||||
<template v-if="form.image">
|
||||
<el-image :src="form.image" shape="square"
|
||||
class="hidden-sm-and-down bg-white w-[128px] h-[128px] cursor-pointer rounded-avatar shadow-sm hover:shadow mr-4"/>
|
||||
<!-- <el-image :src="form.image" shape="square" :size="80"-->
|
||||
<!-- class="hidden-sm-and-up bg-white rounded-avatar-xs shadow-sm hover:shadow mr-4"/>-->
|
||||
</template>
|
||||
<div class="title flex flex-col">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl">
|
||||
<span v-if="form.tenantName">{{ form.tenantName }}</span>
|
||||
<span v-if="form.title">{{ form.title }}</span>
|
||||
</h1>
|
||||
<div class="my-1 text-sm text-gray-500 w-auto sm:max-w-3xl max-w-xs flex-1 dark:text-gray-400">
|
||||
{{ form?.comments || desc }}
|
||||
@@ -45,43 +45,10 @@
|
||||
<!-- {{ form.companyName || 'WebSoft Inc.' }}-->
|
||||
<!-- </a>-->
|
||||
<el-rate v-model="form.rate" disabled />
|
||||
<div class="btn" v-if="form.companyId">
|
||||
<div class="btn">
|
||||
<el-space class="mt-4">
|
||||
<template v-if="form.isBuy">
|
||||
<el-button v-if="form.installed" type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
<el-button v-else type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="form.chargingMethod == 0" type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
<el-button v-else type="warning" @click.stop="openSpmUrl(`/product/create`,form,form.companyId,true)">立即开通
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- <el-button @click.stop="openSpmUrl(`https://${form.domain}`,form,form.companyId,true)">产品控制台</el-button>-->
|
||||
<el-button @click="openSpmUrl(`/ask`,form,form.companyId,true)">帮助文档</el-button>
|
||||
<!-- <template v-for="(item,index) in form.links" :key="index">-->
|
||||
<!-- <div v-if="item.qrcode">-->
|
||||
<!-- <el-popover-->
|
||||
<!-- placement="top-start"-->
|
||||
<!-- :width="200"-->
|
||||
<!-- trigger="hover"-->
|
||||
<!-- >-->
|
||||
<!-- <template #default>-->
|
||||
<!-- <div class=" p-2 flex justify-center">-->
|
||||
<!-- <el-image :src="item.qrcode" :size="160"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <template #reference>-->
|
||||
<!-- <el-button :icon="ElIconFullScreen">{{ item.type }}</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-popover>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-button-->
|
||||
<!-- v-else-->
|
||||
<!-- @click="openSpmUrl(`${item.domain}`,item,item.tenantId,true)"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item.type }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </template>-->
|
||||
<el-button>产品控制台</el-button>
|
||||
<el-button>帮助文档</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,7 +65,7 @@ import {FullScreen} from '@element-plus/icons-vue'
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import {loginAdminByToken, loginDeveloperCenterByToken, openSpmUrl} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
const token = useToken();
|
||||
|
||||
@@ -107,7 +74,7 @@ const props = withDefaults(
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Company;
|
||||
form?: CmsArticle;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<PageBanner :form="form" @done="reload"/>
|
||||
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card shadow="hover" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>应用介绍</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions title="应用参数" :column="2" border>
|
||||
<el-descriptions-item :span="2" label="应用名称">{{form.tenantName}}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="租户ID"><span class="text-orange-500">{{form.tenantId}}</span></el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="插件ID"><span class="text-orange-500">{{form.menuId || '-'}}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="控制台"><a class="cursor-pointer" @click="openUrl(`https://${form.domain}`)">{{form.domain}}</a></el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in form.parameters" :key="index" :label="item.name">{{ item.value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template v-if="form.accounts && form.accounts.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="登录账号" :column="1" border>
|
||||
<template v-for="(item,index) in form.accounts" :key="index">
|
||||
<el-descriptions-item :label="item.type" v-if="item.account">
|
||||
还没有账号? <el-button type="text" @click="openSpmUrl(`/passport/regis`)">立即注册</el-button>
|
||||
</el-descriptions-item>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.gits && form.gits.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="代码仓库" :column="1" border>
|
||||
<el-descriptions-item v-for="(item,index) in form.gits" :key="index" :label="item.title">
|
||||
<el-input v-model="item.domain" readonly />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.content">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="详细说明" />
|
||||
<p v-html="form.content" class="content"></p>
|
||||
</template>
|
||||
<template v-if="form.files && form.files.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="应用截图" />
|
||||
<div v-for="(item,index) in form.files" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item.url"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" v-if="form.files?.length" class="hover:border-green-50 hover:border-2 mb-5">-->
|
||||
<!-- <template #header>-->
|
||||
<!-- <div class="card-header font-bold text-xl">-->
|
||||
<!-- <span>应用截图</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <div class="flex gap-xl">-->
|
||||
<!-- <template v-for="(item,index) in form.files" :key="index" class="text item">-->
|
||||
<!-- <el-image-->
|
||||
<!-- :src="item.url"-->
|
||||
<!-- :zoom-rate="1.2"-->
|
||||
<!-- :max-scale="7"-->
|
||||
<!-- :min-scale="0.2"-->
|
||||
<!-- :preview-src-list="srcList"-->
|
||||
<!-- :initial-index="4"-->
|
||||
<!-- fit="contain"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- 产品评论 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card shadow="hover" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>开发者信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-space class="flex items-center">
|
||||
<div class="avatar">
|
||||
<el-avatar :size="55" :src="form.companyLogo"/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-bold text-lg text-gray-600">{{ form.companyName }}</span>
|
||||
<span class="text-gray-400 pb-1 line-clamp-2">{{ form.comments }}</span>
|
||||
</div>
|
||||
</el-space>
|
||||
<div class="flex flex-col text-gray-500 justify-between leading-7 mt-3">
|
||||
<el-space class="flex items-center"><el-icon><Download /></el-icon>下载:6</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Star /></el-icon>收藏:0</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Coin /></el-icon>赞赏:0</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Tickets /></el-icon>文档:0</el-space>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Cpu,Download,Star,Coin,Tickets} from '@element-plus/icons-vue'
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import {getIdBySpm, openUrl} from "~/utils/common";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import Comments from './components/Comments.vue';
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const comments = ref<CompanyComment[]>([]);
|
||||
const commentsTotal = ref(0);
|
||||
const commentsPage = ref(1);
|
||||
const activeName = ref();
|
||||
const url =
|
||||
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
|
||||
const srcList = ref<any[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Company>({
|
||||
companyId: undefined,
|
||||
type: undefined,
|
||||
shortName: undefined,
|
||||
companyName: undefined,
|
||||
companyType: undefined,
|
||||
companyTypeMultiple: undefined,
|
||||
appType: undefined,
|
||||
companyLogo: undefined,
|
||||
image: undefined,
|
||||
files: undefined,
|
||||
content: undefined,
|
||||
companyCode: undefined,
|
||||
domain: undefined,
|
||||
phone: undefined,
|
||||
tel: undefined,
|
||||
email: undefined,
|
||||
InvoiceHeader: undefined,
|
||||
startTime: undefined,
|
||||
expirationTime: undefined,
|
||||
version: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: undefined,
|
||||
members: undefined,
|
||||
storage: undefined,
|
||||
storageMax: undefined,
|
||||
buys: undefined,
|
||||
clicks: undefined,
|
||||
users: undefined,
|
||||
departments: undefined,
|
||||
industryParent: undefined,
|
||||
industryChild: undefined,
|
||||
country: undefined,
|
||||
province: undefined,
|
||||
city: undefined,
|
||||
region: undefined,
|
||||
address: undefined,
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
businessEntity: undefined,
|
||||
comments: undefined,
|
||||
authentication: undefined,
|
||||
industryId: undefined,
|
||||
industryName: undefined,
|
||||
status: undefined,
|
||||
userId: undefined,
|
||||
official: undefined,
|
||||
price: undefined,
|
||||
planId: undefined,
|
||||
sortNumber: undefined,
|
||||
authoritative: undefined,
|
||||
menuId: undefined,
|
||||
merchantId: undefined,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined,
|
||||
tenantCode: undefined,
|
||||
modules: undefined,
|
||||
requestUrl: undefined,
|
||||
socketUrl: undefined,
|
||||
serverUrl: undefined,
|
||||
modulesUrl: undefined,
|
||||
merchantUrl: undefined,
|
||||
websiteUrl: undefined,
|
||||
mpWeixinCode: undefined,
|
||||
mpAlipayCode: undefined,
|
||||
h5Code: undefined,
|
||||
androidUrl: undefined,
|
||||
iosUrl: undefined,
|
||||
avatar: undefined,
|
||||
nickname: undefined,
|
||||
code: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
collection: undefined,
|
||||
recommend: undefined,
|
||||
title: undefined,
|
||||
parentName: undefined,
|
||||
categoryName: undefined,
|
||||
parameters: undefined,
|
||||
links: undefined,
|
||||
accounts: undefined,
|
||||
gits: undefined,
|
||||
isBuy: undefined,
|
||||
installed: undefined
|
||||
});
|
||||
const doComments = async (page: any) => {
|
||||
commentsPage.value = page;
|
||||
await reloadComments();
|
||||
}
|
||||
|
||||
|
||||
// 加载评论
|
||||
const reloadComments = async () => {
|
||||
const {data: commentsResponse} = await useServerRequest<ApiResult<PageResult<CompanyComment>>>('/system/company-comment/page', {
|
||||
params: {
|
||||
companyId: getIdBySpm(5),
|
||||
page: commentsPage.value,
|
||||
// status: 1
|
||||
}
|
||||
})
|
||||
if(commentsResponse.value && commentsResponse.value?.data){
|
||||
comments.value = commentsResponse.value?.data?.list
|
||||
commentsTotal.value = commentsResponse.value?.data?.count;
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const {data: item} = await useServerRequest<ApiResult<Company>>('/system/company/' + getIdBySpm(5))
|
||||
if (item.value?.data) {
|
||||
assignFields(item.value.data)
|
||||
form.title = item.value?.data?.title;
|
||||
form.parentName = '产品';
|
||||
form.categoryName = '产品详情';
|
||||
if (item.value.data.files) {
|
||||
form.files = JSON.parse(item.value?.data?.files)
|
||||
srcList.value = form.files?.map(d => d.url)
|
||||
}
|
||||
form.comments = item.value?.data?.comments;
|
||||
}
|
||||
|
||||
await reloadComments();
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.tenantName} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
120
pages/links/[id].vue
Normal file
120
pages/links/[id].vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto">
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col :span="5" class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" />
|
||||
</el-col>
|
||||
<el-col :span="19" class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>{{ page.title }}</h2>
|
||||
<Breadcrumb :data="page" :categoryName="$t('articleTitle')" />
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 文章模型 -->
|
||||
<ul class="news_listn clearfix">
|
||||
<CmsArticleList :data="list" />
|
||||
</ul>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {getNavIdByParamsId, paramsId} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import CmsArticleList from "~/components/CmsArticleList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const navId = ref();
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref<number>(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
getCmsNavigation(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
console.log(categoryData)
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
</script>
|
||||
28
pages/m.vue
Normal file
28
pages/m.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import 'assets/css/base.css';
|
||||
import 'assets/css/main.css';
|
||||
import 'assets/css/model.css';
|
||||
import 'assets/m/reset.css';
|
||||
import 'assets/m/ui.css';
|
||||
import {getSiteInfo} from "~/api/layout";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
definePageMeta({
|
||||
layout: 'mobile'
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<meta content="width=device-width,minimum-scale=1.0,maximum-scale=1.0" name="viewport" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta content="telephone=no" name="format-detection" />
|
||||
<div class="mobile-page">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobile-page{
|
||||
}
|
||||
</style>
|
||||
131
pages/m/article/[id].vue
Normal file
131
pages/m/article/[id].vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 文章模型 -->
|
||||
<ul class="news_listn clearfix pt-2">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<li class="clearfix">
|
||||
<a :href="mDetail(item)" class=" fl mr-1">
|
||||
<el-image :src="item.image" :fit="`scale-down`" class="w-[120px] h-[80px]" :alt="item.title" />
|
||||
</a>
|
||||
<div class="list">
|
||||
<h3 class="text-lg"><a :href="mDetail(item)" :title="item.title" class="line-clamp-1 text-left">{{ item.title }}</a></h3>
|
||||
<div v-html="item.comments" class="line-clamp-2"></div>
|
||||
<!-- <div class="date">发布日期:{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>-->
|
||||
<!-- <div class="n-more" ><a :href="detail(item)" >查看更多>></a></div>-->
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<div class="clearboth"></div>
|
||||
</ul>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import dayjs from "dayjs";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import {mDetail, paramsId} from "~/utils/common";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
getCmsNavigation(paramsId()).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
|
||||
</script>
|
||||
129
pages/m/case/[id].vue
Normal file
129
pages/m/case/[id].vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content py-3">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24" v-for="(item,index) in list" key="index">
|
||||
<div class="cast-item">
|
||||
<a :href="mDetail(item)" :title="item.title" class="img"><el-image :fit="`scale-down`" :src="item.image" :alt="item.title" /></a>
|
||||
<div class="flex flex-col leading-7">
|
||||
<a :href="mDetail(item)" class="text-[16px] font-bold" :title="item.title">{{ item.title }}</a>
|
||||
<a :href="mDetail(item)" class="more"><span class="text-gray-400">{{ $t('case.detail') }}>></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import {paramsId} from "~/utils/common";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import CmsArticleList from "~/components/CmsArticleList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await getCmsNavigation(paramsId()).then(data => {
|
||||
page.value = data;
|
||||
// layout.value = data.design;
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(navigation => {
|
||||
category.value = navigation;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = page.value.navigationId;
|
||||
}else {
|
||||
where.categoryId = page.value.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response.count;
|
||||
list.value = response.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.case_listn li{
|
||||
max-width: 200px;
|
||||
}
|
||||
.cast-item{
|
||||
border: 1px solid #eeeeee;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
67
pages/m/components/Carousel.vue
Normal file
67
pages/m/components/Carousel.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<!-- 移动端 -->
|
||||
<div class="sm:hidden w-full bg-white mb-3 " v-if="ad">
|
||||
<el-carousel indicator-position="none">
|
||||
<el-carousel-item v-for="(item,index) in ad?.imageList" :key="index">
|
||||
<el-image :src="item.url" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {CmsAd} from "~/api/cms/cmsAd/model";
|
||||
import {pageCmsAd} from "~/api/cms/cmsAd";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config?: any;
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const ad = ref<CmsAd>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
pageCmsAd({
|
||||
type: 1,
|
||||
lang: getLang()
|
||||
}).then(res => {
|
||||
if(res){
|
||||
ad.value = res.list[0];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.config,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-carousel__container{
|
||||
height: 240px !important;
|
||||
}
|
||||
</style>
|
||||
202
pages/m/detail/[id].vue
Normal file
202
pages/m/detail/[id].vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout"/>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ form.categoryName }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 新闻详细 -->
|
||||
<div class="news_detail">
|
||||
<h1 class="title">{{ form.title }}</h1>
|
||||
<div class="info_title clearfix">
|
||||
<h3 class="text-center text-gray-400">
|
||||
{{ $t('createTime') }}:<span>{{ dayjs(form.createTime).format('YYYY-MM-DD') }}</span>
|
||||
{{ $t('author') }}:<span>{{ form.nickname }}</span>
|
||||
{{ $t('click') }}:<span>{{ form.actualViews }}</span>
|
||||
</h3>
|
||||
<div class="share">
|
||||
<!-- Baidu Button BEGIN -->
|
||||
<div class="bdsharebuttonbox">
|
||||
<a href="#" class="bds_more" data-cmd="more"></a>
|
||||
<a href="#" class="bds_qzone" data-cmd="qzone"></a>
|
||||
<a href="#" class="bds_tsina" data-cmd="tsina"></a>
|
||||
<a href="#" class="bds_tqq" data-cmd="tqq"></a>
|
||||
<a href="#" class="bds_renren" data-cmd="renren"></a>
|
||||
<a href="#" class="bds_weixin" data-cmd="weixin"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content text-lg" v-html="form.content"></div>
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {locationUrl, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import dayjs from "dayjs";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = ref<Layout>({});
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const showPassword = ref<boolean>();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户ID
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(paramsId()).then(data => {
|
||||
assignFields(data)
|
||||
layout.value.banner = data?.banner;
|
||||
// 二级栏目分类
|
||||
if (data.parentId) {
|
||||
listCmsNavigation({parentId: data.parentId}).then(list => {
|
||||
category.value = list;
|
||||
})
|
||||
}
|
||||
|
||||
if(form.permission === 1){
|
||||
console.log('登录可见')
|
||||
return;
|
||||
}
|
||||
if(form.permission === 2){
|
||||
console.log('需要密码')
|
||||
showPassword.value = true;
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form?.comments,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content p{
|
||||
}
|
||||
.content img{
|
||||
padding: 10px 0;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.content video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
183
pages/m/index.vue
Normal file
183
pages/m/index.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
|
||||
<!-- 首页banner -->
|
||||
<Carousel ref="CarouselRef"/>
|
||||
|
||||
<div class="search">
|
||||
<el-input v-model="keyword" :placeholder="$t('index.keyword')" :suffix-icon="Search" @change="onSearch"/>
|
||||
</div>
|
||||
<!-- 产品分类 -->
|
||||
<div class="cp p-3" v-if="cpCategory">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6" v-for="(item,index) in cpCategory" :key="index">
|
||||
<div class="item flex flex-col justify-center mb-2" v-if="index < 8">
|
||||
<a :href="navTo(item)" class="img" style="border-radius: 100%;">
|
||||
<el-avatar fit="fill" :size="65" :src="`${item.icon}`" style="border: 1px solid #FFFFFF; box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);"/>
|
||||
</a>
|
||||
<div class="text-center py-1"><a :href="navTo(item)">{{ item.title }}</a></div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
|
||||
<div class="advs"><p><img :src="config?.MobileIndexInfoTopImage"
|
||||
title="1729219778290291.jpg" alt="1.jpg"/></p>
|
||||
<p><br/></p>
|
||||
<p><br/></p></div>
|
||||
|
||||
|
||||
<div class="clearboth"></div>
|
||||
|
||||
<div class="products">
|
||||
<div class="products_title">
|
||||
{{ $t('index.companyProfile') }}
|
||||
</div>
|
||||
<div class="contentss clearfix">
|
||||
<div class="tps"><p><a :href="i18n.locale.value == 'en' ? `/en/m/page/1100.html` : `/m/page/1025.html`" target="_self"><img
|
||||
:src="config.MobileIndexInfoImage" title="1729219913337024.jpg"
|
||||
alt="2.jpg"/></a></p>
|
||||
<p><br/></p></div>
|
||||
<div class="nrs">
|
||||
{{ config.MobileIndexInfo }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="products" v-if="caseCategory">
|
||||
<div class="products_title">
|
||||
{{ $t('index.parentNameCase') }}
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item,index) in caseCategory" :key="index">
|
||||
<a :href="navTo(item)" title="轨道交通建设项目" class="img">
|
||||
<img :src="item.icon" alt="轨道交通建设项目"/>
|
||||
<h3>{{ item.title }}</h3>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tu"><p><img :src="config.MobileIndexInfoHZGYImage" title="活动房安装"
|
||||
alt="活动房安装" border="0" vspace="0" style="white-space: normal;"/></p>
|
||||
<p><br/></p></div>
|
||||
|
||||
<div class="hezuo">
|
||||
<div class="products_title">
|
||||
<a><span class="text-[#0c6fcd]" >{{ $t('index.partner') }}</span></a>
|
||||
</div>
|
||||
<div class="con"><p><a><img
|
||||
:src="config.MobileIndexInfoLogoList" title="1587462469987062.jpg"
|
||||
alt="手机合作.jpg"/></a></p></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 服务中国 走向海外 -->
|
||||
<div v-html="config.MobileIndexCode" class="mservice"></div>
|
||||
|
||||
|
||||
<div class="advs"><a :href="`${i18n.locale.value == 'en' ? '/en/m/video/1075.html' : '/m/video/1000.html'}`"><img :src="config.MobileIndexBottomBanner"
|
||||
title="1553249693490374.jpg" alt="555.jpg" border="0" vspace="0"/></a></div>
|
||||
|
||||
|
||||
<div class="news">
|
||||
<div class="products_title">
|
||||
{{ $t('index.news') }}
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item,index) in hotNews" :key="index">
|
||||
<a :href="mDetail(item)" :title="item.title">
|
||||
{{ item.title }}
|
||||
</a>
|
||||
<div class="line-clamp-1 max-w-[320px]" v-html="item.comments"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
|
||||
|
||||
<div class="products">
|
||||
<div class="products_title">
|
||||
{{ $t('index.contact') }}
|
||||
</div>
|
||||
<div class="flex flex-col p-3 leading-8">
|
||||
<span class="text-left">{{ $t('page.tel') }}:{{ config.tel }}</span>
|
||||
<span class="text-left">{{ $t('page.phone') }}:{{ config.fax }}</span>
|
||||
<span class="text-left">{{ $t('page.fax') }}:{{ config.phone }}</span>
|
||||
<span class="text-left">{{ $t('page.email') }}:{{ config.email }}</span>
|
||||
<span class="text-left">{{ $t('page.address') }}:{{ config.address }}</span>
|
||||
<span class="text-left">{{ $t('page.factory') }}:{{ config.address2 }}</span>
|
||||
<span class="text-left">{{ $t('page.domain') }}:{{ config.pageLeftInfoUrl }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="distraction">
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Carousel from "~/pages/m/components/Carousel.vue";
|
||||
import {useConfigInfo, useWebsite} from "~/composables/configState";
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {navTo} from "~/utils/common";
|
||||
import {getSiteInfo} from "~/api/layout";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name?: any;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const config = useConfigInfo();
|
||||
const website = useWebsite();
|
||||
const keyword = ref();
|
||||
const i18n = useI18n();
|
||||
const hotNews = ref<CmsArticle[]>([]);
|
||||
const topNavs = ref<any>([]);
|
||||
const cpCategory = ref<CmsNavigation[]>([]);
|
||||
const caseCategory = ref<any>([]);
|
||||
|
||||
|
||||
const onSearch = () => {
|
||||
window.location.href = `/m/search/${keyword.value}`;
|
||||
}
|
||||
|
||||
// TODO 读取服务器缓存数据
|
||||
await getSiteInfo({
|
||||
lang: i18n.locale.value
|
||||
}).then(data => {
|
||||
website.value = data
|
||||
topNavs.value = data.topNavs?.filter(d => d.title === '产品系列' || d.title === 'Product')
|
||||
if(topNavs.value[0].children){
|
||||
cpCategory.value = topNavs.value[0].children;
|
||||
}
|
||||
caseCategory.value = data.topNavs?.filter(d => d.title === '项目展示' || d.title === 'Case')[0].children
|
||||
})
|
||||
|
||||
const reload = async () => {
|
||||
// 新闻头条
|
||||
pageCmsArticle({
|
||||
limit: 4,
|
||||
status: 0,
|
||||
recommend: 1,
|
||||
parentId: i18n.locale.value == 'en' ? 1077 : 1002,
|
||||
lang: i18n.locale.value
|
||||
}).then(res => {
|
||||
if(res){
|
||||
hotNews.value = res?.list;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
reload();
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
220
pages/m/item/[id].vue
Normal file
220
pages/m/item/[id].vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<script setup lang="ts">
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {locationUrl, navTo, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = ref<Layout>({});
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const showPassword = ref<boolean>();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const fileList = ref<any>();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(paramsId()).then(data => {
|
||||
assignFields(data)
|
||||
layout.value.banner = data?.banner;
|
||||
// 二级栏目分类
|
||||
if (data.parentId) {
|
||||
listCmsNavigation({parentId: data.parentId}).then(list => {
|
||||
category.value = list;
|
||||
})
|
||||
}
|
||||
|
||||
if(form.permission === 1){
|
||||
console.log('登录可见')
|
||||
return;
|
||||
}
|
||||
if(form.permission === 2){
|
||||
console.log('需要密码')
|
||||
showPassword.value = true;
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<Banner :layout="layout"/>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="clearfix p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ form.categoryName || '分类名称' }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="detail-container">
|
||||
|
||||
<!-- 产品详细 -->
|
||||
<div class="product_detail" id="pd1">
|
||||
<div class="allcontent clearfix">
|
||||
<div style="float: left;">
|
||||
<div class="img clearfix" id="play">
|
||||
<el-carousel v-if="form.files" :interval="4000">
|
||||
<el-carousel-item v-for="item in JSON.parse(form.files)" :key="item" style="display: flex; align-items: center; justify-content: center">
|
||||
<el-image fit="cover" :src="item" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list mb-10">
|
||||
<ul class="list_p">
|
||||
<h1 class="title">{{ form.title }}</h1>
|
||||
<li><h2>{{ $t('categoryName') }}:<a :href="navTo(form,`/product/${form.categoryId}`)"><strong>{{ form.categoryName }}</strong></a></h2></li>
|
||||
<li class="text-left">{{ $t('createTime') }}:<span>{{ form.createTime }}</span></li>
|
||||
|
||||
<li class="text-left">{{ $t('overview') }}:</li>
|
||||
<div class="bg-gray-100 p-2 w-[360px]">{{ form.comments }}</div>
|
||||
|
||||
<li class="inquiry"><a :href="navTo(form,`/m/order/${form.articleId}.html}`)">{{ $t('onlineInquiry') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
<div class="p_detail">
|
||||
<ul id="product-tab" class="product-tab clearfix">
|
||||
<li class="cur">{{ $t('show.detail') }}</li>
|
||||
|
||||
</ul>
|
||||
<div class="content tab-content text-sm" v-html="form.content"></div>
|
||||
</div>
|
||||
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content p{
|
||||
text-indent: 0;
|
||||
}
|
||||
.content img{
|
||||
padding: 5px 0;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.content video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.product_detail{
|
||||
padding: 5px 0 !important;
|
||||
}
|
||||
.product_detail .list{
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
.product_detail .img{
|
||||
width: 90vw !important;
|
||||
}
|
||||
</style>
|
||||
232
pages/m/order/[id].vue
Normal file
232
pages/m/order/[id].vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<template>
|
||||
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="clearfix p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>{{ $t('order.title') }}</h2>
|
||||
<Breadcrumb :data="page" :categoryName="page.categoryName" />
|
||||
</div>
|
||||
<div class="form-box p-5">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120"
|
||||
label-position="left"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item :label="$t('order.title')" prop="title" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.title" :placeholder="$t('order.title')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.content')" prop="content" class="hover:bg-gray-50 p-2">
|
||||
<el-input type="textarea" :rows="5" cols="80" v-model="form.content" :placeholder="$t('order.content')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.realName')" prop="realName" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.realName" :placeholder="$t('order.realName')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.phone')" prop="phone" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.phone" :maxlength="11" :placeholder="$t('order.phone')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.email')" prop="email" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.email" :placeholder="$t('order.email')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.address')" prop="address" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.address" :placeholder="$t('order.address')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.code')" prop="code" class="hover:bg-gray-50 p-2">
|
||||
<el-space class="flex">
|
||||
<el-input size="large" :placeholder="$t('order.imgCode')" maxlength="5" v-model="form.code" />
|
||||
<el-image :alt="$t('order.imgCode')" v-if="captcha" :src="captcha" @click="changeCaptcha" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="submitForm ml-2">
|
||||
<el-button type="primary" size="large" @click="submitForm(formRef)">
|
||||
{{ $t('order.submit') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<el-image w-full :src="dialogImageUrl" alt="查看证件" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import {paramsId} from "~/utils/common";
|
||||
import {ArrowRight} from '@element-plus/icons-vue'
|
||||
import type {FormInstance, FormRules} from 'element-plus'
|
||||
import type {CmsOrder} from "~/api/cms/cmsOrder/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import {addCmsOrder} from "~/api/cms/cmsOrder";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {getCaptcha} from "~/api/passport/login";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = useLayout();
|
||||
const page = usePage();
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogImageUrl = ref('')
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
const text = ref<string>('');
|
||||
|
||||
|
||||
const {form, resetFields} = useFormData<CmsOrder>({
|
||||
// 订单号
|
||||
orderId: undefined,
|
||||
// 模型名称
|
||||
model: 'order',
|
||||
// 订单标题
|
||||
title: undefined,
|
||||
// 订单编号
|
||||
orderNo: undefined,
|
||||
// 订单类型,0商城 1询价 2留言
|
||||
type: undefined,
|
||||
// 关联项目ID,配合订单类型使用
|
||||
articleId: undefined,
|
||||
// 真实姓名
|
||||
realName: undefined,
|
||||
// 手机号码
|
||||
phone: undefined,
|
||||
// 电子邮箱
|
||||
email: undefined,
|
||||
// 收货地址
|
||||
address: undefined,
|
||||
// 订单内容
|
||||
content: undefined,
|
||||
// 订单总额
|
||||
totalPrice: '0.00',
|
||||
// 实际付款
|
||||
payPrice: '0.00',
|
||||
// 报价询价
|
||||
price: '0.00',
|
||||
// 购买数量
|
||||
totalNum: undefined,
|
||||
// 二维码地址,保存订单号,支付成功后才生成
|
||||
qrcode: undefined,
|
||||
// 下单渠道,0网站 1小程序 2其他
|
||||
channel: undefined,
|
||||
// 过期时间
|
||||
expirationTime: undefined,
|
||||
// 订单是否已结算(0未结算 1已结算)
|
||||
isSettled: undefined,
|
||||
// 用户id
|
||||
userId: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 图像验证码
|
||||
code: '',
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<CmsOrder>>({
|
||||
title: [
|
||||
{required: true, message: '请输入产品名称', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
realName: [
|
||||
{required: true, message: '请输入联系人姓名', trigger: 'blur'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入留言内容', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
getCaptcha().then(captchaData => {
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
})
|
||||
};
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
getCmsArticle(paramsId()).then(data => {
|
||||
form.articleId = data.articleId;
|
||||
layout.value.banner = data.banner;
|
||||
form.content = ''
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
changeCaptcha();
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if(form.code !== text.value){
|
||||
await reload();
|
||||
ElMessage.error('验证码不正确!');
|
||||
return false;
|
||||
}
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
addCmsOrder(form).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
resetFields();
|
||||
reload();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 在这里放置你想要在组件渲染完成后执行的代码
|
||||
console.log('组件已挂载');
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
overflow: hidden;
|
||||
text-indent: 2em;
|
||||
}
|
||||
.content p{
|
||||
line-height: 2em;
|
||||
}
|
||||
.content img{
|
||||
padding: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
78
pages/m/page/[id].vue
Normal file
78
pages/m/page/[id].vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="clearfix p-5">
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>{{ page.title }}</h2>
|
||||
</div>
|
||||
<div class="content text-lg" v-html="page.design?.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import { useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {paramsId} from "~/utils/common";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = useLayout();
|
||||
const page = usePage();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
|
||||
|
||||
// 加载页面布局
|
||||
const reload = async () => {
|
||||
getCmsNavigation(paramsId()).then(data => {
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
// 二级栏目分类
|
||||
if(data.parentId && data.parentId > 0){
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(list => {
|
||||
category.value = list
|
||||
})
|
||||
}
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content p{
|
||||
}
|
||||
.content img{
|
||||
padding: 5px 0;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.content video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
122
pages/m/product/[id].vue
Normal file
122
pages/m/product/[id].vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="clearfix p-5">
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<el-row :gutter="20">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<el-col :span="24" class="my-3">
|
||||
<div class="item p-2 border-1 border-gray-200 border-solid hover:border-blue-500">
|
||||
<a :href="mDetail(item)" :title="item.title" class="img"><el-image :fit="`scale-down`" :src="item.image" :alt="item.title" /></a>
|
||||
<h3>
|
||||
<a :href="mDetail(item)" :title="item.title" class="text-lg font-bold">{{ item.title }}</a>
|
||||
<span class="line-clamp-1 text-gray-400 text-sm font-normal">{{ item.comments }}</span>
|
||||
</h3>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {mDetail, paramsId} from "~/utils/common";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = async () => {
|
||||
console.log(paramsId())
|
||||
getCmsNavigation(paramsId()).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
147
pages/m/search/[id].vue
Normal file
147
pages/m/search/[id].vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ $t('searchKeywords') }}:{{ where.keywords }}
|
||||
</h2>
|
||||
</div>
|
||||
<el-alert v-if="where.keywords" :title="`${$t('search.results')}:${$t('search.find')} ${total} ${$t('search.nums')}`" type="warning" :closable="false" />
|
||||
<div class="content">
|
||||
|
||||
<ul class="news_listn clearfix">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<li class="clearfix">
|
||||
<div class="">
|
||||
<h3 class="text-lg"><a :href="mDetail(item)" :title="item.title" v-html="replaceKeywords(item.title)"></a></h3>
|
||||
<div v-html="replaceKeywords(item.comments)" class="line-clamp-2 text-gray-400"></div>
|
||||
<div class="date text-gray-400">{{ $t('createTime') }}:{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<div class="clearboth"></div>
|
||||
</ul>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useConfigInfo, useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import dayjs from "dayjs";
|
||||
import {getPath} from "~/utils/common";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsModel} from "~/api/cms/cmsModel";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
const config = useConfigInfo();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 10,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const replaceKeywords = (text: any) => {
|
||||
return text.replace(`${where.keywords}`,'<font color=#ff0000>' + where.keywords + '</font>');
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
listCmsModel({
|
||||
model: getPath(2)
|
||||
}).then(response => {
|
||||
const data = response[0];
|
||||
if(data){
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data?.comments || `${route.params.id}`,
|
||||
keywords: `${route.params.id}`,
|
||||
titleTemplate: `【搜索结果】${route.params.id}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: i18n.locale.value == 'en' ? 1073 : 998,
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(!getPath(1)){
|
||||
return ElMessage.error('请输入搜索关键词!');
|
||||
}
|
||||
where.keywords = `${route.params.id}`;
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
useHead({
|
||||
title: `搜索结果 - ${config.value.siteName || runtimeConfig.public.siteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.sitemp h2{
|
||||
width: 500px !important;
|
||||
}
|
||||
</style>
|
||||
185
pages/m/show/[id].vue
Normal file
185
pages/m/show/[id].vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<Banner :layout="layout"/>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ form.categoryName || '分类名称' }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="detail-container">
|
||||
|
||||
<!-- 产品详细 -->
|
||||
<div class="product_detail" id="pd1">
|
||||
<div class="allcontent clearfix">
|
||||
<div class="text-center text-xl text-gray-800 py-2">{{ form.title }}</div>
|
||||
<el-carousel v-if="form.files" :interval="4000">
|
||||
<el-carousel-item v-for="item in form.files" :key="item" class="" style="display: flex; align-items: center; justify-content: center">
|
||||
<el-image :src="item" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
<div class="p_detail">
|
||||
<ul id="product-tab" class="product-tab clearfix">
|
||||
<li class="cur">{{ $t('show.detail') }}</li>
|
||||
|
||||
</ul>
|
||||
<div class="content tab-content text-sm" v-html="form.content"></div>
|
||||
</div>
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {locationUrl, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = ref<Layout>({});
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(paramsId()).then(data => {
|
||||
|
||||
assignFields(data);
|
||||
layout.value.banner = data?.banner;
|
||||
if (data.files) {
|
||||
form.files = JSON.parse(data.files);
|
||||
}
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId
|
||||
}).then(list => {
|
||||
category.value = list
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'err')
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content p{
|
||||
}
|
||||
.content img{
|
||||
padding: 10px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.content video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
135
pages/m/tags/[id].vue
Normal file
135
pages/m/tags/[id].vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ $t('label') }}:{{ where.tags }}
|
||||
</h2>
|
||||
</div>
|
||||
<el-alert v-if="where.keywords" :title="`${$t('search.results')}:${$t('search.find')} ${total} ${$t('search.nums')}`" type="warning" :closable="false" />
|
||||
<div class="content">
|
||||
|
||||
|
||||
<ul class="news_listn clearfix">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<li class="clearfix">
|
||||
<div class="">
|
||||
<h3 class="text-lg"><a :href="mDetail(item)" :title="item.title" v-html="replaceKeywords(item.title)"></a></h3>
|
||||
<div v-html="replaceKeywords(item.comments)" class="line-clamp-2 text-gray-400"></div>
|
||||
<div class="date text-gray-400">{{ $t('createTime') }}:{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<div class="clearboth"></div>
|
||||
</ul>
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import dayjs from "dayjs";
|
||||
import {getPath} from "~/utils/common";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsModel} from "~/api/cms/cmsModel";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
tags: '',
|
||||
page: 1,
|
||||
limit: 10,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const replaceKeywords = (text: any) => {
|
||||
return text.replace(`${where.keywords}`,'<font color=#ff0000>' + where.keywords + '</font>');
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
listCmsModel({
|
||||
model: getPath(2)
|
||||
}).then(response => {
|
||||
const data = response[0];
|
||||
if(data){
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data?.comments || `${route.params.id}`,
|
||||
keywords: `${route.params.id}`,
|
||||
titleTemplate: `【搜索结果】${route.params.id}` + ' - %s',
|
||||
})
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: i18n.locale.value == 'en' ? 1073 : 998,
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(!getPath(1)){
|
||||
return ElMessage.error('请输入搜索关键词!');
|
||||
}
|
||||
where.tags = `${route.params.id}`;
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.sitemp h2{
|
||||
width: 500px !important;
|
||||
}
|
||||
</style>
|
||||
110
pages/m/video/[id].vue
Normal file
110
pages/m/video/[id].vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<MCmsProductList :data="list" />
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {paramsId} from "~/utils/common";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import MCmsProductList from "~/components/MCmsProductList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = async () => {
|
||||
getCmsNavigation(paramsId()).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
183
pages/m/video/detail/[id].vue
Normal file
183
pages/m/video/detail/[id].vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<Banner :layout="layout"/>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="p-5">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ form.categoryName || '分类名称' }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="detail-container">
|
||||
|
||||
<!-- 产品详细 -->
|
||||
<div class="product_detail" id="pd1">
|
||||
<div class="allcontent clearfix">
|
||||
<div class="text-center text-xl text-gray-800 py-5">{{ form.title }}</div>
|
||||
<el-carousel v-if="form.files" :interval="4000" height="400px">
|
||||
<el-carousel-item v-for="item in form.files" :key="item" style="display: flex; align-items: center; justify-content: center">
|
||||
<el-image :src="item" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
<div class="p_detail">
|
||||
<ul id="product-tab" class="product-tab clearfix">
|
||||
<li class="cur">{{ $t('show.detail') }}</li>
|
||||
|
||||
</ul>
|
||||
<!-- 内容组件 -->
|
||||
<MContent class="text-sm" :data="form.content" />
|
||||
</div>
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {locationUrl, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
import MContent from "~/components/MContent.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const i18n = useI18n();
|
||||
const layout = ref<Layout>({});
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const parentName = ref('项目展示');
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
tags: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(paramsId()).then(data => {
|
||||
|
||||
assignFields(data);
|
||||
|
||||
layout.value.banner = data?.banner;
|
||||
if (data.files) {
|
||||
form.files = JSON.parse(data.files);
|
||||
}
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId,
|
||||
lang: i18n.locale.value
|
||||
}).then(list => {
|
||||
category.value = list
|
||||
// 宣传视频
|
||||
if(data.categoryName == '宣传视频'){
|
||||
parentName.value = '宣传视频'
|
||||
category.value = [];
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'err')
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -1,358 +0,0 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业官网 WDS
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="1">
|
||||
<span>概况</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="2">
|
||||
<span>栏目管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>文章列表</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>素材管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>广告管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>订单管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="7">
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="8">
|
||||
<span>系统设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</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, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
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 handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,373 +0,0 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业官网 WDS
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item v-for="(item,index) in menus" :key="item.menuId" :index="`${item.menuId}`">
|
||||
<span>{{ item.title }}</span>
|
||||
</el-menu-item>
|
||||
<!-- <el-menu-item index="1">-->
|
||||
<!-- <span>概况</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="2">-->
|
||||
<!-- <span>栏目管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="3">-->
|
||||
<!-- <span>文章列表</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="4">-->
|
||||
<!-- <span>素材管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="5">-->
|
||||
<!-- <span>广告管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="6">-->
|
||||
<!-- <span>订单管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="7">-->
|
||||
<!-- <span>用户管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="8">-->
|
||||
<!-- <span>系统设置</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer" @click="openSpmUrl(`/detail`,{},729,true)">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</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, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const menus = ref<Menu[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
import type {Menu} from "~/api/system/menu/model";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
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 handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
const {data: response2} = await useServerRequest<ApiResult<PageResult<Menu>>>('/system/menu',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
parentId: 64688,
|
||||
menuType: 0,
|
||||
hide: 0
|
||||
}})
|
||||
if(response2.value?.data){
|
||||
if (response2.value?.data) {
|
||||
menus.value = response2.value.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,358 +0,0 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业商城 EMall
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="1">
|
||||
<span>概况</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="2">
|
||||
<span>栏目管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>文章列表</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>素材管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>广告管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>订单管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="7">
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="8">
|
||||
<span>系统设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</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, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
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 handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,86 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in data?.list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
<el-tag v-if="item.chargingMethod === 0" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo" />
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<!-- <el-button @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>-->
|
||||
<!-- 已购买 -->
|
||||
<!-- <template v-if="item.isBuy">-->
|
||||
<!-- <el-button v-if="item.isBuy && item.installed" type="success" @click.stop="openSpmUrl(`https://${item.domain}`,item,item.companyId,true)">进入控制台</el-button>-->
|
||||
<!-- <el-button v-if="item.isBuy && !item.installed" type="primary" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">立即开通</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- 未够买 -->
|
||||
<!-- <template v-if="!item.isBuy">-->
|
||||
<!-- <div class="flex items-center cursor-pointer" v-if="!item.isBuy" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">-->
|
||||
<!-- <div class="flex items-center text-red-600">-->
|
||||
<!-- <span>¥{{ item.price }}</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 2">/年</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 3">/月</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 4">/天</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="flex justify-center py-3">
|
||||
<el-pagination background layout="prev, pager, next" :total="data?.count" @change="onPage" />
|
||||
</div>
|
||||
<!-- <div v-if="disabled" class="px-1 text-center text-gray-500">-->
|
||||
<!-- 没有更多了-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data?: PageResult<Company>;
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', index: number): void;
|
||||
}>();
|
||||
|
||||
// const load = () => {
|
||||
// if(!props.disabled){
|
||||
// emit('done')
|
||||
// }
|
||||
// }
|
||||
|
||||
const onPage = (index: number) => {
|
||||
emit('done',index)
|
||||
}
|
||||
</script>
|
||||
@@ -1,75 +1,198 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :data="data" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 应用市场 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-space class="flex items-center">
|
||||
<!-- <el-select v-model="value" clearable placeholder="筛选" style="width: 240px">-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="item in options"-->
|
||||
<!-- :key="item.value"-->
|
||||
<!-- :label="item.label"-->
|
||||
<!-- :value="item.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<el-button-group>
|
||||
<el-button>最新</el-button>
|
||||
<el-button>免费</el-button>
|
||||
<el-button>付费</el-button>
|
||||
<el-button>综合</el-button>
|
||||
</el-button-group>
|
||||
<el-input v-model="where.keywords" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="reload"/>
|
||||
</el-space>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="8" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.websiteLogo" shape="square" :size="55" style="background-color: white;"/>
|
||||
<div class="flex-1 text-lg cursor-pointer flex flex-col">
|
||||
{{ item.websiteName }}
|
||||
<div class="flex justify-between items-center">
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">{{ item.comments || '暂无描述' }}</sapn>
|
||||
<el-button size="small" round>获取</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-image pt-3">
|
||||
<el-image :src="`https://oss.wsdns.cn/20250212/a00f01813e474e1fb2f0732182bc82d3.jpg?x-oss-process=image/resize,m_fixed,w_1680/quality,Q_90`" class="w-full h-1/2" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import { ElNotification as notify } from 'element-plus'
|
||||
import { useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import dayjs from "dayjs";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {pageCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
import type {CmsWebsite, CmsWebsiteParam} from "~/api/cms/cmsWebsite/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const router = useRouter();
|
||||
const navId = ref();
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const data = ref<PageResult<Company>>();
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const list = ref<CmsWebsite[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
const activeName = ref('2839');
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
const where = reactive<CmsWebsiteParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const onSearch = (index: number) => {
|
||||
page.value = index;
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await pageCmsWebsiteAll(where).then(response => {
|
||||
if(response?.list){
|
||||
list.value = response?.list;
|
||||
total.value = response.count;
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const bakReload = async () => {
|
||||
await getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data;
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(navigation => {
|
||||
category.value = navigation;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = page.value.navigationId;
|
||||
}else {
|
||||
where.categoryId = page.value.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response.count;
|
||||
list.value = response.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 12,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
data.value = response.value?.data;
|
||||
}
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
const value = ref('')
|
||||
const options = [
|
||||
{
|
||||
value: 'Option1',
|
||||
label: 'Option1',
|
||||
},
|
||||
{
|
||||
value: 'Option2',
|
||||
label: 'Option2',
|
||||
},
|
||||
{
|
||||
value: 'Option3',
|
||||
label: 'Option3',
|
||||
},
|
||||
{
|
||||
value: 'Option4',
|
||||
label: 'Option4',
|
||||
},
|
||||
{
|
||||
value: 'Option5',
|
||||
label: 'Option5',
|
||||
},
|
||||
]
|
||||
|
||||
watch(
|
||||
() => getIdBySpm(5),
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
console.log(id,'id = .>>>>')
|
||||
// navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.right .content img{
|
||||
width: auto !important;
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const user = useUser();
|
||||
const layout = ref<any>();
|
||||
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/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
} else {
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {query: {path: route.path}})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// 未登录状态(是否强制登录)
|
||||
if (!token.value || token.value == '') {
|
||||
if (config.value.MustLogin) {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 判断是否开发者身份
|
||||
if(!user.value?.merchantId){
|
||||
navigateTo('/user/auth');
|
||||
return false;
|
||||
}
|
||||
// 开发者身份者跳转控制台
|
||||
if(user.value.merchantId){
|
||||
console.log('已认证未开发者>')
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `现代Web应用开发(Vue)框架 · WEBSOFT`,
|
||||
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) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
228
pages/order/[id].vue
Normal file
228
pages/order/[id].vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto bg-white">
|
||||
<el-row :gutter="50" id="container" class="clearfix">
|
||||
<el-col :span="5" class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" />
|
||||
</el-col>
|
||||
<el-col :span="19" class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between pt-5">
|
||||
<h2>{{ page.title }}</h2>
|
||||
<Breadcrumb :data="page" />
|
||||
</div>
|
||||
<div class="form-box p-5">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120"
|
||||
label-position="left"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item :label="$t('order.title')" prop="title" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.title" :placeholder="$t('order.title')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.content')" prop="content" class="hover:bg-gray-50 p-2">
|
||||
<el-input type="textarea" :rows="5" cols="80" v-model="form.content" :placeholder="$t('order.content')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.realName')" prop="realName" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.realName" :placeholder="$t('order.realName')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.phone')" prop="phone" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.phone" :maxlength="11" :placeholder="$t('order.phone')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.email')" prop="email" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.email" :placeholder="$t('order.email')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.address')" prop="address" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.address" :placeholder="$t('order.address')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.code')" prop="code" class="hover:bg-gray-50 p-2">
|
||||
<el-space class="flex">
|
||||
<el-input size="large" :placeholder="$t('order.imgCode')" maxlength="5" v-model="form.code" />
|
||||
<el-image :alt="$t('order.imgCode')" v-if="captcha" :src="captcha" @click="changeCaptcha" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="submitForm ml-2">
|
||||
<el-button type="primary" size="large" @click.stop="submitForm(formRef)">
|
||||
{{ $t('order.submit') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<el-image w-full :src="dialogImageUrl" alt="查看证件" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {getNavIdByParamsId} from "~/utils/common";
|
||||
import type {FormInstance, FormRules} from 'element-plus'
|
||||
import type {CmsOrder} from "~/api/cms/cmsOrder/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {addCmsOrder} from "~/api/cms/cmsOrder";
|
||||
import {getCaptcha} from "~/api/passport/login";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const navId = ref();
|
||||
const layout = useLayout();
|
||||
const page = usePage();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogImageUrl = ref('')
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
const text = ref<string>('');
|
||||
|
||||
|
||||
const {form, resetFields} = useFormData<CmsOrder>({
|
||||
// 订单号
|
||||
orderId: undefined,
|
||||
// 模型名称
|
||||
model: 'order',
|
||||
// 订单标题
|
||||
title: undefined,
|
||||
// 订单编号
|
||||
orderNo: undefined,
|
||||
// 订单类型,0商城 1询价 2留言
|
||||
type: undefined,
|
||||
// 关联项目ID,配合订单类型使用
|
||||
articleId: undefined,
|
||||
// 真实姓名
|
||||
realName: undefined,
|
||||
// 手机号码
|
||||
phone: undefined,
|
||||
// 电子邮箱
|
||||
email: undefined,
|
||||
// 收货地址
|
||||
address: undefined,
|
||||
// 订单内容
|
||||
content: undefined,
|
||||
// 订单总额
|
||||
totalPrice: '0.00',
|
||||
// 实际付款
|
||||
payPrice: '0.00',
|
||||
// 报价询价
|
||||
price: '0.00',
|
||||
// 购买数量
|
||||
totalNum: undefined,
|
||||
// 二维码地址,保存订单号,支付成功后才生成
|
||||
qrcode: undefined,
|
||||
// 下单渠道,0网站 1小程序 2其他
|
||||
channel: undefined,
|
||||
// 过期时间
|
||||
expirationTime: undefined,
|
||||
// 订单是否已结算(0未结算 1已结算)
|
||||
isSettled: undefined,
|
||||
// 用户id
|
||||
userId: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 图像验证码
|
||||
code: '',
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<CmsOrder>>({
|
||||
title: [
|
||||
{required: true, message: '请输入产品名称', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
realName: [
|
||||
{required: true, message: '请输入联系人姓名', trigger: 'blur'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入留言内容', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
getCaptcha().then(captchaData => {
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
})
|
||||
};
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
})
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
changeCaptcha();
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if(form.code !== text.value){
|
||||
changeCaptcha();
|
||||
ElMessage.error('验证码不正确!');
|
||||
return false;
|
||||
}
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
addCmsOrder(form).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
resetFields();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
72
pages/page/[id].vue
Normal file
72
pages/page/[id].vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-2">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> {{ page.title }} </span>
|
||||
</template>
|
||||
<el-card shadow="hover" class=" my-5">
|
||||
<!-- 内容组件 -->
|
||||
<Content class="content bg-white mt-5" :data="page.design?.content" />
|
||||
</el-card>
|
||||
</el-page-header>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {getNavIdByParamsId, getViews, paramsId} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import { ArrowLeft,View } from '@element-plus/icons-vue'
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Content from "~/components/Content.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navId = ref();
|
||||
const layout = useLayout();
|
||||
const page = usePage();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 加载页面布局
|
||||
const reload = async () => {
|
||||
getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
// 二级栏目分类
|
||||
if(data.parentId && data.parentId > 0){
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(list => {
|
||||
category.value = list
|
||||
})
|
||||
}
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 简单模式(常规单页面) -->
|
||||
<PageContainer :form="form" :layout="layout" />
|
||||
|
||||
<!-- 高级模式(自定义组件) -->
|
||||
<div class="flex flex-col" v-if="layout?.showLayout">
|
||||
{{ layout }}
|
||||
</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 type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
import PageContainer from "~/components/PageContainer.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const layout = ref<any>();
|
||||
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/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
}else{
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
console.log(path,'=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -15,17 +15,17 @@
|
||||
<el-tabs v-model="activeName" class="demo-tabs ">
|
||||
<el-tab-pane label="账号登录" name="account">
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<el-form :model="form" label-width="auto" class="w-[330px]">
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" placeholder="租户ID" :prefix-icon="Shop" v-model="form.tenantId" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item prop="username">
|
||||
<el-form-item>
|
||||
<el-input class="w-full" size="large" placeholder="登录账号" :prefix-icon="Avatar" v-model="form.username" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-form-item>
|
||||
<el-input type="password" size="large" maxlength="100" placeholder="登录密码" :prefix-icon="Briefcase" v-model="form.password" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-form-item>
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="图形验证码" maxlength="5" v-model="form.code" @keyup.enter.prevent="onSubmit" />
|
||||
<el-image alt="" :src="captcha" @click="changeCaptcha" />
|
||||
@@ -43,13 +43,13 @@
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="短信登录" name="sms">
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<el-form-item prop="phone">
|
||||
<el-form :model="form" label-width="auto" class="w-[330px]">
|
||||
<el-form-item>
|
||||
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
|
||||
<template #prepend>+86</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-form-item>
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />
|
||||
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">
|
||||
@@ -80,18 +80,18 @@
|
||||
<!-- 注册界面 -->
|
||||
<el-space class="tabs pt-5 text-xl flex justify-center" v-if="!loginBar">
|
||||
<el-tabs v-model="activeName" class="demo-tabs ">
|
||||
<el-tab-pane label="注册账号" name="sms">
|
||||
<div class="custom-style ">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<el-form-item prop="phone">
|
||||
<span class="text-sm text-gray-400 mb-4">
|
||||
未注册手机号验证通过后将自动注册
|
||||
</span>
|
||||
<el-tab-pane label="手机号注册" name="sms">
|
||||
<span class="text-sm text-gray-400">
|
||||
未注册手机号验证通过后将自动注册
|
||||
</span>
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" label-width="auto" class="w-[330px]">
|
||||
<el-form-item>
|
||||
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
|
||||
<template #prepend>+86</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-form-item>
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" />
|
||||
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">
|
||||
@@ -100,12 +100,6 @@
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item prop="companyName">
|
||||
<el-input class="w-full" size="large" placeholder="企业名称" v-model="form.companyName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="email">
|
||||
<el-input class="w-full" size="large" placeholder="邮箱地址" v-model="form.email" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.isAgree">我已阅读并同意</el-checkbox>
|
||||
<a href="#" class="text-gray-700">《用户协议》</a>
|
||||
@@ -118,41 +112,41 @@
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="账号密码注册" name="account">-->
|
||||
<!-- <div class="custom-style my-4">-->
|
||||
<!-- <el-form :model="form" label-width="auto" class="w-[330px]">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" maxlength="30" placeholder="登录账号" v-model="form.username" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input type="password" size="large" maxlength="30" placeholder="登录密码" v-model="form.password" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">-->
|
||||
<!-- <template #prepend>+86</template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-space class="flex justify-between w-full">-->
|
||||
<!-- <el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />-->
|
||||
<!-- <el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">-->
|
||||
<!-- <span v-if="!countdownTime">发送验证码</span>-->
|
||||
<!-- <span v-else>已发送 {{ countdownTime }} s</span>-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-space>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-checkbox v-model="form.isAgree">我已阅读并同意</el-checkbox>-->
|
||||
<!-- <a href="#" class="text-gray-700">《用户协议》</a>-->
|
||||
<!-- <a href="#" class="text-gray-700">《隐私政策》</a>-->
|
||||
<!-- <a href="#" class="text-gray-700">《产品服务协议》</a>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-button type="primary" size="large" class="w-full" :disabled="!form.isAgree" @click="onRegister">注册</el-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<el-tab-pane label="账号密码注册" name="account">
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" label-width="auto" class="w-[330px]">
|
||||
<el-form-item>
|
||||
<el-input class="w-full" size="large" maxlength="30" placeholder="登录账号" v-model="form.username" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input type="password" size="large" maxlength="30" placeholder="登录密码" v-model="form.password" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
|
||||
<template #prepend>+86</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />
|
||||
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">
|
||||
<span v-if="!countdownTime">发送验证码</span>
|
||||
<span v-else>已发送 {{ countdownTime }} s</span>
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.isAgree">我已阅读并同意</el-checkbox>
|
||||
<a href="#" class="text-gray-700">《用户协议》</a>
|
||||
<a href="#" class="text-gray-700">《隐私政策》</a>
|
||||
<a href="#" class="text-gray-700">《产品服务协议》</a>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" class="w-full" :disabled="!form.isAgree" @click="onRegister">注册</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-space>
|
||||
</el-card>
|
||||
@@ -163,13 +157,11 @@ import {useConfigInfo, useToken, useUser, useWebsite} from "~/composables/config
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import type {FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import { Shop, Key, Avatar, Briefcase } from '@element-plus/icons-vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model";
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import {getCaptcha, sendSmsCaptcha} from "~/api/passport/login";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
@@ -177,7 +169,6 @@ const website = useWebsite();
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const user = useUser();
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const activeName = ref('account')
|
||||
|
||||
@@ -196,20 +187,14 @@ const countdownTime = ref(0);
|
||||
// 验证码倒计时定时器
|
||||
let countdownTimer: number | null = null;
|
||||
|
||||
if(getIdBySpm(0) == 'register'){
|
||||
loginBar.value = false;
|
||||
}
|
||||
|
||||
// 配置信息
|
||||
const { form,assignFields, resetFields } = useFormData<User>({
|
||||
const { form } = useFormData<User>({
|
||||
userId: undefined,
|
||||
companyName: undefined,
|
||||
email: undefined,
|
||||
username: undefined,
|
||||
phone: undefined,
|
||||
password: undefined,
|
||||
code: undefined,
|
||||
smsCode: undefined,
|
||||
username: '',
|
||||
phone: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
isAgree: false,
|
||||
remember: true,
|
||||
isSuperAdmin: true
|
||||
@@ -236,33 +221,6 @@ const checkUser = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 验证规则
|
||||
const rules = reactive<FormRules<User>>({
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
password: [
|
||||
{required: true, message: '请输入密码', trigger: 'blur'},
|
||||
],
|
||||
code: [
|
||||
{required: true, message: '请输入验证码', trigger: 'blur'},
|
||||
],
|
||||
smsCode: [
|
||||
{required: true, message: '请输入短信验证码', trigger: 'blur'},
|
||||
],
|
||||
username: [
|
||||
{required: true, message: '请输入用户名', trigger: 'blur'},
|
||||
],
|
||||
companyName: [
|
||||
{required: true, message: '请输入公司名称', trigger: 'blur'},
|
||||
],
|
||||
email: [
|
||||
{required: true, message: '请输入邮箱', trigger: 'blur'},
|
||||
{pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
/* 发送短信验证码 */
|
||||
const sendCode = async () => {
|
||||
@@ -273,10 +231,9 @@ const sendCode = async () => {
|
||||
imgCode.value = text.value;
|
||||
codeLoading.value = true;
|
||||
|
||||
const {data: smsCode } = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
|
||||
sendSmsCaptcha({
|
||||
phone: form.phone
|
||||
}});
|
||||
if(smsCode.value?.code == 0){
|
||||
}).then(res => {
|
||||
codeLoading.value = false;
|
||||
countdownTime.value = 30;
|
||||
// 开始对按钮进行倒计时
|
||||
@@ -287,10 +244,9 @@ const sendCode = async () => {
|
||||
}
|
||||
countdownTime.value--;
|
||||
}, 1000);
|
||||
}
|
||||
if(smsCode.value?.code != 0){
|
||||
ElMessage.error(smsCode.value?.message);
|
||||
}
|
||||
}).catch(msg => {
|
||||
ElMessage.error(msg)
|
||||
})
|
||||
};
|
||||
|
||||
const navigateTo = (url: string) => {
|
||||
@@ -298,24 +254,19 @@ const navigateTo = (url: string) => {
|
||||
}
|
||||
|
||||
const onLoginBar = () => {
|
||||
// if(loginBar.value){
|
||||
// return navigateTo(`/passport/register`)
|
||||
// }
|
||||
loginBar.value = !loginBar.value
|
||||
activeName.value = loginBar.value ? 'account' : 'sms'
|
||||
}
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
const {data: captchaInfo } = await useServerRequest<ApiResult<CaptchaResult>>('/captcha',{baseURL: runtimeConfig.public.apiServer});
|
||||
const captchaData = captchaInfo.value?.data
|
||||
if(captchaData){
|
||||
getCaptcha().then(captchaData => {
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
}
|
||||
|
||||
// 已经登录跳转首页
|
||||
if(token.value && token.value.length > 0){
|
||||
navigateTo('/user')
|
||||
return;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -328,19 +279,7 @@ useHead({
|
||||
* 执行登录
|
||||
*/
|
||||
const onSubmit = async () => {
|
||||
if(!form.username){
|
||||
ElMessage.error('请填登录账号')
|
||||
return;
|
||||
}
|
||||
if(!form.password){
|
||||
ElMessage.error('请填密码')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form})
|
||||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: 'https://server.gxwebsoft.com/api',method: "post",body: form})
|
||||
// 登录成功
|
||||
if(response.value?.code == 0){
|
||||
ElMessage.success(response.value?.message)
|
||||
@@ -356,14 +295,6 @@ const onSubmit = async () => {
|
||||
* 短信验证码登录
|
||||
*/
|
||||
const onSubmitBySms = async () => {
|
||||
if(!form.phone){
|
||||
ElMessage.error('请填手机号码')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/loginBySms',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
|
||||
phone: form.phone,
|
||||
code: form.code,
|
||||
@@ -384,28 +315,12 @@ const onSubmitBySms = async () => {
|
||||
* 账号密码注册
|
||||
*/
|
||||
const onRegister = async () => {
|
||||
if(!form.phone){
|
||||
ElMessage.error('请填手机号码')
|
||||
return;
|
||||
}
|
||||
if(!form.companyName){
|
||||
ElMessage.error('请填写企业名称')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
if(!form.email){
|
||||
ElMessage.error('请填邮箱')
|
||||
return;
|
||||
}
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading'
|
||||
})
|
||||
await useClientRequest<ApiResult<LoginResult>>('/register',{method: "post",body: {
|
||||
companyName: form.companyName,
|
||||
companyName: '应用名称',
|
||||
username: form.phone,
|
||||
phone: form.phone,
|
||||
password: form.password,
|
||||
@@ -471,21 +386,4 @@ changeCaptcha();
|
||||
color: #606266;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* 改变激活标签的颜色 */
|
||||
//.el-tabs__item.is-active {
|
||||
// color: #cf1313;
|
||||
//}
|
||||
//
|
||||
///* 改变标签边界的颜色 */
|
||||
//.el-tabs__item:not(.is-disabled):focus,
|
||||
//.el-tabs__item:not(.is-disabled):hover {
|
||||
// border-color: #cf1313;
|
||||
//}
|
||||
//
|
||||
///* 改变标签栏指示器的颜色 */
|
||||
//.el-tabs--top .el-tabs__active-bar,
|
||||
//.el-tabs--bottom .el-tabs__active-bar {
|
||||
// background: #cf1313;
|
||||
//}
|
||||
</style>
|
||||
|
||||
185
pages/product/[id].vue
Normal file
185
pages/product/[id].vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> {{ page.title }} </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="flex items-center">
|
||||
<el-select v-model="value" clearable placeholder="筛选" style="width: 200px">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" class="left">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer" @click="navigateTo(`/item/${item.articleId}.html`)">
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.image" shape="square" :size="44" style="background-color: white;"/>
|
||||
<div class="flex-1 text-xl cursor-pointer flex flex-col">
|
||||
{{ item.title }}
|
||||
<sapn class="text-xs text-gray-400">site.websoft.top</sapn>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">-->
|
||||
<!-- <div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="button-group flex justify-between items-center mt-3 text-sm">-->
|
||||
<!-- <el-space class="flex items-end">-->
|
||||
<!-- <div class="text-gray-400 gap-1 flex items-center"><el-icon><View /></el-icon><span>{{ getViews(item) }}</span></div>-->
|
||||
<!-- </el-space>-->
|
||||
<!-- <el-button>控制台</el-button>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import { ArrowLeft,View } from '@element-plus/icons-vue'
|
||||
import { ElNotification as notify } from 'element-plus'
|
||||
import {useConfigInfo, useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import { h } from 'vue'
|
||||
import dayjs from "dayjs";
|
||||
import {detail, getNavIdByParamsId, navTo, paramsId} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
|
||||
const route = useRoute();
|
||||
const navId = ref();
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
const activeName = ref('2839');
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
console.log('go back')
|
||||
notify('Back')
|
||||
route.back();
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data;
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(navigation => {
|
||||
category.value = navigation;
|
||||
// 加载文章列表
|
||||
where.categoryId = page.value.navigationId;
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response.count;
|
||||
list.value = response.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
const value = ref('')
|
||||
const options = [
|
||||
{
|
||||
value: 'Option1',
|
||||
label: 'Option1',
|
||||
},
|
||||
{
|
||||
value: 'Option2',
|
||||
label: 'Option2',
|
||||
},
|
||||
{
|
||||
value: 'Option3',
|
||||
label: 'Option3',
|
||||
},
|
||||
{
|
||||
value: 'Option4',
|
||||
label: 'Option4',
|
||||
},
|
||||
{
|
||||
value: 'Option5',
|
||||
label: 'Option5',
|
||||
},
|
||||
]
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.right .content img{
|
||||
width: auto !important;
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false"
|
||||
_locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<el-steps
|
||||
style="max-width: 600px"
|
||||
:space="200"
|
||||
class="px-3"
|
||||
:active="active"
|
||||
finish-status="success"
|
||||
>
|
||||
<el-step title="选择套餐" />
|
||||
<el-step title="订单确认" />
|
||||
<el-step title="支付订单" />
|
||||
<el-step title="完成订购" />
|
||||
</el-steps>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
active?: number;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates',
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
@@ -1,226 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active" />
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>订单确认</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>
|
||||
<el-descriptions-item label="购买数量:">1 {{ form.unitName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">1 年</el-descriptions-item>
|
||||
<el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="其他服务" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="UI设计服务:">不包含</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="总金额:">¥3000.00</el-descriptions-item>
|
||||
<el-descriptions-item label="优惠价格:">¥1280.00</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @click="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer" @click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer" @click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>扫码支付</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<el-radio-group v-model="form.code">
|
||||
<el-radio-button value="1" border>微信支付</el-radio-button>
|
||||
<el-radio-button value="12" border>支付宝</el-radio-button>
|
||||
<el-radio-button value="24" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4">
|
||||
<el-avatar :size="250" src="https://oss.wsdns.cn/20240409/247a492abda94b08ace33fa5405628ca.jpeg"
|
||||
shape="square" />
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="danger" :disabled="!isAgree" class="w-full" size="large" @click="onSubmit">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
{{ form }}
|
||||
<!-- <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">-->
|
||||
<!-- <div class="flash bg-white rounded-lg px-7 py-4 w-full">-->
|
||||
<!-- <el-tabs class="flash bg-white 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:py-2">-->
|
||||
<!-- <el-form-item label="商品类型">-->
|
||||
<!-- <el-input v-model="form.title" placeholder="请输入真实姓名" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="资源包类型">-->
|
||||
<!-- <el-input v-model="form.productId" 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-tabs>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- </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 type {Product} from "~/api/oa/product/model";
|
||||
import PageBanner from "./components/PageBanner.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(false);
|
||||
const active = ref<number>(2);
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Product>({
|
||||
// 自增ID
|
||||
productId: undefined,
|
||||
// 类型 0软件产品 1实物商品 2虚拟商品
|
||||
type: undefined,
|
||||
// 产品编码
|
||||
code: undefined,
|
||||
// 产品标题
|
||||
title: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 产品详情
|
||||
content: undefined,
|
||||
// 父级分类ID
|
||||
parentId: undefined,
|
||||
// 产品分类ID
|
||||
categoryId: undefined,
|
||||
// 产品规格 0单规格 1多规格
|
||||
specs: undefined,
|
||||
// 货架
|
||||
position: undefined,
|
||||
// 单位名称 (个)
|
||||
unitName: undefined,
|
||||
// 进货价格
|
||||
price: undefined,
|
||||
// 销售价格
|
||||
salePrice: undefined,
|
||||
// 库存计算方式(10下单减库存 20付款减库存)
|
||||
deductStockType: undefined,
|
||||
// 轮播图
|
||||
files: undefined,
|
||||
// 销量
|
||||
sales: undefined,
|
||||
// 库存
|
||||
stock: undefined,
|
||||
// 消费赚取积分
|
||||
gainIntegral: undefined,
|
||||
// 推荐
|
||||
recommend: undefined,
|
||||
// 商户ID
|
||||
merchantId: undefined,
|
||||
// 状态(0:未上架,1:上架)
|
||||
isShow: undefined,
|
||||
// 状态, 0上架 1待上架 2待审核 3审核不通过
|
||||
status: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 修改时间
|
||||
updateTime: undefined,
|
||||
});
|
||||
|
||||
const changeIsAgree = (index: number) => {
|
||||
if(index){
|
||||
active.value = 3;
|
||||
}
|
||||
}
|
||||
|
||||
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<Product>>(`/cms/cms-product/${getIdBySpm(5)}`, {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '订单确认'
|
||||
useHead({
|
||||
title: `${form.title}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in data?.list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
<el-tag v-if="item.chargingMethod === 0" size="small" type="success" class="text-white ml-2"
|
||||
effect="dark">免费
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo"/>
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<template v-if="item.isBuy">
|
||||
<el-button v-if="item.installed" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="item.chargingMethod == 0" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="warning" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">立即开通
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="flex justify-center py-3">
|
||||
<el-pagination background layout="prev, pager, next" :total="data?.count" @change="onPage"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginAdminByToken, loginByToken, openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data?: PageResult<Company>;
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const tid = ref<number>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', index: number): void;
|
||||
}>();
|
||||
|
||||
// const load = () => {
|
||||
// if(!props.disabled){
|
||||
// emit('done')
|
||||
// }
|
||||
// }
|
||||
|
||||
const onPage = (index: number) => {
|
||||
emit('done', index)
|
||||
}
|
||||
tid.value = localStorage.getItem('TenantId');
|
||||
</script>
|
||||
@@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false"
|
||||
_locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<el-steps
|
||||
style="max-width: 600px"
|
||||
:space="200"
|
||||
class="px-3"
|
||||
:active="active"
|
||||
finish-status="success"
|
||||
>
|
||||
<el-step title="选择套餐" />
|
||||
<el-step title="订单确认" />
|
||||
<el-step title="支付订单" />
|
||||
<el-step title="完成订购" />
|
||||
</el-steps>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
active?: number;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
@@ -1,439 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active"/>
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-form :model="form" label-width="auto" label-position="left" class="w-full">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span v-if="form.type === 0">【产品】</span>
|
||||
<span v-if="form.type === 1">【插件】</span>
|
||||
<span>{{ form.shortName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-form-item label="套餐版本">
|
||||
<el-radio-group v-model="form.image">
|
||||
<el-radio-button value="1" border>基础版</el-radio-button>
|
||||
<el-radio-button value="2" border disabled>专业版</el-radio-button>
|
||||
<el-radio-button value="3" border disabled>定制版</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称">
|
||||
<el-input v-model="cart.appName" style="width: 360px" placeholder="网宿软件"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="二级域名">
|
||||
<el-input
|
||||
v-model="cart.domain"
|
||||
style="width: 360px"
|
||||
placeholder="websoft"
|
||||
>
|
||||
<template #prepend>Https://</template>
|
||||
<template #append>.wsdns.cn</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="类型">-->
|
||||
<!-- <el-radio-button v-if="form.type == 0" border>完整应用</el-radio-button>-->
|
||||
<!-- <el-radio-button v-if="form.type == 1" border>插件</el-radio-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="交付方式" v-if="form.deliveryMethod">-->
|
||||
<!-- <el-radio-button v-if="form.deliveryMethod == 1" border>SaaS交付</el-radio-button>-->
|
||||
<!-- <el-radio-button v-if="form.deliveryMethod == 2" border>源码交付</el-radio-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="购买时长" v-if="form.chargingMethod && form.chargingMethod > 1">
|
||||
<el-radio-group v-model="cart.month" @change="handleChargingMethod">
|
||||
<el-radio-button :value="1" border>1个月</el-radio-button>
|
||||
<el-radio-button :value="12" border>1年</el-radio-button>
|
||||
<el-radio-button :value="24" border>2年</el-radio-button>
|
||||
<el-radio-button :value="36" border>3年</el-radio-button>
|
||||
<el-radio-button :value="60" border>5年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 单价 {{ form.price }} x 购买时长 {{ cart.month }} x 数量 {{ cart.num }} x 折扣 0.1= {{ cart.totalPrice }}-->
|
||||
<!-- <el-form-item label="购买数量">-->
|
||||
<!-- <el-input-number v-model="cart.num" :min="1" :max="form.canBuyNumber" @change="handleChange"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @change="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《产品购买协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>订单详情</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.shortName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交付方式:">{{ form.deliveryMethod == 1 ? 'SaaS交付' : '源码交付' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应用名称:">{{ cart.appName }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>-->
|
||||
<el-descriptions-item label="购买数量:">{{ cart.num }} 套</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">{{ cart.month }} 个月</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="订单金额:"><span class="line-through">¥</span><span
|
||||
class="text-xl line-through">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="实付金额:"><span class="text-red-600">¥</span><span
|
||||
class="font-bold text-xl text-red-600">{{ cart.payPrice }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form-item>
|
||||
<el-button type="danger" class="w-full" :disabled="!isAgree" size="large" @click="onPay">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="订单确认"
|
||||
align-center
|
||||
width="450"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<div class="flex justify-center pt-3">
|
||||
<el-radio-group v-model="cart.payType" @change="handlePayType">
|
||||
<el-radio-button :value="102" border>微信支付</el-radio-button>
|
||||
<!-- <el-radio-button :value="3" border>支付宝</el-radio-button>-->
|
||||
<el-radio-button :value="0" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4" v-if="cart.payType == 102">
|
||||
<el-avatar :size="250" :src="wxPayQrCode"
|
||||
shape="square"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer text-center pb-3" v-if="cart.payType != 0">
|
||||
<el-tag type="success" size="large">
|
||||
使用微信扫码完成支付
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center w-1/2 m-auto pb-12" v-if="cart.payType === 0">
|
||||
<span class="py-2 text-center">可用余额:¥{{ userInfo?.balance || 0.00 }}</span>
|
||||
<div class="flex flex-col">
|
||||
<el-input type="password" class="py-2" size="large" v-model="payPassword" maxlength="6" show-password placeholder="请输入支付密码" />
|
||||
<el-button type="danger" class="py-2 my-3" size="large" @click="onDone">
|
||||
确定支付
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useUser, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import QRCode from 'qrcode';
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import type {FormInstance} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(true);
|
||||
const active = ref<number>(1);
|
||||
const visible = ref<boolean>(false);
|
||||
const wxPayQrCode = ref<string>()
|
||||
const payPassword = ref<string>()
|
||||
const formRef = ref<FormInstance>()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = useUser();
|
||||
const cart = useCart();
|
||||
const qrOptions = ref({
|
||||
width: 200, // 二维码宽度
|
||||
margin: 10, // 二维码边距
|
||||
color: { dark: '#000', light: '#fff' }, // 二维码颜色
|
||||
});
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Company>({
|
||||
companyId: undefined,
|
||||
menuId: undefined,
|
||||
type: undefined,
|
||||
appName: undefined,
|
||||
shortName: undefined,
|
||||
companyName: undefined,
|
||||
companyType: undefined,
|
||||
companyTypeMultiple: undefined,
|
||||
appType: undefined,
|
||||
companyLogo: undefined,
|
||||
image: undefined,
|
||||
companyCode: undefined,
|
||||
domain: undefined,
|
||||
phone: undefined,
|
||||
tel: undefined,
|
||||
email: undefined,
|
||||
InvoiceHeader: undefined,
|
||||
startTime: undefined,
|
||||
expirationTime: undefined,
|
||||
version: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: undefined,
|
||||
members: undefined,
|
||||
storage: undefined,
|
||||
storageMax: undefined,
|
||||
buys: undefined,
|
||||
clicks: undefined,
|
||||
users: undefined,
|
||||
departments: undefined,
|
||||
industryParent: undefined,
|
||||
industryChild: undefined,
|
||||
country: undefined,
|
||||
province: undefined,
|
||||
city: undefined,
|
||||
region: undefined,
|
||||
address: undefined,
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
businessEntity: undefined,
|
||||
comments: undefined,
|
||||
authentication: undefined,
|
||||
industryId: undefined,
|
||||
industryName: undefined,
|
||||
status: undefined,
|
||||
userId: undefined,
|
||||
official: undefined,
|
||||
deliveryMethod: undefined,
|
||||
chargingMethod: undefined,
|
||||
price: undefined,
|
||||
planId: undefined,
|
||||
sortNumber: undefined,
|
||||
authoritative: undefined,
|
||||
merchantId: undefined,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined,
|
||||
tenantCode: undefined,
|
||||
modules: undefined,
|
||||
requestUrl: undefined,
|
||||
socketUrl: undefined,
|
||||
serverUrl: undefined,
|
||||
modulesUrl: undefined,
|
||||
merchantUrl: undefined,
|
||||
websiteUrl: undefined,
|
||||
mpWeixinCode: undefined,
|
||||
mpAlipayCode: undefined,
|
||||
h5Code: undefined,
|
||||
androidUrl: undefined,
|
||||
iosUrl: undefined,
|
||||
avatar: undefined,
|
||||
nickname: undefined,
|
||||
code: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
collection: undefined,
|
||||
recommend: undefined,
|
||||
title: undefined,
|
||||
parentName: undefined,
|
||||
categoryName: undefined,
|
||||
});
|
||||
|
||||
const handleChange = (index:any): void => {
|
||||
cart.value.num = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const handleChargingMethod = (index: any): void => {
|
||||
cart.value.month = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const computeTotalPrice = () => {
|
||||
// 计算公式 = (商品价格 * 数量 * 月份) / 折扣率
|
||||
cart.value.totalPrice = Math.round(Number(form.price)*Number(cart.value.num)*Number(cart.value.month));
|
||||
cart.value.payPrice = Math.round(Number(cart.value.totalPrice) * Number(0.1));
|
||||
}
|
||||
|
||||
const changeIsAgree = (index: any) => {
|
||||
if (index) {
|
||||
active.value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const handlePayType = (index: any) => {
|
||||
cart.value.payType = index;
|
||||
onPay();
|
||||
}
|
||||
|
||||
// 统一下单
|
||||
const onPay = () => {
|
||||
visible.value = true;
|
||||
active.value = 3;
|
||||
// 余额支付
|
||||
if(cart.value.payType == 0){
|
||||
return;
|
||||
}
|
||||
// 微信支付
|
||||
if(cart.value.payType == 102){
|
||||
useClientRequest<ApiResult<any>>(`/system/wx-native-pay/codeUrl`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(async res => {
|
||||
if (res.code == 0) {
|
||||
try {
|
||||
// 生成二维码图像数据URL
|
||||
wxPayQrCode.value = await QRCode.toDataURL(res.data);
|
||||
} catch (error) {
|
||||
console.error('Error generating QR code:', error);
|
||||
}
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 支付宝支付
|
||||
if(cart.value.payType == 3){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 () => {
|
||||
// 要求登录
|
||||
if (!token.value || token.value == '') {
|
||||
navigateTo(`/passport/login`)
|
||||
return false;
|
||||
}
|
||||
// useClientRequest(`/auth/user`, {}).then(res => {
|
||||
// if(res?.data?.balance){
|
||||
// cart.value.balance = res?.data?.balance;
|
||||
// }
|
||||
// })
|
||||
const {data: response} = await useServerRequest<ApiResult<Company>>(`/system/company/${getIdBySpm(5)}`, {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '选择套餐';
|
||||
cart.value.comments = `购买${response.value?.data.shortName}`;
|
||||
if(response.value?.data.price){
|
||||
cart.value.payPrice = response.value?.data.price;
|
||||
cart.value.adminUrl = response.value?.data.domain;
|
||||
cart.value.menuId = response.value?.data.menuId;
|
||||
computeTotalPrice();
|
||||
}
|
||||
useHead({
|
||||
title: `${form.shortName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 扫码支付
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
ElMessage.error('还没有评分哦!')
|
||||
// if (form.rate === 0) {
|
||||
// ElMessage.error('还没有评分哦!')
|
||||
// return false;
|
||||
// }
|
||||
// form.productId = props.productId;
|
||||
// useClientRequest<ApiResult<any>>(`/cms/cms-product-comment/`, {
|
||||
// method: 'POST',
|
||||
// body: form
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// visible.value = false
|
||||
// resetFields();
|
||||
// emit('done',1)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
// 余额支付
|
||||
const onDone = () => {
|
||||
cart.value.type = 1;
|
||||
cart.value.list = [];
|
||||
cart.value.list?.push(form);
|
||||
useClientRequest<ApiResult<any>>(`/system/order/createOrder`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(res => {
|
||||
if(res.code == 0){
|
||||
ElMessage.success('购买成功');
|
||||
visible.value = !visible.value;
|
||||
setTimeout(() => {
|
||||
openSpmUrl(`https://console.websoft.top/cost-center/order`)
|
||||
},500)
|
||||
}
|
||||
if(res.code == 1){
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
|
||||
}).catch(res => {
|
||||
|
||||
})
|
||||
|
||||
// useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
// method: 'POST',
|
||||
// body: {
|
||||
// companyId: getIdBySpm(5)
|
||||
// }
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,389 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active"/>
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-form :model="form" label-width="auto" label-position="left" class="w-full">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span v-if="form.type === 0">【产品】</span>
|
||||
<span v-if="form.type === 1">【插件】</span>
|
||||
<span>{{ form.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-form-item label="类型">
|
||||
<el-radio-button v-if="form.type == 0" border>完整应用</el-radio-button>
|
||||
<el-radio-button v-if="form.type == 1" border>插件</el-radio-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐版本">
|
||||
<el-radio-group v-model="form.image">
|
||||
<el-radio-button value="1" border>基础版</el-radio-button>
|
||||
<el-radio-button value="2" border>标准版</el-radio-button>
|
||||
<el-radio-button value="3" border>专业版</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="交付方式" v-if="form.deliveryMethod">-->
|
||||
<!-- <el-tag v-if="form.deliveryMethod == 1">SaaS交付</el-tag>-->
|
||||
<!-- <el-tag v-if="form.deliveryMethod == 2">源码交付</el-tag>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="购买时长" v-if="form.chargingMethod && form.chargingMethod > 1">
|
||||
<el-radio-group v-model="form.chargingMethod" @change="handleChargingMethod">
|
||||
<el-radio-button :value="1" border>1个月</el-radio-button>
|
||||
<el-radio-button :value="12" border>1年</el-radio-button>
|
||||
<el-radio-button :value="24" border>2年</el-radio-button>
|
||||
<el-radio-button :value="36" border>3年</el-radio-button>
|
||||
<el-radio-button :value="60" border>5年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="购买数量">-->
|
||||
<!-- <el-input-number v-model="cart.num" :min="1" :max="form.canBuyNumber" @change="handleChange"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @change="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《产品购买协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>配置清单</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>
|
||||
<el-descriptions-item label="购买数量:">{{ cart.num }} 套</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">{{ cart.month }} 个月</el-descriptions-item>
|
||||
<el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="订单金额:"><span class="line-through">¥</span><span
|
||||
class="text-xl line-through">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="实付金额:"><span class="text-red-600">¥</span><span
|
||||
class="font-bold text-2xl text-red-600">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form-item>
|
||||
<el-button type="danger" class="w-full" :disabled="!isAgree" size="large" @click="onPay">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="订单确认"
|
||||
align-center
|
||||
width="400"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<div class="flex justify-center pt-3">
|
||||
<el-radio-group v-model="cart.payType" @change="handlePayType">
|
||||
<el-radio-button :value="102" border>微信支付</el-radio-button>
|
||||
<el-radio-button :value="3" border>支付宝</el-radio-button>
|
||||
<el-radio-button :value="0" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4">
|
||||
<el-avatar :size="250" src="https://oss.wsdns.cn/20240409/247a492abda94b08ace33fa5405628ca.jpeg"
|
||||
shape="square"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer text-center pb-3">
|
||||
<el-button type="success" @click="onDone">
|
||||
已完成支付
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</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 PageBanner from './components/PageBanner.vue';
|
||||
import type {FormInstance} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
import type {CmsProductParameter} from "~/api/cms/cmsProductParameter/model";
|
||||
import type {CmsProductUrl} from "~/api/cms/cmsProductUrl/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(true);
|
||||
const active = ref<number>(1);
|
||||
const visible = ref<boolean>(false);
|
||||
const formRef = ref<FormInstance>()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const cart = useCart();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsProduct>({
|
||||
// 自增ID
|
||||
productId: undefined,
|
||||
// 类型 0软件产品 1实物商品 2虚拟商品
|
||||
type: undefined,
|
||||
// 产品编码
|
||||
code: undefined,
|
||||
// 产品标题
|
||||
title: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 产品详情
|
||||
content: undefined,
|
||||
// 父级分类ID
|
||||
parentId: undefined,
|
||||
// 产品分类ID
|
||||
categoryId: undefined,
|
||||
// 产品规格 0单规格 1多规格
|
||||
specs: undefined,
|
||||
// 货架
|
||||
position: undefined,
|
||||
// 单位名称 (个)
|
||||
unitName: undefined,
|
||||
// 进货价格
|
||||
price: undefined,
|
||||
// 销售价格
|
||||
salePrice: undefined,
|
||||
// 库存计算方式(10下单减库存 20付款减库存)
|
||||
deductStockType: undefined,
|
||||
// 轮播图
|
||||
files: undefined,
|
||||
// 销量
|
||||
sales: undefined,
|
||||
// 库存
|
||||
stock: undefined,
|
||||
// 安装次数
|
||||
install: undefined,
|
||||
// 消费赚取积分
|
||||
gainIntegral: undefined,
|
||||
// 计费方式
|
||||
durationMethod: undefined,
|
||||
// 推荐
|
||||
recommend: undefined,
|
||||
// 商户ID
|
||||
merchantId: undefined,
|
||||
merchantName: undefined,
|
||||
merchantAvatar: undefined,
|
||||
merchantComments: undefined,
|
||||
// 状态(0:未上架,1:上架)
|
||||
isShow: undefined,
|
||||
// 状态, 0上架 1待上架 2待审核 3审核不通过
|
||||
status: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 修改时间
|
||||
updateTime: undefined,
|
||||
// 父级分类名称
|
||||
parentName: undefined,
|
||||
// 父级分类路径
|
||||
parentPath: undefined,
|
||||
// 分类名称
|
||||
categoryName: undefined,
|
||||
// 评分
|
||||
rate: undefined,
|
||||
// 是否已购买
|
||||
isBuy: undefined,
|
||||
// 是否已安装插件
|
||||
installed: undefined,
|
||||
// 产品参数
|
||||
parameters: undefined,
|
||||
// 产品链接
|
||||
links: undefined,
|
||||
// 插件入口
|
||||
path: undefined,
|
||||
// 标签
|
||||
tag: undefined,
|
||||
// 菜单ID
|
||||
menuId: undefined,
|
||||
});
|
||||
|
||||
const handleChange = (index:any): void => {
|
||||
cart.value.num = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const handleChargingMethod = (index: any): void => {
|
||||
cart.value.month = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const computeTotalPrice = () => {
|
||||
// cart.value.totalPrice = cart.value.num * cart.value.payPrice * cart.value.month;
|
||||
// cart.value.totalPrice = Math.round(cart.value.totalPrice * 100) / 100;
|
||||
}
|
||||
|
||||
const changeIsAgree = (index: any) => {
|
||||
if (index) {
|
||||
active.value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const handlePayType = (index: any) => {
|
||||
cart.value.payType = index;
|
||||
onPay();
|
||||
}
|
||||
|
||||
// 统一下单
|
||||
const onPay = () => {
|
||||
visible.value = true;
|
||||
active.value = 3;
|
||||
useClientRequest<ApiResult<any>>(`/shop/shop-order/createOrder`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 () => {
|
||||
// 要求登录
|
||||
if (!token.value || token.value == '') {
|
||||
navigateTo(`/passport/login`)
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<CmsProduct>>(`/cms/cms-product/${getIdBySpm(5)}`)
|
||||
console.log(response.value?.data,'response')
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '选择套餐';
|
||||
if(response.value?.data.price){
|
||||
cart.value.payPrice = response.value?.data.price;
|
||||
cart.value.comments = `${response.value?.data.title}`;
|
||||
if(cart.value.num){
|
||||
cart.value.totalPrice = response.value?.data.price * cart.value.num
|
||||
}
|
||||
let goodsName = '';
|
||||
// if(form.type == 0){
|
||||
// goodsName = `【软件】${form.title}`
|
||||
// }
|
||||
// if(form.type == 1){
|
||||
// goodsName = `【插件】${form.title}`
|
||||
// }
|
||||
// cart.value.orderProduct?.push({
|
||||
// goodsId: form.productId,
|
||||
// goodsName: goodsName,
|
||||
// image: form.image,
|
||||
// price: response.value?.data.price,
|
||||
// totalPrice: cart.value.totalPrice,
|
||||
// num: cart.value.num,
|
||||
// month: cart.value?.month,
|
||||
// payType: cart.value.payType
|
||||
// });
|
||||
}
|
||||
useHead({
|
||||
title: `${form.title}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 扫码支付
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
ElMessage.error('还没有评分哦!')
|
||||
// if (form.rate === 0) {
|
||||
// ElMessage.error('还没有评分哦!')
|
||||
// return false;
|
||||
// }
|
||||
// form.productId = props.productId;
|
||||
// useClientRequest<ApiResult<any>>(`/cms/cms-product-comment/`, {
|
||||
// method: 'POST',
|
||||
// body: form
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// visible.value = false
|
||||
// resetFields();
|
||||
// emit('done',1)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
// 完成支付
|
||||
const onDone = () => {
|
||||
useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
productId: getIdBySpm(5)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 创建站点
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const {data: website} = await useServerRequest<ApiResult<Company>>('/cms/website/createWebsite', {
|
||||
baseURL: 'http://127.0.0.1:9002/api',
|
||||
method: 'post',
|
||||
body: company.value?.data
|
||||
})
|
||||
if (website.value?.code == 401) {
|
||||
console.log('网站不存在', website.value)
|
||||
}
|
||||
if (website.value?.code == 0) {
|
||||
console.log('网站存在', website.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 创建站点
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const {data: website} = await useServerRequest<ApiResult<Company>>('/cms/website/createWebsite', {
|
||||
baseURL: 'http://127.0.0.1:9002/api',
|
||||
method: 'post',
|
||||
body: company.value?.data
|
||||
})
|
||||
if (website.value?.code == 401) {
|
||||
console.log('网站不存在', website.value)
|
||||
}
|
||||
if (website.value?.code == 0) {
|
||||
console.log('网站存在', website.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,168 +0,0 @@
|
||||
<template>
|
||||
<el-affix :offset="0" @change="onAffix">
|
||||
<div class="affix justify-between p-2 opacity-90 border-b-solid border-gray-200 border-b-1" :class="affix ? 'bg-white w-full' : 'hidden'">
|
||||
<div class="w-3/4 m-auto flex justify-between">
|
||||
<a class="goods-name text-xl font-bold cursor-pointer hover:text-gray-900">{{ goods?.goodsName }}</a>
|
||||
<div class="affix-bar">
|
||||
<el-anchor :offset="100" direction="horizontal" :marker="false">
|
||||
<el-anchor-link :href="`#basic`">
|
||||
参数信息
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#photo`">
|
||||
图文详情
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#comment`">
|
||||
用户评价
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#buynow`">
|
||||
<el-button type="danger" size="small">立即购买</el-button>
|
||||
</el-anchor-link>
|
||||
</el-anchor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-affix>
|
||||
|
||||
<div id="buynow" class="flex flex-col w-full md:w-screen-xl m-auto md:pt-[60px]" v-if="goods">
|
||||
|
||||
<Breadcrumb :data="goods" />
|
||||
|
||||
<div class="content bg-white rounded-xl">
|
||||
|
||||
<!-- <ProductShopInfo :data="goods?.merchant" />-->
|
||||
|
||||
<div id="buynow" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<div class="goods-image flex gap-xl">
|
||||
<div class="gap-xs flex flex-col" v-if="goods?.files">
|
||||
<el-avatar v-for="item in JSON.parse(goods.files)" :src="item.url" size="large" shape="square" />
|
||||
</div>
|
||||
<el-image :src="goods?.image" fit="contain" class="w-2xl h-2xl bg-gray-100 border-radius:30px"></el-image>
|
||||
</div>
|
||||
<div class="goods-info flex flex-col gap-xs">
|
||||
<div class="goods-name text-2xl">{{ goods.goodsName }}</div>
|
||||
<div class="goods-price text-2xl red">¥{{ Number(goods?.salePrice) * Number(goods?.num) }}</div>
|
||||
<div class="text-green-7">购买得积分</div>
|
||||
<div class="text-gray-4">配送:无需配送</div>
|
||||
<div class="text-gray-4">保障:假一赔四 退货包运费 极速退款</div>
|
||||
<div class="text-gray-4">销量: {{ goods.sales }}</div>
|
||||
<!-- <template v-for="spec in goods?.goodsSpecValue">-->
|
||||
<!-- <div class="flex items-center">-->
|
||||
<!-- <div class="text-gray-4">{{ spec.value }}:</div>-->
|
||||
<!-- <el-radio-group v-model="goods.radio">-->
|
||||
<!-- <el-radio v-for="(specValue,specIndex) in spec.detail" :label="specIndex" border>{{ specValue }}</el-radio>-->
|
||||
<!-- </el-radio-group>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<div class="text-gray-4">
|
||||
已选中:{{ goods.radio }}
|
||||
</div>
|
||||
<div class="text-gray-4">
|
||||
数量:
|
||||
<el-input-number v-model="goods.num" :min="1" :max="10" label="描述文字"></el-input-number>
|
||||
</div>
|
||||
<div class="py-5">
|
||||
<el-button-group size="large">
|
||||
<el-button type="danger">立即购买</el-button>
|
||||
<el-button type="warning">加入购物车</el-button>
|
||||
</el-button-group>
|
||||
<el-button size="large" class="ml-3">收藏</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="basic" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<el-descriptions class="margin-top" title="参数信息" :column="1" border>
|
||||
<el-descriptions-item label="品牌">websoft</el-descriptions-item>
|
||||
<el-descriptions-item label="版本">2.0</el-descriptions-item>
|
||||
<el-descriptions-item label="开发语言">Java、Vue3、Nuxt</el-descriptions-item>
|
||||
<el-descriptions-item label="版本">
|
||||
<el-tag size="small">授权版</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">江苏省苏州市吴中区吴中大道 1188 号</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div id="photo" class="bg-white p-4 mt-4 flex gap-xl rounded-xl flex-col">
|
||||
<div class="font-bold">图文详情</div>
|
||||
<div class="files flex flex-col w-3/4" v-if="goods?.files">
|
||||
<el-image v-for="item in JSON.parse(goods.files)" :src="item.url" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="comment" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<div class="font-bold">用户评价</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!goods">
|
||||
<el-empty description="404 该商品找不到了222"></el-empty>
|
||||
</div>
|
||||
</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 Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {useProductAffix} from "~/composables/configState";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const affix = useProductAffix();
|
||||
|
||||
// 商品ID
|
||||
const goodsId = ref();
|
||||
// 页面信息
|
||||
const form = ref<Navigation>();
|
||||
// 商品信息
|
||||
const goods = ref<Goods>();
|
||||
|
||||
const onAffix = (index: boolean) => {
|
||||
affix.value = index;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
const reload = async () => {
|
||||
// TODO 请求导航页面数据
|
||||
// const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{
|
||||
// query: {
|
||||
// path: goodsId.value
|
||||
// }
|
||||
// })
|
||||
// if(nav.value?.data){
|
||||
// form.value = nav.value.data;
|
||||
// }
|
||||
|
||||
// TODO 获取商品详情
|
||||
console.log(goodsId.value,'sss')
|
||||
const { data: info } = await useServerRequest<ApiResult<Goods>>('/shop/goods/' + getIdBySpm(5))
|
||||
|
||||
goods.value = info.value?.data;
|
||||
console.log(goods.value)
|
||||
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.query.spm,
|
||||
(spm) => {
|
||||
console.log(spm)
|
||||
// TODO 方案一:从spm参数提取商品ID
|
||||
const spmValue = String(spm).split('.')
|
||||
if(spmValue[5]){
|
||||
goodsId.value = spmValue[5];
|
||||
}
|
||||
console.log(goodsId.value)
|
||||
// TODO 方案二(优先级更高):从params获取商品ID
|
||||
const { detail } = route.params
|
||||
const split = String(detail).split('.');
|
||||
if(Number(split[0]) > 0){
|
||||
goodsId.value = split[0];
|
||||
}
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,75 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0}" :data="data" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const data = ref<PageResult<Company>>();
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = (index: number) => {
|
||||
page.value = index;
|
||||
reload();
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 12,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
data.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => getIdBySpm(5),
|
||||
(id) => {
|
||||
console.log(id,'id = .>>>>')
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
146
pages/search/[id].vue
Normal file
146
pages/search/[id].vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col :span="5" class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" />
|
||||
</el-col>
|
||||
<el-col :span="19" class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ $t('searchKeywords') }}:{{ where.keywords }}
|
||||
</h2>
|
||||
</div>
|
||||
<el-alert v-if="where.keywords" :title="`${$t('search.results')}:${$t('search.find')} ${total} ${$t('search.nums')}`" type="warning" :closable="false" />
|
||||
<div class="content">
|
||||
|
||||
<ul class="news_listn clearfix">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<li class="clearfix">
|
||||
<a :href="detail(item)" target="_blank" class="n-left fl">
|
||||
<el-image :src="item.image" :fit="`scale-down`" class="w-[240px] h-[158px]" :alt="item.title" />
|
||||
</a>
|
||||
<div class="n-right fr">
|
||||
<h3><a :href="detail(item)" target="_blank" :title="item.title" v-html="replaceKeywords(item.title)"></a></h3>
|
||||
<div v-html="replaceKeywords(item.comments)" class="line-clamp-2"></div>
|
||||
<div class="date">{{ $t('createTime') }}:{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>
|
||||
<div class="date">{{ $t('search.column') }}:{{ item.categoryName }}</div>
|
||||
<div class="n-more"><a :href="detail(item)">{{ $t('seeMore') }}>></a></div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<div class="clearboth"></div>
|
||||
</ul>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import dayjs from "dayjs";
|
||||
import {ArrowRight} from '@element-plus/icons-vue'
|
||||
import {detail, getPath} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsModel} from "~/api/cms/cmsModel";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 10,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const replaceKeywords = (text: any) => {
|
||||
return text.replace(`${where.keywords}`,'<font color=#ff0000>' + where.keywords + '</font>');
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
listCmsModel({
|
||||
model: getPath(1)
|
||||
}).then(response => {
|
||||
const data = response[0];
|
||||
if(data){
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data?.comments || `${route.params.id}`,
|
||||
keywords: `${route.params.id}`,
|
||||
titleTemplate: `【搜索结果】${route.params.id}` + ' - %s',
|
||||
})
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: 2847,
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(!getPath(1)){
|
||||
return ElMessage.error('请输入搜索关键词!');
|
||||
}
|
||||
where.keywords = `${route.params.id}`;
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.sitemp h2{
|
||||
width: 500px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,138 +0,0 @@
|
||||
<template>
|
||||
<div class="login-layout mt-[100px] min-h-2xl m-auto sm:w-screen-sm w-full">
|
||||
<div class="title text-xl text-gray-700 px-4 py-2 font-500 text-center">
|
||||
<div class="sm:w-screen-sm w-full">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
class="w-full"
|
||||
size="large"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tabs v-model="activeName" class="my-3" @tab-click="handleClick">
|
||||
<el-tab-pane label="应用" name="web"></el-tab-pane>
|
||||
<el-tab-pane label="公众号" name="mp-official"></el-tab-pane>
|
||||
<el-tab-pane label="小程序" name="mp-weixin"></el-tab-pane>
|
||||
<el-tab-pane label="移动应用" name="app"></el-tab-pane>
|
||||
<el-tab-pane label="小商店" name="mp-shop"></el-tab-pane>
|
||||
<el-tab-pane label="其他" name="other"></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="activeName === 'web'">
|
||||
<div class="search bg-white rounded-lg p-3 w-full" v-if="websites.length > 0">
|
||||
<div class="title text-gray-400 px-4 py-2 mb-4">网站应用</div>
|
||||
<div class="result px-3">
|
||||
<div v-for="(item,index) in websites" :key="index" class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-2 cursor-pointer">
|
||||
<el-space @click="navTo(item)">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="item.websiteLogo" style="background-color: #f3f3f3" size="large" />
|
||||
</div>
|
||||
<div class="app-info flex flex-col">
|
||||
<div class="text-lg">{{ item.websiteName }}</div>
|
||||
<div class="text-gray-400">{{ item.comments }}</div>
|
||||
<div class="text-gray-300 text-xs-1">{{ item.websiteType }}</div>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="px-1 text-center text-gray-500 min-h-xs">
|
||||
{{ resultText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getPath} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
import {navigateTo} from "#imports";
|
||||
import type {Website} from "~/api/cms/website/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Tenant[]>([]);
|
||||
const websites = ref<Website[]>([]);
|
||||
const activeName = ref('web');
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const navTo = (item: Website) => {
|
||||
if(item.domain){
|
||||
openSpmUrl(`https://${item.domain}`,item,item.tenantId)
|
||||
return;
|
||||
}
|
||||
openSpmUrl(`https://${item.websiteCode}.wsdns.cn`,item,item.tenantId)
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: getPath()}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
useHead({
|
||||
title: `搜索结果 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
</script>
|
||||
286
pages/show/[id].vue
Normal file
286
pages/show/[id].vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<!-- <template #breadcrumb>-->
|
||||
<!-- <Breadcrumb :data="form" :categoryName="form?.categoryName" />-->
|
||||
<!-- </template>-->
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> {{ page.title }} </span>
|
||||
</template>
|
||||
<el-row :gutter="24" class="mt-5">
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card shadow="hover" class="mb-5">
|
||||
<el-descriptions title="参数信息" :column="2" border>
|
||||
<el-descriptions-item :span="2" label="产品名称">{{page.title}}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="租户ID"><span class="text-orange-500">{{form.title}}</span></el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="插件ID"><span class="text-orange-500">{{form.menuId || '-'}}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="控制台"><a class="cursor-pointer" @click="openUrl(`https://${form.domain}`)">{{form.domain}}</a></el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in form.parameters" :key="index" :label="item.name">{{ item.value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template v-if="form.accounts && form.accounts.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="登录账号" :column="1" border>
|
||||
<template v-for="(item,index) in form.accounts" :key="index">
|
||||
<el-descriptions-item :label="item.type" v-if="item.account">
|
||||
还没有账号? <el-button type="text" @click="openSpmUrl(`/passport/regis`)">立即注册</el-button>
|
||||
</el-descriptions-item>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.gits && form.gits.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="代码仓库" :column="1" border>
|
||||
<el-descriptions-item v-for="(item,index) in form.gits" :key="index" :label="item.title">
|
||||
<el-input v-model="item.domain" readonly />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.files && form.files.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="图文详情" />
|
||||
<div v-for="(item,index) in JSON.parse(form.files)" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.content">
|
||||
<p v-html="form.content" class="content"></p>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- 产品评论 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card shadow="hover" class="mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>推荐产品</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <el-space class="flex items-center">-->
|
||||
<!-- <div class="avatar">-->
|
||||
<!-- <el-avatar :size="55" :src="form.image"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="flex flex-col">-->
|
||||
<!-- <span class="font-bold text-lg text-gray-600">{{ form.title }}</span>-->
|
||||
<!-- <span class="text-gray-400 pb-1 line-clamp-2">{{ form.comments }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-space>-->
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useLayout, usePage, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import {getIdBySpm, getNavIdByParamsId, openUrl} from "~/utils/common";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Comments from './components/Comments.vue';
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
import {getCmsArticle, pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const i18n = useI18n();
|
||||
const total = ref(0);
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const comments = ref<CompanyComment[]>([]);
|
||||
const commentsTotal = ref(0);
|
||||
const commentsPage = ref(1);
|
||||
const navId = ref();
|
||||
const activeName = ref();
|
||||
const url =
|
||||
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
|
||||
const srcList = ref<any[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 标签
|
||||
tags: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户ID
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const doComments = async (page: any) => {
|
||||
commentsPage.value = page;
|
||||
await reloadComments();
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
}
|
||||
|
||||
// 加载评论
|
||||
const reloadComments = async () => {
|
||||
const {data: commentsResponse} = await useServerRequest<ApiResult<PageResult<CompanyComment>>>('/system/company-comment/page', {
|
||||
params: {
|
||||
companyId: getIdBySpm(5),
|
||||
page: commentsPage.value,
|
||||
// status: 1
|
||||
}
|
||||
})
|
||||
if(commentsResponse.value && commentsResponse.value?.data){
|
||||
comments.value = commentsResponse.value?.data?.list
|
||||
commentsTotal.value = commentsResponse.value?.data?.count;
|
||||
}
|
||||
}
|
||||
|
||||
// 读取导航详情
|
||||
const reload = async () => {
|
||||
getCmsArticle(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
assignFields(data)
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
// listCmsNavigation({
|
||||
// parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
// }).then(categoryData => {
|
||||
// category.value = categoryData;
|
||||
// // 加载文章列表
|
||||
// if(data.parentId == 0 && category.value.length > 0){
|
||||
// where.parentId = data.navigationId;
|
||||
// }else {
|
||||
// where.categoryId = data.navigationId;
|
||||
// }
|
||||
// pageCmsArticle(where).then(response => {
|
||||
// if(response){
|
||||
// total.value = response?.count;
|
||||
// list.value = response?.list;
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
pages/show/components/Comments.vue
Normal file
201
pages/show/components/Comments.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-card shadow="hover" v-if="comments" class="mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl flex justify-between">
|
||||
<span>评分和评价</span>
|
||||
<div class="comments">
|
||||
<el-button @click="onComplaint">投诉</el-button>
|
||||
<el-button type="primary" @click="onComments">发表评论</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default>
|
||||
<template v-if="comments.length > 0">
|
||||
<div class="w-full">
|
||||
<div v-for="(item,index) in comments" :key="index"
|
||||
class="flex flex-col border-b-2 border-gray-200 pb-2 mb-3"
|
||||
style="border-bottom:1px solid #f3f3f3">
|
||||
<el-space class="user-info flex items-start" style="align-items:normal">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="item.logo"/>
|
||||
</div>
|
||||
<div class="nickname flex flex-col">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<span class="font-bold">{{ item.tenantName }}</span>
|
||||
<el-rate v-model="item.rate" disabled size="small"/>
|
||||
</el-space>
|
||||
<span class="text-xs text-gray-400">{{ item.createTime }}</span>
|
||||
<div class="comments py-2" v-html="item.comments"></div>
|
||||
<template v-if="item.children" v-for="(sub,index2) in item.children" :key="index2">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<el-avatar :src="sub.logo" size="small"/>
|
||||
<span class="font-bold">{{ sub.tenantName }}</span>
|
||||
<span class="text-xs text-gray-400">{{ sub.createTime }}</span>
|
||||
</el-space>
|
||||
<div class="comments py-2" v-html="sub.comments"></div>
|
||||
</template>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination flex justify-center">
|
||||
<el-pagination background layout="prev, pager, next" size="small" :total="count" @change="onPageChange"/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
暂无用户评论
|
||||
</template>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- 发表评论 -->
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="发表评论"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<el-form-item prop="rate">
|
||||
<el-rate v-model="form.rate" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="comments">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="最多300字,支持 markdown 语法。请认真填写评论内容,以便于帮助作者更好完善插件,如需反馈问题,请到下方插件提问进行提交。"/>
|
||||
</el-form-item>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 发起投诉 -->
|
||||
<el-dialog
|
||||
v-model="visible2"
|
||||
title="为什么举报此内容?"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible2 = false"
|
||||
>
|
||||
<el-checkbox-group v-model="checkList">
|
||||
<el-checkbox label="与我无关" value="与我无关"/>
|
||||
<el-checkbox label="文章过时" value="文章过时"/>
|
||||
<el-checkbox label="标题有误" value="标题有误"/>
|
||||
<el-checkbox label="图像质量差或视觉缺陷" value="图像质量差或视觉缺陷"/>
|
||||
<el-checkbox label="垃圾邮件" value="垃圾邮件"/>
|
||||
<el-checkbox label="成人或违法违规内容" value="成人或违法违规内容"/>
|
||||
<el-checkbox label="侵犯知识产权" value="侵犯知识产权"/>
|
||||
</el-checkbox-group>
|
||||
<div class="py-3">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="在此处输入反馈。请记住不要包含个人信息,如电话号码。"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible2 = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {FullScreen} from '@element-plus/icons-vue'
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {FormInstance, FormRules} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import {reactive, ref} from "vue";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
companyId?: number;
|
||||
comments?: CompanyComment[];
|
||||
count?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const visible = ref<boolean>(false);
|
||||
const visible2 = ref<boolean>(false);
|
||||
const checkList = ref<string[]>([]);
|
||||
const loading = ref<boolean>(true)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', page: number): void
|
||||
}>()
|
||||
|
||||
// 配置信息
|
||||
const {form, resetFields} = useFormData<CompanyComment>({
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
userId: undefined,
|
||||
companyId: undefined,
|
||||
rate: undefined,
|
||||
sortNumber: undefined,
|
||||
comments: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<any>>({
|
||||
rate: [
|
||||
{required: true, message: '请输入评分', trigger: 'blur'},
|
||||
],
|
||||
comments: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
})
|
||||
|
||||
const onComments = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
const onComplaint = () => {
|
||||
visible2.value = true;
|
||||
}
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
emit('done', page)
|
||||
}
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (form.rate === 0) {
|
||||
ElMessage.error('还没有评分哦!')
|
||||
return false;
|
||||
}
|
||||
form.companyId = Number(getIdBySpm(5));
|
||||
useClientRequest<ApiResult<any>>(`/system/company-comment`, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
visible.value = false
|
||||
resetFields();
|
||||
emit('done',0)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
135
pages/show/components/PageBanner.vue
Normal file
135
pages/show/components/PageBanner.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex mt-15">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto">
|
||||
<Breadcrumb :data="form" :categoryName="form?.categoryName"/>
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full sm:px-0 px-4">
|
||||
<div class="flex flex-1">
|
||||
<template v-if="form.image">
|
||||
<el-image :src="form.image" shape="square"
|
||||
class="hidden-sm-and-down bg-white w-[128px] h-[128px] cursor-pointer rounded-avatar shadow-sm hover:shadow mr-4"/>
|
||||
<!-- <el-image :src="form.image" shape="square" :size="80"-->
|
||||
<!-- class="hidden-sm-and-up bg-white rounded-avatar-xs shadow-sm hover:shadow mr-4"/>-->
|
||||
</template>
|
||||
<div class="title flex flex-col">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl">
|
||||
<span v-if="form.title">{{ form.title }}</span>
|
||||
</h1>
|
||||
<div class="my-1 text-sm text-gray-500 w-auto sm:max-w-3xl max-w-xs flex-1 dark:text-gray-400">
|
||||
{{ form?.comments || desc }}
|
||||
</div>
|
||||
<!-- <a class="company-name text-sm my-1">-->
|
||||
<!-- {{ form.companyName || 'WebSoft Inc.' }}-->
|
||||
<!-- </a>-->
|
||||
<el-rate v-model="form.rate" disabled />
|
||||
<div class="btn">
|
||||
<el-space class="mt-4">
|
||||
<el-button>产品控制台</el-button>
|
||||
<el-button>帮助文档</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {FullScreen} from '@element-plus/icons-vue'
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
const token = useToken();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: CmsArticle;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void
|
||||
}>()
|
||||
|
||||
const onBuy = (item: Company) => {
|
||||
// if(item.type === 1){
|
||||
// // 插件
|
||||
// openSpmUrl(`/product/checkout`,item,item.productId)
|
||||
// }else {
|
||||
// // 产品
|
||||
// openSpmUrl(`/product/create`,item,item.productId)
|
||||
// }
|
||||
if (!token.value || token.value == '') {
|
||||
ElMessage.error('请先登录');
|
||||
setTimeout(() => {
|
||||
openUrl(`/product/create`, item, item.companyId)
|
||||
}, 500)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 安装插件
|
||||
const installPlug = () => {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '安装中...'
|
||||
})
|
||||
useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
companyId: getIdBySpm(5)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
setTimeout(() => {
|
||||
ElMessage.success(res.message);
|
||||
loading.close()
|
||||
emit('done')
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.rounded-avatar {
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.rounded-avatar-xs {
|
||||
border-radius: 20px;
|
||||
}
|
||||
</style>
|
||||
151
pages/tags/[id].vue
Normal file
151
pages/tags/[id].vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div id="container" class="clearfix xl:w-screen-xl m-auto">
|
||||
|
||||
<div class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" :title="$t('product.title')" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ $t('label') }}:{{ where.tags }}
|
||||
</h2>
|
||||
<Breadcrumb :data="page" :categoryName="$t('label')" />
|
||||
</div>
|
||||
<el-alert v-if="where.tags" :title="`${$t('search.results')}:${$t('search.find')} ${total} ${$t('search.nums')}`" type="warning" :closable="false" />
|
||||
<div class="content">
|
||||
|
||||
<ul class="news_listn clearfix">
|
||||
<template v-for="(item,index) in list" key="index">
|
||||
<li class="clearfix">
|
||||
<a :href="detail(item)" target="_blank" class="n-left fl">
|
||||
<el-image :src="item.image" :fit="`scale-down`" class="w-[240px] h-[158px]" :alt="item.title" />
|
||||
</a>
|
||||
<div class="n-right fr">
|
||||
<h3><a :href="detail(item)" target="_blank" :title="item.title" v-html="replaceKeywords(item.title)"></a></h3>
|
||||
<div v-html="replaceKeywords(item.comments)" class="line-clamp-2"></div>
|
||||
<div class="date">{{ $t('createTime') }}:{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>
|
||||
<div class="date">{{ $t('search.column') }}:{{ item.categoryName }}</div>
|
||||
<div class="n-more"><a :href="detail(item)">{{ $t('seeMore') }}>></a></div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<div class="clearboth"></div>
|
||||
</ul>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import dayjs from "dayjs";
|
||||
import {ArrowRight} from '@element-plus/icons-vue'
|
||||
import {detail, getPath} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {findTags, pageCmsArticle, pageTags} from "~/api/cms/cmsArticle";
|
||||
import {listCmsModel} from "~/api/cms/cmsModel";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
tags: '',
|
||||
page: 1,
|
||||
limit: 10,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const replaceKeywords = (text: any) => {
|
||||
return text.replace(`${where.tags}`,'<font color=#ff0000>' + where.tags + '</font>');
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
listCmsModel({
|
||||
model: getPath(1)
|
||||
}).then(response => {
|
||||
const data = response[0];
|
||||
if(data){
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data?.comments || `${route.params.id}`,
|
||||
keywords: `${route.params.id}`,
|
||||
titleTemplate: `【搜索结果】${route.params.id}` + ' - %s',
|
||||
})
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: i18n.locale.value == 'en' ? 1073 : 998,
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(!getPath(1)){
|
||||
return ElMessage.error('请输入搜索关键词!');
|
||||
}
|
||||
where.tags = `${route.params.id}`;
|
||||
pageTags({
|
||||
tags: `${route.params.id}`
|
||||
}).then(response => {
|
||||
if(response){
|
||||
total.value = response?.length;
|
||||
list.value = response;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.sitemp h2{
|
||||
width: 500px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-button class="w-full" @click="openSpmUrl(`https://${item.tenantId}.wsdns.cn`,item,item.companyId,true)">预览</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: Company[];
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto md:p-0 px-4">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span v-if="layout.title">{{ layout.title }}</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
<div class="flex justify-between w-full items-center mt-4">
|
||||
<el-space>
|
||||
<div class="w-[500px]">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button
|
||||
size="large"
|
||||
@click="openSpmUrl(`/passport/login`)"
|
||||
>
|
||||
创建
|
||||
</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const config = useConfigInfo();
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Company[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/pageAll',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<PageBanner title="实名认证" desc="Authentication"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3 ">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="reload"/>
|
||||
<div class="flash bg-white rounded-lg px-8 py-4 w-full">
|
||||
<Auth @done="reload"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useWebsite} from "~/composables/configState";
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import Auth from './components/Auth.vue';
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import Base from "~/pages/user/components/Base.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const merchantApply = ref<ShopMerchantApply>();
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchantApply>>('/shop/shop-merchant-apply/getByUserId', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
merchantApply.value = response.value.data;
|
||||
}
|
||||
if(config.value){
|
||||
useHead({
|
||||
title: `实名认证 - ${config.value?.siteName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
@@ -1,460 +0,0 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="loading"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-tabs v-model="form.type" class="flash bg-white ml-0">
|
||||
<el-tab-pane :name="0" label="个人认证"/>
|
||||
<el-tab-pane :name="1" label="企业认证"/>
|
||||
</el-tabs>
|
||||
<!-- 已完成认证 -->
|
||||
<template v-if="form.status === 1">
|
||||
<template v-if="form.type == 0">
|
||||
<el-result
|
||||
icon="success"
|
||||
title="个人认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="企业认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 申请被驳回 -->
|
||||
<template v-if="form.status === 2">
|
||||
<el-result
|
||||
icon="error"
|
||||
title="您的申请已被驳回"
|
||||
:sub-title="`${form.reason}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<!-- 未完成认证 -->
|
||||
<template v-if="form.status === 0 && form.checkStatus">
|
||||
<el-result
|
||||
icon="warning"
|
||||
title="审核中"
|
||||
:sub-title="`您的申请已提交,请耐心等待工作人员的审核,非常感谢`"
|
||||
>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-if="form.status === 0 && !form.checkStatus">
|
||||
<template v-if="form.type == 1">
|
||||
<el-form-item label="企业名称" prop="merchantName">
|
||||
<el-input v-model="form.merchantName" placeholder="请输入企业名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="社会信用代码" prop="merchantCode">
|
||||
<el-input v-model="form.merchantCode" placeholder="请输入社会信用代码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照" required>
|
||||
<el-upload
|
||||
v-model:file-list="yyzzFile"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="yyzzRemove"
|
||||
:on-success="yyzzOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
<el-form-item label="门头照片">
|
||||
<el-upload
|
||||
v-model:file-list="image"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="imageRemove"
|
||||
:on-success="imageOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
<el-form-item label="其他证件">
|
||||
<el-upload
|
||||
v-model:file-list="files"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="9"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="filesRemove"
|
||||
:on-success="filesOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="真实姓名" prop="realName">
|
||||
<el-input v-model="form.realName" placeholder="请输入真实姓名"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" placeholder="请输入真实有效的手机号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号码" prop="idCard">
|
||||
<el-input v-model="form.idCard" placeholder="请输入证件号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证" required>
|
||||
<el-upload
|
||||
v-model:file-list="sfzFile"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="2"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="sfzRemove"
|
||||
:on-success="sfzSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业" prop="category">
|
||||
<el-cascader
|
||||
v-model="industry"
|
||||
:options="industryData"
|
||||
placeholder="请选择所属行业"
|
||||
class="w-full"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务描述" prop="comments">
|
||||
<el-input v-model="form.comments" placeholder="请输入公司业务介绍" :rows="5" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="注册协议">
|
||||
<el-checkbox v-model="isAgree">
|
||||
请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-space class="flex">
|
||||
<el-button type="primary" size="large" :disabled="!isAgree" @click="submitForm(formRef)">
|
||||
{{ isUpdate ? '提交修改' : '提交申请' }}
|
||||
</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-form>
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<el-image w-full :src="dialogImageUrl" alt="查看证件" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import {Plus} from '@element-plus/icons-vue'
|
||||
import type {FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import industryData from '@/assets/json/industry-data.json';
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ShopMerchant} from "~/api/shop/shopMerchant/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
|
||||
const token = useToken();
|
||||
const tenantId = localStorage.getItem('TID_ADMIN')
|
||||
const formRef = ref<FormInstance>()
|
||||
const yyzzFile = ref<UploadUserFile[]>([])
|
||||
const sfzFile = ref<UploadUserFile[]>([])
|
||||
const sfzStr = ref<string[]>([]);
|
||||
const files = ref<UploadUserFile[]>([])
|
||||
const filesStr = ref<string[]>([])
|
||||
const image = ref<UploadUserFile[]>([])
|
||||
const industry = ref<any[]>([])
|
||||
const loading = ref<boolean>(true)
|
||||
const isUpdate = ref<boolean>(false)
|
||||
const isAgree = ref<boolean>(false)
|
||||
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const {form, assignFields, resetFields} = useFormData<ShopMerchantApply>({
|
||||
applyId: undefined,
|
||||
type: 0,
|
||||
merchantName: undefined,
|
||||
merchantCode: undefined,
|
||||
image: undefined,
|
||||
phone: undefined,
|
||||
realName: undefined,
|
||||
idCard: undefined,
|
||||
sfz1: undefined,
|
||||
sfz2: undefined,
|
||||
yyzz: undefined,
|
||||
name2: undefined,
|
||||
shopType: undefined,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
category: undefined,
|
||||
commission: undefined,
|
||||
keywords: undefined,
|
||||
files: undefined,
|
||||
ownStore: undefined,
|
||||
recommend: undefined,
|
||||
completedTime: undefined,
|
||||
goodsReview: undefined,
|
||||
userId: undefined,
|
||||
comments: undefined,
|
||||
reason: undefined,
|
||||
checkStatus: undefined,
|
||||
status: 0,
|
||||
sortNumber: undefined
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<ShopMerchantApply>>({
|
||||
realName: [
|
||||
{required: true, message: '请输入真实姓名', trigger: 'blur'},
|
||||
{min: 2, max: 5, message: '长度应为2-5个字符', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
idCard: [
|
||||
{required: true, message: '请输入证件号码', trigger: 'blur'},
|
||||
{min: 18, max: 18, message: '证件号码长度应为18位', trigger: 'blur'},
|
||||
],
|
||||
merchantName: [
|
||||
{required: true, message: '请输入企业名称', trigger: 'blur'}
|
||||
],
|
||||
merchantCode: [
|
||||
{required: true, message: '请输入社会信用代码', trigger: 'blur'}
|
||||
],
|
||||
category: [
|
||||
{required: true, message: '请选择所属行业', trigger: 'change'}
|
||||
]
|
||||
})
|
||||
|
||||
const yyzzRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.yyzz = '';
|
||||
}
|
||||
|
||||
const yyzzOnSuccess = (e: any) => {
|
||||
form.yyzz = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const sfzRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.sfz1 = '';
|
||||
form.sfz2 = '';
|
||||
}
|
||||
|
||||
const sfzSuccess = (e:any) => {
|
||||
sfzStr.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
const imageOnSuccess = (e: any) => {
|
||||
form.image = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const imageRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.image = '';
|
||||
}
|
||||
|
||||
const filesRemove: UploadProps['onRemove'] = (uploadFile) => {
|
||||
const index = filesStr.value.findIndex(f => f == uploadFile.url);
|
||||
filesStr.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const filesOnSuccess = (e: any) => {
|
||||
filesStr.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
// 所属行业
|
||||
const handleChange = (value: any) => {
|
||||
let parent = ''
|
||||
let category = ''
|
||||
industryData.map(d => {
|
||||
if (d.value == value[0]) {
|
||||
form.parentId = d.value
|
||||
parent = d.label
|
||||
if (d.children) {
|
||||
d.children.map(c => {
|
||||
if (c.value == value[1]) {
|
||||
category = c.label
|
||||
form.categoryId = c.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
form.category = `${parent}/${category}`
|
||||
}
|
||||
|
||||
const onUpdate = () => {
|
||||
form.status = 0;
|
||||
form.checkStatus = false
|
||||
}
|
||||
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
dialogImageUrl.value = uploadFile.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
console.log('submit!',valid)
|
||||
if (form.type == 0) {
|
||||
form.shopType = '个人开发者';
|
||||
form.merchantName = form.realName;
|
||||
}
|
||||
if(form.type == 1){
|
||||
form.shopType = '企业开发者';
|
||||
if(form.yyzz == ''){
|
||||
return ElMessage.error('请上营业执照');
|
||||
}
|
||||
if(filesStr.value.length > 0){
|
||||
form.files = JSON.stringify(filesStr.value)
|
||||
}
|
||||
}
|
||||
if(sfzStr.value.length == 1){
|
||||
return ElMessage.error('请上传身份证正反面');
|
||||
}
|
||||
form.sfz1 = sfzStr.value[0];
|
||||
form.sfz2 = sfzStr.value[1];
|
||||
useClientRequest<ApiResult<any>>(`/shop/shop-merchant-apply`, {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: isUpdate.value ? 'PUT' : 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
reload();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!', fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
resetFields();
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchant>>('/shop/shop-merchant-apply/getByUserId')
|
||||
if (response.value?.data) {
|
||||
isUpdate.value = true;
|
||||
assignFields(response.value.data)
|
||||
industry.value = []
|
||||
industry.value.push(form.parentId)
|
||||
industry.value.push(form.categoryId)
|
||||
files.value = []
|
||||
filesStr.value = []
|
||||
yyzzFile.value = []
|
||||
sfzFile.value = []
|
||||
sfzStr.value = []
|
||||
image.value = []
|
||||
if(form.sfz1){
|
||||
sfzFile.value.push({
|
||||
uid: 1,
|
||||
url: form.sfz1,
|
||||
name: '身份证正面',
|
||||
})
|
||||
}
|
||||
if(form.sfz2){
|
||||
sfzFile.value.push({
|
||||
uid: 2,
|
||||
url: form.sfz2,
|
||||
name: '身份证反面',
|
||||
})
|
||||
}
|
||||
if(form.yyzz){
|
||||
yyzzFile.value.push({
|
||||
uid: 3,
|
||||
url: form.yyzz,
|
||||
name: '营业执照',
|
||||
})
|
||||
}
|
||||
if(form.image){
|
||||
image.value.push({
|
||||
uid: 4,
|
||||
url: form.image,
|
||||
name: '',
|
||||
})
|
||||
}
|
||||
if (form.files) {
|
||||
const arr = JSON.parse(form.files)
|
||||
let i = 1;
|
||||
arr.map(d => {
|
||||
files.value.push({
|
||||
uid: i++,
|
||||
url: d,
|
||||
name: '',
|
||||
})
|
||||
filesStr.value.push(d)
|
||||
})
|
||||
}
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
<style scoped>
|
||||
/* 自定义 el-dialog 的样式 */
|
||||
.el-dialog {
|
||||
background: rgba(255, 255, 255, 0.5); /* 设置背景颜色为半透明白色 */
|
||||
backdrop-filter: blur(10px); /* 可选:为背景添加模糊效果 */
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top">
|
||||
<el-form-item label="租户ID" class="px-4">
|
||||
<el-input disabled v-model="form.tenantId"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" class="px-4">
|
||||
<el-input disabled v-model="form.mobile"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应用名称" class="px-4">
|
||||
<el-input v-model="form.tenantName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" class="px-4">
|
||||
<el-input v-model="form.nickname"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" class="px-4">
|
||||
<el-input v-model="form.email" placeholder="邮箱账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" class="px-4">
|
||||
<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="个人签名" class="px-4">
|
||||
<el-input v-model="form.comments" type="textarea" placeholder="个人签名" :rows="4"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<el-table :data="tableData">
|
||||
<el-table-column prop="orderId" label="订单号" />
|
||||
<el-table-column prop="comments" label="产品名称" />
|
||||
<el-table-column prop="payPrice" label="订单金额(元)" />
|
||||
<el-table-column prop="month" label="购买时长(月)" />
|
||||
<el-table-column prop="appName" label="应用名称" />
|
||||
<el-table-column prop="createTime" label="下单时间" />
|
||||
<el-table-column prop="action" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button @click="openSpmUrl(`https://${scope.row.tenantId}.websoft.top`,form,scope.row.tenantId,true)">控制台</el-button></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ref} from "vue";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {OrderGoods} from "~/api/system/orderGoods/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const tableData = ref<OrderGoods[]>([]);
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<OrderGoods>>>('/system/order-goods/page',{
|
||||
params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}
|
||||
})
|
||||
if(response.value?.data){
|
||||
tableData.value = response.value.data.list;
|
||||
console.log(tableData.value,'tableData')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top">
|
||||
<el-form-item label="旧密码" class="px-4">
|
||||
<el-input v-model="form.oldPassword" placeholder="请输入旧密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" class="px-4">
|
||||
<el-input v-model="form.password" type="password" placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" class="px-4">
|
||||
<el-input v-model="form.password2" type="password" placeholder="请确认新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4">
|
||||
<el-button type="primary" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<el-space direction="vertical" class="sm:w-[140px] sm:flex sm:mb-0 mb-5 w-full pr-7">
|
||||
<div class="py-2" v-for="(item,index) in activities" :index="`${item.path}`" :key="index">
|
||||
<el-button :icon="item.icon" link class="text-gray-500" plain @click="navigateTo(item.path)">{{ item.name }}</el-button>
|
||||
</div>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {User,Lock,Postcard,Tickets,SwitchButton} from '@element-plus/icons-vue'
|
||||
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 = [
|
||||
{
|
||||
icon: User,
|
||||
name: '账号信息',
|
||||
path: '/user'
|
||||
},
|
||||
{
|
||||
icon: Lock,
|
||||
name: '密码修改',
|
||||
path: '/user/password'
|
||||
},
|
||||
{
|
||||
icon: Postcard,
|
||||
name: '实名认证',
|
||||
path: '/user/auth'
|
||||
},
|
||||
{
|
||||
icon: SwitchButton,
|
||||
name: '退出登录',
|
||||
path: '/user/logout'
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
const handleSelect = (index: string) => {
|
||||
emit('done', index)
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.custom-menu-item:hover {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<PageBanner title="账户中心" desc="Account Center"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" class="sm:flex hidden"/>
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">账号信息</div>
|
||||
<div class="sm:w-screen-md w-full sm:px-4 sm:py-2">
|
||||
<Base :form="form"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {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 Base from './components/Base.vue';
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
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,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
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 token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
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>
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useToken} from "~/composables/configState";
|
||||
|
||||
const token = useToken();
|
||||
token.value = '';
|
||||
localStorage.clear();
|
||||
setTimeout(() => {
|
||||
navigateTo('/')
|
||||
return;
|
||||
}, 1000)
|
||||
</script>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<PageBanner title="订单列表" desc="Order List" />
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:p-0 p-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" />
|
||||
<div class=" bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">订单列表</div>
|
||||
<div class="flash bg-white rounded-lg px-8 py-4 w-auto">
|
||||
<Order :form="form" />
|
||||
</div>
|
||||
</div>
|
||||
</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 Order from './components/Order.vue';
|
||||
import Auth from "~/pages/user/auth.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
|
||||
});
|
||||
|
||||
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 token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
// const {data: response} = await useServerRequest<ApiResult<Order>>('/system/order',{baseURL: runtimeConfig.public.apiServer})
|
||||
// if(response.value?.data){
|
||||
// console.log(response.value,'order')
|
||||
// // userInfo.value = response.value?.data;
|
||||
// // assignFields(response.value?.data);
|
||||
// }
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,139 +0,0 @@
|
||||
<template>
|
||||
<PageBanner title="修改密码" desc="Change Password"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" class="sm:flex hidden"/>
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">修改密码</div>
|
||||
<div class="sm:w-screen-md w-full sm:px-4 sm:py-2">
|
||||
<Password :form="form"/>
|
||||
</div>
|
||||
</div>
|
||||
</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 Password from './components/Password.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
|
||||
});
|
||||
|
||||
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 onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
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>
|
||||
119
pages/video/[id].vue
Normal file
119
pages/video/[id].vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div id="container" class="clearfix xl:w-screen-xl m-auto">
|
||||
|
||||
<div class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" />
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
<Breadcrumb :data="page" :categoryName="page.parentName || page.categoryName" />
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<CmsProductList :data="list" />
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import {useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticle, CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {paramsId} from "~/utils/common";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import CmsProductList from "~/components/CmsProductList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const list = ref<CmsArticle[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsArticleParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = async () => {
|
||||
getCmsNavigation(paramsId()).then(data => {
|
||||
// 获取栏目信息
|
||||
page.value = data
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(categoryData => {
|
||||
category.value = categoryData;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = data.navigationId;
|
||||
}else {
|
||||
where.categoryId = data.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response?.count;
|
||||
list.value = response?.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
console.log('路由变化了',route.path)
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
198
pages/video/detail/[id].vue
Normal file
198
pages/video/detail/[id].vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<Banner :layout="layout"/>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div id="container" class="clearfix xl:w-screen-xl m-auto">
|
||||
|
||||
<div class="left">
|
||||
<!-- 内页左侧组件 -->
|
||||
<Left :category="category" :title="$t('video.title')" />
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>
|
||||
{{ form.categoryName || '分类名称' }}
|
||||
</h2>
|
||||
<Breadcrumb :data="form" :categoryName="form.categoryName" :detail="$t('detail')" />
|
||||
</div>
|
||||
<div class="detail-container">
|
||||
|
||||
<!-- 产品详细 -->
|
||||
<div class="product_detail" id="pd1">
|
||||
<div class="allcontent clearfix">
|
||||
<div class="text-center text-xl text-gray-800 py-5">{{ form.title }}</div>
|
||||
<el-carousel v-if="form.files" :interval="4000" height="400px">
|
||||
<el-carousel-item v-for="item in form.files" :key="item" style="display: flex; align-items: center; justify-content: center">
|
||||
<el-image :src="item" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
|
||||
<div class="clearboth"></div>
|
||||
<div class="p_detail">
|
||||
<ul id="product-tab" class="product-tab clearfix">
|
||||
<li class="cur">{{ $t('show.detail') }}</li>
|
||||
|
||||
</ul>
|
||||
<!-- 内容组件 -->
|
||||
<Content class="text-sm" :data="form.content" />
|
||||
</div>
|
||||
<h3 class="tag">{{ $t('articleUrl') }}:{{ locationUrl() }} </h3>
|
||||
<Tags :data="form.tags" />
|
||||
<NextArticle />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 相关产品和相关新闻 -->
|
||||
<div class="relate_list">
|
||||
<CmsProductRelated :data="form" />
|
||||
<CmsArticleRelated :data="form" />
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {getPath, locationUrl, paramsId} from "~/utils/common";
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import Banner from "@/components/Banner.vue";
|
||||
import type {Layout} from "~/api/layout/model";
|
||||
import Left from "~/components/Left.vue";
|
||||
import {getCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import Tags from "~/components/Tags.vue";
|
||||
import Content from "~/components/Content.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const i18n = useI18n();
|
||||
const layout = ref<Layout>({});
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const parentName = ref('项目展示');
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsArticle>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章模型
|
||||
model: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
tags: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined,
|
||||
// 详情页路径
|
||||
detail: undefined
|
||||
});
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
await getCmsArticle(paramsId()).then(data => {
|
||||
|
||||
assignFields(data);
|
||||
|
||||
layout.value.banner = data?.banner;
|
||||
if (data.files) {
|
||||
form.files = JSON.parse(data.files);
|
||||
}
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId,
|
||||
lang: i18n.locale.value
|
||||
}).then(list => {
|
||||
category.value = list
|
||||
// 宣传视频
|
||||
if(data.categoryName == '宣传视频'){
|
||||
parentName.value = '宣传视频'
|
||||
category.value = [];
|
||||
}
|
||||
})
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err,'err')
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
Reference in New Issue
Block a user