<!-- @format --> <template> <a-layout-header 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 v-if="menuList?.length" style="width: 100%" mode="horizontal" :selectedKeys="initHeaderKey"> <a-menu-item v-for="item in menuList" :key="item.name"> <div @click="tochildren(item)"> <ns-icon :name="item.meta.icon" size="16" /><span>{{ item.meta.title }}</span> </div> </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="16" /><span >{{ 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="16" />{{ 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="16" />{{ item.meta.title }} <a target="_blank" rel="noopener noreferrer"> </a> </div> </a-menu-item> </template> </template> </a-menu> <div v-else> <div v-if="configStore.projectName" class="projectTitle">{{ configStore.projectName }}</div> </div> </div> <div class="nsHeader_action"> <div> <component v-if="headerSlot" :is="headerSlot.component" v-bind="headerSlot.componentProps" /> </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"> <!-- <img src="/asset/image/login/adminIcon.png" /> --> <!-- <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 } from 'pinia'; // import { appConfig } from '/@/config/app.config.ts'; export default defineComponent({ name: 'NsHeader', components: { SettingOutlined, CheckOutlined, }, props: { // eslint-disable-next-line vue/require-valid-default-prop headerList: { type: Array, default: [] }, initHeaderKey: { type: Array }, }, setup: (props: any) => { const configStore = appConfigStore(); const { getThemeConfig: themeConfig } = storeToRefs(configStore); const headerSlot = computed(() => { return configStore.headerSlotConfig; }); const { getHeaderBellInfo: headerBellInfo } = storeToRefs(configStore); const messagecountStore = messagecount(); const bellInfo = ref(); // const messageCount = ref(); const time = ref(); const toCustomUrl = (val: string) => { window.open(`${val}?nervsid=${Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)}`); // window.location.href = `${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 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, ); } const projectName = computed(() => authorizationStore.getProjectName); const enterpriseName = computed(() => authorizationStore.getEnterpriseName); initUserInfo ? (userName.value = JSON.parse(initUserInfo).accountRealName ? JSON.parse(initUserInfo).accountRealName : JSON.parse(initUserInfo).accountName) : ''; const menuList = ref([]); const subMenuList = ref([]); const router = useRouter(); const tochildren = (val) => { if (val.children === undefined || val.children.length === 0) { router.push({ name: val.name, }); } else { for (var i = 0; i < val.children.length; i++) { if (!val.children[i].isHide) { tochildren(val.children[i]); break; } } } }; 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; } }; props.headerList.forEach((item) => { if (!item.isHide) { if (!item.meta.isNewOpen) { menuList.value.push(item); } else { subMenuList.value.push(item); } } }); watch( () => props.headerList, (e) => { menuList.value = []; subMenuList.value = []; e.forEach((item) => { if (!item.isHide) { if (!item.meta.isNewOpen) { menuList.value.push(item); } else { subMenuList.value.push(item); } } }); }, { deep: true }, ); const toUpdate = () => { router.push({ name: 'UpdatePassWord', }); }; const backMessage = () => { if (headerBellInfo.value.toRouterName) { router.push({ name: headerBellInfo.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(); authorizationStore.clearAuthorization(); } }; 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 { themeConfig, backDoor, themeColorList, currentThemeColor, settingVisible, changeSettingVisible, checkMenus, toCustomUrl, time, messageCount, backMessage, bellInfo, showProject: configStore.showProject, projectName, enterpriseName, tochildren, menuList, toUpdate, subMenuList, changeTheme, userName, dropOut, getOPMenu, configStore, headerSlot, }; }, 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; } } } :deep(.ant-menu-submenu-title) { // color: rgba(255, 255, 255, 0.9); } .header-menu { flex: 1; .projectTitle { font-size: 28px; font-weight: 500; letter-spacing: 2.33px; line-height: 40px; color: @primary-color; text-align: left; overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis; } } :deep(.header-menu .ant-menu-title-content) { div { // color: rgba(255, 255, 255, 0.9); .ns-icon { margin-right: 7px; transform: translateY(2px); } } } :deep(.ant-menu-submenu-title .ant-menu-title-content) { // color: rgba(255, 255, 255, 0.9); .ns-icon { margin-right: 7px; transform: translateY(2px); } } .header { padding: 0; position: fixed; top: 0; z-index: 999; width: 100%; // background-image: url(/asset/image/header.png); // background-size: 100% 100%; box-shadow: @ns-box-shadow; display: flex; align-items: center; } :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(55%, -10%); } :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); .ant-menu-root { background-color: transparent; } } .logo { width: calc(@layout-sider-width - 16px); min-width: @layout-sider-width !important; display: flex; padding-left: 16px; align-items: center; img { width: 100%; } // .headerLogin { // width: @layout-sider-width; // height: 48px; // } } .ant-layout-header { display: flex; padding: 0 !important; } .nsHeader_action { color: @black; padding-left: 16px; padding-right: 16px; // 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%; // width: 50px; padding-left: 12px; padding-right: 12px; display: flex; align-items: center; justify-content: center; } .projectName { color: rgba(255, 255, 255, 0.9); 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%; } .headerAdminIcon { margin-right: 8px; } .downArrow { margin-left: 8px; } span { user-select: none; } } .menuItem { height: 80px; display: flex; align-items: center; } :deep(.ant-menu-horizontal) { border: unset !important; } :deep(.ant-menu-item-selected) { background: @primary-color; } </style>