feat: 添加管理端前端 (HMS 基座 React 管理面板)
- 从 HMS 基座复制 apps/web/ (React + Ant Design + Vite + TypeScript) - 管理端自动代理 API 到 localhost:3000 (vite.config.ts) - 更新 scripts/dev.sh 支持三端启动: backend/admin/app - 登录验证通过, 用户管理/角色权限/审计日志等页面正常 - 添加 .gitignore 排除 node_modules/dist
This commit is contained in:
85
apps/web/src/constants/health.test.ts
Normal file
85
apps/web/src/constants/health.test.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
GENDER_OPTIONS,
|
||||
BLOOD_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
} from './health'
|
||||
|
||||
describe('GENDER_OPTIONS', () => {
|
||||
it('should be an array with 3 items', () => {
|
||||
expect(GENDER_OPTIONS).toBeInstanceOf(Array)
|
||||
expect(GENDER_OPTIONS).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('each item should have value and label string properties', () => {
|
||||
for (const opt of GENDER_OPTIONS) {
|
||||
expect(opt).toHaveProperty('value')
|
||||
expect(opt).toHaveProperty('label')
|
||||
expect(typeof opt.value).toBe('string')
|
||||
expect(typeof opt.label).toBe('string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should contain expected gender values', () => {
|
||||
const values = GENDER_OPTIONS.map((o) => o.value)
|
||||
expect(values).toEqual(['male', 'female', 'other'])
|
||||
})
|
||||
|
||||
it('should contain expected Chinese labels', () => {
|
||||
const labels = GENDER_OPTIONS.map((o) => o.label)
|
||||
expect(labels).toEqual(['男', '女', '其他'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('BLOOD_TYPE_OPTIONS', () => {
|
||||
it('should be an array with 4 items', () => {
|
||||
expect(BLOOD_TYPE_OPTIONS).toBeInstanceOf(Array)
|
||||
expect(BLOOD_TYPE_OPTIONS).toHaveLength(4)
|
||||
})
|
||||
|
||||
it('each item should have value and label string properties', () => {
|
||||
for (const opt of BLOOD_TYPE_OPTIONS) {
|
||||
expect(opt).toHaveProperty('value')
|
||||
expect(opt).toHaveProperty('label')
|
||||
expect(typeof opt.value).toBe('string')
|
||||
expect(typeof opt.label).toBe('string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should contain expected blood type values', () => {
|
||||
const values = BLOOD_TYPE_OPTIONS.map((o) => o.value)
|
||||
expect(values).toEqual(['A', 'B', 'AB', 'O'])
|
||||
})
|
||||
|
||||
it('should contain expected Chinese labels', () => {
|
||||
const labels = BLOOD_TYPE_OPTIONS.map((o) => o.label)
|
||||
expect(labels).toEqual(['A 型', 'B 型', 'AB 型', 'O 型'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('STATUS_OPTIONS', () => {
|
||||
it('should be an array with 4 items', () => {
|
||||
expect(STATUS_OPTIONS).toBeInstanceOf(Array)
|
||||
expect(STATUS_OPTIONS).toHaveLength(4)
|
||||
})
|
||||
|
||||
it('each item should have value and label string properties', () => {
|
||||
for (const opt of STATUS_OPTIONS) {
|
||||
expect(opt).toHaveProperty('value')
|
||||
expect(opt).toHaveProperty('label')
|
||||
expect(typeof opt.value).toBe('string')
|
||||
expect(typeof opt.label).toBe('string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should include an empty-string value for "all statuses" filter', () => {
|
||||
const allOption = STATUS_OPTIONS.find((o) => o.value === '')
|
||||
expect(allOption).toBeDefined()
|
||||
expect(allOption!.label).toBe('全部状态')
|
||||
})
|
||||
|
||||
it('should contain expected status values', () => {
|
||||
const values = STATUS_OPTIONS.map((o) => o.value)
|
||||
expect(values).toEqual(['', 'active', 'inactive', 'deceased'])
|
||||
})
|
||||
})
|
||||
200
apps/web/src/constants/health.ts
Normal file
200
apps/web/src/constants/health.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 健康管理模块共享常量
|
||||
*
|
||||
* 集中定义性别、血型、患者状态、严重度、告警状态、设备类型等映射,
|
||||
* 供各健康模块页面复用。避免在组件中重复定义。
|
||||
*/
|
||||
|
||||
// --- 性别 ---
|
||||
export const GENDER_OPTIONS = [
|
||||
{ value: "male", label: "男" },
|
||||
{ value: "female", label: "女" },
|
||||
{ value: "other", label: "其他" },
|
||||
];
|
||||
|
||||
export const GENDER_LABEL: Record<string, string> = {
|
||||
male: "男",
|
||||
female: "女",
|
||||
other: "其他",
|
||||
};
|
||||
|
||||
// --- 血型 ---
|
||||
export const BLOOD_TYPE_OPTIONS = [
|
||||
{ value: "A", label: "A 型" },
|
||||
{ value: "B", label: "B 型" },
|
||||
{ value: "AB", label: "AB 型" },
|
||||
{ value: "O", label: "O 型" },
|
||||
];
|
||||
|
||||
// --- 患者状态 ---
|
||||
export const STATUS_OPTIONS = [
|
||||
{ value: "", label: "全部状态" },
|
||||
{ value: "active", label: "活跃" },
|
||||
{ value: "inactive", label: "停用" },
|
||||
{ value: "deceased", label: "已故" },
|
||||
];
|
||||
|
||||
// --- 严重度(统一 5 处重复定义: AlertDashboard, AlertList, AlertRuleList, DoctorDashboard) ---
|
||||
export const SEVERITY_COLOR: Record<string, string> = {
|
||||
info: "default",
|
||||
warning: "orange",
|
||||
critical: "red",
|
||||
urgent: "magenta",
|
||||
high: "red",
|
||||
medium: "orange",
|
||||
};
|
||||
|
||||
export const SEVERITY_LABEL: Record<string, string> = {
|
||||
info: "提示",
|
||||
warning: "警告",
|
||||
critical: "严重",
|
||||
urgent: "紧急",
|
||||
high: "严重",
|
||||
medium: "中等",
|
||||
};
|
||||
|
||||
export const SEVERITY_OPTIONS = [
|
||||
{ value: "info", label: "提示" },
|
||||
{ value: "warning", label: "警告" },
|
||||
{ value: "medium", label: "中等" },
|
||||
{ value: "critical", label: "严重" },
|
||||
{ value: "high", label: "严重" },
|
||||
{ value: "urgent", label: "紧急" },
|
||||
];
|
||||
|
||||
// --- 告警状态(统一 3 处: AlertDashboard, AlertList) ---
|
||||
export const ALERT_STATUS_COLOR: Record<string, string> = {
|
||||
pending: "orange",
|
||||
active: "gold",
|
||||
acknowledged: "blue",
|
||||
resolved: "green",
|
||||
dismissed: "default",
|
||||
};
|
||||
|
||||
export const ALERT_STATUS_LABEL: Record<string, string> = {
|
||||
pending: "待处理",
|
||||
active: "活跃",
|
||||
acknowledged: "已确认",
|
||||
resolved: "已恢复",
|
||||
dismissed: "已忽略",
|
||||
};
|
||||
|
||||
export const ALERT_STATUS_OPTIONS = [
|
||||
{ value: "", label: "全部状态" },
|
||||
{ value: "active", label: "活跃" },
|
||||
{ value: "pending", label: "待处理" },
|
||||
{ value: "acknowledged", label: "已确认" },
|
||||
{ value: "resolved", label: "已恢复" },
|
||||
{ value: "dismissed", label: "已忽略" },
|
||||
];
|
||||
|
||||
// --- 设备类型(统一 3 处: DeviceManage, DeviceReadingsTab, AlertRuleList) ---
|
||||
export const DEVICE_TYPE_OPTIONS = [
|
||||
{ value: "blood_pressure", label: "血压" },
|
||||
{ value: "blood_glucose", label: "血糖" },
|
||||
{ value: "heart_rate", label: "心率" },
|
||||
{ value: "blood_oxygen", label: "血氧" },
|
||||
{ value: "temperature", label: "体温" },
|
||||
{ value: "steps", label: "步数" },
|
||||
{ value: "sleep", label: "睡眠" },
|
||||
{ value: "stress", label: "压力" },
|
||||
];
|
||||
|
||||
export const DEVICE_TYPE_COLOR: Record<string, string> = {
|
||||
blood_pressure: "red",
|
||||
blood_glucose: "purple",
|
||||
heart_rate: "volcano",
|
||||
blood_oxygen: "blue",
|
||||
temperature: "orange",
|
||||
steps: "green",
|
||||
sleep: "cyan",
|
||||
stress: "geekblue",
|
||||
};
|
||||
|
||||
// --- 告警规则条件类型 ---
|
||||
export const CONDITION_TYPE_OPTIONS = [
|
||||
{ value: "single_threshold", label: "单次阈值" },
|
||||
{ value: "consecutive", label: "连续触发" },
|
||||
{ value: "trend", label: "趋势变化" },
|
||||
];
|
||||
|
||||
// --- 设备连接状态 ---
|
||||
export const DEVICE_STATUS_OPTIONS = [
|
||||
{ value: "", label: "全部状态" },
|
||||
{ value: "online", label: "在线" },
|
||||
{ value: "offline", label: "离线" },
|
||||
{ value: "paired", label: "已配对" },
|
||||
{ value: "error", label: "异常" },
|
||||
];
|
||||
|
||||
export const DEVICE_STATUS_COLOR: Record<string, string> = {
|
||||
online: "green",
|
||||
offline: "default",
|
||||
paired: "blue",
|
||||
error: "red",
|
||||
};
|
||||
|
||||
// --- 设备连接类型 ---
|
||||
export const CONNECTION_TYPE_OPTIONS = [
|
||||
{ value: "ble", label: "蓝牙" },
|
||||
{ value: "gateway", label: "网关" },
|
||||
{ value: "manual", label: "手动录入" },
|
||||
];
|
||||
|
||||
// --- 实时监控卡片指标 ---
|
||||
export const VITAL_CARD_METRICS = [
|
||||
{ key: "heart_rate", label: "心率", unit: "bpm", color: "#ff4d4f" },
|
||||
{ key: "blood_oxygen", label: "血氧", unit: "%", color: "#1890ff" },
|
||||
{ key: "blood_pressure", label: "血压", unit: "mmHg", color: "#f5222d" },
|
||||
{ key: "blood_glucose", label: "血糖", unit: "mg/dL", color: "#722ed1" },
|
||||
{ key: "temperature", label: "体温", unit: "°C", color: "#fa8c16" },
|
||||
{ key: "steps", label: "步数", unit: "步", color: "#52c41a" },
|
||||
] as const;
|
||||
|
||||
// --- 告警标题中英文映射 ---
|
||||
export const ALERT_TITLE_MAP: Record<string, string> = {
|
||||
"BP Critical High": "血压严重偏高",
|
||||
"BP Critical Low": "血压严重偏低",
|
||||
"Heart Rate Abnormal": "心率异常",
|
||||
"Blood Sugar Elevated": "血糖偏高",
|
||||
"Blood Sugar Critical": "血糖危急值",
|
||||
"Blood Sugar Low": "血糖偏低",
|
||||
"Weight Gain Alert": "体重增长异常",
|
||||
"Missed Medication": "漏服药物",
|
||||
"SpO2 Low": "血氧偏低",
|
||||
"Temperature High": "体温偏高",
|
||||
"Temperature Low": "体温偏低",
|
||||
"BP Trending High": "血压趋势偏高",
|
||||
"BP Trending Low": "血压趋势偏低",
|
||||
"Heart Rate High": "心率偏高",
|
||||
"Heart Rate Low": "心率偏低",
|
||||
};
|
||||
|
||||
/** 翻译告警标题:优先精确匹配,其次回退原文 */
|
||||
export function translateAlertTitle(title: string): string {
|
||||
return ALERT_TITLE_MAP[title] ?? title;
|
||||
}
|
||||
|
||||
// --- 通用状态标签(StatusTag 组件统一引用) ---
|
||||
export const STATUS_TAG_CONFIG: Record<
|
||||
string,
|
||||
{ color: string; label: string }
|
||||
> = {
|
||||
// 预约状态
|
||||
pending: { color: "gold", label: "待确认" },
|
||||
confirmed: { color: "blue", label: "已确认" },
|
||||
completed: { color: "green", label: "已完成" },
|
||||
cancelled: { color: "default", label: "已取消" },
|
||||
no_show: { color: "red", label: "未到诊" },
|
||||
// 随访状态
|
||||
overdue: { color: "red", label: "逾期" },
|
||||
in_progress: { color: "processing", label: "进行中" },
|
||||
// 咨询状态
|
||||
waiting: { color: "gold", label: "等待中" },
|
||||
active: { color: "green", label: "进行中" },
|
||||
closed: { color: "default", label: "已关闭" },
|
||||
// 患者状态
|
||||
inactive: { color: "default", label: "停用" },
|
||||
deceased: { color: "default", label: "已故" },
|
||||
verified: { color: "green", label: "已认证" },
|
||||
};
|
||||
Reference in New Issue
Block a user