<!-- @format --> <template> <a-layout-header :style="`background-image:url(${headerBgImg})`" class="header"> <div class="logo"> <img v-if="themeConfig.logoLessUrl" :src="themeConfig.logoLessUrl" style="width: 192px; height: 48px; object-fit: contain" /> <ns-icon v-else name="headerLogin" class="headerLogin" style="width: auto; height: 48px" /> </div> <div class="header-menu"> <a-menu style="width: 100%" theme="dark" mode="horizontal" :selectedKeys="initHeaderKey"> <a-menu-item @click="tochildren(item)" v-for="item in menuList" :key="item.code"> <i style="margin-right: 5px; transform: translateY(2px); font-size: 14px; color: #ffffff" v-if="item.icon?.includes('icon-')" :class="`iconfont ${item.icon}`"></i> <ns-icon v-else :name="item.icon" size="16" style="margin-right: 5px; transform: translateY(2px); color: #ffffff" /> <span style="font-size: 14px; color: #ffffff">{{ item.label }}</span> </a-menu-item> <template v-for="item in subMenuList" :key="item.name"> <a-sub-menu v-if="getOPMenu(item.meta?.itemList)?.length > 1"> <template #title> <ns-icon :name="item.meta.icon" size="12" style="margin-right: 5px; transform: translateY(1px)" /><span style="font-size: 14px" >{{ item.meta.title }} </span></template > <a-menu-item :key="menuItem.name" v-for="menuItem in item.meta?.itemList ? getOPMenu(item.meta?.itemList) : []"> <a style="color: inherit !important" :href="menuItem.url ? menuItem.url : menuItem.getUrl ? menuItem.getUrl() : ''" target="_blank" rel="noopener noreferrer"> {{ menuItem.name }} </a></a-menu-item > </a-sub-menu> <template v-if="checkMenus(item.meta?.itemList)?.length == 1"> <a-menu-item :key="menuItem.name" v-for="menuItem in checkMenus(item.meta?.itemList) ? checkMenus(item.meta?.itemList) : []"> <div v-if="!item.meta.isNewOpen"> <ns-icon :name="item.meta.icon" size="12" style="margin-right: 5px; transform: translateY(1px)" />{{ item.meta.title }} <a :href="menuItem.url ? menuItem.url : menuItem.getUrl ? menuItem.getUrl() : ''" target="_blank" rel="noopener noreferrer"> </a> </div> <div v-else @click="toCustomUrl(menuItem.url)"> <ns-icon :name="item.meta.icon" size="14" style="margin-right: 5px; transform: translateY(1px)" />{{ item.meta.title }} <a target="_blank" rel="noopener noreferrer"> </a> </div> </a-menu-item> </template> </template> </a-menu> </div> <div class="nsHeader_action"> <div class="projectName action" v-if="showProject"> {{ projectName ? projectName : enterpriseName }} </div> <div class="projectName action" v-if="['服务管理平台', '报表中心'].includes(configStore.resourceInfo?.application?.label)" @click="backDoor" >{{ '返回门户' }}</div > <div v-if="bellInfo.isShow" class="bells action" @click="backMessage"> <a-badge :count="messageCount > 99 ? 99 : messageCount"> <ns-icon name="bells" size="32" /> </a-badge> </div> <a-dropdown :trigger="['hover']"> <div class="userName action"> <ns-icon class="headerAdminIcon" name="headerAdminIcon" size="20" /> <span style=" display: block; max-width: 100px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; " >{{ userName }}</span > <ns-icon class="downArrow" name="downArrow" size="20" /> </div> <template #overlay> <a-menu> <a-menu-item class="menuItem" style=" display: flex; align-items: center; height: 52px; font-size: 14px; justify-content: center; color: rgba(0, 0, 0, 0.65); "> <ns-icon name="updatePassword" size="18" style="position: relative; top: 2px" /> <a href="javascript:;" @click="toUpdate" style="margin-left: 10px">修改密码</a> </a-menu-item> <a-menu-item class="menuItem" style=" display: flex; align-items: center; height: 52px; font-size: 14px; justify-content: center; color: rgba(0, 0, 0, 0.65); "> <ns-icon name="leaveout" size="14" style="position: relative; top: 2px; left: 2px" /> <a href="javascript:;" @click="dropOut" style="margin-left: 12px">退出登录</a> </a-menu-item> </a-menu> </template> </a-dropdown> <!-- <div class="setting action" v-if="currentThemeColor" @click="changeSettingVisible(true)"> <setting-outlined /> </div> --> </div> <a-drawer v-model:visible="settingVisible" title="外观管理" placement="right"> <a-divider>主题</a-divider> <div class="theme-picker"> <template v-for="color in themeColorList" :key="color"> <span class="theme-picker_item" @click="changeTheme(color)" :class="{ current: currentThemeColor === color }" :style="{ backgroundColor: color }" ><check-outlined /></span ></template> </div> </a-drawer> </a-layout-header> </template> <script lang="ts"> import { defineComponent, ref, watch, computed, getCurrentInstance } from 'vue'; import { useRouter } from 'vue-router'; import { message } from 'ant-design-vue'; import { Cookies } from '/nerv-lib/util/cookie'; import { getLocalSetting, setLocalSetting, http } from '/nerv-lib/saas'; import { appConfigStore } from '/nerv-lib/saas/store/modules/app-config'; import { useTags } from '/nerv-base/store/modules/tags'; import { authorizationService } from '/nerv-base/store/modules/authorization-service'; import { replaceStyleVariables } from 'vite-plugin-theme/es/client'; import { getThemeColors } from '../../../../../build/themeConfig'; import { SettingOutlined, CheckOutlined } from '@ant-design/icons-vue'; import { messagecount } from '/nerv-lib/saas/store/modules/messagecount'; import { storeToRefs, mapWritableState } from 'pinia'; export default defineComponent({ name: 'CustomHeader', components: { SettingOutlined, CheckOutlined, }, props: { initHeaderKey: { type: Array }, }, setup: () => { const configStore = appConfigStore(); const { getThemeConfig: themeConfig } = storeToRefs(configStore); const messagecountStore = messagecount(); const bellInfo = ref(); const subMenuList = ref([]); const router = useRouter(); const time = ref(); const headerBgImg = computed(() => { if (themeConfig.topUrl) { return themeConfig.topUrl; } else { return `${import.meta.env.VITE_PUBLIC_PATH}asset/image/header.png`; } }); const toCustomUrl = (val: string) => { window.open(`${val}?nervsid=${Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)}`); }; const messageCount = computed(() => { return messagecountStore.getCount; }); bellInfo.value = configStore.headerBellInfo; if (bellInfo.value.isShow) { async function initMessageCount() { try { const res = await http.get(bellInfo.value.api); if (res.success) { messagecountStore.updateCount( (bellInfo.value.dealData ? bellInfo.value.dealData(res) : res.data.count) > 99 ? '99+' : bellInfo.value.dealData ? bellInfo.value.dealData(res) : res.data.count, ); } } catch (err) {} } if (!configStore?.customInitMessageCount) { initMessageCount(); time.value = setInterval(initMessageCount, 10000); } } const userName = ref<string>('-'); const authorizationStore = authorizationService(); // const { getInitMenus: menuList } = storeToRefs(authorizationStore); const menuList = computed(() => { let list = []; subMenuList.value = []; const initMenu = authorizationStore.getInitMenus; if (initMenu && initMenu.length) { initMenu.forEach((item) => { if (item.extend) { let extend = JSON.parse(item.extend); if (extend.itemList) { item.meta = { title: '智慧社区大屏', icon: 'shujudaping', index: 1000, isNewOpen: true, itemList: [ { code: 'wisdomCommunity', name: '智慧社区运营管理驾驶舱', url: '/nervui-community-screen/community-screen', }, { code: 'wisdomCommunity1', name: '智慧社区安防监控驾驶舱', url: '/nervui-community-screen/community-security', }, ], }; subMenuList.value.push(item); } else { list.push(item); } } else { list.push(item); } }); } return list; }); const { getProjectName: projectName } = storeToRefs(authorizationStore); const { getEnterpriseName: enterpriseName } = storeToRefs(authorizationStore); const initUserInfo = window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]; if (!authorizationStore.getProjectName && Cookies.get('projectName')) { authorizationStore.setProjectName(Cookies.get('projectName')); } if ( !authorizationStore.getEnterpriseName && window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH] ) { const userInfo = JSON.parse(window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]); authorizationStore.setEnterpriseName( !userInfo.organizationalName ? '' : userInfo.organizationalName, ); } initUserInfo ? (userName.value = JSON.parse(initUserInfo).accountRealName ? JSON.parse(initUserInfo).accountRealName : JSON.parse(initUserInfo).accountName) : ''; const tochildren = (val) => { if (val.type === 'menus') { if (!val.menus || val.menus.length === 0) { let code = val.code; if (configStore.resourceName) { code = code.toString().replace(configStore.resourceName, ''); } router.push({ name: code, }); } else { tochildren(val.menus[0]); } } else { if (val.type === 'noChildrenMenu') { let code = val.code; if (configStore.resourceName) { code = code.toString().replace(configStore.resourceName, ''); } if (val.menus) { router.push({ name: `${code}Index`, }); } else { router.push({ name: `${code}`, }); } } } }; const getOPMenu = (list) => { if (configStore.enablePermissions !== undefined && configStore.enablePermissions) { return list.filter((item) => { return authorizationStore.checkPermission(item.code); }); } else { return list; } }; const checkMenus = (list) => { if (configStore.enablePermissions !== undefined && configStore.enablePermissions) { return list.filter((item) => { return authorizationStore.checkAllPermission(item.code); }); } else { return list; } }; const toUpdate = () => { router.push({ name: 'UpdatePassWord', }); }; const backMessage = () => { if (bellInfo.value.toRouterName) { router.push({ name: bellInfo.value.toRouterName, }); } }; const dropOut = () => { message.loading('正在退出', 0.1); if (configStore.dropOut) { configStore.dropOut(Cookies, router, useTags, authorizationStore, http); } else { Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`); sessionStorage.clear(); router.push('/login'); useTags().clearTags(); configStore.setInitThemeCoinfig(false); authorizationStore.clearAuthorization(); } }; const updatePassWord = () => { router.push('/synthetical/user/updatePassWord'); }; const currentThemeColor = ref(null); function changeTheme(color: string) { replaceStyleVariables({ colorVariables: [...getThemeColors(color)] }); currentThemeColor.value = color; setLocalSetting({ themeColor: color }); } const appConfig = getCurrentInstance()?.appContext.config.globalProperties.$appConfig; const themeColor = getLocalSetting()?.themeColor || appConfig?.themeColor; if (themeColor) { changeTheme(themeColor); } const settingVisible = ref<boolean>(false); function changeSettingVisible(visible: boolean) { settingVisible.value = visible; } const themeColorList = [ '#37abc4', '#1677FF', '#009688', '#536dfe', '#ff5c93', '#ee4f12', '#0096c7', '#9c27b0', '#ff9800', ]; const backDoor = () => { let protocol = window.location.protocol; const nervsid = Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`); http.get('/portalurl.json').then((res) => { if (nervsid) { window.location.href = `${protocol}//${res.url}report?nervsid=${nervsid}`; } else { window.location.href = `${protocol}//${res.url}report`; } }); }; return { headerBgImg, backDoor, themeColorList, currentThemeColor, settingVisible, changeSettingVisible, checkMenus, toCustomUrl, time, messageCount, backMessage, bellInfo, showProject: configStore.showProject, projectName, enterpriseName, tochildren, menuList, toUpdate, subMenuList, changeTheme, userName, dropOut, updatePassWord, getOPMenu, configStore, themeConfig, }; }, beforeUnmount() { clearInterval(this.time); }, }); </script> <style lang="less" scoped> .theme-picker { display: flex; flex-wrap: wrap; margin: 16px 0; justify-content: space-around; .theme-picker_item { display: flex; justify-content: center; align-items: center; width: 20px; height: 20px; cursor: pointer; border: 1px solid #ddd; border-radius: 2px; span { display: none; font-size: 12px; color: #fff; } &.current span { display: flex; } } } .header { padding: 0; position: fixed; top: 0; z-index: 505; width: 100%; } :deep(.ant-badge-count) { // width: 16px; height: 16px; line-height: 8px; text-align: center; padding: 4px; // line-height: 16px; // background: #ec4d28; border-radius: 8px; box-shadow: 0 0 0 0px #fff; transform: translate(45%, -35%); } :deep(.hiden) { opacity: 0 !important; position: absolute; user-select: none; width: 1px !important; height: 1px !important; } .header-menu { width: calc(100% - @layout-sider-width - 208px); } .logo { width: calc(@layout-sider-width - 16px); min-width: @layout-sider-width !important; display: flex; padding-left: 16px; align-items: center; img { width: 100%; } } .ant-layout-header { display: flex; padding: 0 !important; } .nsHeader_action { color: #fff; padding-left: 24px; padding-right: 24px; width: 208px; display: flex; align-items: center; justify-content: right; .action { cursor: pointer; &:hover { background-color: @layout-header-hover; } } .projectName { white-space: nowrap; } .bells, .setting { height: 100%; display: flex; align-items: center; justify-content: center; } .projectName { color: #ffffff; white-space: nowrap; padding-left: 10px; padding-right: 10px; } // padding-right: 30px; .dropOut { height: 100%; padding-right: 16px; padding-left: 16px; } .userName { line-height: 48px; display: flex; white-space: nowrap; align-items: center; cursor: pointer; padding-left: 10px; padding-right: 10px; height: 100%; } img { width: 24px; height: 24px; margin-right: 12px; } span { user-select: none; } } .menuItem { height: 80px; display: flex; align-items: center; } :deep( .ant-menu.ant-menu-dark .ant-menu-item-selected, .ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected ) { background-color: @layout-header-hover; } :deep(.ant-menu.ant-menu-dark .ant-menu-item) { &:hover { background-color: @layout-header-hover; } } .headerAdminIcon { margin-right: 8px; } .downArrow { margin-left: 8px; } </style>