@@ -282,3 +282,9 @@ h6 {
|
|||||||
.top-right-btn {
|
.top-right-btn {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* horizontal el menu */
|
||||||
|
.el-menu--horizontal .el-menu-item .svg-icon + span,
|
||||||
|
.el-menu--horizontal .el-sub-menu__title .svg-icon + span {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 导航栏布局枚举
|
||||||
|
*/
|
||||||
|
export enum NavTypeEnum {
|
||||||
|
/**
|
||||||
|
* 左侧导航
|
||||||
|
*/
|
||||||
|
LEFT = 'left',
|
||||||
|
/**
|
||||||
|
* 顶部导航
|
||||||
|
*/
|
||||||
|
TOP = 'top',
|
||||||
|
/**
|
||||||
|
* 混合导航
|
||||||
|
*/
|
||||||
|
MIX = 'mix'
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar" :class="'nav' + navType">
|
||||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
|
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
|
||||||
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
|
||||||
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
|
|
||||||
|
|
||||||
|
<breadcrumb v-if="navType == NavTypeEnum.LEFT" id="breadcrumb-container" class="breadcrumb-container" />
|
||||||
|
<top-nav v-if="navType == NavTypeEnum.MIX" id="topmenu-container" class="topmenu-container" />
|
||||||
|
|
||||||
|
<template v-if="navType == NavTypeEnum.TOP">
|
||||||
|
<logo v-show="showLogo" :collapse="false"></logo>
|
||||||
|
<top-bar id="topbar-container" class="topbar-container" />
|
||||||
|
</template>
|
||||||
<div class="right-menu flex align-center">
|
<div class="right-menu flex align-center">
|
||||||
<template v-if="appStore.device !== 'mobile'">
|
<template v-if="appStore.device !== 'mobile'">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -99,6 +104,9 @@ import { TenantVO } from '@/api/types';
|
|||||||
import notice from './notice/index.vue';
|
import notice from './notice/index.vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type';
|
import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
|
import Logo from "@/layout/components/Sidebar/Logo.vue";
|
||||||
|
import TopBar from './TopBar'
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@@ -109,6 +117,9 @@ const newNotice = ref(<number>0);
|
|||||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||||
|
|
||||||
const userId = ref(userStore.userId);
|
const userId = ref(userStore.userId);
|
||||||
|
const navType = computed(() => settingsStore.navType);
|
||||||
|
const showLogo = computed(() => settingsStore.sidebarLogo);
|
||||||
|
|
||||||
const companyName = ref(undefined);
|
const companyName = ref(undefined);
|
||||||
const tenantList = ref<TenantVO[]>([]);
|
const tenantList = ref<TenantVO[]>([]);
|
||||||
// 是否切换了租户
|
// 是否切换了租户
|
||||||
@@ -201,6 +212,12 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.navbar.navtop {
|
||||||
|
.hamburger-container {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-select .el-input__wrapper) {
|
:deep(.el-select .el-input__wrapper) {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
@@ -223,14 +240,22 @@ watch(
|
|||||||
position: relative;
|
position: relative;
|
||||||
//background: #fff;
|
//background: #fff;
|
||||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// padding: 0 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.hamburger-container {
|
.hamburger-container {
|
||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float: left;
|
//float: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s;
|
transition: background 0.3s;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, 0.025);
|
background: rgba(0, 0, 0, 0.025);
|
||||||
@@ -238,7 +263,8 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-container {
|
.breadcrumb-container {
|
||||||
float: left;
|
//float: left;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topmenu-container {
|
.topmenu-container {
|
||||||
@@ -246,16 +272,28 @@ watch(
|
|||||||
left: 50px;
|
left: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topbar-container {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.errLog-container {
|
.errLog-container {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-menu {
|
.right-menu {
|
||||||
float: right;
|
//float: right;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -1,5 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
|
<el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
|
||||||
|
<h3 class="drawer-title">菜单导航设置</h3>
|
||||||
|
<div class="nav-wrap">
|
||||||
|
<el-tooltip content="左侧菜单" placement="bottom">
|
||||||
|
<div
|
||||||
|
class="item left"
|
||||||
|
@click="handleNavType(NavTypeEnum.LEFT)"
|
||||||
|
:style="{ '--theme': theme }"
|
||||||
|
:class="{ activeItem: navType == NavTypeEnum.LEFT }"
|
||||||
|
>
|
||||||
|
<b></b><b></b>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<el-tooltip content="混合菜单" placement="bottom">
|
||||||
|
<div
|
||||||
|
class="item mix"
|
||||||
|
@click="handleNavType(NavTypeEnum.MIX)"
|
||||||
|
:style="{ '--theme': theme }"
|
||||||
|
:class="{ activeItem: navType == NavTypeEnum.MIX }"
|
||||||
|
>
|
||||||
|
<b></b><b></b>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip content="顶部菜单" placement="bottom">
|
||||||
|
<div
|
||||||
|
class="item top"
|
||||||
|
@click="handleNavType(NavTypeEnum.TOP)"
|
||||||
|
:style="{ '--theme': theme }"
|
||||||
|
:class="{ activeItem: navType == NavTypeEnum.TOP }"
|
||||||
|
>
|
||||||
|
<b></b><b></b>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 class="drawer-title">主题风格设置</h3>
|
<h3 class="drawer-title">主题风格设置</h3>
|
||||||
|
|
||||||
<div class="setting-drawer-block-checbox">
|
<div class="setting-drawer-block-checbox">
|
||||||
@@ -45,13 +80,6 @@
|
|||||||
|
|
||||||
<h3 class="drawer-title">系统布局配置</h3>
|
<h3 class="drawer-title">系统布局配置</h3>
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>开启 TopNav</span>
|
|
||||||
<span class="comp-style">
|
|
||||||
<el-switch v-model="settingsStore.topNav" class="drawer-switch" @change="topNavChange" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="drawer-item">
|
<div class="drawer-item">
|
||||||
<span>开启 Tags-Views</span>
|
<span>开启 Tags-Views</span>
|
||||||
<span class="comp-style">
|
<span class="comp-style">
|
||||||
@@ -101,6 +129,7 @@ import { useSettingsStore } from '@/store/modules/settings';
|
|||||||
import { usePermissionStore } from '@/store/modules/permission';
|
import { usePermissionStore } from '@/store/modules/permission';
|
||||||
import { handleThemeStyle } from '@/utils/theme';
|
import { handleThemeStyle } from '@/utils/theme';
|
||||||
import { SideThemeEnum } from '@/enums/SideThemeEnum';
|
import { SideThemeEnum } from '@/enums/SideThemeEnum';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
import defaultSettings from '@/settings';
|
import defaultSettings from '@/settings';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||||
@@ -113,7 +142,7 @@ const theme = ref(settingsStore.theme);
|
|||||||
const sideTheme = ref(settingsStore.sideTheme);
|
const sideTheme = ref(settingsStore.sideTheme);
|
||||||
const storeSettings = computed(() => settingsStore);
|
const storeSettings = computed(() => settingsStore);
|
||||||
const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
|
const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
|
||||||
|
const navType = ref(settingsStore.navType);
|
||||||
// 是否暗黑模式
|
// 是否暗黑模式
|
||||||
const isDark = useDark({
|
const isDark = useDark({
|
||||||
storageKey: 'useDarkKey',
|
storageKey: 'useDarkKey',
|
||||||
@@ -130,11 +159,26 @@ watch(isDark, () => {
|
|||||||
});
|
});
|
||||||
const toggleDark = () => useToggle(isDark);
|
const toggleDark = () => useToggle(isDark);
|
||||||
|
|
||||||
const topNavChange = (val: any) => {
|
/** 菜单导航设置 */
|
||||||
if (!val) {
|
watch(
|
||||||
appStore.toggleSideBarHide(false);
|
() => navType,
|
||||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
|
(val: string) => {
|
||||||
}
|
if (val.value === NavTypeEnum.TOP) {
|
||||||
|
appStore.toggleSideBarHide(true);
|
||||||
|
permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
|
||||||
|
} else if (val.value === NavTypeEnum.LEFT) {
|
||||||
|
appStore.toggleSideBarHide(false);
|
||||||
|
permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
|
||||||
|
} else if (val.value === NavTypeEnum.MIX) {
|
||||||
|
appStore.toggleSideBarHide(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNavType = (val: NavTypeEnum) => {
|
||||||
|
settingsStore.navType = val;
|
||||||
|
navType.value = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
const dynamicTitleChange = () => {
|
const dynamicTitleChange = () => {
|
||||||
@@ -158,7 +202,6 @@ const handleTheme = (val: string) => {
|
|||||||
const saveSetting = () => {
|
const saveSetting = () => {
|
||||||
proxy?.$modal.loading('正在保存到本地,请稍候...');
|
proxy?.$modal.loading('正在保存到本地,请稍候...');
|
||||||
const settings = useStorage<LayoutSetting>('layout-setting', defaultSettings);
|
const settings = useStorage<LayoutSetting>('layout-setting', defaultSettings);
|
||||||
settings.value.topNav = storeSettings.value.topNav;
|
|
||||||
settings.value.tagsView = storeSettings.value.tagsView;
|
settings.value.tagsView = storeSettings.value.tagsView;
|
||||||
settings.value.tagsIcon = storeSettings.value.tagsIcon;
|
settings.value.tagsIcon = storeSettings.value.tagsIcon;
|
||||||
settings.value.fixedHeader = storeSettings.value.fixedHeader;
|
settings.value.fixedHeader = storeSettings.value.fixedHeader;
|
||||||
@@ -166,6 +209,7 @@ const saveSetting = () => {
|
|||||||
settings.value.dynamicTitle = storeSettings.value.dynamicTitle;
|
settings.value.dynamicTitle = storeSettings.value.dynamicTitle;
|
||||||
settings.value.sideTheme = storeSettings.value.sideTheme;
|
settings.value.sideTheme = storeSettings.value.sideTheme;
|
||||||
settings.value.theme = storeSettings.value.theme;
|
settings.value.theme = storeSettings.value.theme;
|
||||||
|
settings.value.navType = storeSettings.value.navType;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
proxy?.$modal.closeLoading();
|
proxy?.$modal.closeLoading();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -243,4 +287,67 @@ defineExpose({
|
|||||||
margin: -3px 8px 0px 0px;
|
margin: -3px 8px 0px 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导航模式
|
||||||
|
.nav-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.activeItem {
|
||||||
|
border: 2px solid #{'var(--theme)'} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 56px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
b:first-child {
|
||||||
|
display: block;
|
||||||
|
height: 30%;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
b:last-child {
|
||||||
|
width: 30%;
|
||||||
|
background: #1b2a47;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mix {
|
||||||
|
b:first-child {
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
display: block;
|
||||||
|
height: 30%;
|
||||||
|
background: #1b2a47;
|
||||||
|
}
|
||||||
|
b:last-child {
|
||||||
|
width: 30%;
|
||||||
|
background: #1b2a47;
|
||||||
|
position: absolute;
|
||||||
|
height: 70%;
|
||||||
|
border-radius: 0 0 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top {
|
||||||
|
b:first-child {
|
||||||
|
display: block;
|
||||||
|
height: 30%;
|
||||||
|
background: #1b2a47;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,18 +2,17 @@
|
|||||||
<div
|
<div
|
||||||
class="sidebar-logo-container"
|
class="sidebar-logo-container"
|
||||||
:class="{ collapse: collapse }"
|
:class="{ collapse: collapse }"
|
||||||
:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
|
|
||||||
>
|
>
|
||||||
<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
|
<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
|
||||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
<h1 v-else class="sidebar-title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h1>
|
</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
<h1 class="sidebar-title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h1>
|
</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -26,6 +25,7 @@ import variables from '@/assets/styles/variables.module.scss';
|
|||||||
import logo from '@/assets/logo/logo.png';
|
import logo from '@/assets/logo/logo.png';
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
collapse: {
|
collapse: {
|
||||||
@@ -37,6 +37,28 @@ defineProps({
|
|||||||
const title = import.meta.env.VITE_APP_LOGO_TITLE;
|
const title = import.meta.env.VITE_APP_LOGO_TITLE;
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||||
|
|
||||||
|
// 获取Logo背景色
|
||||||
|
const getLogoBackground = computed(() => {
|
||||||
|
if (settingsStore.isDark) {
|
||||||
|
return 'var(--sidebar-bg)'
|
||||||
|
}
|
||||||
|
if (settingsStore.navType == NavTypeEnum.TOP) {
|
||||||
|
return variables.menuLightBackground
|
||||||
|
}
|
||||||
|
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBackground
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取Logo文字颜色
|
||||||
|
const getLogoTextColor = computed(() => {
|
||||||
|
if (settingsStore.isDark) {
|
||||||
|
return 'var(--sidebar-text)'
|
||||||
|
}
|
||||||
|
if (settingsStore.navType == NavTypeEnum.TOP) {
|
||||||
|
return variables.logoLightTitleColor
|
||||||
|
}
|
||||||
|
return sideTheme.value === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -51,10 +73,9 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
|||||||
|
|
||||||
.sidebar-logo-container {
|
.sidebar-logo-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
background: #2b2f3a;
|
background: v-bind(getLogoBackground);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@@ -67,21 +88,17 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .sidebar-title {
|
& .sidebar-title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #fff;
|
color: v-bind(getLogoTextColor);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family:
|
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||||
Avenir,
|
|
||||||
Helvetica Neue,
|
|
||||||
Arial,
|
|
||||||
Helvetica,
|
|
||||||
sans-serif;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<el-menu class="topbar-menu" :ellipsis="false" :default-active="activeMenu" :active-text-color="theme" mode="horizontal">
|
||||||
|
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" />
|
||||||
|
|
||||||
|
<el-sub-menu index="more" class="el-sub-menu__hide-arrow" v-if="moreRoutes.length > 0">
|
||||||
|
<template #title>
|
||||||
|
<span>更多菜单</span>
|
||||||
|
</template>
|
||||||
|
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" />
|
||||||
|
</el-sub-menu>
|
||||||
|
</el-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import SidebarItem from '../Sidebar/SidebarItem'
|
||||||
|
import {useAppStore} from '@/store/modules/app'
|
||||||
|
import {useSettingsStore} from '@/store/modules/settings'
|
||||||
|
import {usePermissionStore} from '@/store/modules/permission'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
|
||||||
|
const theme = computed(() => settingsStore.theme)
|
||||||
|
const device = computed(() => appStore.device)
|
||||||
|
const activeMenu = computed(() => {
|
||||||
|
const { meta, path } = route
|
||||||
|
if (meta.activeMenu) {
|
||||||
|
return meta.activeMenu
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
})
|
||||||
|
|
||||||
|
const visibleNumber = ref(5)
|
||||||
|
const topMenus = computed(() => {
|
||||||
|
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(0, visibleNumber.value)
|
||||||
|
})
|
||||||
|
const moreRoutes = computed(() => {
|
||||||
|
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value, sidebarRouters.value.length - visibleNumber.value)
|
||||||
|
})
|
||||||
|
function setVisibleNumber() {
|
||||||
|
let width = document.body.getBoundingClientRect().width
|
||||||
|
if (width >= 1000) {
|
||||||
|
width -= 500
|
||||||
|
}
|
||||||
|
visibleNumber.value = parseInt(width / 3 / 85)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', setVisibleNumber)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', setVisibleNumber)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setVisibleNumber()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
/* menu item */
|
||||||
|
#app .topbar-menu.el-menu--horizontal .el-sub-menu__title, #app .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||||
|
padding: 0 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar-menu.el-menu--horizontal > .el-menu-item {
|
||||||
|
float: left;
|
||||||
|
height: 50px !important;
|
||||||
|
line-height: 50px !important;
|
||||||
|
color: #303133 !important;
|
||||||
|
padding: 0 5px !important;
|
||||||
|
margin: 0 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu.is-active .svg-icon, .el-menu-item.is-active .svg-icon + span, .el-sub-menu.is-active .svg-icon + span, .el-sub-menu.is-active .el-sub-menu__title span {
|
||||||
|
color: v-bind(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sub-menu item */
|
||||||
|
.topbar-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
||||||
|
float: left;
|
||||||
|
line-height: 50px !important;
|
||||||
|
color: #303133 !important;
|
||||||
|
margin: 0 15px -3px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* topbar more arrow */
|
||||||
|
.topbar-menu .el-sub-menu .el-sub-menu__icon-arrow {
|
||||||
|
position: static;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-top: 0px;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* menu__title el-menu-item */
|
||||||
|
.topbar-menu.el-menu--horizontal .el-sub-menu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -26,6 +26,7 @@ import SideBar from './components/Sidebar/index.vue';
|
|||||||
import { AppMain, Navbar, Settings, TagsView } from './components';
|
import { AppMain, Navbar, Settings, TagsView } from './components';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
import { initWebSocket } from '@/utils/websocket';
|
import { initWebSocket } from '@/utils/websocket';
|
||||||
import { initSSE } from '@/utils/sse';
|
import { initSSE } from '@/utils/sse';
|
||||||
|
|
||||||
@@ -35,6 +36,13 @@ const sidebar = computed(() => useAppStore().sidebar);
|
|||||||
const device = computed(() => useAppStore().device);
|
const device = computed(() => useAppStore().device);
|
||||||
const needTagsView = computed(() => settingsStore.tagsView);
|
const needTagsView = computed(() => settingsStore.tagsView);
|
||||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||||
|
const layout = computed(() => settingsStore.navType);
|
||||||
|
|
||||||
|
// 根据布局模式判断是否显示侧边栏
|
||||||
|
const showSidebar = computed(() => {
|
||||||
|
if (sidebar.value.hide) return false;
|
||||||
|
return layout.value === NavTypeEnum.LEFT || layout.value === NavTypeEnum.MIX;
|
||||||
|
});
|
||||||
|
|
||||||
const classObj = computed(() => ({
|
const classObj = computed(() => ({
|
||||||
hideSidebar: !sidebar.value.opened,
|
hideSidebar: !sidebar.value.opened,
|
||||||
|
|||||||
+4
-3
@@ -1,4 +1,5 @@
|
|||||||
import { LanguageEnum } from '@/enums/LanguageEnum';
|
import { LanguageEnum } from '@/enums/LanguageEnum';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
|
|
||||||
const setting: DefaultSettings = {
|
const setting: DefaultSettings = {
|
||||||
/**
|
/**
|
||||||
@@ -18,9 +19,9 @@ const setting: DefaultSettings = {
|
|||||||
showSettings: true,
|
showSettings: true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示顶部导航
|
* 默认布局
|
||||||
*/
|
*/
|
||||||
topNav: false,
|
navType: NavTypeEnum.LEFT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示 tagsView
|
* 是否显示 tagsView
|
||||||
@@ -35,7 +36,7 @@ const setting: DefaultSettings = {
|
|||||||
/**
|
/**
|
||||||
* 是否固定头部
|
* 是否固定头部
|
||||||
*/
|
*/
|
||||||
fixedHeader: false,
|
fixedHeader: true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示logo
|
* 是否显示logo
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import defaultSettings from '@/settings';
|
|||||||
import { useDynamicTitle } from '@/utils/dynamicTitle';
|
import { useDynamicTitle } from '@/utils/dynamicTitle';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
|
|
||||||
export const useSettingsStore = defineStore('setting', () => {
|
export const useSettingsStore = defineStore('setting', () => {
|
||||||
const storageSetting = useStorage<LayoutSetting>('layout-setting', {
|
const storageSetting = useStorage<LayoutSetting>('layout-setting', {
|
||||||
@@ -13,13 +14,13 @@ export const useSettingsStore = defineStore('setting', () => {
|
|||||||
sidebarLogo: defaultSettings.sidebarLogo,
|
sidebarLogo: defaultSettings.sidebarLogo,
|
||||||
dynamicTitle: defaultSettings.dynamicTitle,
|
dynamicTitle: defaultSettings.dynamicTitle,
|
||||||
sideTheme: defaultSettings.sideTheme,
|
sideTheme: defaultSettings.sideTheme,
|
||||||
theme: defaultSettings.theme
|
theme: defaultSettings.theme,
|
||||||
|
navType: defaultSettings.navType
|
||||||
});
|
});
|
||||||
const title = ref<string>(defaultSettings.title);
|
const title = ref<string>(defaultSettings.title);
|
||||||
const theme = ref<string>(storageSetting.value.theme);
|
const theme = ref<string>(storageSetting.value.theme);
|
||||||
const sideTheme = ref<string>(storageSetting.value.sideTheme);
|
const sideTheme = ref<string>(storageSetting.value.sideTheme);
|
||||||
const showSettings = ref<boolean>(defaultSettings.showSettings);
|
const showSettings = ref<boolean>(defaultSettings.showSettings);
|
||||||
const topNav = ref<boolean>(storageSetting.value.topNav);
|
|
||||||
const tagsView = ref<boolean>(storageSetting.value.tagsView);
|
const tagsView = ref<boolean>(storageSetting.value.tagsView);
|
||||||
const tagsIcon = ref<boolean>(storageSetting.value.tagsIcon);
|
const tagsIcon = ref<boolean>(storageSetting.value.tagsIcon);
|
||||||
const fixedHeader = ref<boolean>(storageSetting.value.fixedHeader);
|
const fixedHeader = ref<boolean>(storageSetting.value.fixedHeader);
|
||||||
@@ -27,6 +28,7 @@ export const useSettingsStore = defineStore('setting', () => {
|
|||||||
const dynamicTitle = ref<boolean>(storageSetting.value.dynamicTitle);
|
const dynamicTitle = ref<boolean>(storageSetting.value.dynamicTitle);
|
||||||
const animationEnable = ref<boolean>(defaultSettings.animationEnable);
|
const animationEnable = ref<boolean>(defaultSettings.animationEnable);
|
||||||
const dark = ref<boolean>(defaultSettings.dark);
|
const dark = ref<boolean>(defaultSettings.dark);
|
||||||
|
const navType = ref<NavTypeEnum>(storageSetting.value.navType || NavTypeEnum.LEFT);
|
||||||
|
|
||||||
const setTitle = (value: string) => {
|
const setTitle = (value: string) => {
|
||||||
title.value = value;
|
title.value = value;
|
||||||
@@ -37,7 +39,6 @@ export const useSettingsStore = defineStore('setting', () => {
|
|||||||
theme,
|
theme,
|
||||||
sideTheme,
|
sideTheme,
|
||||||
showSettings,
|
showSettings,
|
||||||
topNav,
|
|
||||||
tagsView,
|
tagsView,
|
||||||
tagsIcon,
|
tagsIcon,
|
||||||
fixedHeader,
|
fixedHeader,
|
||||||
@@ -45,6 +46,7 @@ export const useSettingsStore = defineStore('setting', () => {
|
|||||||
dynamicTitle,
|
dynamicTitle,
|
||||||
animationEnable,
|
animationEnable,
|
||||||
dark,
|
dark,
|
||||||
|
navType,
|
||||||
setTitle
|
setTitle
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
import type { PropType as VuePropType, ComponentInternalInstance as ComponentInstance } from 'vue';
|
import type { PropType as VuePropType, ComponentInternalInstance as ComponentInstance } from 'vue';
|
||||||
import { LanguageEnum } from '@/enums/LanguageEnum';
|
import { LanguageEnum } from '@/enums/LanguageEnum';
|
||||||
|
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
/** vue Instance */
|
/** vue Instance */
|
||||||
@@ -90,9 +91,9 @@ declare global {
|
|||||||
}
|
}
|
||||||
declare interface LayoutSetting {
|
declare interface LayoutSetting {
|
||||||
/**
|
/**
|
||||||
* 是否显示顶部导航
|
* 默认布局
|
||||||
*/
|
*/
|
||||||
topNav: boolean;
|
navType: NavTypeEnum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示多标签导航
|
* 是否显示多标签导航
|
||||||
|
|||||||
Reference in New Issue
Block a user