<template>
<div class="pl-concise-sidebar">
<div ref="conciseHead" class="pl-tabs-hd concise"></div>
<div class="menus">
<!-- 主菜单区域 -->
<ul class="area">
<!-- 全部功能按钮 -->
<li>
<div
title="全部功能"
ref="navMenuBtn"
@mouseenter="handleNavMenuBtnMouseEnter"
@mouseleave="handleNavMenuBtnMouseLeave"
>
<div class="tip">
<i class="iconfont icon-func"></i>
</div>
</div>
</li>
<!-- 用户信息菜单项 -->
<li>
<template v-if="sysUserInfo.children && sysUserInfo.children.length">
<div
class="item"
:title="sysUserInfo.name"
@mouseenter="handleFuncMenus(sysUserInfo, $event)"
>
<div class="tip">
<i class="iconfont" :class="sysUserInfo.iconClass"></i>
</div>
</div>
</template>
</li>
<!-- 系统功能菜单项 -->
<template v-for="item in sysFuncMenus">
<li v-if="item.type != 'split'" :key="item.resId">
<template v-if="item.type == 'withNumTips'">
<div
class="item"
:title="item.name"
@click.prevent="handleFuncMenus(item, $event)"
@mouseenter="handleFuncMenus(item, $event, true)"
>
<div class="tip">
<i class="iconfont" :class="item.iconClass"></i>
</div>
</div>
</template>
<!-- 普通菜单项 -->
<template v-else>
<!-- 有子菜单的项 -->
<template v-if="item.children && item.children.length">
<div class="item" @mouseenter="handleFuncMenus(item, $event)">
<div class="tip">
<i class="iconfont" :class="item.iconClass"></i>
</div>
</div>
</template>
<!-- 无子菜单的项 -->
<template v-else>
<div
class="item"
@click.prevent="handleFuncMenus(item, $event)"
>
<div class="tip">
<i class="iconfont" :class="item.iconClass"></i>
</div>
</div>
</template>
</template>
</li>
</template>
</ul>
<!-- 系统功能菜单区域 -->
<ul class="area">
<!-- 项目管理菜单项 -->
<li v-if="sysProjectManage">
<a
:href="sysProjectManage.url"
target="_blank"
class="item"
:title="sysProjectManage.name"
>
<i class="iconfont" :class="sysProjectManage.iconClass"></i>
<span class="title">{{ sysProjectManage.name }}</span>
</a>
</li>
<!-- 语言切换菜单项 -->
<li v-if="langList">
<template v-if="langList.children && langList.children.length">
<div
class="item"
:title="langList.name"
@mouseenter="handleFuncMenus(langList, $event)"
>
<div class="tip">
<i class="iconfont" :class="langList.iconClass"></i>
</div>
</div>
</template>
</li>
<!-- 设置菜单项 -->
<li v-if="hasSetting">
<div class="item" :title="$t('hivuiMain_customWorkbench')">
<a href="#design" target="_blank"
><i class="iconfont icon-set-fill"></i
></a>
</div>
</li>
<!-- 退出登录菜单项 -->
<li>
<div
class="item"
:title="$t('hivuiMain_logout')"
@click="handleLogout"
@mouseenter="handleFuncMenus(null, null, true)"
>
<a><i class="iconfont icon-tuichu"></i></a>
</div>
</li>
</ul>
</div>
<!-- 右键上下文菜单组件 -->
<vueContextMenu
ref="cmp-funcMenu"
:menu-items="menuItems"
field="name"
:is-bottom="true"
></vueContextMenu>
<modifyPw></modifyPw>
<!-- 导航菜单组件 -->
<navmenu
ref="navmenu"
:targetElRect="navMenuBtnRect"
@mouseenter.native="handleNavMenuMouseEnter"
@mouseleave.native="handleNavMenuMouseLeave($event)"
></navmenu>
</div>
</template>
<script>
import vueContextMenu from "@main/components/contextMenu";
import modifyPw from "@main/views/layout/components/modifyPw";
import navmenu from "./concisemenu.vue";
import { sortMapTurnArr } from "@main/utils/index";
import screenfull from "screenfull";
export default {
components: { vueContextMenu, modifyPw, navmenu },
inject: ["addTab", "showTab", "hideTab"],
props: {},
data() {
return {
menuItems: [], // 菜单项数据
navMenuBtnRect: {}, // 导航菜单按钮位置信息
};
},
computed: {
/**
* 是否显示设置按钮
*/
hasSetting() {
let cfg = this.$store.state.app.config || {};
if (cfg.hideSettingBtn) {
return false;
}
let _arr = sortMapTurnArr(cfg.sysSetTabs);
return _arr && _arr.length;
},
/**
* 系统列表
*/
sysList() {
let cfg = this.$store.state.app.config || {};
return cfg.sysList || [];
},
/**
* 语言列表
*/
langList() {
let cfg = this.$store.state.app.config || {};
if (!cfg.showChangeLangBtn) {
return;
}
let menus = cfg.langList || [];
return menus;
},
/**
* 用户信息菜单
*/
sysUserInfo() {
let me = this;
let cfg = this.$store.state.app.config || {};
// 替换数据中的变量
function replaceData(str) {
if (!str) return ["", false];
var _arr = str.match(/\${(\S*?)}/g);
var nullNum = 0;
if (_arr && _arr.length > 0) {
for (let i of _arr) {
var _data = me.userInfo[i.match(/\${(\S*)}/)[1]];
if (typeof _data != "undefined") {
str = str.replace(i, _data);
} else {
str = str.replace(i, me.$t("hivuiMain_header_null"));
nullNum++;
}
}
return [str, nullNum == _arr.length ? false : true];
}
return [str, true];
}
// 多岗位信息文字模板格式化
function orgBzTplFormat(str, item) {
let fieldArr = str.match(/(?<=\${).*?(?=})/g);
fieldArr.forEach((_i) => {
if (typeof item[_i] != "undefined") {
str = str.replaceAll("${" + _i + "}", item[_i]);
}
});
return str;
}
let _userInfo = cfg.userInfo;
if (_userInfo) {
_userInfo.name = replaceData(_userInfo.name)[0];
if (_userInfo.children && _userInfo.children.length > 0) {
_userInfo.children = _userInfo.children.filter((item) => {
let _rData = replaceData(item.name);
item.name = _rData[0];
// 根据不同类型设置处理函数
switch (item.type) {
case "modifyUserInfo":
item.handler = me.modifyUserInfo;
break;
case "modifyPw":
item.handler = me.modifyPw;
break;
case "personalCenter":
item.handler = me.personalCenter;
break;
case "orgBz":
if (Array.isArray(me.bzList) && me.bzList.length > 1) {
item.children = [];
for (let i of me.bzList) {
let _pushData = {
iconClass: "icon-user",
name: cfg.orgBzTpl
? orgBzTplFormat(cfg.orgBzTpl, i)
: `${i.fbzname}/${i.fbzid}`,
handler: function () {
me.__changeOrgBz(`${i.fbzid}`);
},
};
item.children.push(_pushData);
}
}
break;
}
// 只返回有效的菜单项
if (_rData[1]) {
return item;
}
});
}
}
return _userInfo || [];
},
/**
* 系统功能菜单
*/
sysFuncMenus() {
let cfg = this.$store.state.app.config || {};
let menus = sortMapTurnArr(cfg.sysFuncMenus) || [];
return menus;
},
/**
* 项目管理菜单
*/
sysProjectManage() {
let cfg = this.$store.state.app.config || {};
let item = cfg.sysProjectMange || {},
flag = false;
this.$store.state.app.powerList.forEach((p) => {
if (p.resUrl.indexOf(item.uri) > -1) {
flag = true;
return true;
}
});
if (flag) {
return cfg.sysProjectMange;
} else {
return null;
}
},
/**
* 用户信息
*/
userInfo() {
return this.$store.state.user.userInfo;
},
isFullscreen() {
return screenfull.isFullscreen;
},
},
methods: {
/**
* 更新导航菜单按钮位置信息
*/
updateNavMenuBtnRect() {
if (this.$refs && this.$refs.navMenuBtn) {
this.navMenuBtnRect = this.$refs.navMenuBtn.getBoundingClientRect();
}
},
/**
* 鼠标进入导航菜单按钮处理
*/
handleNavMenuBtnMouseEnter() {
this.$refs["cmp-funcMenu"].close();
if (this.$refs.navmenu) {
this.$refs.navmenu.show();
}
},
/**
* 鼠标离开导航菜单按钮处理
*/
handleNavMenuBtnMouseLeave() {
// 延迟隐藏导航菜单
setTimeout(() => {
if (this.$refs.navmenu && !this.$refs.navmenu.isMouseOverMenu) {
this.$refs.navmenu.hide();
}
}, 100);
},
/**
* 鼠标进入导航菜单处理
*/
handleNavMenuMouseEnter() {
// 鼠标进入菜单区域,取消隐藏
if (this.$refs.navmenu) {
this.$refs.navmenu.isMouseOverMenu = true;
}
},
/**
* 鼠标离开导航菜单处理
*/
handleNavMenuMouseLeave(e) {
// 鼠标离开菜单区域,检查是否移向.pl-menus-item元素
if (e) {
const relatedTarget = e.relatedTarget || e.toElement;
if (
relatedTarget &&
relatedTarget.closest &&
relatedTarget.closest(".pl-menus-item")
) {
// 如果移向.pl-menus-item元素,保持菜单显示
if (this.$refs.navmenu) {
this.$refs.navmenu.isMouseOverMenu = true;
}
return;
}
}
if (this.$refs.navmenu) {
this.$refs.navmenu.isMouseOverMenu = false;
this.$refs.navmenu.hide();
}
},
/**
* 处理功能菜单点击
* @param {Object} item - 菜单项
* @param {Event} $event - 事件对象
* @param {Boolean} isClose - 是否关闭菜单
*/
handleFuncMenus(item, $event, isClose) {
if (isClose) {
this.$refs["cmp-funcMenu"].close();
return;
}
$event && $event.stopPropagation();
// 处理链接类型菜单
if (item && item.type == "link") {
window.open(item.resUrl, item.name);
return;
}
// 处理带token的URL
var _url = item ? item.url : "";
if (item && (item.type == "widthToken" || item.type == "withToken")) {
_url = item.url + "&access_token=" + this.token;
}
// 处理有URL的菜单项
if (item && item.url) {
item.target == "tab"
? this.addTab({
resUrl: _url,
name: item.name,
resId: item.id,
openType: item.openType,
})
: window.open(_url);
return;
} else {
item && item.handler && item.handler();
}
if (item && item.children && item.children.length) {
this.menuItems = item.children;
this.$refs["cmp-funcMenu"].open($event);
if (item.type == "lang") {
item.children.forEach((__item, index) => {
if (!__item.handler) {
__item.handler = () => {
window.localStorage.setItem(
"locale",
JSON.stringify({
desc: __item.name,
name: __item.key,
})
);
Cookies.set("locale", __item.key);
window.location.reload();
};
}
});
}
}
},
/**
* 处理退出登录
*/
async handleLogout() {
if (window.customSysCofig.logoutBeforeCallback) {
const bool = window.customSysCofig.logoutBeforeCallback();
if (bool) {
return;
}
}
await this.$store.dispatch("user/logout").then(() => {
window.sessionStorage.removeItem("bzid");
if (window.customSysCofig.logoutCallback) {
window.customSysCofig.logoutCallback();
} else {
if (
window.HIVUI_SETTING.isSingleLogin &&
window.HIVUI_SETTING.singleLogoutUrl
) {
location = window.HIVUI_SETTING.singleLogoutUrl;
} else {
location = window.HIVUI_SETTING.loginUrl;
}
}
});
},
},
mounted() {
this.updateNavMenuBtnRect();
this.$store.dispatch("app/getSysConfig");
this.sysFuncMenus.filter((item) => {
if (item.type == "withNumTips" && item.numFunc) {
const intervalFunc = () => {
item
.numFunc()
.then((res) => {
this.msgCount = res.num;
if (res.num && !item.hideNotific) {
window.SysPage.notificationFunc(
this.$t("hivuiMain_header_notification_title"),
item.messageText(res),
() => {
this.handleFuncMenus(item);
}
);
}
})
.catch((err) => {
clearInterval(this.numTipTimer);
});
};
clearInterval(this.numTipTimer);
this.numTipTimer = setInterval(
intervalFunc,
item.numIntervalTime || 1000 * 30
);
intervalFunc();
}
});
},
};
</script>
<style lang="less" scoped>
.pl-concise-sidebar {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 36px;
background: rgba(235, 241, 247, 1);
border-right: 1px solid rgba(235, 241, 247, 1);
// 重置默认样式
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
&.showFuncMenu {
.menus {
margin-top: 40px;
}
}
.menus {
flex: 1;
padding: 8px 4px;
display: flex;
flex-direction: column;
justify-content: space-between;
.area {
display: flex;
flex-direction: column;
gap: 8px;
color: #4e5969;
li {
cursor: pointer;
line-height: 30px;
background-color: transparent;
transition: all 0.3s;
&:hover {
background-color: #c7d5e2;
border-radius: 5px;
}
> div {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.item {
display: flex;
align-items: center;
}
}
}
}
}
</style>