You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
18 KiB

7 months ago
<!-- @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`)}`);
7 months ago
};
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];
7 months ago
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]);
7 months ago
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`);
7 months ago
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`);
7 months ago
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>