<template>
<div
class="navbar"
:style="{
backgroundColor:
sideTheme === 'theme-dark'
? variables.menuBackground
: variables.headerBackgroundColor,
width: settingsStore.topNav ? '' : '600px',
}"
>
<!-- width:showSearchs? '745px' : '300px' -->
<!-- <hamburge
id="hamburger-container"
:is-active="appStore.sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/> -->
<!-- <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" /> -->
<top-nav
id="topmenu-container"
class="topmenu-container"
v-if="settingsStore.topNav"
/>
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<div class="inputDemo notice-box">
<el-dropdown
trigger="click"
@command="updateBubgPermissions"
:max-height="300"
>
<span class="el-dropdown-link">
<span class="dept_text">
{{ bubg }}
</span>
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(item, index) in bubgList"
:key="index"
:command="item"
:disabled="item.disabled"
>{{ item.label }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- <el-switch
v-if="isAdmin"
v-model="isShould"
@change="switchChange"
class="ml-2"
inline-prompt
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
active-text="V1"
inactive-text="V2"
/> -->
<el-badge is-dot class="item" :hidden="reddotsIndicateMessage">
<el-icon class="icon_message" @click="openMessage"><Bell /></el-icon>
</el-badge>
<header-search
id="header-search"
class="right-menu-item"
@showSearch="showSearch"
/>
<!-- <screenfull id="screenfull" class="right-menu-item hover-effect" /> -->
<!-- <el-button @click="openAiBtn">Ai</el-button> -->
<!-- <span
><img
@click="openAiBtn"
style="width: 32px; cursor: pointer"
src="../../assets/images/AItubiao.png"
alt=""
/></span> -->
<LangSelect @selectCallback="langSelectCallback" />
<!-- <el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip> -->
</template>
<div class="avatar-container">
<el-dropdown
@command="handleCommand"
class="right-menu-item hover-effect"
trigger="click"
>
<div class="avatar-wrapper">
<img
v-if="userStore.avatar"
:src="userStore.avatar"
class="user-avatar"
/>
<img v-else src="@/assets/images/profile.jpg" class="user-avatar" />
<!-- <el-icon><caret-bottom /></el-icon> -->
<div class="userName">{{ userStore.nickName }}</div>
</div>
<template #dropdown>
<el-dropdown-menu>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item command="changeSys">
<span>切换后台</span>
</el-dropdown-item>
<el-dropdown-item command="setLayout">
<span>布局设置</span>
</el-dropdown-item>
<a data-v-bd143a29="" href="https://hiway.feishu.cn/wiki/WgiDw97BmiNYHKkEBZvclRE5nce" class="">
<li data-el-collection-item="" aria-disabled="false" class="el-dropdown-menu__item" tabindex="-1"
role="menuitem"> 更新日志
</li>
</a>
<el-dropdown-item v-if="!isFeishu()" divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
<el-dropdown-item v-else divided command="logoutFeishu">
<span>重新进入</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- <p class="user_name">{{ userStore.nickName }}</p> -->
</div>
<el-drawer v-model="messageVisi" title="消息列表" size="60%">
<MessageDrawer
v-if="messageVisi"
ref="messageDrawer"
@messageCount="getMessageCount"
/>
</el-drawer>
<el-dialog
ref="aiContainer"
class="ai-contaoner"
v-model="aiDialogVisible"
title="向 Ai 请教"
width="45%"
:before-close="handleClose"
>
<!-- <template #header="{ close, titleId, titleClass }">
<div class="my-header">
<div>向 Ai 请教</div>
</div>
</template> -->
<AiMessage @scrollToBottom="scrollToBottomCak"></AiMessage>
<!-- <template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="aiDialogVisible = false">
取消
</el-button>
</div>
</template> -->
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, onUnmounted, computed } from 'vue'
import { messages, connect, disconnect } from './websocket-client' // 引入WebSocket客户端代码
import AiMessage from './aiMessage.vue'
import {
ElNotification,
ElMessageBox,
ElMessage,
ElLoading,
} from 'element-plus'
import MessageDrawer from '@/components/messageDrawer'
import { getUserNotifyCount } from '@/api/bpm/notify'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
// import Hamburger from "@/components/Hamburger";
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch'
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
import variables from '@/assets/styles/variables.module.scss'
import LangSelect from '@/components/LangSelect'
import { getDeptByBuDeptTreeselect } from '@/api/login'
import useTagsViewStore from '@/store/modules/tagsView'
const { proxy } = getCurrentInstance()
const router = useRouter()
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
const showSearchs = ref(false)
const bubg = ref(null)
const bubgList = ref([])
const route = useRoute()
const sideTheme = computed(() => settingsStore.sideTheme)
let messageVisi = ref(false)
const aiDialogVisible = ref(false)
let reddotsIndicateMessage = ref(true)
const visitedViews = computed(() => useTagsViewStore().visitedViews)
const isShould = ref(false)
const aiContainer = ref()
const isShoulds = computed(() => {
const flag = localStorage.getItem('isShould')
return flag
})
watch(
() => isShoulds,
(nval, oval) => {
if (nval.value === '2') {
isShould.value = false
} else {
isShould.value = true
}
},
{ immediate: true }
)
const isAdmin = computed(() => {
if (userStore.userName === 'admin') {
return true
} else {
return false
}
})
function toggleSideBar() {
appStore.toggleSideBar()
}
function handleCommand(command) {
switch (command) {
case 'changeSys':
window.open(`${window.location.origin}/qms-base-web/index`, '_self')
break
case 'setLayout':
setLayout()
break
case 'logout':
logout()
break
case 'logoutFeishu':
logoutFeishu()
break
default:
break
}
}
// 国际化回调
function langSelectCallback() {
// 头部信息
// 强制刷新子组件
this.$emit('selectCallback')
}
function scrollToBottomCak() {
var elDialogBody = document.querySelector('.el-dialog__body')
elDialogBody.scrollTop = elDialogBody.scrollHeight
}
function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
userStore.LogOut().then(() => {
location.href = '/qmsMain/loginDefault?v=' + new Date().getTime()
})
})
.catch(() => {})
}
function logoutFeishu() {
ElMessageBox.confirm('确定重新登入系统么?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
userStore.LogOut().then(() => {
location.href = '/qmsMain/login'
})
})
.catch(() => {})
}
function openMessage() {
messageVisi.value = true
}
function handleLookClick() {}
const emits = defineEmits(['setLayout'])
function setLayout() {
emits('setLayout')
}
// watch(showSearchs, (value) => {
// showSearchs.value = value
// })
function isFeishu() {
return /Lark|Feishu/i.test(navigator.userAgent)
}
function updateBubgPermissions(e) {
bubg.value = e.label
localStorage.setItem('groupName', e.label)
localStorage.setItem('groupId', e.code)
// this.$store.commit("SET_GROUP_ID", selectedItem.code);
// this.$store.commit("SET_GROUP_NAME", selectedItem.label);
// this.refreshView();
refreshSelectedTag() // 刷新当前页面
// closeAllTags() //关闭所有页面
}
function showSearch(flag) {
showSearchs.value = flag
}
function getBubgData() {
getDeptByBuDeptTreeselect().then((response) => {
let checkList = []
let isAdmin = userStore.userName === 'admin' ? true : false
if (userStore.permissionsDataBubgDtos.length == 0) {
checkList.push(userStore.groupId)
} else {
checkList = userStore.permissionsDataBubgDtos
}
bubgList.value = response.data
bubg.value = userStore.groupName || ''
if (!isAdmin) {
bubgList.value.forEach((bubg) => {
if (checkList == null) {
bubg.disabled = true
} else {
if (checkList.indexOf(bubg.code) == -1) {
bubg.disabled = true
} else {
bubg.disabled = false
}
}
})
} else {
bubgList.value.forEach((bubg) => {
bubg.disabled = false
})
}
bubgList.value = bubgList.value.filter(item => !item.disabled)
})
}
// 获取消息数量
function getMessageCount(count) {
showReddotsIndicateMessage(count)
}
// 控制消息红点是否显示
function showReddotsIndicateMessage(count) {
reddotsIndicateMessage.value = count <= 0
}
function refreshSelectedTag(view) {
if (route.meta.link) {
// useTagsViewStore().delIframeView(route);
proxy.$bus.$emit('refreshIframe', route.path) // 触发刷新当前页面(iframe)
return
}
let pathStr = route.path
// 必须要配置
if (pathStr.includes('bpm')) {
proxy.$bus.$emit('refreshPage-bpm', route.path) // 触发刷新当前页面
} else {
proxy.$tab.refreshPage(route)
}
}
function openAiBtn() {
aiDialogVisible.value = true
}
function switchChange(e) {
if (e) {
localStorage.setItem('isShould', '1')
} else {
localStorage.setItem('isShould', '2')
}
closeAllTags()
}
function toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
router.push(latestView.fullPath)
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
// to reload home page
router.replace({ path: '/redirect' + view.fullPath })
} else {
router.push('/')
}
}
}
function closeAllTags(view) {
const originObj = {
fullPath: '/index',
path: '/index',
name: 'Index',
meta: {
title: '首页',
icon: 'dashboard',
affix: true,
},
title: '首页',
}
proxy.$tab.closeAllPage().then(({ visitedViews }) => {
toLastView(visitedViews, originObj)
})
}
watch(
() => messages.value,
(newMessages) => {
if (newMessages.length > 0) {
const latestMessage = newMessages[newMessages.length - 1]
const message = JSON.parse(latestMessage.text)
// 播放提示音
const audio = new Audio('/sounds/sound.mp3') // 这里放置提示音文件的路径
audio.play()
const notificationInstance = ElNotification({
title: message.title,
message: message.content,
type: message.type.toLowerCase() || 'info',
position: 'top-right', // 设置弹出位置为右上角
duration: 30000, // 30秒
showClose: true, // 显示关闭按钮
onClick: () => {
messageVisi.value = true // 打开消息抽屉
// 延时 2 秒后关闭通知
setTimeout(() => {
notificationInstance.close() // 手动关闭特定的通知实例
}, 2000) // 2000 毫秒 = 2 秒
},
})
}
},
{ deep: true } // 设置深度监听
)
// 在组件挂载时连接 WebSocket
onMounted(() => {
connect()
getUserNotifyCount()
.then((res) => {
showReddotsIndicateMessage(res.data)
})
.catch((err) => {
console.error('Error fetching notifications:', err)
})
})
// 在组件卸载时断开 WebSocket 连接
onUnmounted(() => {
disconnect()
})
getBubgData()
</script>
<style lang="scss" scoped>
.navbar {
height: 34px;
overflow: hidden;
position: relative;
background: #eff2fb !important;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.breadcrumb-container {
float: left;
}
.topmenu-container {
position: absolute;
background-color: #eff2fb;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 34px;
display: flex;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding-right: 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
// color: #fff;
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
.avatar-container {
// margin-right: 40px;
.avatar-wrapper {
// margin-top: 5px;
position: relative;
display: flex;
align-items: center;
height: 100%;
.user-avatar {
cursor: pointer;
width: 25px;
height: 25px;
border-radius: 20px;
}
i {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
}
::v-deep .el-badge__content.is-fixed {
top: 10px;
}
::v-deep .el-badge__content.is-fixed.is-dot {
right: 13px;
}
.icon_message {
height: 100%;
margin: 0px 10px;
cursor: pointer;
}
.header_title {
display: inline-block;
height: 100%;
width: 300px;
line-height: 50px;
padding-left: 10px;
font-family: 'Microsoft YaHei';
// line-height: 1em;
color: #f0f0f0;
font-weight: bold;
font-size: 30px;
text-shadow: 0px 0px 0 rgb(216, 216, 216), 1px 1px 0 rgb(192, 192, 192),
2px 2px 0 rgb(168, 168, 168), 3px 3px 0 rgb(144, 144, 144),
4px 4px 0 rgb(120, 120, 120), 5px 5px 0 rgb(96, 96, 96),
6px 6px 5px rgba(0, 0, 0, 0.35), 6px 6px 1px rgba(0, 0, 0, 0.5),
0px 0px 5px rgba(0, 0, 0, 0.2);
}
.user_name {
display: inline-block;
height: 100%;
line-height: 34px;
margin: 0px;
}
.inputDemo {
display: flex;
align-items: center;
white-space: nowrap;
input {
width: 150px;
font-size: 12px;
border: none;
background: none;
text-align: center;
font-weight: bold;
}
.el-input__suffix {
i {
margin-left: 0.06rem;
font-weight: bold;
}
}
/* WebKit browsers */
::-webkit-input-placeholder {
font-weight: bold;
}
}
::v-deep .ai-contaoner .el-dialog__body {
height: calc(100vh - 180px);
overflow-y: auto;
padding: 0px;
background-color: #292a2d;
// position: relative;
}
::v-deep .el-select .el-input__wrapper {
background-color: #eff2fb !important;
}
::v-deep .ai-contaoner .el-dialog__header {
color: #fff;
margin-right: 0px;
background-image: linear-gradient(
to right,
rgb(96, 62, 132),
rgb(91, 96, 195)
);
}
::v-deep .ai-contaoner .el-dialog__title {
color: #fff;
}
::v-deep .ai-contaoner .el-dialog__close {
color: #fff;
}
.userName {
height: 100%;
width: 80px;
line-height: 35px;
// display: flex;
// align-items: center;
padding-left: 5px;
font-size: 14px;
white-space: nowrap; /* 确保文本在一行内显示 */
overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis; /* 使用打点表示文字溢出 */
}
.el-dropdown-link {
.dept_text {
display: inline-block;
white-space: nowrap;
width: 200px;
text-align: right;
//overflow: hidden; /* 隐藏溢出的内容 */
//text-overflow: ellipsis; /* 使用打点表示文字溢出 */
}
}
@keyframes gradient-blink-animation {
0% {
color: #ff0000; /* 红色 */
}
20% {
color: #ffaa00; /* 橙色 */
}
40% {
color: #ffff00; /* 黄色 */
}
60% {
color: #00ff00; /* 绿色 */
}
80% {
color: #0000ff; /* 蓝色 */
}
100% {
color: #aa00ff; /* 紫色 */
}
}
</style>
dept_text下拉选择自适应宽度