/** * 角色 / 会话 / TabBar 配置 * ------------------------------------------------------------ * 双角色设计核心: * - 救援人员端(rescuer):首页(日常学习) / 事件(执行态) / 我的 —— 按「任务状态」分 Tab * - 仓管人员端(keeper):扫码 / 仓库 / 设备 / 消息 / 我 —— 按「工作对象」分 Tab * 不同角色的 TabBar 与可用操作页面由本文件统一裁决,页面内不要硬编码角色判断。 */ import { reactive } from 'vue' import { xStore } from "@/uni_modules/tmx-ui/index.uts" import { TABBAR_ITEM_INFO } from "@/uni_modules/tmx-ui/interface.uts" import { rescuerUser, keeperUser } from "@/mock/index.uts" import { HrRole, HrUser } from "@/mock/types.uts" import { hrUnread, badgeText } from "@/utils/unread.uts" /** 当前登录会话 */ export type HrSession = { /** 角色 */ role : HrRole /** 姓名 */ name : string /** 头像文字 */ avatar : string /** 职称 */ title : string /** 科室 */ dept : string /** 工号 */ jobNo : string /** 角色中文名 */ roleLabel : string /** 能力标签 */ tags : string[] /** 累计学时 */ hours : number /** 完成课程 */ courseCount : number /** 有效证书 */ certCount : number /** 是否已登录 */ logged : boolean } const guest : HrSession = { role: 'rescuer', name: '未登录', avatar: '医', title: '', dept: '', jobNo: '', roleLabel: '', tags: [] as string[], hours: 0, courseCount: 0, certCount: 0, logged: false } as HrSession /** 全局会话(页面统一从这里取用户信息) */ export const hrSession = reactive(guest as HrSession) /** 按角色登录 */ export function loginAs(role : HrRole) : void { const u : HrUser = role == 'keeper' ? keeperUser : rescuerUser hrSession.role = u.role hrSession.name = u.name hrSession.avatar = u.avatar hrSession.title = u.title hrSession.dept = u.dept hrSession.jobNo = u.jobNo hrSession.roleLabel = u.roleLabel hrSession.tags = u.tags hrSession.hours = u.hours hrSession.courseCount = u.courseCount hrSession.certCount = u.certCount hrSession.logged = true } /** 退出登录 */ export function logout() : void { hrSession.role = 'rescuer' hrSession.name = '未登录' hrSession.avatar = '医' hrSession.logged = false } /* ============================ TabBar ============================ */ /** * 救援人员端 3 Tab:首页 / 事件 / 我的 * 事件 Tab 的角标取自全局未读数(查看事件列表后清零 → 角标隐藏)。 */ export function rescuerTabs() : TABBAR_ITEM_INFO[] { return [ { title: '首页', icon: 'home-3-line', selectedIcon: 'home-3-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO, { title: '事件', icon: 'alarm-warning-line', selectedIcon: 'alarm-warning-fill', color: '#9CA3AF', selectedColor: '#2563EB', dotLabel: badgeText(hrUnread.events) } as TABBAR_ITEM_INFO, { title: '我的', icon: 'user-3-line', selectedIcon: 'user-3-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO ] } /** 仓管人员端 5 Tab:扫码 / 仓库 / 设备 / 消息 / 我 */ export function keeperTabs() : TABBAR_ITEM_INFO[] { return [ { title: '扫码', icon: 'qr-scan-line', selectedIcon: 'qr-scan-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO, { title: '仓库', icon: 'archive-line', selectedIcon: 'archive-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO, { title: '设备', icon: 'settings-3-line', selectedIcon: 'settings-3-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO, { title: '消息', icon: 'mail-line', selectedIcon: 'mail-fill', color: '#9CA3AF', selectedColor: '#2563EB', dotLabel: badgeText(hrUnread.messages) } as TABBAR_ITEM_INFO, { title: '我', icon: 'user-3-line', selectedIcon: 'user-3-fill', color: '#9CA3AF', selectedColor: '#2563EB' } as TABBAR_ITEM_INFO ] } /** * 取当前角色的 TabBar 项 * 每次调用重新构造数组 → 未读数变化时调用方的 computed 会重新求值(角标随之隐藏)。 */ export function currentTabs() : TABBAR_ITEM_INFO[] { return hrSession.role == 'keeper' ? keeperTabs() : rescuerTabs() } /** 当前角色 Tab 对应的页面路径(顺序与 currentTabs 一致) */ export function currentTabPages() : string[] { if (hrSession.role == 'keeper') { return [ '/pages/warehouse/scan', '/pages/warehouse/stock', '/pages/warehouse/devices', '/pages/message/index', '/pages/mine/index' ] } return [ '/pages/home/index', '/pages/event/index', '/pages/mine/index' ] } /** 同步 TabBar 配置进 tmx-ui 全局状态(x-tabbar 会读取) */ export function syncTabbar(activeIndex : number) : void { xStore.xTabbarConfig.list = currentTabs() xStore.xTabbarConfig.tabbarActiveIndex = activeIndex } function rescueHas(arr : string[], p : string) : boolean { let hit = false for (let i = 0; i < arr.length; i++) { if (arr[i] == p) { hit = true; break } } return hit } function keeperHas(arr : string[], p : string) : boolean { let hit = false for (let i = 0; i < arr.length; i++) { if (arr[i] == p) { hit = true; break } } return hit } /** * 某个角色是否可访问某页面 * 用于「可用操作页面的区分」:仓管端不出现检伤/事件工作台,救援端不出现出入库。 */ export function canAccess(path : string) : boolean { const keeperOnly : string[] = ['/pages/warehouse/scan', '/pages/warehouse/stock', '/pages/warehouse/goods-detail', '/pages/warehouse/devices', '/pages/warehouse/device-detail', '/pages/message/index'] const rescuerOnly : string[] = ['/pages/home/index', '/pages/event/index', '/pages/event/detail', '/pages/triage/victims', '/pages/triage/create', '/pages/triage/detail', '/pages/triage/batch-treat', '/pages/triage/batch-transfer', '/pages/triage/transfer', '/pages/triage/treat-records', '/pages/triage/offline-sync', '/pages/course/library', '/pages/course/subject', '/pages/course/media', '/pages/course/practice', '/pages/course/quiz', '/pages/course/answer-card', '/pages/study/exams', '/pages/study/certificates', '/pages/study/wrong-book', '/pages/study/center', '/pages/message/list', '/pages/message/contacts'] const p = path.split('?')[0] if (hrSession.role == 'keeper') { return rescueHas(rescuerOnly, p) ? false : true } return keeperHas(keeperOnly, p) ? false : true }