<!-- @format --> <template> <div class="ns-left-menu-space" :class="{ 'ns-left-menu-space-collapsed': collapsed }"> <a-layout-sider class="ns-left-menu" :width="208" :style="`background-image:url(${sideBgImg})`" :collapsedWidth="48" :collapsed="collapsed" breakpoint="lg" :trigger="null"> <a-menu mode="inline" :inlineIndent="16" :openKeys="collapsed ? [] : initSiderOpenKey" :selectedKeys="initSiderKey" v-for="(item, index) in menuList" :key="index"> <a-sub-menu v-if="item.type === 'menus' && item.menus && item.menus.length" :key="item.code"> <template #title> <span role="img" class="anticon"> <i style="margin-right: 5px; transform: translateY(2px); font-size: 14px" v-if="item.icon?.includes('icon-')" :class="`iconfont ${item.icon}`"></i> <ns-icon v-else :name="item.icon ? item.icon : ''" size="16" /> </span> <span>{{ item.label }}</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 item.menus" :key="sitem.code"> <a-menu-item v-if=" sitem.type === 'noChildrenMenu' || (sitem.type === 'menus' && (!sitem.menus || !sitem.menus?.length)) " :key="sitem.code"> <a @click="goTargetRouter(sitem)"> <span style="margin-left: 8px"> {{ sitem.label }} </span> </a> </a-menu-item> <a-sub-menu v-if="sitem.type === 'menus' && sitem.menus && sitem.menus.length" :key="sitem.code"> <template #title> <span role="img" style="margin-left: 8px" class="anticon" v-show="sitem.icon"> <i style="margin-right: 5px; transform: translateY(2px); font-size: 14px" v-if="sitem.icon?.includes('icon-')" :class="`iconfont ${sitem.icon}`"></i> <ns-icon v-else :name="sitem.icon" size="16" /> </span> <span>{{ sitem.label }}</span> </template> <a-menu-item v-for="ditem in sitem.menus" :key="ditem.code"> <a @click="goTargetRouter(ditem)"> <span style="margin-left: 8px">{{ ditem.label }}</span> </a> </a-menu-item> </a-sub-menu> </div> <!-- </div> --> </a-sub-menu> <a-menu-item v-if=" item.type === 'noChildrenMenu' || (item.type === 'menus' && (!item.menus || !item.menus?.length)) " :class="initSiderKey.includes(item.code) ? 'firstMenuItem-selected' : ''" :key="item.code"> <a @click="goTargetRouter(item)"> <span role="img" class="anticon" v-show="item.icon"> <i style="margin-right: 5px; transform: translateY(2px); font-size: 14px" v-if="item.icon?.includes('icon-')" :class="`iconfont ${item.icon}`"></i> <ns-icon v-else :name="item.icon ? item.icon : ''" size="16" /> </span> <span>{{ item.label }}</span> </a> </a-menu-item> </a-menu> <div class="ns-left-menu-trigger" @click="leftMenuTrigger"> <menu-unfold-outlined v-if="collapsed" class="trigger" /> <menu-fold-outlined v-else class="trigger" /> </div> </a-layout-sider> <div class="ns-left-menu-trigger" @click="leftMenuTrigger"> <ns-icon name="trigger" class="trigger" size="22" /> </div> </div> </template> <script lang="ts"> import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'; import { defineComponent, inject, ref, watchEffect, computed } from 'vue'; import { Emitter } from 'mitt'; import { useRouter } from 'vue-router'; import { appConfigStore } from '/nerv-lib/saas/store/modules/app-config'; import { emitEvents } from '/nerv-base/view/system/application.d'; import { storeToRefs } from 'pinia'; export default defineComponent({ name: 'CustomSider', components: { MenuUnfoldOutlined, MenuFoldOutlined, }, props: { menuList: { type: Array }, initSiderKey: { type: Array }, initSiderOpenKey: { type: Array }, }, setup: (props) => { const mittEmit = inject('mittEmit') as Emitter<emitEvents>; const configStore = appConfigStore(); const { getThemeConfig: themeConfig } = storeToRefs(configStore); const router = useRouter(); 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); } } }); }; const sideBgImg = computed(() => { if (themeConfig.sideUrl) { return themeConfig.sideUrl; } else { return `${import.meta.env.VITE_PUBLIC_PATH}asset/image/side.png`; } }); const goTargetRouter = (menu: object) => { let code = menu.code; if (configStore.resourceName) { code = code.toString().replace(configStore.resourceName, ''); } let toName = code; if (menu.type === 'noChildrenMenu' && menu.menus && menu.menus.length) { toName = `${code}Index`; } router.push({ name: toName, }); }; watchEffect(() => { if (!router.currentRoute.value.redirectedFrom) { dealRouter(props.menuList, router.currentRoute.value); } }); const collapsed = ref<boolean>(false); const leftMenuTrigger = () => { collapsed.value = !collapsed.value; mittEmit.emit('leftMenuTrigger', collapsed.value); }; return { sideBgImg, themeConfig, goTargetRouter, leftMenuTrigger, collapsed, }; }, }); </script> <style lang="less" scoped> .ns-left-menu { position: fixed; top: 0; left: 0; height: 100%; background-color: @white; padding-top: 48px; :deep(::-webkit-scrollbar) { display: none; } } #iframeApplication .ns-left-menu { padding-top: 0px; } .ant-menu-submenu-title .anticon + span { margin-left: 8px !important; } .ns-left-menu-space { width: 208px; height: 100%; background-color: #fff; overflow: hidden; transition: all 0.2s; flex: 0 0 208px; &.ns-left-menu-space-collapsed { width: 48px; flex: 0 0 48px; } } .ns-left-menu-space-collapsed { .ns-left-menu-trigger { width: 48px !important; justify-content: center; padding-left: 0px; } .trigger { padding: 0; transform: rotate(180deg); // transform: rotateX('90deg'); } } .ns-left-menu-trigger { width: 208px; 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.3s; } .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; } </style>