Ver Fonte

成渝项目菜单修改

duy há 3 meses atrás
pai
commit
8e39959e26

+ 87 - 14
src/layout/index.vue

@@ -1,19 +1,32 @@
 <template>
-    <el-container v-loading="isAppLoadings" class="hc-layout-box" :class="[!isNullES(isLayout) && isLayout === 'no' ? 'is-no-layout' : '']">
+    <el-container
+        v-loading="isAppLoadings" class="hc-layout-box" 
+
+        :class="{ 'yn-theme': !isYunNanProject, 'is-no-layout': !isNullES(isLayout) && isLayout === 'no' }"
+    >
         <div v-if="appTheme === 'dark'" class="hc-app-bg-box">
             <img :src="appViewBg" alt="">
         </div>
         <el-header class="hc-layout-header">
-            <div class="hc-layout-header-logo" :style="`width: ${isCollapse ? '0px' : '200px'};`" @click="logoClick">
+            <div v-if="!isYunNanProject" class="hc-layout-header-logo" :style="`width: ${isCollapse ? '0px' : '200px'};`" @click="logoClick">
                 <!-- <img id="logo-icon" :src="appLogoIcon" alt=""> -->
                 <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt="">
             </div>
+            <div v-else class="hc-layout-header-logo" @click="logoClick">
+                <img id="logo-icon" :src="appLogoIcon" alt="">
+                <!-- <img v-show="!isCollapse" id="logo-name" :src="appLogoName" alt=""> -->
+
+                <div class="ml-2">
+                    <div class="text-18px font-900">工程项目档案资料管理系统</div>
+                    <div class="text-12px">Engineering Priject File Manegement Platform</div>
+                </div>
+            </div>
             <div class="header-top-collapse-bar" @click="collapseChange">
                 <HcIcon v-if="isCollapse" name="menu-unfold" />
                 <HcIcon v-else name="menu-fold" />
             </div>
             <div class="header-top-menu-bar">
-                <HcTopMenuBar @load="topMenuLoad" @change="topMenuChange" />
+                <HcTopMenuBar v-if="!isYunNanProject" @load="topMenuLoad" @change="topMenuChange" />
             </div>
             <div class="header-content-bar">
                 <HcCascader v-if="userRoleId !== website.role_id" @send="cascaderSend" @change="cascaderChange" />
@@ -21,9 +34,9 @@
                     <HcIcon name="arrow-right" class="project-icon" />
                     <div class="truncate">{{ projectInfoData?.projectName ?? '请先选择项目' }}</div>
                 </div>
-                <hc-upload-bar />
-                <HelpInfoBar />
-                <ConfigBar />
+                <hc-upload-bar v-if="!isYunNanProject" />
+                <HelpInfoBar v-if="!isYunNanProject" />
+                <ConfigBar v-if="!isYunNanProject" />
                 <UserInfoBar @load="userInfoLoad" />
             </div>
         </el-header>
@@ -53,8 +66,8 @@
 </template>
 
 <script setup>
-import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
-import { getObjValue, isNullES, useClick } from 'js-fast-way'
+import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
+import { getArrValue, getObjValue, isNullES, useClick } from 'js-fast-way'
 import { HcSocket } from '~src/plugins/HcSocket'
 import { useRoute, useRouter } from 'vue-router'
 import { useAppStore } from '~src/store'
@@ -62,6 +75,8 @@ import { initButtons } from '~sto/app'
 import { HcAnnouncement } from 'hc-vue3-ui'
 import { useProject } from '~sto/useProject'
 import website from '~src/config'
+import appLogoIcon from '~src/assets/logo/yunnan.png'
+
 
 //初始组合式
 const router = useRouter()
@@ -71,6 +86,11 @@ const store = useAppStore()
 const reloadRouter = ref(false)
 const userRoleId = ref(store.getRoleId)
 const projectInfoData = ref(store.getProjectInfo)
+const projectId = ref(store.getProjectId)
+// 判断是否为云南项目
+const isYunNanProject = computed(() => {
+    return projectId.value === '1904814720589430785'
+})
 
 //子组件
 import HcTopMenuBar from './modules/HcTopMenu.vue'
@@ -93,6 +113,7 @@ const isLayout = ref('')
 
 //获取项目信息
 const { isAppLoading } = useProject()
+// 在 setup 中添加
 
 //渲染完成
 onMounted(() => {
@@ -148,14 +169,41 @@ const topMenuLoad = () => {
     isAsideMenu.value = false
 }
 
-//顶部菜单导航被点击
+// 修改菜单数据的监听逻辑
+watch(() => store.getMenus, (val) => {
+    if (isYunNanProject.value) {
+        // 云南项目的菜单处理逻辑
+        const filterAndRenameMenus = (menus) => {
+            return menus.filter(menu => {
+                // 过滤掉个人中心和系统设置
+                if (menu.name === '个人中心' || menu.name === '系统设置') {
+                    return false
+                }
+                
+                // 重命名档案收集为文件管理
+                if (menu.name === '档案收集') {
+                    menu.name = '文件管理'
+                }
+                
+                // 递归处理子菜单
+                if (menu.children && menu.children.length > 0) {
+                    menu.children = filterAndRenameMenus(menu.children)
+                }
+                
+                return true
+            })
+        }
+        
+        menuBarData.value = filterAndRenameMenus(getArrValue(val))
+    }
+}, { immediate: true, deep: true })
+//顶部菜单导航被点击// 修改顶部菜单切换方法
 const topMenuChange = (data) => {
-    if (!isNullES(data)) {
+    if (!isYunNanProject.value && !isNullES(data)) {
         menuBarData.value = data
         isAsideMenu.value = true
     }
 }
-
 //菜单被点击
 const menuBarChange = ({ code }) => {
     menuBarKey.value = code
@@ -328,9 +376,34 @@ onUnmounted(() => {
     if (!isNullES(socket)) socket.close()
     closeAnnFun()
     closeAnnUpdate()
+    return () => {
+    if (currentStyle) {
+      document.head.removeChild(currentStyle)
+    }
+  }
 })
+// 动态加载样式
+const loadStyles = () => {
+  const styleElement = document.createElement('style')
+  styleElement.type = 'text/css'
+  const styleContent = isYunNanProject.value
+    ? import('./test-yn/index-yn.scss')
+    : import('./index.scss')
+  styleElement.textContent = styleContent
+  document.head.appendChild(styleElement)
+  return styleElement
+}
+let currentStyle = null
+
+// 监听项目类型变化,动态切换样式
+watch(() => isYunNanProject.value, () => {
+  if (currentStyle) {
+    document.head.removeChild(currentStyle)
+  }
+  currentStyle = loadStyles()
+}, { immediate: true })
+
+// 组件卸载时清理样式
 </script>
 
-<style lang="scss">
-@import "./index.scss";
-</style>
+

+ 10 - 4
src/layout/modules/MenuItem.vue

@@ -12,7 +12,8 @@
                 </div>
                 <HcIcon name="arrow-down-s" ui="el-icon el-sub-menu__icon-arrow" />
             </template>
-            <MenuItem :datas="item?.children" :cur="curKey" :msg-count="msgCount" @change="MenuClick" />
+            <MenuItem v-if="!isYunNanProject" :datas="item?.children" :cur="curKey" :msg-count="msgCount" @change="MenuClick" />
+            <MenuItem2 v-else :datas="item?.children" :cur="curKey" :msg-count="msgCount" @change="MenuClick" />
         </el-sub-menu>
         <el-menu-item v-else :index="item?.code" @click="MenuClick(item)">
             <div class="hc-aside-menu-item">
@@ -29,9 +30,10 @@
 </template>
 
 <script setup>
-import { ref, watch } from 'vue'
+import { computed, ref, watch } from 'vue'
 import MenuItem from './MenuItem.vue'
-
+import MenuItem2 from '../test-yn/MenuItem-yn.vue'
+import { useAppStore } from '~src/store'
 const props = defineProps({
     datas: {
         type: Array,
@@ -61,7 +63,11 @@ const props = defineProps({
 })
 //事件
 const emit = defineEmits(['change'])
-
+const store = useAppStore()
+const projectId = ref(store.getProjectId)
+const isYunNanProject = computed(() => {
+    return projectId.value === '1904814720589430785'
+})
 //初始变量
 const curKey = ref(props.cur)
 const isCollapse = ref(props.collapse)

+ 7 - 3
src/layout/modules/UserInfoBar.vue

@@ -2,8 +2,8 @@
     <el-dropdown size="large">
         <div class="header-bar user-info-bar">
             <img :alt="userInfo.account" :src="userInfo.avatar || avatarPng" class="user-avatar">
-            <span class="user-name">{{ userInfo.real_name }}</span>
-            <HcIcon name="arrow-down-s" ui="arrow-icon" />
+            <span class="user-name" :style="{ color: isYunNanProject ? 'black' : 'white' }">{{ userInfo.real_name }}</span>
+            <HcIcon name="arrow-down-s" ui="arrow-icon" :style="{ color: isYunNanProject ? 'black' : 'white' }" />
         </div>
         <template #dropdown>
             <el-dropdown-menu>
@@ -19,7 +19,7 @@
 </template>
 
 <script setup>
-import { onMounted, ref, watch } from 'vue'
+import { computed, onMounted, ref, watch } from 'vue'
 import { useRouter } from 'vue-router'
 import { useAppStore } from '~src/store'
 import website from '~src/config/index'
@@ -37,6 +37,10 @@ const store = useAppStore()
 const userInfo = ref(store.getUserInfo)
 const refreshLock = ref(false)
 
+const projectId = ref(store.getProjectId)
+const isYunNanProject = computed(() => {
+    return projectId.value === '1904814720589430785'
+})
 //监听
 watch(() => store.getUserInfo, (info) => {
     userInfo.value = info

+ 117 - 0
src/layout/test-yn/MenuItem-yn.vue

@@ -0,0 +1,117 @@
+<template>
+    <template v-for="item in datas" :key="item?.code">
+        <el-sub-menu v-if="item?.children && item?.children.length > 0" :index="item?.code" :popper-offset="0" :popper-class="`aside-menu-popper ${curKey}`" :class="getMenuLevel(item)">
+            <template #title>
+                <div class="hc-aside-menu-item">
+                    <div class="menu---item">
+                        <HcIcon v-if="item?.source" :name="item?.source" :fill="curKey === item?.code" class="hc-menu-icon" />
+                        <div v-if="isCollapse" class="name truncate">{{ item?.name.substring(0, 2) }}</div>
+                        <div v-else class="name truncate">{{ item?.name }}</div>
+                        <el-badge v-if="item?.code === 'tasks' && msgCount?.allCount > 0" :value="msgCount.allCount" />
+                    </div>
+                </div>
+                <HcIcon name="arrow-down-s" ui="el-icon el-sub-menu__icon-arrow" />
+            </template>
+            <MenuItem v-if="!isYunNanProject" :datas="item?.children" :cur="curKey" :msg-count="msgCount" @change="MenuClick" />
+            <MenuItem2 v-else :datas="item?.children" :cur="curKey" :msg-count="msgCount" @change="MenuClick" />
+        </el-sub-menu>
+        <el-menu-item v-else :index="item?.code" :class="getMenuLevel(item)" @click="MenuClick(item)">
+            <div class="hc-aside-menu-item">
+                <div class="menu---item">
+                    <HcIcon v-if="item?.source" :name="item?.source" :fill="curKey === item?.code" class="hc-menu-icon" />
+                    <div v-if="isCollapse" class="name truncate">{{ item?.name.substring(0, 2) }}</div>
+                    <div v-else class="name truncate">{{ item?.name }}</div>
+                    <el-badge v-if="item?.code === 'tasks-data' && msgCount?.taskCount > 0" :value="msgCount.taskCount" />
+                    <el-badge v-if="item?.code === 'message-data' && msgCount?.messageCount > 0" :value="msgCount.messageCount" />
+                </div>
+            </div>
+        </el-menu-item>
+    </template>
+</template>
+
+
+<script setup>
+import { computed, ref, watch } from 'vue'
+import MenuItem from '../modules/MenuItem.vue'
+import MenuItem2 from '../test-yn/MenuItem-yn.vue'
+import { useAppStore } from '~src/store'
+const props = defineProps({
+    datas: {
+        type: Array,
+        default: () => ([]),
+    },
+    cur: {
+        type: String,
+        default: '',
+    },
+    collapse: {
+        type: Boolean,
+        default: false,
+    },
+    msgCount: {
+        type: Object,
+        default: () => ({
+            allCount: 0,
+            taskCount: 0,
+            messageCount: 0,
+            messageCount_1: 0,
+            messageCount_2: 0,
+            messageCount_3: 0,
+            messageCount_4: 0,
+            messageCount_5: 0,
+        }),
+    },
+})
+//事件
+const emit = defineEmits(['change'])
+const store = useAppStore()
+const projectId = ref(store.getProjectId)
+const isYunNanProject = computed(() => {
+    return projectId.value === '1891312830718746625'
+})
+//初始变量
+const curKey = ref(props.cur)
+const isCollapse = ref(props.collapse)
+
+//监听
+watch(() => [
+    props.cur,
+    props.collapse,
+], ([cur, collapse]) => {
+    curKey.value = cur
+    isCollapse.value = collapse
+})
+
+const MenuClick = (item) => {
+    emit('change', item)
+}
+// 获取菜单层级样式
+const getMenuLevel = (item) => {
+  const level = item.code.split('-').length
+  return `menu-level-${level}`
+}
+</script>
+
+<style lang="scss" scoped>
+.menu-level-1 {
+  padding-left: 0px;
+}
+
+.menu-level-2 {
+  padding-left: 20px !important;
+  
+  :deep(.el-sub-menu__title),
+  :deep(.el-menu-item) {
+    padding-left: 20px !important;
+  }
+}
+
+.menu-level-3 {
+  padding-left: 40px !important;
+  
+  :deep(.el-sub-menu__title),
+  :deep(.el-menu-item) {
+    padding-left: 40px !important;
+  }
+}
+</style>

+ 152 - 0
src/layout/test-yn/UserInfoBar-un.vue

@@ -0,0 +1,152 @@
+<template>
+    <el-dropdown size="large">
+        <div class="header-bar user-info-bar">
+            <img :alt="userInfo.account" :src="userInfo.avatar || avatarPng" class="user-avatar">
+            <span class="user-name">{{ userInfo.real_name }}</span>
+            <HcIcon name="arrow-down-s" ui="arrow-icon" />
+        </div>
+        <template #dropdown>
+            <el-dropdown-menu>
+                <el-dropdown-item v-for="item in options" :key="item.key">
+                    <div class="hc-dropdown-item" @click="handleSelect(item.key)">
+                        <HcIcon :name="item.icon" class="icon" />
+                        <span class="label">{{ item.label }}</span>
+                    </div>
+                </el-dropdown-item>
+            </el-dropdown-menu>
+        </template>
+    </el-dropdown>
+</template>
+
+<script setup>
+import { onMounted, ref, watch } from 'vue'
+import { useRouter } from 'vue-router'
+import { useAppStore } from '~src/store'
+import website from '~src/config/index'
+import avatarPng from '~src/assets/images/avatar.png'
+import { LogOut, RefreshToken } from '~sto/user'
+import { calcDate, isNullES } from 'js-fast-way'
+import { getStore } from 'hc-vue3-ui'
+
+//事件
+const emit = defineEmits(['load'])
+
+//变量
+const router = useRouter()
+const store = useAppStore()
+const userInfo = ref(store.getUserInfo)
+const refreshLock = ref(false)
+
+//监听
+watch(() => store.getUserInfo, (info) => {
+    userInfo.value = info
+    emit('load', info)
+})
+
+onMounted(() => {
+    getRefreshToken()
+    emit('load', userInfo.value)
+})
+
+//刷新token
+const getRefreshToken = () => {
+    setInterval(() => {
+        const token = getStore('token', true) || {}
+        const date = calcDate(token.datetime, new Date().getTime())
+        if (isNullES(date)) return
+        if (date.seconds >= website.tokenTime && !refreshLock.value) {
+            refreshLock.value = true
+            console.log('刷新token')
+            RefreshToken().then(() => {
+                refreshLock.value = false
+            }).catch(() => {
+                refreshLock.value = false
+                router.push({ name: 'login-main' })
+            })
+        }
+    }, 10000)
+}
+
+const options = [
+    {
+        key: 'my',
+        label: '个人中心',
+        icon: 'user-3',
+    },
+    {
+        key: 'logout',
+        label: '退出登录',
+        icon: 'login-box',
+    },
+]
+
+const handleSelect = (key) => {
+    if (key === 'my') {
+        router.push({ name: 'user-index' })
+    } else if (key === 'logout') {
+        LogOut().then()
+        window.$message?.info('退出成功')
+        router.push({ name: 'login-main' })
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.user-info-bar {
+    position: relative;
+    display: flex;
+    align-items: center;
+    height: 100%;
+    cursor: pointer;
+    padding-left: 16px;
+    margin-left: 4px;
+    outline: none;
+    .user-avatar {
+        width: 26px;
+        height: 26px;
+        border-radius: 50%;
+        background: white;
+        object-fit: cover;
+    }
+    .user-name {
+        font-size: 16px;
+        margin-left: 10px;
+       
+        color: black;
+    }
+    .arrow-icon {
+        margin-left: 5px;
+        font-size: 20px;
+        color: white;
+    }
+    &::before {
+        position: absolute;
+        content: '';
+        left: 0;
+        width: 0;
+        height: 20px;
+        border-left: 1px solid #7291ff;
+    }
+}
+.hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .user-info-bar {
+    color: inherit;
+    .user-name {
+        color: white;
+    }
+    .arrow-icon {
+        color: white;
+    }
+    &::before {
+        border-left: 1px solid white;
+    }
+}
+
+.hc-dropdown-item {
+    display: flex;
+    align-items: center;
+    .icon {
+        font-size: 14px;
+        margin-right: 8px;
+    }
+}
+</style>

+ 524 - 0
src/layout/test-yn/index-yn.scss

@@ -0,0 +1,524 @@
+
+
+.hc-layout-box {
+    position: relative;
+    height: 100vh;
+    width: 100%;
+    .hc-app-bg-box {
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        top: 0;
+        z-index: 0;
+        display: flex;
+        img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+        }
+    }
+    .hc-layout-header {
+        position: relative;
+        display: flex;
+        align-items: center;
+        flex-direction: row;
+        --el-header-padding: 0;
+        --el-header-height: 44px;
+        background: #ffffff;
+        color: #333333;
+        box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
+        border-bottom: 1px solid #EBEFF2;
+        margin-bottom: 4px;
+
+        .hc-layout-header-logo {
+            position: relative;
+            display: flex;
+            align-items: center;
+            // justify-content: center;
+            transition: opacity 0.3s;
+            cursor: pointer;
+            height: 100%;
+            // box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.4);
+            z-index: 222;
+            #logo-icon {
+                height: 40px;
+                width: 52px;
+                filter: none;
+            }
+            #logo-name {
+                height: 32px;
+                margin-left: 5px;
+                filter: none;
+            }
+        }
+
+        .header-top-collapse-bar {
+            position: relative;
+            height: 100%;
+            font-size: 20px;
+            display: flex;
+            // justify-content: center;
+            align-items: center;
+            padding: 0 10px;
+            cursor: pointer;
+            transition: opacity .2s;
+            &:hover {
+                opacity: 0.7;
+            }
+        }
+        .header-top-menu-bar {
+            position: relative;
+            padding: 0 4px;
+            height: 100%;
+            flex: 1;
+            .el-scrollbar__view {
+                height: 100%;
+            }
+        }
+        .header-content-bar {
+            position: relative;
+            padding: 0 20px;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            .hc-header-project-name-box {
+                position: relative;
+                padding: 4px 15px;
+                border-radius: 100px;
+                background: white;
+                height: 28px;
+                display: flex;
+                align-items: center;
+                margin-right: 20px;
+                cursor: pointer;
+                user-select: none;
+                max-width: 340px;
+                font-size: 14px;
+                color: #606266;
+                .project-icon {
+                    margin-right: 5px;
+                }
+            }
+            .header-icon-bar {
+                position: relative;
+                height: 100%;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                cursor: pointer;
+                font-size: 25px;
+                margin-right: 8px;
+                color: #efefef;
+                transition: color .2s;
+                &:hover {
+                    color: white;
+                }
+            }
+        }
+    }
+    .hc-layout-container {
+        position: relative;
+        .hc-layout-aside {
+            position: relative;
+            color: white;
+            padding: 8px 0;
+            // background: var(--el-color-primary);
+            background: #001529;
+        }
+        .hc-layout-main {
+            position: relative;
+            overflow: hidden;
+            height: 100%;
+            --el-main-padding: 0;
+            .hc-router-menu-bar {
+                position: relative;
+                height: 36px;
+                padding: 0 10px;
+                background: white;
+                box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .1);
+                z-index: 222;
+            }
+            .hc-main-page {
+                position: relative;
+                height: calc(100% - 36px);
+                overflow: hidden;
+                .hc-main-body {
+                    position: absolute;
+                    padding: 12px;
+                    inset: 0;
+                }
+            }
+        }
+    }
+}
+
+//左侧菜单
+.hc-layout-box .hc-layout-container .hc-layout-aside .el-menu {
+    --el-menu-bg-color: #001529;
+    --el-menu-text-color: #88919B;
+    --el-menu-active-color: #DDE0E4;
+    --el-menu-hover-text-color: #DDE0E4;
+    --el-menu-hover-bg-color: #2D8CF0;
+    --el-menu-item-font-size: 16px;
+    --el-menu-item-height: 48px;
+    border-right: 0;
+    background-color: var(--el-menu-bg-color);
+    &.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item,
+    &.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item-group__title,
+    &.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-sub-menu__title {
+        white-space: nowrap;
+        padding-left: 0;
+    }
+    &.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-sub-menu__title {
+        padding-right: 1px;
+        border: 0;
+    }
+    .el-sub-menu__title {
+        padding: 0;
+    }
+    .el-menu-item, .el-sub-menu {
+        min-width: initial;
+        transition: 0.2s;
+        .hc-aside-menu-item {
+            flex: 1;
+            position: relative;
+            padding: 0 20px;
+            display: flex;
+            align-items: center;
+            transition: 0.2s;
+            .menu---item {
+                display: contents;
+            }
+            .hc-menu-icon {
+                position: relative;
+                font-size: 18px;
+                margin-right: 8px;
+                line-height: initial;
+            }
+            .name {
+                flex: 1;
+                width: 0;
+            }
+        }
+    }
+    .el-sub-menu .el-menu .el-menu-item {
+        padding-left: 24px !important;
+        padding-right: 1px;
+        font-size: 14px;
+        height: 46px;
+        line-height: initial;
+        .hc-aside-menu-item .hc-menu-icon {
+            margin-right: 6px;
+            font-size: 14px;
+        }
+    }
+    .el-sub-menu .el-icon {
+        display: none;
+    }
+    .el-sub-menu .el-icon.hc-icon-i {
+        position: relative;
+        display: inline-block;
+        font-size: 16px;
+        right: 15px;
+        top: initial;
+        height: initial;
+        width: initial;
+        margin-top: 0;
+        vertical-align: initial;
+    }
+    .el-sub-menu.is-active > .el-sub-menu__title {
+        // background-color: var(--el-color-primary-light-3);
+    }
+    .el-menu-item.is-active {
+        // background-color: var(--el-color-primary-dark-2);
+         background-color: #2D8CF0;
+        &::after {
+            content: '';
+            position: absolute;
+            right: 0;
+            top: 0;
+            width: 3px;
+            height: 100%;
+            background-color: white;
+        }
+    }
+    .el-sub-menu .el-sub-menu__title:hover,
+    .el-menu-item:not(.is-active):hover {
+        // background-color: var(--el-color-primary-dark-2);
+    }
+    //折叠状态
+    &.el-menu--collapse {
+        margin-left: 0;
+        width: 90px;
+        .el-sub-menu__title {
+            height: inherit;
+            line-height: initial;
+            width: 90px;
+            justify-content: center;
+            transition: 0.2s;
+        }
+        .el-menu-item, .el-sub-menu {
+            padding: 0 !important;
+            height: 60px;
+            line-height: initial;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 90px;
+            transition: 0.2s;
+            .hc-aside-menu-item {
+                display: inline-flex;
+                align-items: center;
+                justify-content: center;
+                width: 60px;
+                height: 60px;
+                text-align: center;
+                border-radius: 10px;
+                padding: 5px;
+                flex: initial;
+                transition: 0.2s;
+                .menu---item {
+                    position: relative;
+                    display: block;
+                }
+                .hc-menu-icon {
+                    margin-right: 0;
+                }
+                .name {
+                    flex: initial;
+                    width: 100%;
+                }
+                .el-badge, .el-badge .el-badge__content {
+                    vertical-align: initial;
+                }
+                .el-badge {
+                    position: absolute;
+                    top: -20px;
+                    right: -24px;
+                }
+            }
+        }
+        .el-sub-menu .el-icon.hc-icon-i {
+            display: none;
+        }
+        .el-menu-item + .el-menu-item,
+        .el-menu-item + .el-sub-menu,
+        .el-sub-menu + .el-menu-item,
+        .el-sub-menu + .el-sub-menu {
+            margin-top: 12px;
+        }
+        .el-sub-menu.is-active > .el-sub-menu__title {
+            background-color: initial;
+        }
+        .el-menu-item.is-active {
+            background-color: initial;
+        }
+        .el-sub-menu .el-sub-menu__title:hover,
+        .el-menu-item:not(.is-active):hover {
+            background-color: initial;
+        }
+        .el-sub-menu:not(.is-active):hover,
+        .el-menu-item:not(.is-active):hover {
+            .hc-aside-menu-item {
+                background-color: var(--el-color-primary-light-9);
+                color: var(--el-color-primary);
+            }
+        }
+        .el-menu-item.is-active, .el-sub-menu.is-active {
+            .hc-aside-menu-item {
+                color: #DDE0E4 !important;
+                background: #2D8CF0;
+            }
+        }
+    }
+}
+
+//菜单路由
+.hc-layout-box .hc-layout-container .hc-layout-main .hc-router-menu-bar {
+    .el-scrollbar__view {
+        height: 100%;
+    }
+    .hc-router-tab-box {
+        position: relative;
+        display: flex;
+        align-items: center;
+        white-space: nowrap;
+        height: 100%;
+    }
+    .hc-router-tab-item {
+        position: relative;
+        height: 100%;
+        padding: 0 8px;
+        display: inline-flex;
+        align-items: center;
+        cursor: pointer;
+        color: #8F8F8F;
+        user-select: none;
+        transition: .3s;
+        .close-icon {
+            height: 30px;
+            width: 18px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-left: 6px;
+            font-size: 16px;
+            cursor: pointer;
+            transition: color 0.3s;
+            &:hover {
+                color: var(--el-color-primary);
+            }
+        }
+        &::after{
+            content: '';
+            left: 0;
+            bottom: 0;
+            height: 2.5px;
+            width: 100%;
+            position: absolute;
+            background-size: 200%;
+        }
+        &:hover:not([class*='cur']) {
+            color: var(--el-color-primary);
+        }
+        &.cur {
+            color: var(--el-color-primary-dark-2);
+            &::after {
+                background: linear-gradient(to right, var(--el-color-primary-light-5), var(--el-color-primary), var(--el-color-primary-dark-2));
+            }
+        }
+    }
+    .el-scrollbar__bar.is-horizontal {
+        bottom: -10px;
+    }
+    .el-scrollbar__bar.is-vertical {
+        display: none;
+    }
+}
+
+
+.aside-menu-popper.el-popper.is-light {
+    background: initial !important;
+    border: 0 !important;
+    outline: none;
+}
+.aside-menu-popper.el-popper .el-menu--vertical .el-menu {
+    --el-menu-bg-color: #f1f5f8;
+    --el-menu-text-color: #838791;
+    --el-menu-active-color: #ffffff;
+    --el-menu-hover-bg-color: initial;
+    --el-menu-item-font-size: 16px;
+    background-color: #f1f5f8;
+    color: #838791;
+    .el-sub-menu__title {
+        padding: 0;
+        justify-content: center;
+        transition: 0.2s;
+    }
+    .el-menu-item, .el-sub-menu {
+        color: inherit;
+        padding: 0;
+        transition: 0.2s;
+        .hc-aside-menu-item {
+            flex: 1;
+            position: relative;
+            padding: 0 16px;
+            display: flex;
+            align-items: center;
+            transition: 0.2s;
+            .menu---item {
+                display: contents;
+            }
+            .hc-menu-icon {
+                font-size: 22px;
+                margin-right: 10px;
+                line-height: initial;
+            }
+            .name {
+                flex: 1;
+                width: 0;
+            }
+            .el-badge, .el-badge .el-badge__content {
+                vertical-align: initial;
+            }
+        }
+        &.is-active {
+            color: white;
+        }
+    }
+    .el-sub-menu .el-icon {
+        display: none;
+    }
+    .el-sub-menu .el-icon.hc-icon-i {
+        position: relative;
+        display: inline-block;
+        font-size: 16px;
+        right: 10px;
+        top: initial;
+        height: initial;
+        width: initial;
+        margin-top: 0;
+        vertical-align: initial;
+    }
+    .el-sub-menu:not(.is-active) .el-sub-menu__title:hover {
+        background-color: var(--el-color-primary-light-9);
+        color: var(--el-color-primary);
+    }
+    .el-menu-item:not(.is-active):hover {
+        .hc-aside-menu-item {
+            background-color: var(--el-color-primary-light-9);
+            color: var(--el-color-primary);
+        }
+    }
+    .el-menu-item.is-active {
+        .hc-aside-menu-item {
+            background-color: var(--el-color-primary);
+        }
+    }
+    .el-sub-menu.is-active .el-sub-menu__title {
+        background-color: var(--el-color-primary-light-9);
+        color: var(--el-color-primary);
+    }
+}
+
+.aside-menu-popper.el-popper .el-menu--vertical.home-index .el-menu {
+    --el-menu-bg-color: initial;
+    --el-menu-text-color: initial;
+    color: white;
+    background-color: var(--el-color-primary-dark-2);
+}
+
+//深色模式
+html.dark {
+    .hc-layout-box {
+        .hc-layout-header, .hc-layout-container .hc-layout-aside {
+            background: transparent;
+            .el-menu .el-menu-item.is-active::after {
+                display: none;
+            }
+        }
+        .hc-layout-container .hc-layout-main .hc-router-menu-bar {
+            background: #000834;
+            .hc-router-tab-item {
+                color: #7a7a7a;
+            }
+            .hc-router-tab-item.cur {
+                color: #ffffff;
+            }
+        }
+        .header-icon-bar {
+            border: 0;
+        }
+    }
+}
+
+//没有 layout
+.hc-layout-box.is-no-layout {
+    .hc-layout-header, .hc-layout-aside, .hc-router-menu-bar {
+        display: none !important;
+    }
+    .hc-layout-container .hc-layout-main .hc-main-page {
+        height: 100%;
+    }
+}

+ 3 - 2
src/views/statistics/components/echarts/ArrRoundChart.vue

@@ -101,11 +101,12 @@ onMounted(() => {
 
 //被卸载
 onUnmounted(() => {
-    window.removeEventListener('resize', resizeEvent)
+  window.removeEventListener('resize', resizeEvent)
+  if (chart) {
     chart.dispose()
     chart = null
+  }
 })
-
 const onResize = () => {
     nextTick(() => {
         chart.resize()

+ 3 - 2
src/views/statistics/components/echarts/BarChart.vue

@@ -139,11 +139,12 @@ onMounted(() => {
 
 //被卸载
 onUnmounted(() => {
-    window.removeEventListener('resize', resizeEvent)
+  window.removeEventListener('resize', resizeEvent)
+  if (chart) {
     chart.dispose()
     chart = null
+  }
 })
-
 // 暴露出去
 defineExpose({
     onResize,

+ 3 - 1
src/views/statistics/components/echarts/RoundPieChart.vue

@@ -83,9 +83,11 @@ onMounted(() => {
 
 //被卸载
 onUnmounted(() => {
-    window.removeEventListener('resize', resizeEvent)
+  window.removeEventListener('resize', resizeEvent)
+  if (chart) {
     chart.dispose()
     chart = null
+  }
 })
 
 const onResize = () => {