<!-- @format --> <template> <div class="ns-left-menu-space" :class="{ 'ns-left-menu-space-collapsed': collapsed }"> <a-layout-sider class="ns-left-menu" :collapsedWidth="60" :width="sideWidth" :collapsed="collapsed" breakpoint="lg" :trigger="null" v-if="menuList && menuList[0] && menuList[0].children"> {{ $refs.getComputedStyle }} <a-menu mode="inline" :inlineIndent="20" :openKeys="collapsed ? [] : initSiderOpenKey" :selectedKeys="initSiderKey" v-for="(item, index) in menuList[0].children" :key="index"> <a-sub-menu v-if="item.children !== undefined && !item.meta.hideChildren && !item.isHide" :key="item.name"> <template #title> <span role="img" class="anticon"> <ns-icon :name="item.meta.icon ? item.meta.icon : ''" size="15" /> </span> <span>{{ item.meta.title }}</span> </template> <!-- 跳转外部链接 --> <div v-if="item.meta?.isNewOpen"> <div v-for="newOpen in item.meta?.itemList ? item.meta?.itemList : []" :key="newOpen.code"> <a-menu-item v-if="checkOpAuth(newOpen.code)" :key="newOpen.code"> <a :href="newOpen.url ? newOpen.url : newOpen.getUrl ? newOpen.getUrl() : ''" target="_blank" rel="noopener noreferrer"> {{ newOpen.name }} </a> </a-menu-item> </div> </div> <div v-else> <div v-for="sitem in checkAuthList(item.children)" :key="sitem.name"> <a-menu-item v-if="(sitem.children === undefined || sitem.meta.hideChildren) && !sitem.isHide" :key="sitem.name"> <router-link :to="{ name: sitem.name }"> <span role="img" class="anticon" v-if="sitem.meta.icon"> <ns-icon :name="sitem.meta.icon ? sitem.meta.icon : ''" size="15" /> </span> <span>{{ sitem?.meta?.title }}</span> </router-link> </a-menu-item> <a-sub-menu class="threeSubMenu" v-if="sitem.children !== undefined && !sitem.meta.hideChildren && !sitem.isHide" :key="sitem.name"> <template #title> <span role="img" class="anticon" v-show="sitem.meta.icon"> <ns-icon :name="sitem.meta.icon" size="15" /> </span> <span>{{ sitem.meta.title }}</span> </template> <div v-for="ditem in checkAuthList(sitem.children)" :key="ditem.name"> <a-sub-menu class="fourSubMenu" v-if="ditem.children !== undefined && !ditem.isHide" :key="ditem.name"> <template #title> <span role="img" class="anticon" v-show="ditem.meta.icon"> <ns-icon :name="ditem.meta.icon" size="15" /> </span> <span>{{ ditem.meta.title }}</span> </template> <div v-for="fiveFloorItem in checkAuthList(ditem.children)" :key="fiveFloorItem.name"> <a-menu-item :class=" initSiderKey.includes(fiveFloorItem.name) ? 'ant-menu-item-selected' : '' " v-if="fiveFloorItem.meta?.type !== 'op'"> <router-link :to="{ name: fiveFloorItem.name }"> {{ fiveFloorItem?.meta?.title ? fiveFloorItem?.meta?.title : '' }}123 </router-link> </a-menu-item> </div> </a-sub-menu> <a-menu-item v-if="ditem.meta?.type !== 'op' && item.children === undefined"> <router-link :to="{ name: ditem.name }"> <span role="img" class="anticon" v-if="ditem.meta.icon"> <ns-icon :name="ditem.meta.icon ? ditem.meta.icon : ''" size="15" /> </span> <span>{{ ditem?.meta?.title }}123</span> </router-link> </a-menu-item> </div> </a-sub-menu> </div> </div> </a-sub-menu> <a-menu-item :class="initSiderKey.includes(item.name) ? 'firstMenuItem-selected' : ''" v-if=" (item.children === undefined || item.meta.hideChildren) && !item.isHide && item.type !== 'op' " :key="item.name"> <router-link :to="{ name: item.name }"> <span role="img" class="anticon" v-show="item.meta.icon"> <ns-icon :name="item.meta.icon ? item.meta.icon : ''" size="15" /> </span> <span>{{ item.meta.title }}</span> </router-link> </a-menu-item> </a-menu> </a-layout-sider> <div class="ns-left-menu-trigger" @click="leftMenuTrigger"> <!-- <menu-unfold-outlined v-if="collapsed" class="trigger" /> <menu-fold-outlined v-else class="trigger" /> --> <ns-icon name="trigger" class="trigger" size="22" /> </div> </div> </template> <script lang="ts"> import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'; import { computed, defineComponent, inject, onMounted, ref, watchEffect } from 'vue'; import { appConfigStore } from '/nerv-lib/saas/store/modules/app-config'; import { authorizationService } from '/nerv-base/store/modules/authorization-service'; import { Emitter } from 'mitt'; import { useRouter } from 'vue-router'; import { emitEvents } from '/nerv-base/view/system/application.d'; export default defineComponent({ name: 'NsSider', components: { MenuUnfoldOutlined, MenuFoldOutlined, }, props: { menuList: { type: Array }, initSiderKey: { type: Array }, initSiderOpenKey: { type: Array }, }, setup: (props) => { const mittEmit = inject('mittEmit') as Emitter<emitEvents>; const router = useRouter(); const sideWidth = computed(() => { return getComputedStyle(document.querySelector('.ns-left-menu-trigger')!).width; }); const dealRouter = (menuList, routerInfo) => { menuList?.forEach((item) => { if (item.name === routerInfo.name) { const redirectItem = item.children?.filter((x) => !x.isHide)[0]; if (redirectItem) { router.push({ name: redirectItem.name }); } } else { if (item.children?.length) { dealRouter(item.children, routerInfo); } } }); }; watchEffect(() => { if (!router.currentRoute.value.redirectedFrom) { dealRouter(props.menuList, router.currentRoute.value); } }); const authorizationStore = authorizationService(); const configStore = appConfigStore(); const checkOpAuth = (val: any) => { if (configStore.enablePermissions) { return authorizationStore.checkPermission(val); } else { return true; } }; const checkAuth = (val: any) => { if (configStore.enablePermissions) { return authorizationStore.checkPermissionRouter(val); } else { return true; } }; const checkAuthList = (val: any) => { if (Object.prototype.toString.call(val) !== '[object Array]') return; let array: any[] = []; val.forEach((item: any) => { if (checkAuth(item.name)) { array.push(item); } }); return array; }; const collapsed = ref<boolean>(false); const leftMenuTrigger = () => { collapsed.value = !collapsed.value; mittEmit.emit('leftMenuTrigger', collapsed.value); }; return { leftMenuTrigger, collapsed, checkOpAuth, checkAuthList, checkAuth, sideWidth, }; }, }); </script> <style lang="less" scoped> @icon-gap: 12px; // :deep(.ant-layout-sider) { // width: @layout-sider-width !important; // max-width: @layout-sider-width !important; // } .ns-left-menu { position: fixed; top: 0; left: 0; height: calc(100% - 40px); background-color: @white; // background-image: url(/asset/image/side.png); background-size: cover; padding-top: calc(@layout-header-height + @icon-gap); .ant-menu-item-selected { // color: #fff !important; // background: @primary-color; border-radius: 12px; height: 40px; line-height: 40px; padding: 5px @ns-gap; position: relative; a { color: @white !important; } } .router-link-active::before { // background-color: @primary-color; width: 90%; } :deep(.ant-layout-sider-children) { transition: all 0.1s; padding: 0 @ns-gap; // background-color: transparent; .ant-menu-root { // background-color: transparent; } .ant-menu-submenu-title, .ant-menu-item { height: @menu-item-height; border-radius: @ns-border-radius; // overflow: hidden; // color: @black; .ant-menu-submenu-arrow { // color: @black; } } .ant-menu { color: rgba(@black, 0.85); } .ant-menu-sub.ant-menu-inline { background-color: @white; // > div { // margin-inline: 20px; // } } .ant-menu-item-active:not(.ant-menu-item-selected), .ant-submenu-item-active { // color: rgba(@primary-color, 0.1) !important; background-color: rgba(@primary-color, 0.1); border-radius: 12px; } } } .anticon + span { margin-left: @icon-gap !important; } .ns-left-menu-space { width: 100%; height: auto; z-index: 9; box-shadow: @ns-box-shadow; // background-color: #fff; overflow: hidden; transition: all 0.2s; flex: 0 0 @layout-sider-width; // transition: all 0.1s; // &:not(.ns-left-menu-space-collapsed) :deep(.ant-menu-title-content) { // padding-left: 8px; // } :deep(.ant-menu-item-selected) { // color: #fff !important; background: @primary-color; // border-radius: 12px; // height: 40px; // line-height: 40px; // width: auto; // margin-inline: @ns-gap; // padding-left: @ns-gap !important; position: relative; a { color: @white !important; } } } .ns-left-menu-space-collapsed { z-index: 11; width: @layout-sider-collapsed-width; flex: 0 0 @layout-sider-collapsed-width; :deep(.ant-layout-sider-children) { transition: all 0.1s; padding: 0 calc(@ns-gap / 4) !important; } .ns-left-menu-trigger { width: @layout-sider-collapsed-width !important; justify-content: center; padding-left: 0px; } .trigger { padding: 0; transform: rotate(180deg); // transform: rotateX('90deg'); } } .ns-left-menu-trigger { width: @layout-sider-width; height: 40px; transition: all 0.2s; border-top: 1px solid rgba(0, 0, 0, 0.06); position: absolute; z-index: 999; cursor: pointer; // background: #163361; bottom: 0px; display: flex; align-items: center; justify-content: left; padding-left: 24px; // justify-content: center; &:hover { .trigger { color: @primary-color; } } } .trigger { font-size: 18px; line-height: 64px; transform: rotate(0deg); transition: color 0.2s; } // .ant-menu-submenu-selected > .ant-menu-submenu-title > .ant-menu-submenu-expand-icon, // .ant-menu-submenu-selected > .ant-menu-submenu-title > .ant-menu-submenu-arrow { // color: red !important; // } .ant-menu-submenu-selected .secendIcon { color: @primary-color !important; } .ant-menu-inline, .ant-menu-vertical, .ant-menu-vertical-left { border: unset; } :deep(.ant-menu-inline .ant-menu-item-selected::after) { position: absolute; border: unset !important; content: ''; } // :deep(.ant-menu-sub .ant-menu-item-selected::after) { // position: absolute; // width: 2px; // height: 14px; // // background: #fff; // border: unset; // top: 14px; // left: 20px; // right: 0; // bottom: 0; // content: ''; // } // :deep(.ant-menu-submenu .ant-menu-submenu .ant-menu-sub .ant-menu-item-selected::after) { // position: absolute; // width: 2px; // height: 14px; // // background: #fff; // border: unset; // top: 14px; // left: 40px; // right: 0; // bottom: 0; // content: ''; // } // :deep(.firstMenuSub .ant-menu-submenu-title) { // padding-left: 8px !important; // } :deep(.firstMenuItem-selected) { background: @primary-color!important; border-radius: 4px; } :deep(.ns-left-menu .ant-layout-sider-children .ant-menu-submenu-arrow) { // color: rgba(255, 255, 255, 0.5); } </style>