新版本官网优化完成
This commit is contained in:
103
components/AppHeader/AppHeader.vue
Normal file
103
components/AppHeader/AppHeader.vue
Normal 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>
|
||||
25
components/AppHeader/Login/Login.vue
Normal file
25
components/AppHeader/Login/Login.vue
Normal 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>
|
||||
27
components/AppHeader/Logo/Logo.vue
Normal file
27
components/AppHeader/Logo/Logo.vue
Normal 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>
|
||||
95
components/AppHeader/Menu/Menu.vue
Normal file
95
components/AppHeader/Menu/Menu.vue
Normal 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>
|
||||
34
components/AppHeader/TopBar/TopBar.vue
Normal file
34
components/AppHeader/TopBar/TopBar.vue
Normal 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>
|
||||
Reference in New Issue
Block a user