This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
/**
* 角色 / 会话 / 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
}