This commit is contained in:
2026-01-29 10:43:43 +08:00
commit 4a76df3391
426 changed files with 74975 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
<template>
<!-- 头部组件 -->
<div class="sm:p-0 px-4 shadow-sm z-100 top-0 w-full bg-green-700">
<!-- 顶部通栏 -->
<TopBar />
<!-- 导航栏 -->
<div class="flex justify-between xl:w-screen-xl m-auto">
<Menu :data="navigations" />
</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,139 @@
<template>
<div class="fr clearfix flex items-center">
<!-- PC端 -->
<div v-if="setting.searchBtn" class="fl mr-5 hidden-sm-and-down">
<el-input v-model="keywords" :placeholder="`${$t('searchKeywords')}`" :suffix-icon="Search" @change="onSearch"/>
</div>
<template v-if="setting.loginBtn">
<!-- 未登录 -->
<div v-if="!token" class="lang flex justify-center text-center items-center">
<el-space class="hidden-sm-and-down">
<nuxt-link to="/passport/login?type=register" type="text" class="text-sm text-gray-500">注册</nuxt-link>
<el-divider direction="vertical"/>
<nuxt-link to="/passport/login" type="text" class="text-sm text-gray-500">登录</nuxt-link>
</el-space>
</div>
<!-- 已登录 -->
<div v-else>
<div class="header__right items-center pr-4 xl:pr-0 md:flex hidden">
<el-space class="sm:flex hidden" size="large">
<ClientOnly>
<template v-if="token">
<el-dropdown @command="handleCommand">
<el-space class="flex items-center cursor-pointer">
<el-avatar class="cursor-pointer" :src="user?.avatar" :size="30" />
<span>{{ user?.nickname }}</span>
</el-space>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="user"><nuxt-link to="/user">用户中心</nuxt-link></el-dropdown-item>
<el-dropdown-item command="password"><nuxt-link to="/user/password">修改密码</nuxt-link></el-dropdown-item>
<el-dropdown-item command="auth"><nuxt-link to="/user/auth">实名认证</nuxt-link></el-dropdown-item>
<el-dropdown-item command="order" divided><nuxt-link to="/user/order">我的订单</nuxt-link></el-dropdown-item>
<el-dropdown-item divided command="logOut"><nuxt-link to="/user/logout">退出登录</nuxt-link>
</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>
<!-- 移动端 -->
<div class="hidden-sm-and-up mx-7 sm:mx-0">
<div class="el-dropdown-link flex items-center" @click="drawer = true">
<el-avatar v-if="token" class="cursor-pointer" :src="user?.avatar" :size="30"/>
<el-icon v-else color="black" :size="24"><Fold /></el-icon>
</div>
<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 v-if="setting.searchBtn" class="search-tools flex justify-center p-4 mt-5">-->
<!-- <el-input v-model="keywords" placeholder="请输入关键词..." :suffix-icon="Search" @change="onSearch"/>-->
<!-- </div>-->
<!-- <div v-if="setting.langBtn" class="lang flex justify-center text-center">-->
<!-- <el-space>-->
<!-- <a @click="onLang(`zh`)" class="text-sm text-gray-500">中文版</a>-->
<!-- <el-divider direction="vertical" />-->
<!-- <a @click="onLang(`en`)" class="text-sm text-gray-400">English</a>-->
<!-- </el-space>-->
<!-- </div>-->
</div>
</el-drawer>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { Search, Fold } from '@element-plus/icons-vue'
import {useConfigInfo, useMenu, useSetting} from "~/composables/configState";
import type {CmsLangLog} from "~/api/cms/cmsLangLog/model";
import {listCmsLangLog} from "~/api/cms/cmsLangLog";
const token = useToken();
const user = useUser();
const navigations = useMenu();
const setting = useSetting();
const keywords = ref<string>();
const drawer = ref<boolean>(false);
const langList = ref<CmsLangLog[]>([]);
const onSearch = () => {
navigateTo(`/search/${keywords.value || '关键词不能为空!'}`)
}
function handleCommand(command: string) {
switch (command) {
case 'logOut':
logOut();
break;
default:
navigateTo('/user');
break;
}
}
// TODO 读取国际化配置
listCmsLangLog({}).then(list => {
langList.value = list;
})
const onLang = (lang: string) => {
navigateTo(`?lang=${lang}`)
drawer.value = false;
}
function logOut() {
token.value = '';
localStorage.clear();
navigateTo('/passport/login')
}
</script>
<style scoped lang="scss">
.el-menu{
border-right: none;
}
</style>

View File

@@ -0,0 +1,24 @@
<template>
<nuxt-link to="/" class="flex items-center cursor-pointer gap-sm mr-7" v-if="website">
<el-image v-if="website.websiteLogo || logo?.value" :style="`${logo?.style}`" :src="logo?.value || website?.websiteLogo" class="rounded-sm rounded-sm sm:h-[74px] sm:w-[320px] h-[40px] py-1 sm:py-0"/>
<h4 v-else class="text-gray-700 text-xl font-bold" :style="`${logo?.style}`">{{ website?.websiteName }}</h4>
</nuxt-link>
</template>
<script setup lang="ts">
import {useLogo, useWebsite} from "~/composables/configState";
import {listCmsWebsiteField} from "~/api/cms/cmsWebsiteField";
const logo = useLogo()
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,125 @@
<template>
<!-- PC版菜单 -->
<el-menu
class="hidden-sm-and-down"
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}`" class="ws-menu">
<template #title><span class="text-lg hover:text-green-700 menu-1">{{ 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-lg">{{ 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-lg">{{ 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-lg"><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-white text-lg">{{ item.title }}</nuxt-link>
</el-menu-item>
</template>
</el-menu>
</template>
<script setup lang="ts">
import {navigateTo} from "#imports";
import {useMenu} from "~/composables/configState";
const navigations = useMenu();
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>
<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, .el-sub-menu .el-sub-menu__title {
color: #f3f3f3 !important; // 默认字体颜色为白色
}
.el-menu-item:hover, .el-sub-menu .el-sub-menu__title:hover {
background-color: #ecffe7 !important; // 悬停时背景色为白色
color: #1b850c !important; // 悬停时字体颜色改为绿色
}
.el-menu-item.is-active {
color: #1b850c !important;
background: #ecffe7 !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;
}
.text-white {
color: #125c09; // 确保字体颜色为白色
}
.text-white:hover {
color: #1b850c !important;
}
.text-lg {
font-size: 1.125rem; // 保持原有字体大小
}
.menu-1{
color: #f3f3f3;
}
.menu-1:hover{
color: #1b850c !important;
}
//.el-sub-emnu .ws-menu{
// color: #ff0000 !important;
//}
//
//.menu-1{
// color: #ff0000 !important;
//}
//.el-menu--horizontal>.el-sub-menu:hover .el-sub-menu__title{
// color: #1b850c !important;
//}
</style>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import {useConfigInfo} from "~/composables/configState";
import {Search} from '@element-plus/icons-vue'
import Logo from "~/components/AppHeader/Logo/Logo.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 :style="{background: `url(${config.siteLogoBg})`}"
class="text-gray-400 px-0 h-[100px]">
<div class="xl:w-screen-xl m-auto flex justify-between items-center h-[100px]">
<Logo/>
<div class="flex justify-between w-full text-center items-center">
<div class="logo"></div>
<div class="search">
<div class="fl mr-5">
<el-input v-model="keyword" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search"
@change="onSearch"/>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
</style>