feat(msg): SSE 实时通知体系 (#1-#6) + dict 接口权限分层

- BizNotifyService 门面: 5 个语义方法 (expertAuditResult / planAuditResult /
  meetingInvitation / agreementAwaitingSign / projectAssignedToExecutor)
- #3 项目分配执行方: 跨调用对比旧 (sessions, amount) 差集去重
- #5 会议邀请: BizMeetingController.add 全量通知, edit 走 userIds 差集去重
- #6 协议待签: updateLaborProtocol 仅在旧 URL 空 → 新 URL 非空时推
- 前端: AdminLayout 全局 bell 角标 + 全局事件总线, doctor/Home 实时刷新,
  Messages 全部已读, Login.vue 角色统一跳首页, Toast 弹窗上移到 layout
- dict 权限分层 (读开放 / 写 admin-only): BizDictController + SysDictDataController
  避免 dropdown 403, 但写操作仍需 admin 防止误删
This commit is contained in:
郭庆泰
2026-08-22 15:53:01 +08:00
parent b6ac5b7d41
commit 86e85d02cb
11 changed files with 321 additions and 44 deletions
+13 -4
View File
@@ -286,12 +286,13 @@ async function afterLogin(token, displayName) {
ElMessage.success(`欢迎,${displayName}${userTypes.find(u => u.value === role)?.name || role}`)
// 角色不在角色首页映射里 → 拒绝
if (!roleHome[role]) return router.replace({ name: 'login' })
// 带 redirect 回跳 (401/守卫带过来的原页面), 否则跳角色首页
// 所有角色统一跳门户首页 '/' (业务方 2026-08-22 要求, 各自角色菜单从导航栏进入)
// 带 redirect 回跳 (401/守卫带过来的原页面), 但必须属于当前角色 (否则跳过去被踢回 login)
const redirect = route.query.redirect
if (redirect) {
if (redirect && redirectBelongsToRole(String(redirect), role)) {
return router.replace(String(redirect))
}
router.replace(roleHome[role])
router.replace('/')
}
function switchMode(mode) {
@@ -355,10 +356,18 @@ const roleHome = {
admin: '/admin/workbench',
manager: '/manager/workbench',
doctor: '/doctor/home',
executor: '/executor/meetings',
executor: '/executor/overview',
sponsor: '/sponsor/home',
}
/** redirect 是否属于当前角色: 防止 doctor 拿到 manager 的 redirect 后跳过去被踢回 login ("现在评审专家登录后不跳转" 的根本原因) */
function redirectBelongsToRole(path, role) {
if (!path) return false
// role=doctor, path=/doctor/xxx → true
// role=doctor, path=/manager/xxx → false
return path === roleHome[role] || path.startsWith('/' + role + '/')
}
function onRegister() {
showRoleModal.value = true
registerUserType.value = ''