diff --git a/openhis-ui-vue3/src/layout/components/HeaderNotice/index.vue b/openhis-ui-vue3/src/layout/components/HeaderNotice/index.vue index 9bc5172c1..64d42b0f3 100644 --- a/openhis-ui-vue3/src/layout/components/HeaderNotice/index.vue +++ b/openhis-ui-vue3/src/layout/components/HeaderNotice/index.vue @@ -1,5 +1,5 @@ - -
+
navType, val => { if (val.value == 1) { + // 纯左侧菜单 appStore.sidebar.opened = true appStore.toggleSideBarHide(false) + permissionStore.setSidebarRouters(permissionStore.defaultRoutes) } if (val.value == 2) { + // 混合菜单:顶部显示一级菜单,左侧显示子菜单 appStore.sidebar.opened = true + appStore.toggleSideBarHide(false) + permissionStore.setSidebarRouters(permissionStore.defaultRoutes) } if (val.value == 3) { + // 纯顶部菜单:隐藏侧边栏 appStore.sidebar.opened = false appStore.toggleSideBarHide(true) - } - if ([1, 3].includes(val.value)) { - permissionStore.setSidebarRouters(permissionStore.defaultRoutes) + permissionStore.setSidebarRouters(permissionStore.defaultRoutes) } }, { immediate: true, deep: true } ) diff --git a/openhis-ui-vue3/src/layout/components/index.js b/openhis-ui-vue3/src/layout/components/index.js index fd5773137..943baca72 100755 --- a/openhis-ui-vue3/src/layout/components/index.js +++ b/openhis-ui-vue3/src/layout/components/index.js @@ -2,3 +2,4 @@ export { default as AppMain } from './AppMain' export { default as Navbar } from './Navbar' export { default as Settings } from './Settings' export { default as TagsView } from './TagsView/index.vue' +export { default as TopBar } from './TopBar' diff --git a/openhis-ui-vue3/src/layout/index.vue b/openhis-ui-vue3/src/layout/index.vue index 4fed45dda..fa8e472cf 100755 --- a/openhis-ui-vue3/src/layout/index.vue +++ b/openhis-ui-vue3/src/layout/index.vue @@ -7,11 +7,13 @@ @click="handleClickOutside" /> - +
+ +
import {useWindowSize} from '@vueuse/core'; import Sidebar from './components/Sidebar/index.vue'; -import {AppMain, Settings, TagsView, Navbar} from './components'; +import {AppMain, Settings, TagsView, Navbar, TopBar} from './components'; import NoticePopup from '@/components/NoticePopup/index.vue'; import Copyright from './components/Copyright/index.vue'; @@ -54,6 +56,7 @@ const sidebar = computed(() => useAppStore().sidebar); const device = computed(() => useAppStore().device); const needTagsView = computed(() => settingsStore.tagsView); const fixedHeader = computed(() => settingsStore.fixedHeader); +const topNav = computed(() => settingsStore.topNav); const { width } = useWindowSize(); const WIDTH = 992; // refer to Bootstrap's responsive design diff --git a/openhis-ui-vue3/src/permission.js b/openhis-ui-vue3/src/permission.js index 14b53c78a..870b2f904 100755 --- a/openhis-ui-vue3/src/permission.js +++ b/openhis-ui-vue3/src/permission.js @@ -9,6 +9,7 @@ import useUserStore from '@/store/modules/user' import useLockStore from '@/store/modules/lock' import useSettingsStore from '@/store/modules/settings' import usePermissionStore from '@/store/modules/permission' +import useNoticeStore from '@/store/modules/notice' // 全局变量,用于控制公告弹窗只显示一次 let hasShownNoticePopup = false @@ -54,6 +55,7 @@ router.beforeEach(async (to, from) => { } } }) + useNoticeStore().startPolling() return { ...to, replace: true } } catch (err) { console.error('路由加载失败:', err) diff --git a/openhis-ui-vue3/src/store/modules/notice.js b/openhis-ui-vue3/src/store/modules/notice.js index 08ed51754..37cfe5f08 100644 --- a/openhis-ui-vue3/src/store/modules/notice.js +++ b/openhis-ui-vue3/src/store/modules/notice.js @@ -1,11 +1,15 @@ import { getUnreadCount, getUserNotices, markAsRead, markAllAsRead, listNoticeTop } from '@/api/system/notice' +import { ElNotification } from 'element-plus' + +let pollingTimer = null const useNoticeStore = defineStore('notice', { state: () => ({ unreadCount: 0, noticeList: [], readIds: new Set(), - loaded: false + loaded: false, + lastUnreadCount: -1 }), getters: { @@ -21,6 +25,82 @@ const useNoticeStore = defineStore('notice', { }, actions: { + // 启动轮询(登录后调用) + startPolling() { + this.stopPolling() + // 立即拉一次 + this.fetchNotices() + // 每 30 秒轮询未读数 + pollingTimer = setInterval(() => { + this.checkNewNotices() + }, 30000) + }, + + // 停止轮询(退出登录时调用) + stopPolling() { + if (pollingTimer) { + clearInterval(pollingTimer) + pollingTimer = null + } + }, + + // 检查是否有新通知 + async checkNewNotices() { + try { + const res = await getUnreadCount() + const newCount = res.data || 0 + // 首次记录 + if (this.lastUnreadCount === -1) { + this.lastUnreadCount = newCount + this.unreadCount = newCount + return + } + // 未读数增加 → 有新通知 + if (newCount > this.lastUnreadCount) { + this.unreadCount = newCount + this.lastUnreadCount = newCount + // 刷新列表 + await this.fetchNotices(true) + // 弹出浏览器通知 + const diff = newCount - this.lastUnreadCount + (newCount - this.lastUnreadCount) + this.showNewNoticeToast() + } else { + this.unreadCount = newCount + this.lastUnreadCount = newCount + } + } catch (e) { + // ignore + } + }, + + // 新消息提示 + showNewNoticeToast() { + try { + ElNotification({ + title: '新消息', + message: '您有新的通知/公告,请查看消息中心', + type: 'info', + duration: 4000, + position: 'top-right' + }) + } catch (e) { + // ignore + } + }, + + // 获取通知列表 + async fetchNotices(silent = false) { + try { + const res = await getUserNotices() + this.noticeList = res.data || [] + this.loaded = true + this.unreadCount = this.noticeList.filter(n => !n.readFlag || n.readFlag === '0').length + this.lastUnreadCount = this.unreadCount + } catch (e) { + if (!silent) console.error('获取通知列表失败:', e) + } + }, + // 获取未读数量 async fetchUnreadCount() { try { @@ -31,42 +111,18 @@ const useNoticeStore = defineStore('notice', { } }, - // 获取通知列表 - async fetchNotices() { - try { - const res = await getUserNotices() - this.noticeList = res.data || [] - this.loaded = true - // 计算未读数 - this.unreadCount = this.noticeList.filter(n => !n.readFlag || n.readFlag === '0').length - } catch (e) { - // ignore - } - }, - - // 获取顶部通知列表 - async fetchTopNotices(query) { - try { - const res = await listNoticeTop(query) - this.noticeList = res.data || [] - this.loaded = true - } catch (e) { - // ignore - } - }, - // 标记单条已读 async markRead(noticeId) { try { await markAsRead(noticeId) this.readIds.add(noticeId) - // 更新列表中的已读状态 const item = this.noticeList.find(n => n.noticeId === noticeId) if (item) { item.isRead = true item.readFlag = '1' } this.unreadCount = Math.max(0, this.unreadCount - 1) + this.lastUnreadCount = this.unreadCount } catch (e) { // ignore } @@ -86,6 +142,7 @@ const useNoticeStore = defineStore('notice', { n.readFlag = '1' }) this.unreadCount = 0 + this.lastUnreadCount = 0 } } catch (e) { // ignore @@ -94,10 +151,12 @@ const useNoticeStore = defineStore('notice', { // 重置状态 reset() { + this.stopPolling() this.unreadCount = 0 this.noticeList = [] this.readIds = new Set() this.loaded = false + this.lastUnreadCount = -1 } } })