新版本官网优化完成

This commit is contained in:
2025-02-12 16:37:07 +08:00
parent 43a2e17a80
commit 3efdbfc662
547 changed files with 23001 additions and 28169 deletions

View File

@@ -1,113 +0,0 @@
<template>
<footer class="overflow-hidden pt-4">
<!-- 间隔 -->
<div class="sm:h-[3px] h-[15px]" v-if="getPath().startsWith('/passport/login')"></div>
<div class="sm:h-[30px] h-[15px]" v-else></div>
<div class=" bg-white my-3 w-3/4 m-auto" v-if="config.copyrightForDemoData">
<el-alert :title="config.copyrightForDemoData" center show-icon type="warning" />
</div>
<div class="w-full flex flex-col sm:bg-black justify-between">
<!-- PC版 -->
<div class="sub-menu w-full xl:w-screen-xl xl:px-0 px-4 m-auto flex justify-between py-10 text-center hidden-sm-and-down">
<!-- 底部菜单 -->
<div class="left flex flex-col sm:flex-row sm:flex-wrap justify-between gap-3xl sm:w-9/12 flex-wrap w-full">
<template v-for="item in subMenu">
<div class="sub-menu-item text-left">
<div class="pb-4">
<text class="text-gray-400 hover:text-gray-300 font-bold text-[16px]">{{ item.title }}</text>
</div>
<template v-if="item.children">
<div class="sub-menu-children flex flex-col gap-xs">
<template v-for="sub in item.children">
<a @click="openSpmUrl(`${sub.path}`,sub,sub.navigationId)" class="text-gray-400 hover:text-gray-300 cursor-pointer">{{ sub.title }}</a>
</template>
</div>
</template>
</div>
</template>
</div>
<!-- 关注我们 -->
<div class="right w-3/12 pr-3 text-right flex justify-end" v-if="config.wxQrcode">
<div class="qrcode flex flex-col items-center">
<el-image :src="config.wxQrcode" class="w-[100px]" />
<text class="text-gray-400 py-2">{{ config.wxQrcodeText }}</text>
</div>
</div>
</div>
<div class="flex pt-1 w-auto sm:hidden px-4 sm:px-0">
<el-divider><span class="text-gray-400">Footer</span></el-divider>
</div>
<div class="mx-3 sm:hidden p-5 bg-white rounded-xl">
<el-collapse v-model="activeNames" accordion>
<template v-for="(item,index) in subMenu">
<el-collapse-item :title="item.title" :name="index">
<template v-if="item.children">
<template v-for="sub in item.children">
<a :href="sub.path" class="block cursor-pointer text-gray-600 hover:text-gray-900">{{ sub.title }}</a>
</template>
</template>
</el-collapse-item>
</template>
</el-collapse>
</div>
<!-- <div class="sm:hidden p-4 mt-3">-->
<!-- <el-button type="primary" class="w-full" size="large">联系我们</el-button>-->
<!-- </div>-->
<!-- 版权信息 -->
<div class="w-full xl:w-screen-xl xl:px-0 px-4 w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-between justify-center items-center sm:py-10 pt-6 pb-6 text-center">
<div class="text-gray-400 sm:gap-xl leading-7 flex flex-col sm:flex-row">
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号{{ config?.icpNo }}</a>
</div>
<div class="tools flex gap-xl items-center opacity-80 hover:opacity-90 text-gray-400 text-sm">
Powered by <a rel="nofollow" href="https://website.websoft.top" target="_blank" class="text-gray-400 hover:text-gray-50">·企业官网</a>
<!-- <el-tooltip :content="`管理后台`" v-if="config.showAdminIcon">-->
<!-- <a :href="`https://${website.tenantId}.websoft.top`" target="_blank"><img src="@/assets/svg/websoft-mark-white.svg" alt="github" width="28" class="text-gray-400" /></a>-->
<!-- </el-tooltip>-->
</div>
</div>
</div>
</footer>
<footer class="absolute bottom-0 w-full" v-if="getPath().startsWith('/login')">
<div class="w-full xl:w-screen-xl xl:p-0 px-4 w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-center justify-center items-center sm:py-10 pt-6 pb-6 text-center">
<div class="text-gray-400 sm:gap-xl leading-7 flex flex-col sm:flex-row">
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号{{ config?.icpNo }}</a>
</div>
</div>
</footer>
<el-backtop></el-backtop>
</template>
<script setup lang="ts">
// 请求数据
import {getPath, openSpmUrl, useConfigInfo} from "#imports";
import {useSubMenu, useWebsite} from "~/composables/configState";
import { StarFilled } from '@element-plus/icons-vue'
const config = useConfigInfo();
const website = useWebsite();
const subMenu = useSubMenu();
console.log('---------config---------',config.value)
console.log('---------website---------',website.value)
console.log('---------subMenu---------',subMenu.value)
const activeNames = ref(['1'])
</script>
<style lang="scss">
.el-divider__text{
display: block;
margin-top: -1px;
--el-bg-color: #ffffff;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<!-- 底部菜单 -->
<SubMenu />
<!-- 版权信息 -->
<Copyright />
<!-- 侧边工具栏 -->
<ToolBar />
</template>
<script setup lang="ts">
import SubMenu from "~/components/AppFooter/SubMenu/SubMenu.vue";
import Copyright from "~/components/AppFooter/Copyright/Copyright.vue";
import ToolBar from "~/components/AppFooter/ToolBar/ToolBar.vue";
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import {useConfigInfo} from "~/composables/configState";
import Link from "~/components/AppFooter/Link/Link.vue";
const config = useConfigInfo();
</script>
<template>
<div class="w-full bg-black">
<div class="xl:w-screen-xl flex flex-col justify-between m-auto">
<!-- 友情链接 -->
<Link />
<!-- 版权信息 -->
<div class="w-full xl:w-screen-xl xl:px-0 px-4 w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-between justify-center items-center sm:py-3 text-center text-sm">
<div class="text-gray-400 sm:gap-xs leading-7 flex flex-col sm:flex-row">
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
<nuxt-link to="https://beian.miit.gov.cn/" class="visited:text-gray-400 hover:text-gray-200" target="_blank"> <span>备案号{{ config?.icpNo }}</span></nuxt-link>
</div>
<div class="tools flex items-center opacity-80 hover:opacity-90 text-gray-400 text-xs">
Powered by <a rel="nofollow" href="https://site.websoft.top" target="_blank" class="visited:text-gray-400 hover:text-gray-200 ml-1">·企业官网</a>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import {listCmsLink} from "~/api/cms/cmsLink";
import type {CmsLink} from "~/api/cms/cmsLink/model";
const list = ref<CmsLink[]>([])
listCmsLink({}).then(res => {
console.log(res);
list.value = res;
})
</script>
<template>
<el-space class="text-gray-400 text-sm">
<span>友情链接</span>
<template v-for="(item,index) in list" :key="index">
<nuxt-link :to="item.url" target="_blank" class="link:text-gray-400 visited:text-gray-400 hover:text-gray-200">{{ item.name }}</nuxt-link>
<el-divider v-if="list.length-1 != index" direction="vertical" style="border-color: #9ca3af;" />
</template>
</el-space>
<div class="border-b-solid bg-gray-700 border-1 mt-4 opacity-40"></div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import {useConfigInfo, useSubMenu} from "~/composables/configState";
const subMenu = useSubMenu();
const config = useConfigInfo();
</script>
<template>
<div class="w-full bg-black h-[300px] py-5">
<div class="xl:w-screen-xl flex justify-between m-auto">
<template v-for="(item,index) in subMenu">
<div class="item">
<div class="text-base text-gray-400 hover:text-gray-200 py-3 font-bold">{{ item.title }}</div>
<div class="sub-menu flex flex-col">
<template v-for="(sub,subIndex) in item.children">
<nuxt-link :to="navTo(sub)" :target="sub.target" class="py-1 text-sm"><span class="text-gray-400 hover:text-gray-200">{{ sub.title }}</span></nuxt-link>
</template>
</div>
</div>
</template>
<div class="flex flex-col text-base">
<p class="text-base text-gray-400 py-2 font-bold">关注我们</p>
<el-avatar :src="config.wxQrcode" shape="square" :size="120" />
</div>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import {useConfigInfo} from "~/composables/configState";
const config = useConfigInfo();
</script>
<template>
<div id="toolbar" v-if="config">
<ul>
<li><a :href="`http://wpa.qq.com/msgrd?v=3&amp;uin=${config.qqCode}&amp;site=qq&amp;menu=yes`" target="_blank">
<span class="icon-font icon-qq"></span>
<span class="wz">腾讯QQ</span>
</a></li>
<li><a href="javascript:;">
<span class="icon-font icon-phone">config.callPhone</span>
<span class="wz">{{ config.callPhone }}</span>
</a></li>
<li class="ewm">
<span class="icon-font icon-ewm"></span>
<div class="ewm-box"><img :src="config.wxMpQrcode" alt="微信二维码" /></div>
</li>
<li><a href="/order/1003.html">
<span class="icon-font icon-message"></span>
<span class="wz">在线留言</span>
</a></li>
<el-backtop :bottom="50" :right="25"><li class="backtop"><span class="icon-font icon-top"></span></li></el-backtop>
</ul>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,233 +0,0 @@
<template>
<el-affix :offset="0" @change="onAffix">
<header
class="header z-100 top-0 w-full bg-white/75 opacity-90 backdrop-blur border-b border-gray-200 dark:border-gray-800 -mb-px sticky top-0 z-50 lg:mb-0 lg:border-0"
:class="affix ? 'absolute blur-xs' : 'sticky bg-white/75'">
<div class="xl:w-screen-xl xl:p-0 px-4 flex items-center between w-full m-auto">
<div class="header___left flex items-center">
<div class="logo mt-1 sm:w-[150px] h-7 w-auto py-2 flex items-center">
<nuxt-link v-if="config?.siteLogo" to="/">
<div class="flex flex-col text-center xl:p-0">
<el-image
:src="config.siteLogo"
shape="square"
fit="fill"
class="lg:h-7 lg:w-auto pb-2 h-5 w-[90px] h-[28px]"
:alt="config.siteName"
:title="config.siteName"
/>
<!-- <span class="text-gray-500 text-2.5" style="line-height: 1rem">云应用开发平台</span>-->
</div>
</nuxt-link>
<nuxt-link v-else to="/">
<text>{{ config?.siteName }}</text>
</nuxt-link>
</div>
<div class="hidden sm:flex">
<el-menu
:default-active="currentIndex"
mode="horizontal"
background-color="transparent"
:collapse="true"
:ellipsis="false"
style="border: none"
>
<template v-for="(item, index) in navigations">
<el-sub-menu v-if="item?.children && item.children.length > 0" :index="`${item.path}`">
<template #title><h3 @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</h3></template>
<el-menu-item v-for="(sub,subIndex) in item.children" :index="`${sub.path}`">
<el-space @click="openSpmUrl(`${item.path}`,sub,sub.navigationId)">
<el-avatar :src="sub.icon" shape="square" size="small"></el-avatar>
<span class="font-bold">{{ sub.title }}</span>
</el-space>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="`${item.path}`"><h3 @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</h3></el-menu-item>
</template>
<!-- <el-menu-item v-if="user?.certification">-->
<!-- <el-space @click="loginDeveloperCenterByToken({domain:'developer.websoft.top'})">-->
<!-- <h3>开发者中心</h3>-->
<!-- </el-space>-->
<!-- </el-menu-item>-->
<!-- <el-menu-item v-else>-->
<!-- <el-space @click="openSpmUrl(`https://developer.websoft.top`)">-->
<!-- <h3>开发者中心</h3>-->
<!-- </el-space>-->
<!-- </el-menu-item>-->
</el-menu>
</div>
</div>
<div class="header__right items-center pr-4 xl:pr-0 md:flex hidden">
<el-space class="sm:flex hidden" size="large" v-if="config.showSearchTools">
<el-button v-if="token" @click="loginAdminByToken">控制台</el-button>
<!-- <el-button v-if="token" @click="navigateTo(`/manage`)">控制台</el-button>-->
<!-- <el-button v-if="config.showSearchIcon" circle :icon="ElIconSearch" @click="navigateTo('/search')"></el-button>-->
<!-- <el-button v-if="token" @click="loginAdminByToken">控制台</el-button>-->
<ClientOnly>
<template v-if="token">
<el-dropdown @command="handleCommand">
<el-space class="flex items-center cursor-pointer">
<el-avatar class="cursor-pointer" :src="user?.logo" :size="30" />
<span>{{ user?.tenantName }}</span>
</el-space>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="user" @click="openSpmUrl(`/user`)">账户中心</el-dropdown-item>
<el-dropdown-item command="password" @click="openSpmUrl(`/user/password`)">修改密码</el-dropdown-item>
<el-dropdown-item command="auth" @click="openSpmUrl(`/user/auth`)">实名认证</el-dropdown-item>
<el-dropdown-item command="order" @click="openSpmUrl(`/user/order`)">我的订单</el-dropdown-item>
<!-- <el-dropdown-item divided command="order" @click="loginDeveloperCenterByToken({domain:'developer.websoft.top'})">开发者中心</el-dropdown-item>-->
<el-dropdown-item divided command="logOut" @click="openSpmUrl('/user/logout')">退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<template v-else>
<el-button type="primary" v-if="!token" @click="navigateTo(`/passport/login`)">登录/注册</el-button>
<!-- <el-button v-if="config.showLoginButton" circle :icon="ElIconUserFilled" @click="goLogin"></el-button>-->
</template>
</ClientOnly>
</el-space>
</div>
<!-- 移动端菜单 -->
<div class="sm:hidden flex xl:p-0 px-8">
<el-dropdown>
<el-space class="el-dropdown-link flex items-center">
<el-avatar v-if="token" class="cursor-pointer" :src="user?.avatar" :size="30"/>
<el-button v-else :icon="ElIconMenu"></el-button>
</el-space>
<template #dropdown>
<el-dropdown-menu>
<template v-for="(item, index) in navigations">
<el-dropdown-item>
<span @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</span>
</el-dropdown-item>
</template>
<template v-if="token">
<el-dropdown-item divided @click="loginDeveloperCenterByToken">开发者中心</el-dropdown-item>
<!-- <el-dropdown-item divided command="user" @click="navigateTo(`/user`)">账户中心</el-dropdown-item>-->
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
</template>
<el-dropdown-item v-if="!token" divided @click="navigateTo(`/passport/login`)">登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<Passport :visible="showPassport" @done="reload"/>
</header>
<div class="header__height__placeholder"></div>
</el-affix>
</template>
<script setup lang="ts">
// 引入所需的图标
import {
useCompany,
useConfigInfo,
useMenu,
useProductAffix,
useSysDomain,
useToken,
useUser
} from "~/composables/configState";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {User} from "~/api/system/user/model";
import {navigateTo, openSpmUrl} from "#imports";
import type {Company} from "~/api/system/company/model";
import {loginAdminByToken} from "~/utils/common";
const route = useRoute();
// 配置信息
const runtimeConfig = useRuntimeConfig();
const showPassport = ref<boolean>(false);
const token = useToken();
const user = useUser();
const company = useCompany();
const navigations = useMenu();
const config = useConfigInfo();
const affix = useProductAffix();
const sysDomain = useSysDomain();
// 顶部栏初始数
const visibleNumber = ref<number>(6);
config.value.elMenuMaxNumber = 8;
// 当前激活菜单的 index
const currentIndex = ref<string>('/');
function handleCommand(command: string) {
switch (command) {
case 'logOut':
logOut();
break;
default:
navigateTo('/user');
break;
}
}
const onAffix = (index: boolean) => {
affix.value = index;
}
function logOut() {
token.value = ''
localStorage.clear();
navigateTo('/passport/login')
}
function goLogin() {
navigateTo('/passport/login')
}
function handleSelect(key: string, keyPath: any) {
currentIndex.value = key;
navigateTo(`${key}`);
}
const reload = async () => {
const token = localStorage.getItem('token');
// const domain = localStorage.getItem('SysDomain');
// if (domain) {
// sysDomain.value = domain;
// }
// 获取用户信息
if (token && token != '') {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user', {baseURL: runtimeConfig.public.apiServer})
if (response.value?.data) {
user.value = response.value?.data;
}
// 获取企业信息
const {data: companyInfo} = await useServerRequest<ApiResult<Company>>('/system/company/profile', {baseURL: runtimeConfig.public.apiServer})
if(companyInfo.value?.data){
company.value = companyInfo.value?.data;
// sysDomain.value = company.value?.domain || undefined;
// user.value.tenantName = company.value?.tenantName;
// localStorage.setItem('SysDomain', `${company.value?.domain}`);
localStorage.setItem('CompanyLogo', `${company.value?.companyLogo}`)
localStorage.setItem('TenantName', `${company.value?.tenantName}`)
}
}
}
reload();
</script>
<style lang="scss">
body {
background-color: #f3f6f8;
}
.el-menu-item:visited, .el-menu-item:hover {
background: none !important; /* 你可以根据需要设置不同的颜色 */
transition: background-color 0.3s linear;
}
.is-active:hover {
border-bottom: none !important;
}
</style>

View File

@@ -0,0 +1,103 @@
<template>
<!-- 头部组件 -->
<div class="shadow-sm fixed z-100 top-0 w-full bg-white bg-opacity-90">
<!-- 顶部通栏 -->
<TopBar />
<!-- 导航栏 -->
<div class="flex justify-between xl:w-screen-xl m-auto">
<el-space>
<Logo />
<Menu :data="navigations" />
</el-space>
<Login />
</div>
</div>
</template>
<script setup lang="ts">
// 引入所需的图标
import {
useConfigInfo,
useLogo,
useMenu,
useToken,
useUser
} from "~/composables/configState";
import {useI18n} from "#imports";
import Logo from "~/components/AppHeader/Logo/Logo.vue"
import {configWebsiteField, listCmsWebsiteField} from "~/api/cms/cmsWebsiteField";
import {getUserInfo} from "~/api/layout";
import Menu from "~/components/AppHeader/Menu/Menu.vue";
import TopBar from "~/components/AppHeader/TopBar/TopBar.vue";
import Login from "~/components/AppHeader/Login/Login.vue";
// 配置信息
const i18n = useI18n();
const config = useConfigInfo();
const logo = useLogo();
const token = useToken();
const user = useUser();
const navigations = useMenu();
const setNav = () => {
navigations.value = navigations.value.map(item => {
item.showChild = false
item.childHeight = 0;
return item
})
}
setNav()
// 切换语言
const changLang = () => {
const idBySpm = getIdBySpm(0);
if(idBySpm?.startsWith('zh_CN')){
i18n.setLocale('zh');
}else if (idBySpm?.startsWith('en_US')){
i18n.setLocale('en');
}
}
const reload = async () => {
// 切换语言
changLang();
// TODO 读取网站配置信息
configWebsiteField({
lang: i18n.locale.value
}).then(res => {
config.value = res;
if(res?.siteName){
localStorage.setItem('SiteName', res?.siteName)
}
})
// 读取全局配置
listCmsWebsiteField({
categoryId: 0
}).then(list => {
logo.value = list.find(item => item.name === 'siteLogo')
})
}
onMounted(() => {
// 获取用户信息
if (token.value) {
getUserInfo().then(data => {
user.value = data
})
}
reload();
});
</script>
<style lang="scss">
@import 'assets/css/base.css';
@import 'assets/css/model.css';
//@import 'assets/css/main.css';
//@import 'assets/css/user.css';
//@import 'assets/css/index.css';
</style>

View File

@@ -0,0 +1,25 @@
<template>
<div class="fr clearfix flex items-center">
<!-- <div class="fl mr-5">-->
<!-- <el-input v-model="keyword" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="onSearch"/>-->
<!-- </div>-->
<div class="lang flex justify-center text-center items-center">
<el-space>
<nuxt-link to="https://oa.websoft.top/register" type="text" class="text-sm text-gray-500">注册</nuxt-link>
<el-divider direction="vertical"/>
<nuxt-link to="https://oa.websoft.top/passport/login" type="text" class="text-sm text-gray-500">登录</nuxt-link>
</el-space>
</div>
</div>
</template>
<script setup lang="ts">
import { Search } from '@element-plus/icons-vue'
const keyword = ref<string>('');
const onSearch = () => {
window.location.href = `/search/${keyword.value}`;
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,27 @@
<template>
<nuxt-link to="/" class="flex items-center gap-sm mr-7" v-if="logo">
<!-- <el-image src="https://oss.wsdns.cn/20250211/eedb87d7b95b41e3bc9eca1247c85fa4.png" class="h-[30px]" size="small" shape="square" />-->
<!-- <h4 class="text-gray-700 text-xl font-bold">WEBSOFT</h4>-->
<el-image :src="logo?.value" class=" rounded-sm rounded-sm h-[24px]"/>
<!-- <div class="text-sm text-gray-700 text-xl font-bold" :style="`${logo?.style}`">{{ website?.websiteName }}</div>-->
</nuxt-link>
</template>
<script setup lang="ts">
import {useLogo, useWebsite} from "~/composables/configState";
import {listCmsWebsiteField} from "~/api/cms/cmsWebsiteField";
const logo = useLogo()
const config = useAppConfig();
const website = useWebsite();
listCmsWebsiteField({
name: 'siteLogo'
}).then(data => {
if(data[0]){
logo.value = data[0]
}
})
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,95 @@
<script setup lang="ts">
import {navigateTo} from "#imports";
const navigations = useMenu();
import {useMenu} from "~/composables/configState";
const handleSelect = (index: any,keyPath: string[]) => {
const split = index.split('-');
if(split.length == 1){
const item = navigations.value[index];
navigateTo(item)
}
if(split.length == 2){
const item = navigations.value[split[0]];
if(item.children){
const child = item.children[split[1]];
navigateTo(child.path)
}
}
}
</script>
<template>
<el-menu
mode="horizontal"
style="background-color: transparent;"
active-text-color="#1b850c"
:ellipsis="false"
@select="handleSelect"
>
<template v-for="(item,index) in navigations" :key="index">
<template v-if="item.children && item.children.length > 0">
<el-sub-menu :index="`${index}`">
<template #title><span class="text-gray-900 hover:text-green-700 text-[1rem]">{{ item.title }}</span></template>
<template v-for="(sub,subIndex) in item.children" :key="subIndex">
<template v-if="sub.children && sub.children.length > 0">
<el-sub-menu :index="`${index}-${subIndex}`">
<template #title><p class="text-base">{{ sub.title }}</p></template>
<el-menu-item v-for="(sub2,sub2Index) in sub.children" :key="sub2Index" :index="`${index}-${subIndex}-${sub2Index}`">
<nuxt-link :to="navTo(sub2)" class="text-base">{{ sub2.title }}</nuxt-link>
</el-menu-item>
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="`${index}-${subIndex}`">
<nuxt-link :to="sub.path" class="text-base"><el-space><el-image v-if="sub.icon" class="ws-icon" :src="sub.icon" />{{ sub.title }}</el-space></nuxt-link>
</el-menu-item>
</template>
</template>
</el-sub-menu>
</template>
<el-menu-item v-else :index="`${index}`">
<nuxt-link :to="navTo(item)" class="text-base">{{ item.title }}</nuxt-link>
</el-menu-item>
</template>
</el-menu>
</template>
<style scoped lang="scss">
.el-menu--horizontal > .el-menu-item:nth-child(1) {
margin-right: auto;
}
.el-menu{
border-bottom: none;
}
.ws-icon{
width: 24px;
height: 24px;
}
.el-menu-item:hover{
outline: 0 !important;
color: #1b850c !important;
background-color: #f1f8eb !important;
}
.el-menu-item.is-active {
color: #1b850c !important;
background: #FFFFFF !important;
}
.el-sub-menu .el-sub-menu__title:link{
color: #ff9900;
}
.el-sub-menu:hover{
color: #ff9900;
}
.text-base:hover{
color: #1b850c;
}
//
//$--menu-item-hover-fill: #ff0000;
//$--el-menu-hover-bg-color: #1b850c;
.el-menu--horizontal {
color: #f1ffec;
background-color: #1b850c;
}
</style>

View File

@@ -0,0 +1,34 @@
<script setup lang="ts">
import {useConfigInfo} from "~/composables/configState";
import { Search } from '@element-plus/icons-vue'
const config = useConfigInfo();
const keyword = ref<string>('');
const i18n = useI18n();
const onSearch = () => {
window.location.href = `/${i18n.locale.value}/search/${keyword.value}`;
}
</script>
<template>
<div class=" bg-blue-50 text-gray-400 px-2 py-1" v-if="config.showTopBar == 'true'">
<div class="w1200 flex justify-between items-center">
<span>{{ config?.topWelcomeInfo }}</span>
<div class="lang flex justify-center text-center items-center">
<el-space>
<div class="search">
<div class="fl mr-5">
<el-input v-model="keyword" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="onSearch" />
</div>
</div>
<a :href="`/zh?spm=zh_CN`" class="text-sm"><span class="text-gray-500">中文版</span></a>
<el-divider direction="vertical" />
<a :href="`/en?spm=en_US`" class="text-sm"><span class="text-gray-500">English</span></a>
</el-space>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,38 +1,23 @@
<template>
<div class="banner m-auto relative sm:flex">
<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 sm:flex hidden">
<div class="banner-text py-12 md:w-screen-xl m-auto opacity-90 flex flex-col justify-center">
<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="token && layout.buyUrl" type="warning" @click="openSpmUrl(`https://${sysDomain}/token-login`)">产品控制台</el-button>
<el-button v-if="!token" type="warning" @click="navigateTo(`/passport/login`)">立即开通</el-button>
<el-button v-if="layout.docUrl" @click="openSpmUrl(layout.docUrl)">产品文档</el-button>
</div>
<div class="demo-account mt-3 text-blue-7" v-if="layout?.account">账号密码{{ layout.account }}</div>
</div>
<!-- 自定义banner -->
<div class="banner m-auto relative sm:flex hidden-sm-and-down flex justify-center">
<el-image :src="layout?.banner" class="min-h-sm sm:h-auto w-full"></el-image>
<div class="banner-bar absolute top-0 w-full sm:flex hidden">
<div class="banner-text py-12 md:w-screen-xl m-auto opacity-90 flex flex-col justify-center">
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo} from "~/composables/configState";
import {openSpmUrl} from "~/utils/common";
import type {Layout} from "~/api/layout/model";
const token = useToken();
const sysDomain = useSysDomain();
withDefaults(
withDefaults(
defineProps<{
layout?: any;
layout?: Layout;
}>(),
{}
);
const config = useConfigInfo();
</script>

View File

@@ -1,29 +1,35 @@
<template>
<div class="sm:py-4 sm:px-0 mx-3 py-2">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item :to="{ path: '/' }">
<el-icon class="cursor-pointer">
<ElIconHouse/>
</el-icon>
</el-breadcrumb-item>
<el-breadcrumb-item v-if="data?.parentName">
<span class="cursor-pointer hover:font-bold" @click="openSpmUrl(`/product`,data,data.categoryId)">{{ data.parentName }}</span>
</el-breadcrumb-item>
<el-breadcrumb-item v-if="data?.categoryName">{{ title ? title : data.categoryName }}</el-breadcrumb-item>
<el-breadcrumb-item v-if="title">{{ title }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
<!--面包屑-->
<script setup lang="ts">
import {ArrowRight} from '@element-plus/icons-vue'
withDefaults(
defineProps<{
data?: any;
// 父级栏目
categoryName?: string;
// 当前栏目
title?: string;
// 正文
detail?: string;
}>(),
{}
);
</script>
<template>
<el-breadcrumb :separator-icon="ArrowRight" class="py-4">
<el-breadcrumb-item :to="{ path: '/' }">
<el-icon class="cursor-pointer">
<ElIconHouse/>
</el-icon>
</el-breadcrumb-item>
<el-breadcrumb-item>
<nuxt-link :to="data.path ? navTo(data) : navTo(data,`/${data.model}/${data.categoryId}.html`)" class="cursor-pointer hover:font-bold">{{ categoryName || data.parentName }}</nuxt-link>
</el-breadcrumb-item>
<el-breadcrumb-item v-if="detail">
<span>{{ detail }}</span>
</el-breadcrumb-item>
<el-breadcrumb-item v-else>
<span>{{ title || data.title }}</span>
</el-breadcrumb-item>
</el-breadcrumb>
</template>

View File

@@ -1,44 +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 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">
<el-image :src="item.image" fit="contain" :lazy="true" class="w-full h-[150px] cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)" />
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
<p class="text-gray-900 dark:text-white text-base font-semibold flex items-center gap-1.5">
<a class="flex-1 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">{{ item.title }}</a>
</p>
<div class="text-[15px] text-gray-500 dark:text-gray-400 mt-1 line-clamp-2 sm:min-h-[45px]">
{{ item.comments }}
</div>
<div class="button-group flex justify-center mt-3">
<el-button class="w-full" :icon="ElIconView">演示</el-button>
<el-button class="w-full" :icon="ElIconShoppingCart">购买</el-button>
</div>
</div>
</el-card>
</el-col>
</template>
</el-row>
</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;
list?: any[];
}>(),
{}
);
const config = useConfigInfo();
</script>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import {detail} from "~/utils/common";
import dayjs from "dayjs";
const props = withDefaults(
defineProps<{
data?: any;
}>(),
{}
);
</script>
<template>
<template v-if="data" v-for="(item,index) in data" key="index">
<li class="clearfix">
<a :href="detail(item)" 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)" :title="item.title">{{ item.title }}</a></h3>
<div v-html="item.comments" class="line-clamp-2"></div>
<div class="date">{{ $t('createTime') }}{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</div>
<div class="n-more" ><a :href="detail(item)" >{{ $t('seeMore') }}>></a></div>
</div>
</li>
</template>
<div class="clearboth"></div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import {findTags, listCmsArticle, pageCmsArticle} from "~/api/cms/cmsArticle";
const props = withDefaults(
defineProps<{
data?: CmsArticle;
model?: string;
}>(),
{}
);
const i18n = useI18n();
const list = ref<CmsArticle[]>([])
const tags = props.data?.tags ? JSON.parse(props.data?.tags)[0] : '';
const reload = () => {
if(props.data?.tags){
const tags = JSON.parse(props.data?.tags);
findTags({
tags: tags.join(',')
}).then(data => {
if(data){
list.value = data.filter(d => d.model == 'article');
}
})
}
}
watch(
() => props.data?.articleId,
() => {
reload();
},
{immediate: true}
);
</script>
<template>
<div class="relatenew relate"><h4>{{ $t('articleRelated')}}</h4>
<div class="p-1">
<ul id="relate_n" class="news_list clearfix">
<li v-for="(item,index) in list" :key="index"><a :href="detail(item)" :title="item.title">{{ item.title }}</a></li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import {detail} from "~/utils/common";
import dayjs from "dayjs";
const props = withDefaults(
defineProps<{
data?: any;
}>(),
{}
);
</script>
<template>
<el-row :gutter="20">
<template v-for="(item,index) in data" key="index">
<el-col :span="16" class="my-3">
<div class="item p-2 border-1 border-gray-200 border-solid hover:border-blue-500">
<a :href="detail(item)" :title="item.title" class="img"><el-image :fit="`scale-down`" :src="item.image" :alt="item.title" /></a>
<h3>
<a :href="detail(item)" :title="item.title" class="text-lg font-bold">{{ item.title }}</a>
<span class="line-clamp-2 text-gray-400 text-sm font-normal py-3">{{ item.comments }}</span>
</h3>
</div>
</el-col>
</template>
</el-row>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import {findTags} from "~/api/cms/cmsArticle";
const props = withDefaults(
defineProps<{
data?: CmsArticle;
}>(),
{}
);
const i18n = useI18n();
const list = ref<CmsArticle[]>([])
onMounted(() => {
setTimeout(() => {
if(props.data?.tags){
const tags = JSON.parse(props.data?.tags);
findTags({
tags: tags.join(',')
}).then(data => {
if(data){
list.value = data.filter(d => d.model == 'product');
}
})
}
}, 500)
})
</script>
<template>
<div class="relateproduct relate">
<h4>{{ $t('productRelated') }}</h4>
<div class="py-3 px-1">
<el-row :gutter="16">
<el-col :span="6" v-for="(item,index) in list" :key="index" class="my-1">
<a class="flex flex-col items-center text-center block" :href="detail(item)">
<el-image :src="item.image" fit="fill" :alt="item.title" style="height: 150px;"/>
<span class="py-1">{{ item.title }}</span>
</a>
</el-col>
</el-row>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,77 @@
<!-- 最近浏览 -->
<script setup lang="ts">
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
const props = withDefaults(
defineProps<{
data?: CmsArticle;
type?: string;
}>(),
{}
);
const i18n = useI18n();
const list = ref<CmsArticle[]>([])
const key = 'ArticleRecently' + i18n.locale.value
// 加入最近浏览
onMounted(() => {
const ArticleRecently = localStorage.getItem(key)
if(props.data?.articleId){
if(ArticleRecently){
const json = JSON.parse(ArticleRecently);
if(json){
const find = json.find((item: CmsArticle) => item.articleId === props.data?.articleId);
if(!find){
// 保留6条记录
if(json.length > 8){
json.splice(-1, 1)
}
json.unshift({
articleId: props.data?.articleId,
title: props.data?.title,
image: props.data?.image,
detail: props.data.detail || 'item',
})
localStorage.setItem(key,JSON.stringify(json))
}
}
reload();
}else {
localStorage.setItem(key, JSON.stringify([]))
}
}
})
const reload = () => {
const ArticleRecently = localStorage.getItem(key);
if(ArticleRecently){
list.value = JSON.parse(ArticleRecently)
}
}
</script>
<template>
<div class="relateproduct relate">
<h4>{{ $t('recentlyViewed') }}</h4>
<div class="p-1" v-if="type == 'article'">
<ul id="relate_n" class="news_list clearfix">
<li v-for="(item,index) in list" :key="index"><a :href="detail(item)" :title="item.title">{{ item.title }}</a></li>
</ul>
</div>
<div class="py-3 px-1" v-if="type == 'product'">
<el-row :gutter="16">
<el-col :span="6" v-for="(item,index) in list" :key="index" class="my-1">
<a class="flex flex-col items-center text-center block" :href="detail(item)">
<el-image :src="item.image" fit="fill" :alt="item.title" style="height: 150px;"/>
<span class="py-1">{{ item.title }}</span>
</a>
</el-col>
</el-row>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,60 +1,20 @@
<template>
<div v-if="title" class="text-center flex flex-col items-center py-12 z-100 relative">
<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 :span="6" :xs="24" 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 class="lg:w-screen-lg sm:flex xl:p-0 p-4 mb-10 m-auto relative">
<el-row :gutter="24" class="flex w-full">
<el-col v-for="(item,index) in list" :key="index" :span="6" :xs="24" class="mb-6">
<nuxt-link :to="item.path">
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-green-50 cursor-pointer h-[200px] flex items-center justify-center">
<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">
<el-space class="flex-1 cursor-pointer flex items-center flex-col">
<el-image v-if="item.icon" :src="item.icon" class="w-[40px]" />
<span class="py-3 text-lg">{{ item.title }}</span>
</el-space>
</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>
<!-- <el-button type="warning" v-if="item.price" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">-->
<!-- <div class="flex items-center">-->
<!-- <span>{{ item.price * 0.1 }}</span>-->
<!-- <span v-if="item.chargingMethod == 2">/</span>-->
<!-- <span v-if="item.chargingMethod == 3">/</span>-->
<!-- <span v-if="item.chargingMethod == 4">/</span>-->
<!-- </div>-->
<!-- </el-button>-->
</div>
</div>
</el-card>
</el-card>
</nuxt-link>
</el-col>
</template>
<!-- <div class="more flex justify-center w-full"><el-button>查看更多</el-button></div>-->
</el-row>
</div>
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
@@ -63,10 +23,14 @@
</template>
<script setup lang="ts">
import {loginAdminByToken, loginByToken, loginDeveloperCenterByToken, openSpmUrl} from "~/utils/common";
import {loginAdminByToken} from "~/utils/common";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {Company, CompanyParam} from "~/api/system/company/model";
import {pageCompanyAll} from "~/api/system/company";
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
import {navigateTo} from "#imports";
const props = withDefaults(
defineProps<{
@@ -85,18 +49,15 @@ const emit = defineEmits<{
(e: 'done'): void;
}>();
const list = ref<Company[]>([]);
const list = ref<CmsNavigation[]>([]);
// 请求数据
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page', {
params: props.param
listCmsNavigation({
parentId: 2828
}).then(data => {
list.value = data;
})
if (response.value?.data) {
if (response.value?.data.list) {
list.value = response.value?.data.list;
}
}
}
reload();
</script>

36
components/Content.vue Normal file
View File

@@ -0,0 +1,36 @@
<script setup lang="ts">
const props = withDefaults(
defineProps<{
data?: any;
isMobile?: boolean;
}>(),
{}
);
</script>
<template>
<div class="text-[16px]" v-html="data"></div>
</template>
<style lang="scss">
.content {
padding-top: 15px;
overflow: hidden;
color: #333131;
}
.content p{
padding: 0;
line-height: 2.2rem;
text-indent: 2rem;
}
.content img{
padding: 8px 0;
max-width: 100%;
height: auto;
}
.content video {
width: 100%;
height: auto;
}
</style>

100
components/Left.vue Normal file
View File

@@ -0,0 +1,100 @@
<template>
<div v-loading="loading">
<div class="box sort_menu">
<el-menu
default-active="1"
@select="handleSelect"
>
<template v-for="(item,index) in category" :key="index">
<template v-if="item.children && item.children.length > 0">
<el-sub-menu :index="`${index}`">
<template #title>{{ item.title }}</template>
<template v-for="(sub,subIndex) in item.children" :key="subIndex">
<template v-if="sub.children && sub.children.length > 0">
<el-sub-menu :index="`${index}-${subIndex}`">
<template #title>{{ sub.title }}</template>
<el-menu-item v-for="(sub2,sub2Index) in sub.children" :key="sub2Index" :index="`${index}-${subIndex}-${sub2Index}`">
{{ sub2.title }}
</el-menu-item>
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="`${index}-${subIndex}`">
<el-space>
<el-avatar v-if="item.icon" size="small" :src="item.icon" />
{{ sub.title }}
</el-space>
</el-menu-item>
</template>
</template>
</el-sub-menu>
</template>
<el-menu-item v-else :index="`${index}`">
<el-space>
<el-avatar v-if="item.icon" size="small" :src="item.icon" />
{{ item.title }}
</el-space>
</el-menu-item>
</template>
</el-menu>
</div>
</div>
</template>
<script setup lang="ts">
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
import type {CmsLink} from "~/api/cms/cmsLink/model";
import {pageCmsArticle} from "~/api/cms/cmsArticle";
import {pageCmsLink} from "~/api/cms/cmsLink";
import {navigateTo, useMenu, useSubMenu} from "#imports";
import {
ArrowRight
} from '@element-plus/icons-vue'
const props = withDefaults(
defineProps<{
category?: CmsNavigation[];
title?: string;
}>(),
{}
);
const i18n = useI18n();
const list = ref<CmsArticle[]>([]);
const hotKeys = ref<CmsLink[]>([]);
const loading = ref<boolean>(true);
// 加载数据
pageCmsArticle({
limit: 5,
recommend: 1,
status: 0,
categoryId: i18n.locale.value == 'en' ? 1080 : 1005,
lang: i18n.locale.value
}).then(data => {
if(data){
list.value = data?.list;
}
loading.value = false;
})
// 热门关键词
pageCmsLink({
limit: 8,
categoryId: i18n.locale.value == 'en' ? 1115 : 1106,
lang: i18n.locale.value
}).then(res => {
if(res){
hotKeys.value = res.list;
}
})
const handleSelect = (index: any) => {
if(props.category){
const item = props.category[index];
navigateTo(item.path)
}
}
</script>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import {detail} from "~/utils/common";
import dayjs from "dayjs";
const props = withDefaults(
defineProps<{
data?: any;
}>(),
{}
);
</script>
<template>
<el-row :gutter="20">
<template v-for="(item,index) in data" 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="detail(item)" :title="item.title" class="img"><el-image :fit="`scale-down`" :src="item.image" :alt="item.title" /></a>
<h3>
<a :href="detail(item)" :title="item.title" class="text-lg font-bold">{{ item.title }}</a>
<span class="line-clamp-2 text-gray-400 text-sm font-normal py-3 text-left">{{ item.comments }}</span>
</h3>
</div>
</el-col>
</template>
</el-row>
</template>
<style scoped lang="scss">
</style>

35
components/MContent.vue Normal file
View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
const props = withDefaults(
defineProps<{
data?: any;
isMobile?: boolean;
}>(),
{}
);
</script>
<template>
<div class="content text-[16px]" v-html="data"></div>
</template>
<style lang="scss">
.content {
padding-top: 15px;
overflow: hidden;
color: #333131;
}
.content p{
padding: 0;
line-height: 2.2rem;
}
.content img{
padding: 8px 0;
max-width: 100%;
height: auto;
}
.content video {
width: 100%;
height: auto;
}
</style>

34
components/MFooter.vue Normal file
View File

@@ -0,0 +1,34 @@
<template>
<div class="h-[40px]"></div>
<div class="footer">
<div class="ffs">
<a :href="`tel:${config.callPhone}`"><span class="span1">{{ $t('tel') }}</span></a>
<a :href="`/${i18n.locale.value}/m/page/1008.html`"><span class="span2">{{ $t('map') }}</span></a>
<a :href="`/${i18n.locale.value}/m/order/1003.do`"><span class="span3">{{ $t('message') }}</span></a>
</div>
</div>
</template>
<script setup lang="ts">
// 请求数据
import {useConfigInfo} from "#imports";
import {configWebsiteField} from "~/api/cms/cmsWebsiteField";
const config = useConfigInfo();
const i18n = useI18n();
// TODO 读取网站配置信息
configWebsiteField({
lang: i18n.locale.value
}).then(res => {
config.value = res;
})
</script>
<style lang="scss">
.el-divider__text{
display: block;
margin-top: -1px;
--el-bg-color: #ffffff;
}
</style>

150
components/MHeader.vue Normal file
View File

@@ -0,0 +1,150 @@
<template>
<header class="clearfix flex justify-between items-center w-full">
<div class="logo" :class="logo?.style">
<nuxt-link :to="`/m`" target="_top"><img :src="`${logo?.value }`" /></nuxt-link>
</div>
<div class="sub-menu">
<el-icon color="black" :size="20" @click="drawer = true"><Fold /></el-icon>
</div>
</header>
<el-drawer
v-model="drawer"
:size="290"
>
<div id="menu">
<el-menu
currentIndex="/product"
:unique-opened="true"
>
<template v-for="(item,index) in navigations" :key="index">
<el-sub-menu v-if="item?.children && item.children.length > 0" :index="`${index}`">
<template #title><span>{{ item.title }}</span></template>
<el-menu-item v-for="(sub,subIndex) in item.children" :index="`${subIndex}`">
<el-space>
<el-avatar v-if="sub.icon" :src="sub.icon" shape="square" size="small"></el-avatar>
<a :href="navTo(sub)">{{ sub.title }}</a>
</el-space>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="`${index}`"><a :href="navTo(item)">{{ item.title }}</a></el-menu-item>
</template>
</el-menu>
<div class="search-tools flex justify-center p-4 mt-5">
<el-input v-model="keyword" placeholder="请输入关键词..." :suffix-icon="Search" @change="onSearch"/>
</div>
<div class="lang flex justify-center text-center" v-if="langList.length > 0">
<el-space>
<a :href="`/m?spm=zh_CN`" class="text-sm text-gray-500">中文版</a>
<el-divider direction="vertical" />
<a :href="`/en/m?spm=en_US`" class="text-sm text-gray-400">English</a>
</el-space>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
// 引入所需的图标
import {
useConfigInfo,
useMenu,
useToken,
useUser, useWebsite
} from "~/composables/configState";
import {getPath, isMobileDevice, navTo} from "#imports";
import { Search, Expand, Fold } from '@element-plus/icons-vue'
import {configWebsiteField} from "~/api/cms/cmsWebsiteField";
import {getSiteInfo, getUserInfo} from "~/api/layout";
import {listCmsLangLog} from "~/api/cms/cmsLangLog";
import type {CmsLangLog} from "~/api/cms/cmsLangLog/model";
import type {CmsWebsiteField} from "~/api/cms/cmsWebsiteField/model";
import {getWebsiteField} from "~/api/cms/cmsWebsiteField";
const token = useToken();
const user = useUser();
const navigations = useMenu();
const config = useConfigInfo();
const i18n = useI18n();
const isMobile = ref<boolean>(false);
const langList = ref<CmsLangLog[]>([]);
const drawer = ref<boolean>(false);
const logo = ref<CmsWebsiteField>();
const keyword = ref();
const setNav = () => {
navigations.value = navigations.value.map(item => {
item.showChild = false
item.childHeight = 0;
return item
})
}
setNav()
const onSearch = () => {
window.location.href = `/m/search/${keyword.value}`;
}
const reload = async () => {
// TODO 读取网站配置信息
configWebsiteField({
lang: i18n.locale.value
}).then(res => {
config.value = res;
})
// TODO 读取国际化配置
listCmsLangLog({}).then(list => {
langList.value = list;
})
// TODO 读取logo
getWebsiteField(13686).then(data => {
logo.value = data;
})
// TODO 是否跳转H5版
if(isMobileDevice()){
isMobile.value = true;
const path = getPath(1);
if(!path || !path.startsWith('/m')){
let newUrl = '';
let origin = window.location.origin;
let href = window.location.href;
let pathname = window.location.pathname;
if(pathname.startsWith('/m') || pathname.startsWith(`/${i18n.locale.value}/m`)){
return false;
}
newUrl = href.replace(origin,origin + '/m');
window.location.href = newUrl;
}
}
// 获取用户信息
// token.value = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJ1c2VybmFtZVwiOlwiMTM4MDAwMTAzMTdcIixcInRlbmFudElkXCI6MTAzMTd9IiwiZXhwIjoxNzM1Nzk2NjA1LCJpYXQiOjE3MzMyMDQ2MDV9.hx6OV4ZHe6XTnNye1gvsuXjoxHW0YihcEwSuppUcoCc'
// 获取用户信息
if (token.value) {
getUserInfo().then(data => {
user.value = data
})
}
}
onMounted(() => {
// 在这里放置你想要在组件渲染完成后执行的代码
reload();
});
</script>
<style lang="scss">
.el-menu{
border-right: none !important;
}
.el-menu-item, .el-sub-menu{
border-bottom: 1px solid #f3f3f3;
}
</style>

View File

@@ -0,0 +1,44 @@
<!-- 上一篇 下一篇 -->
<script setup lang="ts">
import {detail} from "~/utils/common";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import {getNext, getPrevious} from "~/api/cms/cmsArticle";
const props = withDefaults(
defineProps<{
articleId?: number;
}>(),
{}
);
const i18n = useI18n();
const previousArticle = ref<CmsArticle>();
const nextArticle = ref<CmsArticle>();
// 上一篇
previousArticle.value = await getPrevious({
articleId: props.articleId,
lang: i18n.locale.value
})
// 下一篇
nextArticle.value = await getNext({
articleId: props.articleId,
lang: i18n.locale.value
})
</script>
<template>
<div class="page flex flex-col">
<span class="text-left">
{{ $t('previous') }}<a :href="detail(previousArticle)">{{ previousArticle?.title }}</a>
</span>
<span class="text-left">
{{ $t('next') }}<a :href="detail(nextArticle)">{{ nextArticle?.title }}</a>
</span>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,180 +0,0 @@
<template>
<div :class="form?.style" 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 xl:p-0 xl:px-0 px-4">
<!-- <Breadcrumb :title="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 grid lg:grid-cols-2 lg:items-center">
<div class="flex">
<el-avatar v-if="avatar" :src="avatar" :size="120"></el-avatar>
<div class="title-bar">
<h1 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
<span>{{ title }}</span>
</h1>
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
{{ desc }}
</div>
<div class="flex justify-between w-full items-center mt-4" v-if="showSearch">
<el-space>
<div class="w-[500px] pr-10">
<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-space>
<el-space>
<el-button size="large">我的收藏</el-button>
<el-button size="large">热门推荐</el-button>
</el-space>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 密码可见 -->
<el-dialog
v-model="dialogVisible"
title="请输入查看密码"
:show-close="false"
:close-on-click-modal="false"
width="400"
>
<el-input type="password" v-model="password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="checkPassword">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Navigation} from "~/api/cms/navigation/model";
import {ref} from 'vue'
import {Search} from '@element-plus/icons-vue'
import type {ProductParam} from "~/api/oa/product/model";
withDefaults(
defineProps<{
form?: any;
layout?: any;
title?: string;
desc?: string;
buyUrl?: string;
avatar?: string;
showSearch?: boolean;
}>(),
{
title: 'PageName',
desc: 'Description',
demoUrl: '/product',
buyUrl: '/buy'
}
);
const emit = defineEmits<{
(e: 'done', show: boolean): void;
(e: 'search', where: ProductParam): void;
}>();
const route = useRoute();
const token = useToken();
const form = ref<Navigation>();
const layout = ref<any>();
const password2 = ref('');
// 密码弹窗
const dialogVisible = ref(false)
// 搜索表单
const where = reactive<ProductParam>({
keywords: ''
});
// 验证密码
const checkPassword = async () => {
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-navigation/checkNavigationPassword', {
query: {
password: form.value?.password,
password2: password2.value
}
})
if (response.value?.code === 0) {
dialogVisible.value = false;
emit('done', true);
console.log(response.value.message);
} else {
ElMessage.error(response.value?.message);
}
}
// 搜索结果
const handleClick = async () => {
emit('search',where);
}
const reload = async () => {
// 校验密码
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {
query: {
path: route.path
}
})
if (nav.value?.data) {
form.value = nav.value?.data;
console.log('PageBanner.vue => ', form.value)
// 页面布局
if (form.value?.layout) {
layout.value = JSON.parse(form.value?.layout)
}
// 允许直接访问
if (form.value?.permission === 0) {
emit('done', true);
}
// 要求登录可见
if (form.value?.permission === 1) {
navigateTo(`/passport/login`);
if (token && token.value.length > 0) {
emit('done', true);
}
}
// 要求密码可见
if (form.value?.permission === 2) {
dialogVisible.value = true;
}
}
}
reload();
</script>

View File

@@ -1,30 +0,0 @@
<template>
<div class="container md:w-screen-xl 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>

64
components/Pagination.vue Normal file
View File

@@ -0,0 +1,64 @@
<!--分页组件-->
<script setup lang="ts">
import type { ComponentSize } from 'element-plus'
import type {CmsArticleParam} from "~/api/cms/cmsArticle/model";
import {useIsMobile} from "~/composables/configState";
const props = withDefaults(
defineProps<{
data?: any;
total?: number;
size?: ComponentSize;
}>(),
{}
);
const emit = defineEmits<{
(e: 'done', params: CmsArticleParam): void;
(e: 'update:visible', visible: boolean): void;
}>();
const i18n = useI18n();
// const size = ref<ComponentSize>('default');
const disabled = ref<boolean>(false);
const background = ref(true);
const mobile = useIsMobile();
// 搜索表单
const where = reactive<CmsArticleParam>({
keywords: '',
page: 1,
limit: 20,
status: 0,
parentId: undefined,
categoryId: undefined,
lang: i18n.locale.value
});
// 加载数据
const reload = async () => {
emit('done',where);
};
</script>
<template>
<div class="pagination flex py-5 items-center" :class="mobile ? 'justify-center' : 'justify-between'">
<span class="text-sm pl-2" v-if="!mobile">
{{ $t('pagination.total') }} {{ total }} {{ $t('pagination.unit') }} {{ $t('pagination.perPage') }}{{ where.limit }}{{ $t('pagination.unit') }} {{ where.page }}/{{ Math.ceil(Number(total) / Number(where.limit)) }}
</span>
<el-pagination
v-model:current-page="where.page"
v-model:page-size="where.limit"
:size="size"
:disabled="disabled"
:background="background"
layout="prev, pager, next"
:total="total"
@currentChange="reload"
/>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,94 +0,0 @@
<template>
<div class="text-center flex flex-col items-center py-12 z-100 relative">
<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 :span="6" :xs="24" 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="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.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 class="flex items-end">
<span class="text-red-500 text-xl">{{ item.price }}</span>
<span v-if="item.durationMethod == 1" class="px-1 pb-1 text-xs text-gray-400">/</span>
<span v-if="item.durationMethod == 2" class="px-1 pb-1 text-xs text-gray-400">/</span>
<span v-if="item.durationMethod == 3" class="px-1 pb-1 text-xs text-gray-400">/</span>
</div>
{{ item.chargingMethod }}
<el-button type="warning" @click.stop="openSpmUrl(`/product/create`,item,item.productId,true)">立即开通
</el-button>
</div>
</div>
</el-card>
</el-col>
</template>
<!-- <div class="more flex justify-center w-full"><el-button>查看更多</el-button></div>-->
</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 {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {Product, ProductParam} from "~/api/oa/product/model";
const props = withDefaults(
defineProps<{
param?: ProductParam;
disabled?: boolean;
title?: string;
comments?: string;
fit?: any;
}>(),
{
title: '卡片标题',
comments: '卡片描述',
fit: 'cover'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const list = ref<Product[]>([]);
// 请求数据
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
params: props.param
})
if (response.value?.data) {
if (response.value?.data.list) {
list.value = response.value?.data.list;
}
}
}
reload();
</script>

View File

@@ -1,42 +0,0 @@
<template>
<div class="shopinfo px-3 sm:px-0">
<div class="bg-white p-3 mt-3 flex items-center justify-between rounded-xl">
<div class="left flex gap-xs items-center">
<el-avatar shape="square" :src="data?.image" :size="55" />
<div class="shop-name flex flex-col">
<div class="shop-name cursor-pointer">
<div class="flex items-center">
<text class="text-xl">{{ data?.merchantName }}</text>
<el-tag type="success" size="small" class="ml-2">官方</el-tag>
</div>
</div>
<el-rate
v-model="rate"
show-score
text-color="#ff9900"
score-template="{value}">
</el-rate>
</div>
</div>
<el-button>进入店铺</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import type {Navigation} from "~/api/cms/navigation/model";
import type {Merchant} from "~/api/shop/merchant/model";
withDefaults(
defineProps<{
data?: Merchant;
}>(),
{}
);
const rate = ref(5);
</script>
<style scoped lang="scss">
</style>

25
components/Tags.vue Normal file
View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import {getLang} from "~/utils/common";
const props = withDefaults(
defineProps<{
data?: any;
}>(),
{}
);
const i18n = useI18n();
</script>
<template>
<div class="tag pt-1" v-if="data">
<el-space>
<span>{{ $t('keyword') }}</span>
<el-tag v-for="(item,index) in JSON.parse(data)" :key="index"><a :href="`${i18n.locale.value == 'en' ? '/en' : ''}/tags/${item}`" target="_blank">{{ item }}</a></el-tag>
</el-space>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,17 +1,40 @@
<template>
<el-card class="m-5 w-screen-sm mt-[60px] m-auto">
<!-- 本组件已废弃 -->
<template v-if="website?.running">
<el-card v-if="website?.running != 1" class="m-5 w-screen-sm mt-[60px] m-auto">
<!-- 异常状态 -->
<el-result
:icon="website.statusIcon || 'info'"
: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>
</template>
</el-result>
v-if="website.running == 0"
icon="warning"
:title="`未开通`"
:sub-title="'请先开通该产品'"
/>
<el-result
v-if="website.running == 2"
icon="warning"
:title="`网站维护中`"
:sub-title="website.statusText || '请检查您的网络或与网站管理员联系'"
/>
<el-result
v-if="website.running == 3"
:icon="'error'"
:title="`已关闭`"
:sub-title="'该网站已被管理人员关闭'"
/>
<el-result
v-if="website.running == 4"
:icon="'error'"
:title="`已欠费`"
:sub-title="'请及时缴费以免给您带来不必要的损失'"
/>
<el-result
v-if="website.running == 5"
:icon="'error'"
:title="`违规关停`"
:sub-title="'网站可能因含有违规内容被关停,请联系管理员整改恢复'"
/>
</el-card>
</template>
</template>
<script setup lang="ts">
import {useWebsite} from "~/composables/configState";

View File

@@ -1,225 +0,0 @@
<template>
<div :class="form?.style" 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 xl: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">
<el-space class="flex">
<div class="mr-4" v-if="company.companyLogo">
<el-avatar :src="company.companyLogo" :size="150"></el-avatar>
</div>
<div class="title-bar">
<h1 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
<span>{{ company.shortName }}</span>
</h1>
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
{{ company?.comments || desc }}
</div>
<el-space class="mt-4">
<el-button
:icon="ElIconPlus"
@click="openSpmUrl(`http://git.gxwebsoft.com/gxwebsoft/websoft-cms.git`, {},0,true)"
>
关注
</el-button>
<!-- <el-tree-select-->
<!-- v-model="where.categoryId"-->
<!-- v-if="navigation?.length > 0"-->
<!-- :data="navigation"-->
<!-- placeholder="选择分类"-->
<!-- :render-after-expand="false"-->
<!-- style="width: 240px"-->
<!-- />-->
<div class="w-[300px]">
<el-input
v-model="where.keywords"
placeholder="搜索"
:prefix-icon="Search"
@keydown.enter="reload"
>
<template #append>
<el-button type="primary" @click="reload">搜索</el-button>
</template>
</el-input>
</div>
</el-space>
</div>
</el-space>
</div>
</div>
</div>
<!-- 密码可见 -->
<el-dialog
v-model="dialogVisible"
title="请输入查看密码"
:show-close="false"
:close-on-click-modal="false"
width="400"
>
<el-input type="password" v-model="password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="checkPassword">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
<!-- <el-space class="mt-4">-->
<!-- <el-button-->
<!-- :icon="ElIconBottom"-->
<!-- @click="openSpmUrl(`http://git.gxwebsoft.com/gxwebsoft/websoft-cms.git`, {},0,true)"-->
<!-- >-->
<!-- 关注-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- :icon="ElIconMemo"-->
<!-- @click="openSpmUrl(`/docs`)"-->
<!-- >-->
<!-- 选择分类-->
<!-- </el-button>-->
<!-- <div class="w-[500px]">-->
<!-- <el-input-->
<!-- v-model="where.keywords"-->
<!-- placeholder="搜索"-->
<!-- :prefix-icon="Search"-->
<!-- @keydown.enter="reload"-->
<!-- >-->
<!-- <template #append>-->
<!-- <el-button type="primary" @click="reload">搜索</el-button>-->
<!-- </template>-->
<!-- </el-input>-->
<!-- </div>-->
<!-- </el-space>-->
</template>
<script setup lang="ts">
import {Search} from '@element-plus/icons-vue'
import {openSpmUrl} from "~/utils/common";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Navigation} from "~/api/cms/navigation/model";
import {ref} from 'vue'
import type {CompanyParam} from "~/api/system/company/model";
withDefaults(
defineProps<{
form?: any;
layout?: any;
title?: string;
desc?: string;
buyUrl?: string;
avatar?: string;
}>(),
{
title: 'PageName',
desc: 'Description',
demoUrl: '/product',
buyUrl: '/buy'
}
);
const emit = defineEmits<{
(e: 'done', show: boolean): void;
}>();
const route = useRoute();
const token = useToken();
const form = ref<Navigation>();
const layout = ref<any>();
const password2 = ref('');
// 密码弹窗
const dialogVisible = ref(false)
const company = useCompany();
const navigation = ref<Navigation[]>()
// 搜索表单
const where = reactive<CompanyParam>({
keywords: ''
});
// 验证密码
const checkPassword = async () => {
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-navigation/checkNavigationPassword', {
query: {
password: form.value?.password,
password2: password2.value
}
})
if (response.value?.code === 0) {
dialogVisible.value = false;
emit('done', true);
console.log(response.value.message);
} else {
ElMessage.error(response.value?.message);
}
}
const reload = async () => {
useClientRequest<ApiResult<Navigation[]>>('/cms/cms-navigation', {
params: {
userId: route.params.userId
}
}).then(res => {
navigation.value = res?.data?.map(d => {
return {
label: d.title,
value: d.navigationId,
children: null
}
});
console.log(navigation.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;
console.log('PageBanner.vue => ', form.value)
// 页面布局
if (form.value?.layout) {
layout.value = JSON.parse(form.value?.layout)
}
// 允许直接访问
if (form.value?.permission === 0) {
emit('done', true);
}
// 要求登录可见
if (form.value?.permission === 1) {
navigateTo(`/passport/login`);
if (token && token.value.length > 0) {
emit('done', true);
}
}
// 要求密码可见
if (form.value?.permission === 2) {
dialogVisible.value = true;
}
}
}
reload();
</script>