Browse Source

调整:layout读取方式

master
科技小王子 8 months ago
parent
commit
47c3fe5d68
  1. 4
      api/cms/design/model/index.ts
  2. 4
      api/cms/navigation/model/index.ts
  3. 77
      components/AppHeader.vue
  4. 22
      components/Banner.vue
  5. 30
      components/PageContainer.vue
  6. 51
      components/Passport.vue
  7. 5
      components/UnderMaintenance.vue
  8. 5
      composables/configState.ts
  9. 7
      composables/useServerRequest.ts
  10. 39
      layouts/default.vue
  11. 16
      nuxt.config.ts
  12. 76
      pages/[custom].vue
  13. 79
      pages/_____bak/[custom].vue
  14. 0
      pages/_____bak/a/index.vue
  15. 2
      pages/_____bak/customs/index.vue
  16. 0
      pages/_____bak/page/[id].vue
  17. 0
      pages/_____bak/spm/[spm].vue
  18. 104
      pages/article/[id].vue
  19. 14
      pages/article/detail/[id].vue
  20. 58
      pages/article/detail/name.vue
  21. 76
      pages/docs/index.vue
  22. 4
      pages/index.vue
  23. 11
      pages/product/740a123.html.vue
  24. 170
      pages/product/[id].vue
  25. 5
      pages/product/detail/[id].vue
  26. 77
      pages/product/website.vue
  27. 67
      pages/search/index.vue
  28. 1
      types/global.d.ts
  29. 65
      utils/common.ts

4
api/cms/design/model/index.ts

@ -19,7 +19,7 @@ export interface Design {
// 高
height?: string;
// 页面样式
styles?: string;
style?: string;
// 附件
images?: string;
// 用户ID
@ -39,7 +39,9 @@ export interface Design {
// 页面布局
layout?: string;
backgroundColor?: string;
demoUrl?: string;
buyUrl?: string;
docUrl?: string;
}
/**

4
api/cms/navigation/model/index.ts

@ -3,7 +3,7 @@ import type {Design} from "~/api/cms/design/model";
/**
*
*/
export interface Navigation {
export interface Navigation{
navigationId?: number;
parentId?: number;
title?: string;
@ -32,6 +32,7 @@ export interface Navigation {
createTime?: string;
isMpWeixin?: boolean;
target?: string;
layout?: string;
design?: Design;
// 用于面包肖屑
parentName?: string;
@ -40,6 +41,7 @@ export interface Navigation {
categoryName?: string;
categoryPath?: string;
currentTitle?: string;
style?: string;
}
/**

77
components/AppHeader.vue

@ -1,55 +1,59 @@
<template>
<header class="header bg-black z-100 top-0 w-full" :class="affix ? 'absolute' : 'fixed'">
<div class="flex between md:w-3/4 m-auto px-2">
<div class="header__left flex">
<div class="logo mt-1 sm:w-[170px] py-2 flex items-center">
<header class="header bg-white z-100 top-0 w-full" :class="affix ? 'absolute' : 'fixed'">
<div class="flex items-center between md:w-3/4 m-auto px-2">
<div class="header___left flex items-center">
<div class="logo mt-1 sm:w-[170px] h-7 w-auto py-2 flex items-center">
<nuxt-link v-if="config?.siteLogo" to="/">
<el-image
:src="config.siteLogo"
shape="square"
fit="fill"
class="h-[23px] w-auto sm:h-[30px]"
class="sm:h-7 h-6 sm:w-auto w-17"
:alt="config.siteName"
:title="config.siteName"
/>
</nuxt-link>
<nuxt-link v-else to="/">
<text>{{ config?.siteName || '网宿软件' }}</text>
<text>{{ config?.siteName }}</text>
</nuxt-link>
</div>
<nav class="hidden-sm-and-down">
<el-menu
:default-active="currentIndex"
mode="horizontal"
background-color="#000000"
text-color="#fff"
active-text-color="#ffd04b"
text-color="#000000"
active-text-color="#000000"
:collapse="true"
:ellipsis="true"
style="border-bottom: none"
style="border: none"
@select="handleSelect"
>
<template v-for="(item, index) in navigations">
<el-menu-item :index="item.path" v-if="index < (config.elMenuMaxNumber || 6)">
<el-menu-item :index="`${item.path}`">
<el-sub-menu v-if="item?.children && item.children.length > 0" :index="`${item.path}`">
<template #title>
<text class="text-[17px]">{{ item.title }}</text>
</template>
<el-menu-item v-for="(sub,subIndex) in item.children" :index="`${sub.path}`" :key="subIndex">{{ sub.title }}</el-menu-item>
<template #title><h3>{{ item.title }}</h3></template>
<el-menu-item v-for="(sub,subIndex) in item.children" :index="`${sub.path}`">{{ sub.title }}</el-menu-item>
<!-- <el-sub-menu index="2-4">-->
<!-- <template #title>item four</template>-->
<!-- <el-menu-item index="2-4-1">item one</el-menu-item>-->
<!-- <el-menu-item index="2-4-2">item two</el-menu-item>-->
<!-- <el-menu-item index="2-4-3">item three</el-menu-item>-->
<!-- </el-sub-menu>-->
</el-sub-menu>
<text v-else class="text-[17px]">{{ item.title }}</text>
<el-menu-item v-else :index="`${item.path}`"><h3>{{ item.title }}</h3></el-menu-item>
</el-menu-item>
</template>
<!-- 顶部菜单超出数量折叠 -->
<el-sub-menu index="more" v-if="navigations && navigations.length > (config.elMenuMaxNumber || 6)">
<template #title>更多菜单</template>
<template v-for="(item, index) in navigations">
<el-menu-item :index="item.path" :key="index" v-if="index >= (config.elMenuMaxNumber || 6)">
{{ item.title }}</el-menu-item
>
</template>
</el-sub-menu>
<!-- <el-sub-menu index="more" v-if="navigations && navigations.length > (config.elMenuMaxNumber)">-->
<!-- <template #title>更多菜单</template>-->
<!-- <template v-for="(item, index) in navigations">-->
<!-- <el-menu-item :index="item.path" :key="index" v-if="index >= (config.elMenuMaxNumber)">-->
<!-- <text :class="item.style">{{ item.title }}</text>-->
<!-- </el-menu-item-->
<!-- >-->
<!-- </template>-->
<!-- </el-sub-menu>-->
</el-menu>
</nav>
</div>
@ -82,24 +86,35 @@
</ClientOnly>
</div>
</div>
<Passport :visible="showPassport" @done="reload" />
</header>
<div class="header__height__placeholder"></div>
</template>
<script setup lang="ts">
//
import {useConfigInfo, useMenu, useProductAffix, useWebsite} from "~/composables/configState";
import {
useConfigInfo,
useMenu,
useProductAffix,
useShowLogin,
useToken,
useWebsite
} from "~/composables/configState";
import UnderMaintenance from "~/components/UnderMaintenance.vue";
const route = useRoute();
const website = useWebsite()
const showPassport = ref<boolean>(false);
const searchValue = ref<string>();
const token = ref<string | undefined>(undefined);
const token = useToken();
const showLogin = useShowLogin();
const navigations = useMenu();
const config = useConfigInfo();
const affix = useProductAffix();
//
const visibleNumber = ref<number>(6);
config.value.elMenuMaxNumber = 8;
// index
const currentIndex = ref<string>('2');
@ -116,7 +131,13 @@
function logOut() {}
function goLogin() {}
function goLogin() {
showLogin.value = true;
}
const reload = () => {
}
const handleSearch = (key: string, keyPath: string) => {
console.log(key, keyPath);
@ -124,6 +145,8 @@
};
function handleSelect(key: string, keyPath: any) {
console.log(key);
console.log(keyPath);
currentIndex.value = key;
navigateTo(`${key}`);
}

22
components/Banner.vue

@ -1,14 +1,17 @@
<template>
<div class="banner m-auto sm:pt-[60px] pt-[47px] relative sm:flex">
<template v-if="data">
<el-image :src="data.design?.photo || config.subpageBanner" class="sm:h-auto"></el-image>
<template v-if="layout && layout.showBanner">
<el-image :src="layout?.photo || config.subpageBanner" :class="layout?.style" class="sm:h-auto"></el-image>
<div class="banner-bar absolute top-0 w-full mt-[60px] sm:flex hidden">
<div class="banner-text py-12 md:w-3/4 m-auto opacity-90 flex flex-col justify-center">
<div class="keywords my-4 text-5xl">{{ data.design?.name }}</div>
<div class="description my-4 text-2xl max-w-3xl text-gray-600">{{ data.design?.description }}</div>
<div class="buy-btn" v-if="data?.design?.buyUrl">
<el-button @click="navigateTo(data.design.buyUrl)" type="primary">立即购买</el-button>
<div class="keywords my-4 text-3xl">{{ layout?.name }}</div>
<div class="description mb-4 mt-1 text-xl max-w-3xl text-gray-600">{{ layout?.description }}</div>
<div class="buy-btn">
<el-button v-if="layout.demoUrl" @click="openSpmUrl(layout.demoUrl)" type="primary">演示地址</el-button>
<el-button v-if="layout.buyUrl" type="warning" @click="openSpmUrl(layout.buyUrl)">立即购买</el-button>
<el-button v-if="layout.docUrl" @click="openSpmUrl(layout.docUrl)">产品文档</el-button>
</div>
<div class="demo-account mt-3 text-gray-400" v-if="layout?.account">账号密码{{ layout.account }}</div>
</div>
</div>
</template>
@ -16,15 +19,16 @@
</template>
<script setup lang="ts">
import type {Navigation} from "~/api/cms/navigation/model";
import {useConfigInfo} from "~/composables/configState";
import {openSpmUrl} from "~/utils/common";
withDefaults(
defineProps<{
//
data?: Navigation;
layout?: any;
}>(),
{}
);
const config = useConfigInfo();
</script>

30
components/PageContainer.vue

@ -0,0 +1,30 @@
<template>
<div class="container md:w-3/4 m-auto">
<div class="flex flex-col" v-if="!layout?.showLayout">
<Breadcrumb :data="form" />
<div :class="layout?.style" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form?.design?.content">
</div>
</div>
</div>
</div>
<!-- 空白页 -->
<div class="mt-[60px]" v-if="!layout">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {Navigation} from "~/api/cms/navigation/model";
withDefaults(
defineProps<{
layout?: any;
form?: Navigation;
}>(),
{}
);
</script>

51
components/Passport.vue

@ -0,0 +1,51 @@
<template>
<el-dialog v-model="showLogin" width="500" center>
<el-segmented v-model="loginType" :options="options" block />
<el-form :model="form" label-width="auto" style="max-width: 600px" class="p-4">
<el-form-item label="登录账号">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="登录密码">
<el-input v-model="form.name" type="password" />
</el-form-item>
<el-form-item label="记住密码">
<el-switch v-model="form.delivery" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button >取消</el-button>
<el-button type="primary" >
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import {useShowLogin, useToken} from "~/composables/configState";
const props = defineProps<{
//
visible: boolean;
}>();
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
})
const showLogin = useShowLogin();
const loginType = ref('账号登录');
const options = [
'账号登录',
'扫码登录',
]
const token = useToken();
</script>

5
components/UnderMaintenance.vue

@ -3,8 +3,8 @@
<!-- 异常状态 -->
<el-result
:icon="website.statusIcon || 'info'"
:title="`${website.statusName}`"
:sub-title="website.statusText"
:title="`${website.statusName || '404'}`"
:sub-title="website.statusText || '页面找不到了 :('"
>
<template #extra>
<el-button type="primary" v-if="website.statusUrl" @click="navigateTo(`${website.statusUrl}`)">{{ website.statusBtnText }}</el-button>
@ -12,7 +12,6 @@
</el-result>
</el-card>
<div></div>
</template>
<script setup lang="ts">
import {useWebsite} from "~/composables/configState";

5
composables/configState.ts

@ -39,4 +39,7 @@ export const useProductAffix = () =>
});
// 登录凭证
export const useToken = () => useState('token', () => 'token xxx');
export const useToken = () => useState('token', () => null);
// 是否显示登录弹窗
export const useShowLogin = () => useState('showLogin',() => false)

7
composables/useServerRequest.ts

@ -6,21 +6,20 @@
import { useFetch } from '#app';
import type { UseFetchOptions } from '#app';
import {isArray} from '~/utils/tool';
import {useConfigInfo} from "~/composables/configState";
import {isInteger} from "~/utils/common";
export const useServerRequest = <T>(url: string, opts?: UseFetchOptions<T, unknown>) => {
// 配置信息
const runtimeConfig = useRuntimeConfig();
// 请求接口
const baseUrl = ref();
const baseUrl = ref('');
// 获取 Cookie
const token = useCookie('token');
baseUrl.value = runtimeConfig.public.apiBase;
// 开发环境
if(process.env.NODE_ENV === 'development'){
baseUrl.value = 'http://127.0.0.1:9001/api'
// baseUrl.value = 'https://modules.gxwebsoft.com/api'
baseUrl.value = `${runtimeConfig.public.apiDev}`
}
const defaultOptions: UseFetchOptions<unknown> = {

39
layouts/default.vue

@ -15,11 +15,11 @@
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Domain} from "~/api/cms/domain/model";
import {useConfigInfo, useMenu, useSubMenu, useWebsite} from "~/composables/configState";
import {useConfigInfo, useForm, useMenu, useSubMenu, useWebsite} from "~/composables/configState";
import type {Website} from "~/api/cms/website/model";
import type {Navigation} from "~/api/cms/navigation/model";
import type {Config} from "~/types/global";
import {getLoacl, setLocal} from "~/utils/common";
import {getLoacl, getPath, setLocal} from "~/utils/common";
import UnderMaintenance from "~/components/UnderMaintenance.vue";
//
@ -47,13 +47,24 @@
// })
// TODO 1 ,ID
localStorage.removeItem('TID_DOMAIN');
if (!localStorage.getItem('TID_DOMAIN')) {
const domain = window.location.hostname;
const {data: domainInfo } = await useServerRequest<ApiResult<Domain>>('/cms/domain/getByDomain/' + domain);
const domain = window.location.hostname;
if(domain !== 'localhost'){
//
const runtimeConfig = useRuntimeConfig();
const {data: domainInfo } = await useServerRequest<ApiResult<Domain>>('/cms/domain/getByDomain',{query: {
domain
}});
const data = domainInfo.value?.data;
if (data) {
localStorage.setItem('TID_DOMAIN',`${data?.tenantId}`)
}else {
website.value.statusIcon = 'error';
website.value.statusName = '未开通';
website.value.statusUrl = 'https://websoft.top'
website.value.statusBtnText = '去开通';
website.value.statusText = '请在后台开通产品并绑定域名';
loading.value = true;
return ;
}
}
@ -101,6 +112,22 @@
if(subMenuInfo.value?.data){
subMenu.value = subMenuInfo.value?.data
}
// TODO 8
// const { data: navInfo } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath', {
// query: {
// path: getPath()
// }
// });
// if(navInfo.value?.data){
// form.value = navInfo.value?.data
// }
// TODO 9 SEO
// useHead({
// title: ` - ${website.value?.websiteName}`,
// meta: [{ name: website.value.keywords, content: website.value.comments }]
// });
}
reload()

16
nuxt.config.ts

@ -12,15 +12,15 @@ export default defineNuxtConfig({
app: {
head: {
viewport: 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no',
title: 'websoft官网 - 快速搭建现代WEB应用的开发平台,基于Java、Vue3、TS开发,支持多端分离',
title: '首页',
meta: [
{ name: 'keywords', content: 'websoft,小象CMS,企业网站,网站源码,java,nuxt,antd,vue3' },
{ name: 'keywords', content: '企业官网,建站系统,websoftCMS,网站源码,java,nuxt,antd,vue3' },
{
name: 'description',
content: 'websoft.top是南宁市网宿信息科技有限公司研发的在线企业应用平台,助力企业信息化建设和转型,主要产品有:企业官网,电商系统,微信公众号,微信小程序应用等,使用目前最流行的技术栈打造。'
content: 'weSite企业建站系统,助力企业信息化建设和转型,主要产品有:企业官网,电商系统,微信公众号,微信小程序应用等,使用目前最流行的技术栈打造。https://websoft.top'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
}
},
devServer: {
@ -28,13 +28,15 @@ export default defineNuxtConfig({
},
runtimeConfig: {
public: {
// 一般只需修改租户号和接口地址
// 开发环境需要修改租户ID和接口地址
tenantId: '5',
domain: 'websoft.top',
apiBase: 'https://modules.gxwebsoft.com/api',
apiDev: 'http://127.0.0.1:9001/api',
// 以下一般不需要修改
apiBase: 'https://modules.gxwebsoft.com/api',
apiServer: 'https://server.gxwebsoft.com/api',
globalTitle: '网宿软件',
domain: 'websoft.top',
},
// 私有配置项
}

76
pages/[custom].vue

@ -0,0 +1,76 @@
<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/navigation/' + getIdBySpm(5))
if (nav.value?.data) {
form.value = nav.value.data
}else{
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/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>

79
pages/_____bak/[custom].vue

@ -0,0 +1,79 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<!-- 容器 -->
<div class="container md:w-3/4 m-auto">
<div class="flex flex-col">
<Breadcrumb :data="form" />
<div :class="form.design?.style" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form.design?.content">
</div>
</div>
</div>
</div>
<div class="mt-[60px]" v-if="!form.design">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdBySpm} from "~/utils/common";
//
const route = useRoute();
const website = useWebsite();
const config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
//
const reload = async () => {
// spm()
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5))
if (nav.value?.data) {
form.value = nav.value.data
}
// spm
if(!form.value.navigationId){
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
}
// seo
useHead({
title: `${form.value.title} - ${website.value.websiteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: `console.log(${JSON.stringify(form.value)})`,
},
],
});
//
breadcrumb.value = form.value
}
watch(
() => route.path,
(path) => {
if(path){
reload();
}
},
{ immediate: true }
);
</script>

0
pages/a/index.vue → pages/_____bak/a/index.vue

2
pages/[custom]/index.vue → pages/_____bak/customs/index.vue

@ -1,7 +1,7 @@
<template>
<!-- Banner -->
<Banner :data="form" />
{{ form }}
{{ form }}--
<!-- 容器 -->
<div class="container md:w-3/4 m-auto" v-if="form">
<div class="flex flex-col">

0
pages/page/[id].vue → pages/_____bak/page/[id].vue

0
pages/spm/[spm].vue → pages/_____bak/spm/[spm].vue

104
pages/article/[id].vue

@ -1,10 +1,10 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<Banner :layout="layout" />
<div class="container flex flex-col w-full md:w-3/4 m-auto my-3">
<Breadcrumb :data="form" :title="form?.categoryName || '文章列表'" />
<Breadcrumb :data="form" />
<!-- 左右结构 -->
<div class="news-box sm:mt-0 mt-2 flex sm:flex-row flex-col justify-between sm:space-x-4">
@ -13,7 +13,7 @@
<div class="news-list">
<ul class="infinite-list px-3" v-infinite-scroll="load" :infinite-scroll-disabled="disabled">
<li v-for="item in list">
<div class="item flex py-3 px-4 gap-xl mb-5 rounded-lg mb-3 bg-white hover:shadow">
<div class="item flex justify-between py-3 px-4 gap-xl mb-5 rounded-lg mb-3 bg-white hover:shadow">
<div class="item-info py-2 flex flex-col justify-between">
<div class="title line-clamp-2 overflow-hidden text-ellipsis group-hover:group-hover:text-ellipsis">
<a :href="`/article/detail/${item.articleId}`" target="_blank" class="text-xl">{{ item.title }}</a>
@ -70,54 +70,73 @@
</div>
</div>
</div>
<el-divider />
<div v-if="!list">
<el-empty description="404 页面不存在"></el-empty>
</div>
<!-- <el-divider />-->
<!-- <div v-if="!list">-->
<!-- <el-empty description="404 页面不存在"></el-empty>-->
<!-- </div>-->
</template>
<script setup lang="ts">
import type {ApiResult, PageResult} from "~/api";
import type {Article} from "~/api/cms/article/model";
import {useServerRequest} from "~/composables/useServerRequest";
import type {Design} from "~/api/cms/design/model";
import {useClientRequest} from "~/composables/useClientRequest";
import {pageArticle} from "~/api/cms/article";
import Breadcrumb from "~/components/Breadcrumb.vue";
import {useForm, useWebsite} from "~/composables/configState";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdByParam, getIdBySpm, getPath} from "~/utils/common";
const route = useRoute();
const { query, params } = route;
const { id } = params;
//
const list = ref<Article[]>([]);
const title = ref();
const categoryName = ref();
const count = ref()
const page = ref<number>(1);
const links = ref<any[]>();
const page = ref<number>(0);
const disabled = ref<boolean>(false);
const newList = ref<Article[]>();
const layout = ref<any>();
//
const form = ref<Navigation | any>();
//
const form = ref<Navigation>();
const website = useWebsite();
const load = () => {
if(!disabled.value){
page.value++;
reload();
// reload();
}
}
//
const reload = async () => {
//
const { data: navInfo } = await useServerRequest<ApiResult<Article>>('/cms/navigation/' + id);
console.log(navInfo.value);
// categoryName.value = d.categoryName;
form.value = navInfo.value;
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/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)
}
console.log(layout.value)
//
const { data: articleList } = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/article/page',{
params: {
page: page.value,
categoryId: getIdByParam()
}
})
if(articleList.value?.data){
list.value = articleList.value.data.list
}
console.log(list.value)
useHead({
title: `${form.value.currentTitle} - 网宿软件`,
meta: [{ name: "keywords", content: form.value.title }],
title: `${form.value.title} - ${website.value.websiteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
@ -127,38 +146,15 @@ const reload = async () => {
},
],
});
await useServerRequest<ApiResult<PageResult<Article>>>('/cms/article/page',{
params: {
page: page.value,
categoryId: id
}
}).then(res => {
const data = res.data.value?.data;
count.value = data?.count;
if (data?.list.length == 0) {
disabled.value = true;
return false;
}
if (page.value == 0) {
list.value = data?.list;
}else {
list.value = list.value.concat(data?.list);
}
// list.value = articleList.value.data?.list;
list.value.map((d,i)=>{
if(i === 0){
}
});
})
}
reload();
</script>
<style scoped lang="scss">
watch(
() => route,
() => {
reload();
},
{ immediate: true }
);
</style>
</script>

14
pages/article/detail/[id].vue

@ -4,7 +4,7 @@
<div v-if="form" class="flex flex-col w-full md:w-3/4 m-auto my-3">
<Breadcrumb :data="form" title="文章详情" />
<Breadcrumb :data="layout" title="文章详情" />
<div class="m-3 bg-white p-3 mt-4 flex flex-col gap-xl rounded-lg">
<div class="article-title-box p-4">
@ -24,25 +24,25 @@
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import type {Goods} from "~/api/shop/goods/model";
import type {Article} from "~/api/cms/article/model";
import Breadcrumb from "~/components/Breadcrumb.vue";
import {getIdByParam} from "~/utils/common";
const route = useRoute();
const { params, query } = route;
const { id } = params;
const { spm } = query;
console.log('spm=', spm)
//
const form = ref<Article | any>();
const layout = ref<any>({});
//
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/article/' + getIdByParam(id))
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/article/' + getIdByParam())
form.value = info.value?.data;
form.value.num = 1;
form.value.radio = '0'
layout.value.parentName = form.value?.categoryName;
layout.value.categoryName = form.value?.categoryName;
</script>
<style scoped lang="scss">

58
pages/article/detail/name.vue

@ -1,58 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<div v-if="form" class="flex flex-col w-full md:w-3/4 m-auto my-3">
<Breadcrumb :data="form" />
<div class="p-4 mt-4 flex flex-col gap-xl">
<div class="article-title-box p-4">
<div class="text-3xl">{{ form.title }}</div>
<div class="text-sm pt-3 text-gray-4 flex gap-xl">
<span>{{ form.createTime }}</span>
<span>浏览{{ form.actualViews }}</span>
</div>
<el-divider style="height: 1px " />
</div>
<div class="content leading-8 text-xl p-3 text-gray-8" v-html="form.content"></div>
</div>
</div>
<div v-if="!form">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import type {Goods} from "~/api/shop/goods/model";
import type {Article} from "~/api/cms/article/model";
import Breadcrumb from "~/components/Breadcrumb.vue";
const route = useRoute();
const { query, params } = route;
const { id } = params;
const activeName = ref('parameter');
const rate = ref(4);
//
const form = ref<Article | any>();
//
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/article/' + id)
form.value = info.value?.data;
// form.value.files = JSON.parse(form.value.files);
form.value.num = 1;
form.value.radio = '0'
const handleClick = (tab, event) => {
console.log(tab, event);
}
</script>
<style scoped lang="scss">
.content *{
max-width: 100%;
}
</style>

76
pages/docs/index.vue

@ -0,0 +1,76 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<!-- 容器 -->
<div class="container md:w-3/4 m-auto">
<div class="flex flex-col">
<Breadcrumb :data="form" />
<div :class="form.design?.style" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form.design?.content">
</div>
</div>
</div>
</div>
<div class="mt-[60px]" v-if="!form.design">
<el-empty description="404 页面不存在2"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdBySpm} from "~/utils/common";
//
const route = useRoute();
const website = useWebsite();
const config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
//
const reload = async () => {
// spm()
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5))
if (nav.value?.data) {
form.value = nav.value.data
}else{
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
}
// seo
useHead({
title: `${form.value.title} - ${website.value.websiteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: `console.log(${JSON.stringify(form.value)})`,
},
],
});
//
breadcrumb.value = form.value
}
watch(
() => route.path,
(path) => {
console.log(path,'=>Path')
reload();
},
{ immediate: true }
);
</script>

4
pages/index.vue

@ -51,7 +51,10 @@ import type {ApiResult} from "~/api";
import type {AdItem} from "~/api/cms/ad/model";
import type {Link} from "~/api/cms/link/model";
import {useServerRequest} from "~/composables/useServerRequest";
import {useWebsite} from "~/composables/configState";
import {getPath} from "~/utils/common";
const website = useWebsite();
const banner = ref<any>();
const links = ref<any>();
const movieList = ref<any>();
@ -61,6 +64,7 @@ const screenHeight = window.innerHeight;
//
const reload = async () => {
await nextTick()
const form = useServerRequest('/cms/navigation/getNavigationByPath',{query: {path: getPath()}})
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side');
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接');
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]);

11
pages/product/740a123.html.vue

@ -1,11 +0,0 @@
<script setup lang="ts">
</script>
<template>
<div class="h-[500px] w-[500px] m-auto text-3xl text-center justify-center bg-amber mt-[100px] items-center">a740a123740a123740a123740a123</div>
</template>
<style scoped lang="scss">
</style>

170
pages/product/[id].vue

@ -0,0 +1,170 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<div class="container flex flex-col w-full md:w-3/4 m-auto my-3">
<Breadcrumb :data="form" :title="form?.categoryName || '文章列表'" />
<!-- 左右结构 -->
<div class="news-box sm:mt-0 mt-2 flex sm:flex-row flex-col justify-between sm:space-x-4">
<div class="left sm:w-18/24">
<!-- 文章列表 -->
<div class="news-list">
<ul class="infinite-list px-3" v-infinite-scroll="load" :infinite-scroll-disabled="disabled">
<li v-for="item in list">
<div class="item flex py-3 px-4 gap-xl mb-5 rounded-lg mb-3 bg-white hover:shadow">
<div class="item-info py-2 flex flex-col justify-between">
<div class="title line-clamp-2 overflow-hidden text-ellipsis group-hover:group-hover:text-ellipsis">
<a :href="openSpmUrl(`/product/detail/${item.goodsId}`,item,item?.goodsId)" target="_blank" class="text-xl">{{ item.goodsName }}</a>
</div>
<div class="desc max-w-22/24 text-gray-5 sm:block hidden" v-html="item.comments"></div>
<div class="actions text-gray-4 text-sm flex gap-2xl">
<span href="#">{{ item.updateTime }}</span>
<span href="#">浏览{{ item.actualViews }}</span>
</div>
</div>
<div class="item-image flex items-center" @click="navigateTo(`/goods/detail/${item.goodsId}`)">
<el-image :src="item.image" lazy class="sm:w-[140px] sm:h-[140px] w-[110px] h-[110px] bg-gray-50 cursor-pointer transition rounded-lg sm:rounded-lg ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 duration-300" fit="contain" />
</div>
</div>
</li>
</ul>
<div class="text-center text-gray-4">
<text v-if="disabled">没有更多了</text>
<text @click="load" v-else>加载更多</text>
</div>
</div>
</div>
<div class="right sm:mt-0 mt-4 sm:w-6/24">
<!-- 推荐文章 -->
<div class="category-item bg-white rounded-lg p-3 hover:shadow">
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2">
<el-icon>
<ElIconLink/>
</el-icon>
<text class="ml-2">推荐文章</text>
</div>
<div class="flex flex-wrap px-2 py-4">
<!-- <div v-for="(item,index) in links" class="flex items-center">-->
<!-- <a :href="item.url" target="_blank">{{ item.name }}</a>-->
<!-- <el-divider v-if="index + 1 != links.length" direction="vertical" />-->
<!-- </div>-->
</div>
</div>
<div class="category-item mt-4 bg-white rounded-lg p-3 hover:shadow">
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2">
<el-icon>
<ElIconLink/>
</el-icon>
<text class="ml-2">点击排行</text>
</div>
<div class="flex flex-wrap px-2 py-4">
<!-- <div v-for="(item,index) in links" class="flex items-center">-->
<!-- <a :href="item.url" target="_blank">{{ item.name }}</a>-->
<!-- <el-divider v-if="index + 1 != links.length" direction="vertical" />-->
<!-- </div>-->
</div>
</div>
</div>
</div>
</div>
<!-- <el-divider />-->
<!-- <div v-if="!list">-->
<!-- <el-empty description="404 页面不存在"></el-empty>-->
<!-- </div>-->
</template>
<script setup lang="ts">
import type {ApiResult, PageResult} from "~/api";
import type {Article} from "~/api/cms/article/model";
import {useServerRequest} from "~/composables/useServerRequest";
import Breadcrumb from "~/components/Breadcrumb.vue";
import {useForm, useWebsite} from "~/composables/configState";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdBySpm, getPath, openSpmUrl} from "~/utils/common";
import type {Goods} from "~/api/shop/goods/model";
const route = useRoute();
//
const list = ref<Goods[]>([]);
const title = ref();
const categoryName = ref();
const count = ref()
const page = ref<number>(1);
const disabled = ref<boolean>(false);
const newList = ref<Article[]>();
//
const form = useForm();
const website = useWebsite();
const load = () => {
if(!disabled.value){
page.value++;
// reload();
}
}
//
const reload = async () => {
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: getPath()}})
if(nav.value?.data){
form.value = nav.value?.data;
}
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('Hello World')",
},
],
});
//
useServerRequest<ApiResult<PageResult<Goods>>>('/shop/goods/page',{
params: {
page: page.value,
categoryId: route.query.id
}
}).then(res => {
const data = res.data.value?.data;
count.value = data?.count;
if (data?.list.length == 0) {
disabled.value = true;
return false;
}
if (page.value == 0) {
list.value = data?.list;
}else {
list.value = list.value.concat(data?.list);
}
// list.value = articleList.value.data?.list;
list.value.map((d,i)=>{
if(i === 0){
}
});
})
}
watch(
() => route.path,
(path) => {
console.log(path,'=>Path')
reload();
},
{ immediate: true }
);
</script>

5
pages/product/[detail].vue → pages/product/detail/[id].vue

@ -106,6 +106,7 @@ 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();
@ -136,10 +137,10 @@ const reload = async () => {
// TODO
console.log(goodsId.value,'sss')
const { data: info } = await useServerRequest<ApiResult<Goods>>('/shop/goods/' + goodsId.value)
const { data: info } = await useServerRequest<ApiResult<Goods>>('/shop/goods/' + getIdBySpm(5))
goods.value = info.value?.data;
console.log(goods.value)
console.log(goods.value)
}

77
pages/product/website.vue

@ -0,0 +1,77 @@
<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 Breadcrumb from "~/components/Breadcrumb.vue";
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 config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
const layout = ref<any>();
//
const reload = async () => {
// spm()
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5))
if (nav.value?.data) {
form.value = nav.value.data
}else{
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/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>

67
pages/search/index.vue

@ -1,67 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<!-- 容器 -->
<div class="container md:w-3/4 m-auto">
<div class="flex flex-col">
<Breadcrumb />
</div>
</div>
<div v-if="!form">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {Design} from "~/api/cms/design/model";
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useToken} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
const route = useRoute();
const { params } = route;
const { custom: path } = params;
//
const config = useConfigInfo();
const token = useToken();
//
const form = ref<Navigation | any>();
const breadcrumb = ref<BreadcrumbItem>();
//
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{
query: {
path: '/' + path
}
})
if (nav.value) {
// seo
form.value = nav.value.data
if(form.value){
useHead({
title: `${form.value.title} - 网宿软件`,
meta: [{ name: form.value.design.keywords, content: form.value.design.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: "console.log('Hello World')",
},
],
});
//
breadcrumb.value = form.value.breadcrumb
}
}
</script>
<style scoped lang="scss">
</style>

1
types/global.d.ts

@ -7,6 +7,7 @@ export interface Config {
domain?: string;
siteName?: string;
siteLogo?: string;
siteLogoStyles?: string;
subpageBanner?: string;
bottomBg?: string;
elMenuMaxNumber?: number;

65
utils/common.ts

@ -1,3 +1,5 @@
const route = useRoute();
/**需要进行持久化的数据:把需要持久化的数据放在下面这个对象中,才会持久化,不需要持久化的数据就不用放到这里了。 */
const enduring: { [key: string]: () => Ref<any> } = {
useToken, useConfigInfo
@ -65,7 +67,66 @@ export const isInteger = (num: any) => {
* return 1234
* @param num
*/
export const getIdByParam = (text: any) => {
const split = String(text).split('.')
export const getIdByParam = () => {
const split = String(route.params.id).split('.')
return split[0];
}
/**
* ID
* param 12334.html
* return 1234
* @param num
*/
export const getIdBySpm = (index: number) => {
console.log('split',route.query)
const split = String(route.query.spm).split('.')
return split[index];
}
/**
* Path部分
*/
export const getPath = () => {
return route.path;
}
/**
*
*
* @param path /product/detail.html
* @param id 128
* @param d
* {}{path}?spm=c.{ID}.{ID}.{ID}.{ID}.{timestamp}&token={token}
* @return https:///websoft.top/product/detail/128.html?spm=c.5.3057.10005.undefined&token=DDkr1PpE9DouIVMjLEMt9733QsgG7oNV
*/
export function openSpmUrl(path: string, d?: any, id = 0): void {
const domain = route.path;
const spm = ref<string>('');
const token = ref<string>();
const url = ref<string>();
const tid = getTenantId() || 0;
const pid = d?.parentId || 0;
const cid = d?.categoryId || 0;
const uid = localStorage.getItem('UserId') || 0;
const timestamp = ref(Date.now() / 1000);
token.value = `&token=`;
// TODO 不传data
// if (!d) {
// window.open(`${domain}${path}`);
// return;
// }
// TODO 封装租户ID和店铺ID
spm.value = `?spm=c.${uid}.${tid}.${pid}.${cid}.${id}.${timestamp.value}`;
// TODO 含http直接跳转
if (path.slice(0, 4) == 'http') {
window.open(`${path}${spm.value}`);
return;
}
// 跳转页面
url.value = `${path}${spm.value}${token.value}`;
}

Loading…
Cancel
Save