Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
Internet-hospital
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
yuguo
Internet-hospital
Commits
e15d3424
Commit
e15d3424
authored
Feb 28, 2026
by
yuguo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix
parent
7880e29e
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
316 additions
and
373 deletions
+316
-373
web/fix-gbk.js
web/fix-gbk.js
+0
-285
web/fix-remaining.js
web/fix-remaining.js
+228
-0
web/src/pages/admin/AIConfig/index.tsx
web/src/pages/admin/AIConfig/index.tsx
+15
-15
web/src/pages/admin/Compliance/index.tsx
web/src/pages/admin/Compliance/index.tsx
+22
-22
web/src/pages/admin/Consultations/index.tsx
web/src/pages/admin/Consultations/index.tsx
+1
-1
web/src/pages/admin/Dashboard/index.tsx
web/src/pages/admin/Dashboard/index.tsx
+1
-1
web/src/pages/admin/Doctors/index.tsx
web/src/pages/admin/Doctors/index.tsx
+3
-3
web/src/pages/admin/Patients/index.tsx
web/src/pages/admin/Patients/index.tsx
+1
-1
web/src/pages/admin/Pharmacy/index.tsx
web/src/pages/admin/Pharmacy/index.tsx
+2
-2
web/src/pages/admin/Prescription/index.tsx
web/src/pages/admin/Prescription/index.tsx
+1
-1
web/src/pages/admin/Statistics/index.tsx
web/src/pages/admin/Statistics/index.tsx
+4
-4
web/src/pages/admin/Users/index.tsx
web/src/pages/admin/Users/index.tsx
+3
-3
web/src/pages/doctor/AIAssist/index.tsx
web/src/pages/doctor/AIAssist/index.tsx
+16
-16
web/src/pages/doctor/Certification/index.tsx
web/src/pages/doctor/Certification/index.tsx
+1
-1
web/src/pages/doctor/Consult/index.tsx
web/src/pages/doctor/Consult/index.tsx
+2
-2
web/src/pages/doctor/ConsultRoom/index.tsx
web/src/pages/doctor/ConsultRoom/index.tsx
+2
-2
web/src/pages/doctor/Prescription/index.tsx
web/src/pages/doctor/Prescription/index.tsx
+8
-8
web/src/pages/patient/Doctors/index.tsx
web/src/pages/patient/Doctors/index.tsx
+1
-1
web/src/pages/patient/PreConsult/index.tsx
web/src/pages/patient/PreConsult/index.tsx
+5
-5
No files found.
web/fix-gbk.js
deleted
100644 → 0
View file @
7880e29e
const
fs
=
require
(
'
fs
'
);
const
path
=
require
(
'
path
'
);
function
walk
(
dir
)
{
const
files
=
[];
for
(
const
f
of
fs
.
readdirSync
(
dir
,
{
withFileTypes
:
true
}))
{
const
p
=
path
.
join
(
dir
,
f
.
name
);
if
(
f
.
isDirectory
())
files
.
push
(...
walk
(
p
));
else
if
(
f
.
name
.
endsWith
(
'
.tsx
'
)
||
f
.
name
.
endsWith
(
'
.ts
'
))
files
.
push
(
p
);
}
return
files
;
}
// Complete GBK garbled -> correct Chinese mapping
// These are UTF-8 strings that were read as GBK then written back as UTF-8
const
m
=
{
// ===== Common phrases =====
'
鑾峰彇
'
:
'
获取
'
,
'
澶辫触
'
:
'
失败
'
,
'
鍒楄〃
'
:
'
列表
'
,
'
鎿嶄綔
'
:
'
操作
'
,
'
鎴愬姛
'
:
'
成功
'
,
'
鍒犻櫎
'
:
'
删除
'
,
'
缂栬緫
'
:
'
编辑
'
,
'
娣诲姞
'
:
'
添加
'
,
'
淇濆瓨
'
:
'
保存
'
,
'
纭
'
:
'
确
'
,
'
璇疯緭鍏
'
:
'
请输入
'
,
'
璇烽€夋嫨
'
:
'
请选择
'
,
'
鎼滅储
'
:
'
搜索
'
,
'
鎻愪氦
'
:
'
提交
'
,
'
鍙栨秷
'
:
'
取消
'
,
// ===== Admin Departments =====
'
绉戝鍒楄〃
'
:
'
科室列表
'
,
'
绉戝鍚嶇О
'
:
'
科室名称
'
,
'
绉戝绠$悊
'
:
'
科室管理
'
,
'
绉戝鏇存柊
'
:
'
科室更新
'
,
'
绉戝鍒涘缓
'
:
'
科室创建
'
,
'
娣诲姞绉戝
'
:
'
添加科室
'
,
'
缂栬緫绉戝
'
:
'
编辑科室
'
,
'
鎺掑簭
'
:
'
排序
'
,
'
鍥炬爣
'
:
'
图标
'
,
'
鎺掑簭鍙
'
:
'
排序号
'
,
'
璇疯緭鍏ョ瀹ゅ悕绉
'
:
'
请输入科室名称
'
,
'
璇疯緭鍏moji鍥炬爣
'
:
'
请输入Emoji图标
'
,
'
纭鍒犻櫎
'
:
'
确认删除
'
,
'
纭畾瑕佸垹闄ょ瀹
'
:
'
确定要删除科室
'
,
'
鍚楋紵璇ユ搷浣滀笉鍙仮澶嶃€
'
:
'
吗?该操作不可恢复。
'
,
'
宸插垹闄
'
:
'
已删除
'
,
'
鍒犻櫎澶辫触
'
:
'
删除失败
'
,
'
鑾峰彇绉戝鍒楄〃澶辫触
'
:
'
获取科室列表失败
'
,
'
馃彞
'
:
'
🏥
'
,
// ===== Admin Pharmacy =====
'
鑽搧
'
:
'
药品
'
,
'
鑽搧鍚嶇О
'
:
'
药品名称
'
,
'
鑽搧搴撶鐞
'
:
'
药品库管理
'
,
'
鑽搧鎬绘暟
'
:
'
药品总数
'
,
'
鑽搧宸插垹闄
'
:
'
药品已删除
'
,
'
鑽搧宸叉洿鏂
'
:
'
药品已更新
'
,
'
鑽搧宸叉坊鍔
'
:
'
药品已添加
'
,
'
娣诲姞鑽搧
'
:
'
添加药品
'
,
'
缂栬緫鑽搧
'
:
'
编辑药品
'
,
'
鎼滅储鑽搧
'
:
'
搜索药品
'
,
'
搴撳瓨
'
:
'
库存
'
,
'
搴撳瓨鍏呰冻
'
:
'
库存充足
'
,
'
搴撳瓨棰勮
'
:
'
库存预警
'
,
'
搴撳瓨鏁伴噺
'
:
'
库存数量
'
,
'
搴撳瓨棰勮绾
'
:
'
库存预警线
'
,
'
缂鸿揣
'
:
'
缺货
'
,
'
缂鸿揣鑽搧
'
:
'
缺货药品
'
,
'
閫氱敤鍚
'
:
'
通用名
'
,
'
瑙勬牸
'
:
'
规格
'
,
'
绫诲埆
'
:
'
类别
'
,
'
鍘傚晢
'
:
'
厂商
'
,
'
浠锋牸
'
:
'
价格
'
,
'
鍗曚綅
'
:
'
单位
'
,
'
鍗曚环
'
:
'
单价
'
,
'
鐘舵€
'
:
'
状态
'
,
'
鐘舵€?
'
:
'
状态
'
,
'
鎶楃敓绱
'
:
'
抗生素
'
,
'
瑙g儹闀囩棝
'
:
'
解热镇痛
'
,
'
闄嶇硸鑽
'
:
'
降糖药
'
,
'
闄嶅帇鑽
'
:
'
降压药
'
,
'
娑堝寲绯荤粺
'
:
'
消化系统
'
,
'
蹇冭绠
'
:
'
心血管
'
,
'
鍛煎惛绯荤粺
'
:
'
呼吸系统
'
,
'
缁寸敓绱
'
:
'
维生素
'
,
'
涓垚鑽
'
:
'
中成药
'
,
'
鍏朵粬
'
:
'
其他
'
,
'
纭畾鍒犻櫎璇ヨ嵂鍝侊紵
'
:
'
确定删除该药品?
'
,
'
璇疯緭鍏ヨ嵂鍝佸悕绉
'
:
'
请输入药品名称
'
,
'
璇疯緭鍏ラ€氱敤鍚
'
:
'
请输入通用名
'
,
'
璇疯緭鍏ヨ鏍
'
:
'
请输入规格
'
,
'
閫夋嫨绫诲埆
'
:
'
选择类别
'
,
'
鐢熶骇鍘傚晢
'
:
'
生产厂商
'
,
'
璇疯緭鍏ュ巶鍟
'
:
'
请输入厂商
'
,
'
閫夋嫨鍗曚綅
'
:
'
选择单位
'
,
'
鐩
'
:
'
盒
'
,
'
鐡
'
:
'
瓶
'
,
'
琚
'
:
'
袋
'
,
'
鏀
'
:
'
支
'
,
'
浠锋牸锛堝厓锛
'
:
'
价格(元)
'
,
'
璇疯緭鍏ヤ环鏍
'
:
'
请输入价格
'
,
'
浣庝簬姝ゆ暟閲忛璀
'
:
'
低于此数量预警
'
,
'
鍏呰冻
'
:
'
充足
'
,
'
棰勮
'
:
'
预警
'
,
'
鑽搧淇℃伅
'
:
'
药品信息
'
,
'
褰撳墠鏈
'
:
'
当前有
'
,
'
绉嶈嵂鍝佸簱瀛樹笉瓒筹紝
'
:
'
种药品库存不足,
'
,
'
绉嶈嵂鍝佺己璐э紝璇峰強无惰ˉ璐
'
:
'
种药品缺货,请及时补货
'
,
'
鍏
'
:
'
共
'
,
'
绉
'
:
'
种
'
,
// ===== Doctor AIAssist =====
'
杈呭姪璇婃柇
'
:
'
辅助诊断
'
,
'
AI 杈呭姪璇婃柇
'
:
'
AI 辅助诊断
'
,
'
AI 杈呭姪璇婃柇浠呬緵鍙傝€冿紝鏈€缁堣瘖鏂浠ュ尰鐢熶复搴婂垽鏂负鍑
'
:
'
AI 辅助诊断仅供参考,最终诊断以医生临床判断为准
'
,
'
鐥囩姸鎻忚堪
'
:
'
症状描述
'
,
'
璇疯緭鍏ユ偅鑰呯殑涓昏瘔鍜岀棁鐘舵弿杩..
'
:
'
请输入患者的主诉和症状描述..
'
,
'
AI 鏅鸿兘鍒嗘瀽
'
:
'
AI 智能分析
'
,
'
AI 姝e湪鍒嗘瀽涓..
'
:
'
AI 正在分析中..
'
,
'
闉村埆璇婃柇
'
:
'
鉴别诊断
'
,
'
鐩镐技鐥呬緥
'
:
'
相似病例
'
,
'
鐢ㄨ嵂寤鸿
'
:
'
用药建议
'
,
'
鍖归厤搴
'
:
'
匹配度
'
,
'
鎽樿锛
'
:
'
摘要:
'
,
'
璇婃柇锛
'
:
'
诊断:
'
,
'
娌荤枟锛
'
:
'
治疗:
'
,
'
娣诲姞澶勬柟
'
:
'
添加处方
'
,
'
璇疯緭鍏ョ棁鐘舵弿杩板悗杩涜鍒嗘瀽
'
:
'
请输入症状描述后进行分析
'
,
// AI mock data
'
鎬ユ€т笂鍛煎惛閬撴劅鏌
'
:
'
急性上呼吸道感染
'
,
'
鐢辩梾姣掑紩璧风殑涓婂懠鍚搁亾鐐庣棁锛屽父瑙佺棁鐘朵负榧诲銆佹祦娑曘€佸捊鐥涖€佸挸鍡界瓑
'
:
'
由病毒引起的上呼吸道炎症,常见症状为鼻塞、流涕、咽痛、咳嗽等
'
,
'
鎬ユ€ф敮姘旂鐐
'
:
'
急性支气管炎
'
,
'
鏀皵绠¢粡鑶滅殑鎬ユ€х値鐥囷紝甯哥户鍙戜簬涓婂懠鍚搁亾鎰熸煋
'
:
'
支气管黏膜的急性炎症,常继发于上呼吸道感染
'
,
'
杩囨晱鎬ч蓟鐐
'
:
'
过敏性鼻炎
'
,
'
榧婚粡鑶滈潪鎰熸煋鎬х値鎬х柧鐥咃紝浠ラ樀鍙戞€у柗鍤忋€佹竻姘存牱榧绘稌绛変负鐗瑰緛
'
:
'
鼻黏膜非感染性炎性疾病,以阵发性喷嚏、清水样鼻涕等为特征
'
,
'
鎮h€咃紝鐢凤紝35宀侊紝鍜冲椊銆佹祦娑?澶╋紝浣撴俯37.8掳C
'
:
'
患者,男,35岁,咳嗽、流涕5天,体温37.8°C
'
,
'
瀵圭棁娌荤枟锛屽彛鏈嶅竷娲涜姮閫€鐑紝姘ㄦ捍绱㈠寲鐥
'
:
'
对症治疗,口服布洛芬退热,氨溴索化痰
'
,
'
鎮h€咃紝濂筹紝28宀侊紝鍜界棝銆侀蓟濉?澶╋紝浼磋交寰挸鍡
'
:
'
患者,女,28岁,咽痛、鼻塞3天,伴轻微咳嗽
'
,
'
鍙f湇娓呭紑鐏甸绮掞紝鍚湇瑗跨摐闇滃惈鐗
'
:
'
口服清开灵颗粒,含服西瓜霜含片
'
,
'
鎮h€咃紝鐢凤紝42宀侊紝鍜冲椊1鍛紝鐥版恫榛勮壊
'
:
'
患者,男,42岁,咳嗽1周,痰液黄色
'
,
'
鍙f湇闃胯帿瑗挎灄+姘ㄦ捍绱紝闆惧寲鍚稿叆甯冨湴濂堝痉
'
:
'
口服阿莫西林+氨溴索,雾化吸入布地奈德
'
,
'
甯冩礇鑺紦閲婅兌鍥
'
:
'
布洛芬缓释胶囊
'
,
'
0.3g/绮
'
:
'
0.3g/粒
'
,
'
鍙f湇
'
:
'
口服
'
,
'
姣忔1绮掞紝姣忔棩2娆
'
:
'
每次1粒,每日2次
'
,
'
鑳冩簝鐤℃偅鑰呮厧鐢
'
:
'
胃溃疡患者慎用
'
,
'
姘ㄦ捍绱㈠彛鏈嶆恫
'
:
'
氨溴索口服液
'
,
'
100ml/鐡
'
:
'
100ml/瓶
'
,
'
姣忔10ml锛屾瘡无娆
'
:
'
每次10ml,每日3次
'
,
'
瀛曟棭鏈熺鐢
'
:
'
孕早期禁用
'
,
'
瀵逛箼閰版皒鍩洪厷鐗
'
:
'
对乙酰氨基酚片
'
,
'
0.5g/鐗
'
:
'
0.5g/片
'
,
'
姣忔1鐗囷紝鍙戠儹无舵湇鐢紝闂撮殧4-6灏忔椂
'
:
'
每次1片,发热时服用,间隔4-6小时
'
,
'
鑲濆姛鑳戒笉鍏ㄨ€呮厧鐢
'
:
'
肝功能不全者慎用
'
,
// TODO comments
'
璋冪敤鍚庣 AI 璇婃柇鎺ュ彛
'
:
'
调用后端 AI 诊断接口
'
,
'
妯℃嫙鏁版嵁
'
:
'
模拟数据
'
,
// ===== Doctor PatientList =====
'
鎮h€呭垪琛
'
:
'
患者列表
'
,
'
鎮h€?
'
:
'
患者
'
,
'
鎮h€
'
:
'
患者
'
,
'
绉抈
'
:
'
秒
'
,
'
鍒嗛挓
'
:
'
分钟
'
,
'
灏忔椂
'
:
'
小时
'
,
'
鎷掔粷
'
:
'
拒绝
'
,
'
鎸夐挳琚偣鍑
'
:
'
按钮被点击
'
,
'
鍑芥暟:
'
:
'
函数:
'
,
'
涓嶆槸涓€涓嚱鏁
'
:
'
不是一个函数
'
,
'
鎺ヨ瘖鎸夐挳琚偣鍑
'
:
'
接诊按钮被点击
'
,
'
鎷掔粷鎸夐挳琚偣鍑
'
:
'
拒绝按钮被点击
'
,
'
绛夊緟
'
:
'
等待
'
,
'
待接诊婃偅鑰呮樉绀烘帴璇?鎷掔粷鎸夐挳
'
:
'
待接诊患者显示接诊/拒绝按钮
'
,
// ===== Doctor WaitingQueue =====
'
鍒氬垰
'
:
'
刚刚
'
,
'
鍒哷
'
:
'
分
'
,
'
鏈~鍐欎富璇
'
:
'
未填写主诉
'
,
'
鎷掕瘖
'
:
'
拒诊
'
,
'
杩涜涓棶璇
'
:
'
进行中问诊
'
,
'
绛夊緟鎮h€?
'
:
'
等待患者
'
,
'
宸插畬鎴愰棶璇
'
:
'
已完成问诊
'
,
'
杩涜涓
'
:
'
进行中
'
,
// ===== Doctor Schedule =====
'
鎺掔彮绠$悊
'
:
'
排班管理
'
,
'
鎺掔彮
'
:
'
排班
'
,
'
娣诲姞鎺掔彮
'
:
'
添加排班
'
,
'
鍒犻櫎鎺掔彮
'
:
'
删除排班
'
,
'
涓椂娈
'
:
'
个时段
'
,
'
纭畾瑕佸垹闄ゆ鎺掔彮无舵鍚楋紵
'
:
'
确定要删除此排班时段吗?
'
,
'
淇濆瓨鎺掔彮
'
:
'
保存排班
'
,
'
浠嶢PI鑾峰彇鐪熷疄鏁版嵁
'
:
'
从API获取真实数据
'
,
'
璋冪敤API淇濆瓨鎺掔彮
'
:
'
调用API保存排班
'
,
'
璋冪敤API鍒犻櫎鎺掔彮
'
:
'
调用API删除排班
'
,
'
鏈圖D无
'
:
'
月DD日
'
,
'
褰撴棩
'
:
'
当日
'
,
'
无堕棿娈
'
:
'
时间段
'
,
'
璇烽€夋嫨无堕棿娈
'
:
'
请选择时间段
'
,
'
鏈€澶ф帴璇婃暟
'
:
'
最大接诊数
'
,
'
璇疯緭鍏ユ渶澶ф帴璇婃暟
'
:
'
请输入最大接诊数
'
,
'
鏈€澶?
'
:
'
最大
'
,
'
鍓╀綑
'
:
'
剩余
'
,
'
浜
'
:
'
人
'
,
'
鍙
'
:
'
号
'
,
// ===== Doctor Prescription =====
'
寮€鍏峰鏂
'
:
'
开具处方
'
,
'
澶勬柟
'
:
'
处方
'
,
'
澶勬柟宸茬鍚嶅苟鎻愪氦
'
:
'
处方已签名并提交
'
,
'
鎻愪氦澶勬柟澶辫触
'
:
'
提交处方失败
'
,
'
璇ヨ嵂鍝佸凡娣诲姞
'
:
'
该药品已添加
'
,
'
璇疯嚦灏戞坊鍔犱竴绉嶈嵂鍝
'
:
'
请至少添加一种药品
'
,
'
鐢ㄦ硶
'
:
'
用法
'
,
'
鍗曟鍓傞噺
'
:
'
单次剂量
'
,
'
棰戞
'
:
'
频次
'
,
'
澶╂暟
'
:
'
天数
'
,
'
鏁伴噺
'
:
'
数量
'
,
'
澶栫敤
'
:
'
外用
'
,
'
闈欒剦娉ㄥ皠
'
:
'
静脉注射
'
,
'
鑲岃倝娉ㄥ皠
'
:
'
肌肉注射
'
,
'
闆惧寲鍚稿叆
'
:
'
雾化吸入
'
,
'
姣忔棩1娆
'
:
'
每日1次
'
,
'
姣忔棩2娆
'
:
'
每日2次
'
,
'
姣忔棩3娆
'
:
'
每日3次
'
,
'
蹇呰无
'
:
'
必要时
'
,
'
鐫″墠
'
:
'
睡前
'
,
'
濮撳悕
'
:
'
姓名
'
,
'
鎬у埆
'
:
'
性别
'
,
'
鐢
'
:
'
男
'
,
'
濂
'
:
'
女
'
,
'
骞撮緞
'
:
'
年龄
'
,
'
宀
'
:
'
岁
'
,
'
璇婃柇
'
:
'
诊断
'
,
'
杩囨晱
'
:
'
过敏
'
,
'
杩囨晱鍙
'
:
'
过敏史
'
,
'
鍖诲槺澶囨敞...
'
:
'
医嘱备注...
'
,
'
鎵撳嵃鍔熻兘寮€鍙戜腑
'
:
'
打印功能开发中
'
,
'
鎵撳嵃
'
:
'
打印
'
,
'
绛惧悕鎻愪氦
'
:
'
签名提交
'
,
'
CA 绛惧悕纭
'
:
'
CA 签名确认
'
,
'
纭绛惧悕
'
:
'
确认签名
'
,
'
纭浣跨敤 CA 鏁板瓧璇佷功绛剧讲姝ゅ鏂癸紵
'
:
'
确认使用 CA 数字证书签署此处方?
'
,
'
绛剧讲鍚庡鏂瑰皢鍏锋湁娉曞緥鏁堝姏
'
:
'
签署后处方将具有法律效力
'
,
'
鑽搧锛
'
:
'
药品:
'
,
'
閲戦锛
'
:
'
金额:
'
,
'
鍚堣锛
'
:
'
合计:
'
,
'
璇锋悳绱㈠苟娣诲姞鑽搧
'
:
'
请搜索并添加药品
'
,
'
鎼滅储鑽搧...
'
:
'
搜索药品...
'
,
'
搴撳瓨:
'
:
'
库存:
'
,
'
濡 1绮
'
:
'
如 1粒
'
,
'
濡 0.5g脳24绮?鐩
'
:
'
如 0.5g×24粒/盒
'
,
// ===== Generic =====
'
鍔熻兘寮€鍙戜腑锛屾暚璇锋湡寰
'
:
'
功能开发中,敬请期待
'
,
'
璇疯緭鍏
'
:
'
请输入
'
,
};
function
fixFile
(
filePath
)
{
let
text
=
fs
.
readFileSync
(
filePath
,
'
utf8
'
);
const
original
=
text
;
// Sort keys by length descending (longest first)
const
sortedKeys
=
Object
.
keys
(
m
).
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
);
for
(
const
key
of
sortedKeys
)
{
if
(
text
.
includes
(
key
))
{
text
=
text
.
split
(
key
).
join
(
m
[
key
]);
}
}
// Fix remaining single-char GBK + ? patterns
const
singleFixes
=
{
'
患
'
:
'
者
'
,
'
状
'
:
'
态
'
,
'
医
'
:
'
院
'
,
'
机
'
:
'
构
'
,
'
姓
'
:
'
名
'
,
'
证
'
:
'
书
'
,
'
认
'
:
'
证
'
,
'
审
'
:
'
核
'
,
'
简
'
:
'
介
'
,
'
拒
'
:
'
绝
'
,
'
禁
'
:
'
用
'
,
'
码
'
:
'
码
'
,
'
号
'
:
'
码
'
,
'
理
'
:
'
员
'
,
'
诊
'
:
'
断
'
,
'
血
'
:
'
压
'
,
'
糖
'
:
'
化
'
,
'
药
'
:
'
物
'
,
'
签
'
:
'
名
'
,
'
完
'
:
'
成
'
,
'
取
'
:
'
消
'
,
'
行
'
:
'
中
'
,
'
支
'
:
'
付
'
,
'
接
'
:
'
诊
'
,
'
收
'
:
'
入
'
,
'
结
'
:
'
算
'
,
'
发
'
:
'
送
'
,
'
败
'
:
'
了
'
,
'
处
'
:
'
方
'
,
'
统
'
:
'
计
'
,
'
记
'
:
'
录
'
,
'
调
'
:
'
整
'
,
'
待
'
:
'
处
'
,
'
最
'
:
'
高
'
,
'
症
'
:
'
状
'
,
'
开
'
:
'
始
'
,
'
除
'
:
'
外
'
,
'
一
'
:
'
次
'
,
'
数
'
:
'
量
'
,
'
新
'
:
'
增
'
,
'
更
'
:
'
新
'
,
'
报
'
:
'
告
'
,
'
敏
'
:
'
感
'
,
'
金
'
:
'
额
'
,
'
息
'
:
'
。
'
,
'
跃
'
:
'
量
'
,
'
常
'
:
'
规
'
,
'
保
'
:
'
险
'
,
'
食
'
:
'
物
'
,
'
病
'
:
'
史
'
,
'
问
'
:
'
诊
'
,
'
天
'
:
'
数
'
,
'
药
'
:
'
物
'
,
'
好
'
:
'
评
'
,
'
制
'
:
'
定
'
,
'
过
'
:
'
敏
'
,
'
乏
'
:
'
力
'
,
'
周
'
:
'
期
'
,
'
史
'
:
'
记
'
,
'
方
'
:
'
案
'
,
'
照
'
:
'
片
'
,
'
核
'
:
'
实
'
,
'
缩
'
:
'
压
'
,
'
张
'
:
'
压
'
,
'
坦
'
:
'
类
'
,
'
原
'
:
'
件
'
,
'
释
'
:
'
放
'
,
'
平
'
:
'
均
'
,
'
胶
'
:
'
囊
'
,
'
脲
'
:
'
类
'
,
};
text
=
text
.
replace
(
/
([\u
4e00-
\u
9fff
])\?([
,}'"
\s\r\n\/
)<>;:
\]])
/g
,
(
match
,
ch
,
after
)
=>
{
if
(
singleFixes
[
ch
])
return
ch
+
singleFixes
[
ch
]
+
after
;
return
ch
+
after
;
});
// Fix Chinese?/tag> patterns
text
=
text
.
replace
(
/
([\u
4e00-
\u
9fff
]
+
)\?\/([\w]
+>
)
/g
,
(
match
,
chinese
,
tag
)
=>
{
const
fixes
=
{
'
工作
'
:
'
台
'
,
'
简
'
:
'
介
'
,
'
排
'
:
'
班
'
,
'
认
'
:
'
证
'
,
'
患
'
:
'
者
'
,
'
配送
'
:
'
中
'
,
'
跟踪
'
:
'
中
'
,
'
态
'
:
'
度
'
,
'
诊
'
:
'
断
'
,
};
for
(
const
[
k
,
v
]
of
Object
.
entries
(
fixes
).
sort
((
a
,
b
)
=>
b
[
0
].
length
-
a
[
0
].
length
))
{
if
(
chinese
.
endsWith
(
k
))
return
chinese
+
v
+
'
</
'
+
tag
;
}
return
chinese
+
'
</
'
+
tag
;
});
// Fix remaining isolated garbled sequences
text
=
text
.
replace
(
/
[\u
0200-
\u
06FF
]{3,}
/g
,
''
);
// Fix router.push(-1) -> router.back()
text
=
text
.
replace
(
/router
\.
push
\(
-1
\)
/g
,
'
router.back()
'
);
// Fix useSearchParams
text
=
text
.
replace
(
/const
\[
searchParams
\]
= useSearchParams
\(\)
/g
,
'
const searchParams = useSearchParams()
'
);
if
(
text
!==
original
)
{
fs
.
writeFileSync
(
filePath
,
text
,
'
utf8
'
);
return
true
;
}
return
false
;
}
const
pagesDir
=
path
.
join
(
__dirname
,
'
src
'
,
'
pages
'
);
const
files
=
walk
(
pagesDir
);
let
fixedCount
=
0
;
for
(
const
f
of
files
)
{
if
(
fixFile
(
f
))
{
fixedCount
++
;
console
.
log
(
'
Fixed:
'
+
path
.
relative
(
__dirname
,
f
));
}
}
console
.
log
(
'
\n
Total fixed:
'
+
fixedCount
);
// Verify
let
broken
=
0
;
for
(
const
f
of
files
)
{
const
text
=
fs
.
readFileSync
(
f
,
'
utf8
'
);
const
p1
=
text
.
match
(
/
[\u
4e00-
\u
9fff
]\?[
,}'"
\s\/
)<>;
]
/g
)
||
[];
const
p2
=
text
.
match
(
/
[\u
0200-
\u
06FF
]{3,}
/g
)
||
[];
// Check for common GBK garbled chars
const
gbkChars
=
[
'
鑾峰
'
,
'
宸插
'
,
'
鍒犻
'
,
'
缂栬
'
,
'
娣诲
'
,
'
鎮h
'
,
'
鎺掑
'
,
'
绉戝
'
,
'
鑽搧
'
,
'
鏆傛
'
,
'
璇疯
'
,
'
鎼滅
'
];
let
p3
=
0
;
for
(
const
g
of
gbkChars
)
{
if
(
text
.
includes
(
g
))
p3
++
;
}
if
(
p1
.
length
||
p2
.
length
||
p3
)
{
broken
++
;
console
.
log
(
'
STILL BROKEN:
'
+
path
.
relative
(
__dirname
,
f
)
+
'
(?
'
+
p1
.
length
+
'
garbled:
'
+
p2
.
length
+
'
gbk:
'
+
p3
+
'
)
'
);
if
(
p1
.
length
)
p1
.
slice
(
0
,
3
).
forEach
(
x
=>
console
.
log
(
'
?:
'
+
JSON
.
stringify
(
x
)));
}
}
console
.
log
(
'
\n
Still broken:
'
+
broken
);
web/fix-remaining.js
0 → 100644
View file @
e15d3424
const
fs
=
require
(
'
fs
'
);
const
path
=
require
(
'
path
'
);
// Exact line-level fixes for all remaining corrupted strings
// Format: { file: relative_path, fixes: [ [old, new], ... ] }
const
fileFixes
=
[
{
file
:
'
src/pages/admin/AIConfig/index.tsx
'
,
fixes
:
[
[
`模板?{record.name}`
,
`模板「{record.name}`
],
[
`已激活模板?{record.name}」`
,
`已激活模板「{record.name}」`
],
[
`影响所?AI`
,
`影响所有 AI`
],
[
`保留已保存?Key`
,
`保留已保存的 Key`
],
[
`输入新?Key`
,
`输入新的 Key`
],
[
`请输?API Key`
,
`请输入 API Key`
],
[
`最大输?token ?`
,
`最大输出 token 数`
],
[
`建?0.1-0.3`
,
`建议 0.1-0.3`
],
[
`控?AI`
,
`控制 AI`
],
[
`防?AI`
,
`防止 AI`
],
[
`请输?Prompt`
,
`请输入 Prompt`
],
[
`安全词弹?`
,
`安全词弹窗`
],
[
`'激活失`
,
`'激活失败`
],
[
`>激<`
,
`>激活<`
],
[
`"模型提供 name=`
,
`"模型提供商" name=`
],
[
`"开始 unCheckedChildren`
,
`"开启" unCheckedChildren`
],
]
},
{
file
:
'
src/pages/admin/Compliance/index.tsx
'
,
fixes
:
[
[
`'管理员, role`
,
`'管理员', role`
],
[
`'陈医院, role`
,
`'陈医生', role`
],
[
`'开具处方, module`
,
`'开具处方', module`
],
]
},
{
file
:
'
src/pages/admin/Consultations/index.tsx
'
,
fixes
:
[
[
`搜索患?医生`
,
`搜索患者/医生`
],
]
},
{
file
:
'
src/pages/admin/Dashboard/index.tsx
'
,
fixes
:
[
[
`待审?`
,
`待审核`
],
]
},
{
file
:
'
src/pages/admin/Doctors/index.tsx
'
,
fixes
:
[
[
`医生?{record.name}`
,
`医生「{record.name}」`
],
[
`重置医?$`
,
`重置医生 $`
],
[
`医生?{record.name},删除`
,
`医生「{record.name}」,删除`
],
]
},
{
file
:
'
src/pages/admin/Patients/index.tsx
'
,
fixes
:
[
[
`重置患?$`
,
`重置患者 $`
],
]
},
{
file
:
'
src/pages/admin/Pharmacy/index.tsx
'
,
fixes
:
[
[
`当前有?$`
,
`当前有 $`
],
[
`共?$`
,
`共 $`
],
[
`种峘`
,
`种`
],
]
},
{
file
:
'
src/pages/admin/Prescription/index.tsx
'
,
fixes
:
[
[
`搜索处方/患?医生`
,
`搜索处方/患者/医生`
],
]
},
{
file
:
'
src/pages/admin/Statistics/index.tsx
'
,
fixes
:
[
[
`待集?ECharts`
,
`待集成 ECharts`
],
]
},
{
file
:
'
src/pages/admin/Users/index.tsx
'
,
fixes
:
[
[
`密码?23456`
,
`密码为 123456`
],
[
'
确定?
'
+
'
{action}
'
,
'
确定
'
+
'
${action}
'
],
[
'
搜索用户?手机构
'
,
'
搜索用户名/手机号
'
],
]
},
{
file
:
'
src/pages/doctor/AIAssist/index.tsx
'
,
fixes
:
[
[
`鍜界棝銆侀蓟濉?澶╋紝浼磋交寰挸鍡`
,
`咽痛、鼻塞3天,伴轻微咳嗽`
],
[
`口服娓呭紑鐏甸绮掞紝鍚湇瑗跨摐闇滃惈鐗`
,
`口服清开灵颗粒,含服西瓜霜含片`
],
[
`患者咃紝女筹紝28岁侊紝`
,
`患者,女,28岁,`
],
]
},
{
file
:
'
src/pages/doctor/Certification/index.tsx
'
,
fixes
:
[
[
`管理员将?1-3`
,
`管理员将在 1-3`
],
[
`审核实`
,
`审核。`
],
]
},
{
file
:
'
src/pages/doctor/Consult/index.tsx
'
,
fixes
:
[
[
`已接诊患?$`
,
`已接诊患者 $`
],
[
`|| '患}`
,
`|| '未知'}`
],
[
`拒绝接诊患?$`
,
`拒绝接诊患者 $`
],
]
},
{
file
:
'
src/pages/doctor/ConsultRoom/index.tsx
'
,
fixes
:
[
[
`高血压病?年`
,
`高血压病史5年`
],
[
`高血压病??`
,
`高血压病史5年`
],
[
`降压药物,`
,
`降压药物',`
],
]
},
{
file
:
'
src/pages/doctor/Prescription/index.tsx
'
,
fixes
:
[
[
`鑽搧锛`
,
`药品:`
],
[
`閲戦锛`
,
`金额:`
],
[
`楼{`
,
`¥{`
],
]
},
{
file
:
'
src/pages/patient/Chronic/index.tsx
'
,
fixes
:
[
// Will check specific content
]
},
{
file
:
'
src/pages/patient/Doctors/index.tsx
'
,
fixes
:
[
[
`问诊?{doctor`
,
`问诊 {doctor`
],
]
},
{
file
:
'
src/pages/patient/PreConsult/index.tsx
'
,
fixes
:
[
[
`您好?{patientInfo.name}`
,
`您好,{patientInfo.name}`
],
[
`可以说?我头痛`
,
`可以说:"我头痛`
],
[
`错误?{error}`
,
`错误:{error}`
],
[
`对话?· ?`
,
`对话中 · 第`
],
[
`结束对?· 生成报告`
,
`结束对话 · 生成报告`
],
]
},
{
file
:
'
src/pages/patient/HealthRecords/index.tsx
'
,
fixes
:
[
// Will check specific content
]
},
];
function
fixFile
(
filePath
,
fixes
)
{
let
text
=
fs
.
readFileSync
(
filePath
,
'
utf8
'
);
const
original
=
text
;
for
(
const
[
old
,
replacement
]
of
fixes
)
{
if
(
text
.
includes
(
old
))
{
text
=
text
.
split
(
old
).
join
(
replacement
);
}
}
if
(
text
!==
original
)
{
fs
.
writeFileSync
(
filePath
,
text
,
'
utf8
'
);
return
true
;
}
return
false
;
}
let
fixed
=
0
;
for
(
const
{
file
,
fixes
}
of
fileFixes
)
{
if
(
fixes
.
length
===
0
)
continue
;
const
fullPath
=
path
.
join
(
__dirname
,
file
);
if
(
!
fs
.
existsSync
(
fullPath
))
{
console
.
log
(
'
NOT FOUND:
'
+
file
);
continue
;
}
if
(
fixFile
(
fullPath
,
fixes
))
{
fixed
++
;
console
.
log
(
'
Fixed:
'
+
file
);
}
}
console
.
log
(
'
\n
Fixed:
'
+
fixed
+
'
files
'
);
// Now verify
function
walk
(
dir
)
{
const
files
=
[];
for
(
const
f
of
fs
.
readdirSync
(
dir
,
{
withFileTypes
:
true
}))
{
const
p
=
path
.
join
(
dir
,
f
.
name
);
if
(
f
.
isDirectory
())
files
.
push
(...
walk
(
p
));
else
if
(
f
.
name
.
endsWith
(
'
.tsx
'
)
||
f
.
name
.
endsWith
(
'
.ts
'
))
files
.
push
(
p
);
}
return
files
;
}
const
pagesDir
=
path
.
join
(
__dirname
,
'
src
'
,
'
pages
'
);
const
allFiles
=
walk
(
pagesDir
);
let
broken
=
0
;
for
(
const
f
of
allFiles
)
{
const
t
=
fs
.
readFileSync
(
f
,
'
utf8
'
);
const
lines
=
t
.
split
(
'
\n
'
);
const
issues
=
[];
for
(
let
i
=
0
;
i
<
lines
.
length
;
i
++
)
{
const
l
=
lines
[
i
];
// Skip comment lines
if
(
l
.
trim
().
startsWith
(
'
//
'
))
continue
;
if
(
/
[\u
4e00-
\u
9fff
]\?
/
.
test
(
l
))
{
issues
.
push
((
i
+
1
)
+
'
:
'
+
l
.
trim
().
substring
(
0
,
100
));
}
}
// Check for GBK garbled
const
gbkChars
=
[
'
鑾峰
'
,
'
宸插
'
,
'
鍒犻
'
,
'
缂栬
'
,
'
娣诲
'
,
'
鎮h
'
,
'
鎺掑
'
,
'
绉戝
'
,
'
鑽搧
'
,
'
鏆傛
'
,
'
璇疯
'
,
'
鎼滅
'
,
'
鍙f
'
,
'
姣忔
'
,
'
闈欒
'
,
'
闆惧
'
,
'
鑲岃
'
,
'
甯冩
'
,
'
姘ㄦ
'
,
'
杈呭
'
,
'
鐥囩
'
,
'
璇婃
'
,
'
鍖归
'
,
'
鎽樿
'
,
'
娌荤
'
,
'
鐢ㄨ
'
,
'
搴撳
'
,
'
缂鸿
'
,
'
棰勮
'
,
'
绫诲
'
,
'
浠锋
'
,
'
鍗曚
'
,
'
鐘舵
'
,
'
鎿嶄
'
,
'
鎺掑
'
,
'
閫氱
'
,
'
瑙勬
'
];
let
gbkCount
=
0
;
for
(
const
g
of
gbkChars
)
if
(
t
.
includes
(
g
))
gbkCount
++
;
if
(
issues
.
length
>
0
||
gbkCount
>
0
)
{
broken
++
;
console
.
log
(
'
\n
STILL BROKEN:
'
+
path
.
relative
(
__dirname
,
f
)
+
'
(lines:
'
+
issues
.
length
+
'
gbk:
'
+
gbkCount
+
'
)
'
);
issues
.
slice
(
0
,
5
).
forEach
(
x
=>
console
.
log
(
'
'
+
x
));
}
}
console
.
log
(
'
\n\n
Total still broken:
'
+
broken
);
web/src/pages/admin/AIConfig/index.tsx
View file @
e15d3424
...
...
@@ -261,7 +261,7 @@ const AdminAIConfigPage: React.FC = () => {
const handleDeleteTemplate = (record: PromptTemplateData) => {
Modal.confirm({
title:
'
确认删除该模板?
'
,
content: `模板
?
{record.name}(Key: ${record.template_key})`,
content: `模板
「
{record.name}(Key: ${record.template_key})`,
okType:
'
danger
'
,
onOk: async () => {
try {
...
...
@@ -352,10 +352,10 @@ const AdminAIConfigPage: React.FC = () => {
<
Button
type=
"link"
size=
"small"
icon=
{
<
CheckCircleOutlined
/>
}
onClick=
{
async
()
=>
{
try
{
await
adminApi
.
activatePromptTemplate
(
record
.
id
);
message
.
success
(
`已激活模板
?
{record.name}」,同场景其他模板已自动停用`
);
message
.
success
(
`已激活模板
「
{record.name}」,同场景其他模板已自动停用`
);
fetchTemplates
();
}
catch
{
message
.
error
(
'
激活失); }
}
}
>
激
</
Button
>
}
catch
{
message
.
error
(
'
激活失
败
); }
}
}
>
激
活
</
Button
>
)
}
<
Button
type=
"link"
size=
"small"
icon=
{
<
EditOutlined
/>
}
onClick=
{
()
=>
handleEditTemplate
(
record
)
}
>
编辑
</
Button
>
<
Button
type=
"link"
size=
"small"
danger
icon=
{
<
DeleteOutlined
/>
}
onClick=
{
()
=>
handleDeleteTemplate
(
record
)
}
>
删除
</
Button
>
...
...
@@ -420,7 +420,7 @@ const AdminAIConfigPage: React.FC = () => {
<Col span={16}>
<Card title="AI 模型配置" style={{ borderRadius: 12 }}>
<Alert
message="此配置将全局生效,影响所
?
AI 功能(AI预问诊、辅助诊断、处方审核等
message="此配置将全局生效,影响所
有
AI 功能(AI预问诊、辅助诊断、处方审核等
type="info"
showIcon
style={{ marginBottom: 20 }}
...
...
@@ -428,7 +428,7 @@ const AdminAIConfigPage: React.FC = () => {
<Form form={modelForm} layout="vertical">
<Row gutter={16}>
<Col span={12}>
<Form.Item label="模型提供 name="provider" rules={[{ required: true }]}>
<Form.Item label="模型提供
商"
name="provider" rules={[{ required: true }]}>
<Select
options={providerOptions}
onChange={handleProviderChange}
...
...
@@ -446,9 +446,9 @@ const AdminAIConfigPage: React.FC = () => {
<Form.Item
label="API Key"
name="api_key"
help="留空则保留已保存
?Key;如需更新请输入新?
Key"
help="留空则保留已保存
的 Key;如需更新请输入新的
Key"
>
<Input.Password placeholder="请输
?
API Key" />
<Input.Password placeholder="请输
入
API Key" />
</Form.Item>
</Col>
<Col span={12}>
...
...
@@ -465,12 +465,12 @@ const AdminAIConfigPage: React.FC = () => {
</Col>
<Col span={8}>
<Form.Item label="Max Tokens" name="max_tokens">
<InputNumber style={{ width:
'
100
%
'
}} min={256} max={32768} step={256} placeholder="最大输
?token ?
/>
<InputNumber style={{ width:
'
100
%
'
}} min={256} max={32768} step={256} placeholder="最大输
出 token 数
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="启用 AI 辅助" name="enable_ai" valuePropName="checked">
<Switch checkedChildren="开
始
unCheckedChildren="关闭" />
<Switch checkedChildren="开
启"
unCheckedChildren="关闭" />
</Form.Item>
</Col>
</Row>
...
...
@@ -512,7 +512,7 @@ const AdminAIConfigPage: React.FC = () => {
<p><Text strong>DeepSeek</Text>:国产大模型,性价比高,推荐用于医疗场</p>
<p><Text strong>OpenAI</Text>:GPT系列,英文能力强</p>
<p><Text strong>通义千问</Text>:阿里云大模型,中文能力优秀</p>
<p><Text strong>Temperature</Text>:越低输出越稳定,医疗场景建
?
0.1-0.3</p>
<p><Text strong>Temperature</Text>:越低输出越稳定,医疗场景建
议
0.1-0.3</p>
</div>
</Card>
</Col>
...
...
@@ -526,7 +526,7 @@ const AdminAIConfigPage: React.FC = () => {
children: (
<Card style={{ borderRadius: 12 }}>
<div style={{ marginBottom: 16, display:
'
flex
'
, justifyContent:
'
space
-
between
'
}}>
<Text type="secondary">管理各场景的 AI Prompt 模板,控
?
AI 输出行为</Text>
<Text type="secondary">管理各场景的 AI Prompt 模板,控
制
AI 输出行为</Text>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAddTemplate}>
新建模板
</Button>
...
...
@@ -541,7 +541,7 @@ const AdminAIConfigPage: React.FC = () => {
children: (
<div>
<Alert
message="安全词过滤规则用于防
?
AI 输出不安全或不合规的内容"
message="安全词过滤规则用于防
止
AI 输出不安全或不合规的内容"
type="info"
showIcon
style={{ marginBottom: 16 }}
...
...
@@ -749,7 +749,7 @@ const AdminAIConfigPage: React.FC = () => {
</
Col
>
</
Row
>
<
Form
.
Item
label=
"Prompt 内容"
name=
"content"
rules=
{
[{
required
:
true
,
message
:
'
请输入Prompt内容
'
}]
}
>
<
TextArea
rows=
{
8
}
placeholder=
"请输
?
Prompt 模板内容...支持多行"
/>
<
TextArea
rows=
{
8
}
placeholder=
"请输
入
Prompt 模板内容...支持多行"
/>
</
Form
.
Item
>
<
Alert
message=
'提示:同一场景可创建多个模板,但只有一个“启用”状态的模板会生效。点击“激活”按钮可切换当前场景使用的模板
...
...
@@ -760,7 +760,7 @@ const AdminAIConfigPage: React.FC = () => {
</Form>
</Modal>
{/* 安全词弹
?
*/}
{/* 安全词弹
窗
*/}
<Modal
title="添加安全
open={safeWordModalVisible}
...
...
web/src/pages/admin/Compliance/index.tsx
View file @
e15d3424
...
...
@@ -40,17 +40,17 @@ interface ReportItem {
}
const
mockLogs
:
AuditLog
[]
=
[
{
id
:
'
1
'
,
timestamp
:
'
2026-02-25 18:30:15
'
,
user
:
'
管理员
, role:
'
admin
'
, action:
'
审核通过
'
, module:
'
医生管理
'
, ip:
'
192.168
.
1.100
'
, detail:
'
审核通过医生
周医
?
的资质认证申
,
level
:
'
info
'
},
{
id
:
'
2
'
,
timestamp
:
'
2026-02-25 17:45:22
'
,
user
:
'
陈医
院, role:
'
doctor
'
, action:
'
开具处方
,
module
:
'
处方管理
'
,
ip
:
'
192.168.1.105
'
,
detail
:
'
为患者张三开具处?
RX20260225001
'
,
level
:
'
info
'
},
{
id
:
'
3
'
,
timestamp
:
'
2026-02-25 16:20:08
'
,
user
:
'
管理员
, role:
'
admin
'
, action:
'
修改配置
'
, module:
'
AI
配置
'
, ip:
'
192.168
.
1.100
'
, detail:
'
修改
AI
模型
temperature
参数
?
0.5
?
0.3
'
, level:
'
warning
'
},
{ id:
'
4
'
, timestamp:
'
2026
-
02
-
25
15
:
10
:
33
'
, user:
'
未知用户
'
, role:
'
unknown
'
, action:
'
登录失败
'
, module:
'
系统安全
'
, ip:
'
10.0
.
0.55
'
, detail:
'
连续
3
次密码错误,账户已临时锁
,
level
:
'
danger
'
},
{
id
:
'
5
'
,
timestamp
:
'
2026-02-25 14:05:11
'
,
user
:
'
管理员, role:
'
admin
'
, action:
'
数据导出
'
, module:
'
数据管理
'
, ip:
'
192.168
.
1.100
'
, detail:
'
导出本月问诊数据报表
'
, level:
'
info
'
},
{ id:
'
6
'
, timestamp:
'
2026
-
02
-
25
11
:
30
:
45
'
, user:
'
刘医
院
,
role
:
'
doctor
'
,
action
:
'
异常处方
'
,
module
:
'
处方监管
'
,
ip
:
'
192.168.1.108
'
,
detail
:
'
处方 RX20260225004 被系统标记为异常:抗生素使用不符合指
, level:
'
danger
'
},
{
id
:
'
1
'
,
timestamp
:
'
2026-02-25 18:30:15
'
,
user
:
'
管理员
'
,
role
:
'
admin
'
,
action
:
'
审核通过
'
,
module
:
'
医生管理
'
,
ip
:
'
192.168.1.100
'
,
detail
:
'
审核通过医生周医生的资质认证申请
'
,
level
:
'
info
'
},
{
id
:
'
2
'
,
timestamp
:
'
2026-02-25 17:45:22
'
,
user
:
'
陈医
生
'
,
role
:
'
doctor
'
,
action
:
'
开具处方
'
,
module
:
'
处方管理
'
,
ip
:
'
192.168.1.105
'
,
detail
:
'
为患者张三开具处方
RX20260225001
'
,
level
:
'
info
'
},
{
id
:
'
3
'
,
timestamp
:
'
2026-02-25 16:20:08
'
,
user
:
'
管理员
'
,
role
:
'
admin
'
,
action
:
'
修改配置
'
,
module
:
'
AI配置
'
,
ip
:
'
192.168.1.100
'
,
detail
:
'
修改 AI 模型 temperature 参数从 0.5 到
0.3
'
,
level
:
'
warning
'
},
{
id
:
'
4
'
,
timestamp
:
'
2026-02-25 15:10:33
'
,
user
:
'
未知用户
'
,
role
:
'
unknown
'
,
action
:
'
登录失败
'
,
module
:
'
系统安全
'
,
ip
:
'
10.0.0.55
'
,
detail
:
'
连续3次密码错误,账户已临时锁
定
'
,
level
:
'
danger
'
},
{
id
:
'
5
'
,
timestamp
:
'
2026-02-25 14:05:11
'
,
user
:
'
管理员
'
,
role
:
'
admin
'
,
action
:
'
数据导出
'
,
module
:
'
数据管理
'
,
ip
:
'
192.168.1.100
'
,
detail
:
'
导出本月问诊数据报表
'
,
level
:
'
info
'
},
{
id
:
'
6
'
,
timestamp
:
'
2026-02-25 11:30:45
'
,
user
:
'
刘医
生
'
,
role
:
'
doctor
'
,
action
:
'
异常处方
'
,
module
:
'
处方监管
'
,
ip
:
'
192.168.1.108
'
,
detail
:
'
处方 RX20260225004 被系统标记为异常:抗生素使用不符合指南
'
,
level
:
'
danger
'
},
];
const
mockReports
:
ReportItem
[]
=
[
{ id:
'
1
'
, name:
'
2026
?
月运营月
,
type
:
'
月度报告
'
,
period
:
'
2026-02
'
,
status
:
'
pending
'
},
{
id
:
'
2
'
,
name
:
'
2026
?月运营月
, type:
'
月度报告
'
, period:
'
2026
-
01
'
, status:
'
submitted
'
, generated_at:
'
2026
-
02
-
05
'
, submitted_at:
'
2026
-
02
-
10
'
},
{
id
:
'
1
'
,
name
:
'
2026
年2月运营月报
'
,
type
:
'
月度报告
'
,
period
:
'
2026-02
'
,
status
:
'
pending
'
},
{
id
:
'
2
'
,
name
:
'
2026
年1月运营月报
'
,
type
:
'
月度报告
'
,
period
:
'
2026-01
'
,
status
:
'
submitted
'
,
generated_at
:
'
2026-02-05
'
,
submitted_at
:
'
2026-02-10
'
},
{
id
:
'
3
'
,
name
:
'
2025Q4季度合规报告
'
,
type
:
'
季度报告
'
,
period
:
'
2025-Q4
'
,
status
:
'
submitted
'
,
generated_at
:
'
2026-01-10
'
,
submitted_at
:
'
2026-01-15
'
},
{
id
:
'
4
'
,
name
:
'
处方用药合规专项报告
'
,
type
:
'
专项报告
'
,
period
:
'
2026-02
'
,
status
:
'
generated
'
,
generated_at
:
'
2026-02-20
'
},
{
id
:
'
5
'
,
name
:
'
等保三级自查报告
'
,
type
:
'
安全报告
'
,
period
:
'
2026-02
'
,
status
:
'
pending
'
},
...
...
@@ -75,9 +75,9 @@ const AdminCompliancePage: React.FC = () => {
const
getStatusTag
=
(
status
:
string
)
=>
{
switch
(
status
)
{
case
'
generated
'
: return <Tag color="blue">已生</Tag>;
case
'
pending
'
: return <Tag color="orange">待生</Tag>;
case
'
submitted
'
: return <Tag color="green">已上</Tag>;
case
'
generated
'
:
return
<
Tag
color=
"blue"
>
已生
成
</
Tag
>;
case
'
pending
'
:
return
<
Tag
color=
"orange"
>
待生
成
</
Tag
>;
case
'
submitted
'
:
return
<
Tag
color=
"green"
>
已上
报
</
Tag
>;
default
:
return
<
Tag
>
{
status
}
</
Tag
>;
}
};
...
...
@@ -88,7 +88,7 @@ const AdminCompliancePage: React.FC = () => {
{
title
:
'
角色
'
,
dataIndex
:
'
role
'
,
key
:
'
role
'
,
width
:
80
,
render
:
(
v
:
string
)
=>
{
const map: Record<string, string> = { admin:
'
管理员
,
doctor
:
'
医生
'
,
patient
:
'
患者
, unknown:
'
未知
'
};
const
map
:
Record
<
string
,
string
>
=
{
admin
:
'
管理员
'
,
doctor
:
'
医生
'
,
patient
:
'
患者
'
,
unknown
:
'
未知
'
};
return
<
Tag
>
{
map
[
v
]
||
v
}
</
Tag
>;
},
},
...
...
@@ -103,7 +103,7 @@ const AdminCompliancePage: React.FC = () => {
{
title
:
'
报告名称
'
,
dataIndex
:
'
name
'
,
key
:
'
name
'
,
render
:
(
v
:
string
)
=>
<
Text
strong
>
{
v
}
</
Text
>
},
{
title
:
'
类型
'
,
dataIndex
:
'
type
'
,
key
:
'
type
'
,
width
:
100
,
render
:
(
v
:
string
)
=>
<
Tag
color=
"purple"
>
{
v
}
</
Tag
>
},
{
title
:
'
周期
'
,
dataIndex
:
'
period
'
,
key
:
'
period
'
,
width
:
100
},
{ title:
'
状态
,
dataIndex
:
'
status
'
,
key
:
'
status
'
,
width
:
100
,
render
:
(
v
:
string
)
=>
getStatusTag
(
v
)
},
{
title
:
'
状态
'
,
dataIndex
:
'
status
'
,
key
:
'
status
'
,
width
:
100
,
render
:
(
v
:
string
)
=>
getStatusTag
(
v
)
},
{
title
:
'
生成时间
'
,
dataIndex
:
'
generated_at
'
,
key
:
'
generated_at
'
,
width
:
120
,
render
:
(
v
:
string
)
=>
v
||
'
-
'
},
{
title
:
'
上报时间
'
,
dataIndex
:
'
submitted_at
'
,
key
:
'
submitted_at
'
,
width
:
120
,
render
:
(
v
:
string
)
=>
v
||
'
-
'
},
{
...
...
@@ -132,22 +132,22 @@ const AdminCompliancePage: React.FC = () => {
<
Row
gutter=
{
[
8
,
8
]
}
>
<
Col
span=
{
6
}
>
<
Card
size=
"small"
style=
{
{
borderLeft
:
'
3px solid #1890ff
'
}
}
>
<
Statistic
title=
"审计日志"
value=
{
mockLogs
.
length
}
prefix=
{
<
FileSearchOutlined
/>
}
suffix=
"
?
/>
<
Statistic
title=
"审计日志"
value=
{
mockLogs
.
length
}
prefix=
{
<
FileSearchOutlined
/>
}
suffix=
"
条"
/>
</
Card
>
</
Col
>
<
Col
span=
{
6
}
>
<
Card
size=
"small"
style=
{
{
borderLeft
:
'
3px solid #fa8c16
'
}
}
>
<
Statistic
title=
"安全告警"
value=
{
mockLogs
.
filter
((
l
)
=>
l
.
level
===
'
danger
'
).
length
}
valueStyle=
{
{
color
:
'
#f5222d
'
}
}
suffix=
"
?
/>
<
Statistic
title=
"安全告警"
value=
{
mockLogs
.
filter
((
l
)
=>
l
.
level
===
'
danger
'
).
length
}
valueStyle=
{
{
color
:
'
#f5222d
'
}
}
suffix=
"
条"
/>
</
Card
>
</
Col
>
<
Col
span=
{
6
}
>
<
Card
size=
"small"
style=
{
{
borderLeft
:
'
3px solid #52c41a
'
}
}
>
<
Statistic
title=
"已上
value={mockReports.filter((r) => r.status === 'submitted').length} suffix="
?
/>
<
Statistic
title=
"已上
报"
value=
{
mockReports
.
filter
((
r
)
=>
r
.
status
===
'
submitted
'
).
length
}
suffix=
"条"
/>
</
Card
>
</
Col
>
<
Col
span=
{
6
}
>
<
Card
size=
"small"
style=
{
{
borderLeft
:
'
3px solid #722ed1
'
}
}
>
<
Statistic
title=
"待生
value={mockReports.filter((r) => r.status === 'pending').length} valueStyle={{ color: '#fa8c16' }} suffix="
?
/>
<
Statistic
title=
"待生
成"
value=
{
mockReports
.
filter
((
r
)
=>
r
.
status
===
'
pending
'
).
length
}
valueStyle=
{
{
color
:
'
#fa8c16
'
}
}
suffix=
"条"
/>
</
Card
>
</
Col
>
</
Row
>
...
...
@@ -181,10 +181,10 @@ const AdminCompliancePage: React.FC = () => {
<
Card
size=
"small"
>
<
Row
gutter=
{
[
8
,
8
]
}
>
{
[
{
title
:
'
问诊数据
'
,
desc
:
'
问诊记录、诊断、处方, icon: <FileSearchOutlined className="text-xl text-[#1890ff]" /> },
{
title
:
'
问诊数据
'
,
desc
:
'
问诊记录、诊断、处方
'
,
icon
:
<
FileSearchOutlined
className=
"text-xl text-[#1890ff]"
/>
},
{
title
:
'
用户数据
'
,
desc
:
'
患者和医生基本信息
'
,
icon
:
<
UserOutlined
className=
"text-xl text-green-500"
/>
},
{ title:
'
处方数据
'
, desc:
'
处方记录、用药统计
,
icon
:
<
AuditOutlined
className=
"text-xl text-purple-500"
/>
},
{
title
:
'
系统日志
'
,
desc
:
'
操作日志和审计记录, icon: <SettingOutlined className="text-xl text-orange-500" /> },
{
title
:
'
处方数据
'
,
desc
:
'
处方记录、用药统计
'
,
icon
:
<
AuditOutlined
className=
"text-xl text-purple-500"
/>
},
{
title
:
'
系统日志
'
,
desc
:
'
操作日志和审计记录
'
,
icon
:
<
SettingOutlined
className=
"text-xl text-orange-500"
/>
},
].
map
((
item
,
i
)
=>
(
<
Col
span=
{
6
}
key=
{
i
}
>
<
Card
size=
"small"
hoverable
className=
"text-center"
onClick=
{
()
=>
setExportModalVisible
(
true
)
}
>
...
...
@@ -205,7 +205,7 @@ const AdminCompliancePage: React.FC = () => {
<
Card
size=
"small"
>
<
div
className=
"flex items-center justify-between mb-2"
>
<
span
className=
"text-xs text-gray-400"
>
管理合规报告,支持下载和上报
</
span
>
<
Button
type=
"primary"
size=
"small"
>
生成新报
</
Button
>
<
Button
type=
"primary"
size=
"small"
>
生成新报
告
</
Button
>
</
div
>
<
Table
columns=
{
reportColumns
}
dataSource=
{
mockReports
}
rowKey=
"id"
size=
"small"
pagination=
{
false
}
/>
</
Card
>
...
...
@@ -229,7 +229,7 @@ const AdminCompliancePage: React.FC = () => {
<
div
>
<
Text
strong
className=
"text-xs!"
>
数据脱敏
</
Text
>
<
Select
defaultValue=
"partial"
size=
"small"
style=
{
{
width
:
'
100%
'
,
marginTop
:
4
}
}
options=
{
[
{
label
:
'
部分脱敏(推荐)
'
,
value
:
'
partial
'
},
{
label
:
'
完全脱敏
'
,
value
:
'
full
'
},
{
label
:
'
不脱敏, value:
'
none
'
},
{
label
:
'
部分脱敏(推荐)
'
,
value
:
'
partial
'
},
{
label
:
'
完全脱敏
'
,
value
:
'
full
'
},
{
label
:
'
不脱敏
'
,
value
:
'
none
'
},
]
}
/>
</
div
>
</
div
>
...
...
web/src/pages/admin/Consultations/index.tsx
View file @
e15d3424
...
...
@@ -143,7 +143,7 @@ const AdminConsultationsPage: React.FC = () => {
<Card size="small">
<div className="flex flex-wrap items-center gap-2">
<Input
placeholder="搜索患
?
医生"
placeholder="搜索患
者/
医生"
prefix={<SearchOutlined />}
style={{ width: 160 }}
size="small"
...
...
web/src/pages/admin/Dashboard/index.tsx
View file @
e15d3424
...
...
@@ -78,7 +78,7 @@ const AdminDashboardPage: React.FC = () => {
))
}
</
Row
>
{
/* 平台概况 + 待审
?
*/
}
{
/* 平台概况 + 待审
核
*/
}
<
Row
gutter=
{
[
8
,
8
]
}
>
<
Col
xs=
{
24
}
sm=
{
12
}
>
<
Card
title=
{
<
span
className=
"text-xs font-semibold"
>
平台概况
</
span
>
}
size=
"small"
className=
"h-full!"
>
...
...
web/src/pages/admin/Doctors/index.tsx
View file @
e15d3424
...
...
@@ -107,7 +107,7 @@ const AdminDoctorsPage: React.FC = () => {
const
newStatus
=
record
.
user_status
===
'
active
'
?
'
disabled
'
:
'
active
'
;
Modal
.
confirm
({
title
:
`确认
${
newStatus
===
'
disabled
'
?
'
禁用
'
:
'
启用
'
}
该医生?`
,
content
:
`医生
?{record.name}
`
,
content
:
`医生
「{record.name}」
`
,
onOk
:
async
()
=>
{
try
{
await
adminApi
.
updateUserStatus
(
record
.
user_id
,
newStatus
);
...
...
@@ -123,7 +123,7 @@ const AdminDoctorsPage: React.FC = () => {
const
handleResetPassword
=
(
record
:
DoctorItem
)
=>
{
Modal
.
confirm
({
title
:
'
确认重置密码码,
content: `将重置医
?
${record.name} 的密码为默认密码 123456`,
content: `将重置医
生
${record.name} 的密码为默认密码 123456`,
onOk: async () => {
try {
await adminApi.resetUserPassword(record.user_id);
...
...
@@ -182,7 +182,7 @@ const AdminDoctorsPage: React.FC = () => {
const handleDeleteDoctor = (record: DoctorItem) => {
Modal.confirm({
title:
'
确认删除该医生?
'
,
content: `医生
?{record.name}
,删除后不可恢复`,
content: `医生
「{record.name}」
,删除后不可恢复`,
okType:
'
danger
'
,
onOk: async () => {
try {
...
...
web/src/pages/admin/Patients/index.tsx
View file @
e15d3424
...
...
@@ -65,7 +65,7 @@ const AdminPatientsPage: React.FC = () => {
const handleResetPassword = (record: UserInfo) => {
Modal.confirm({
title:
'
确认重置密码码
,
content
:
`将重置患
?
${
record
.
real_name
||
record
.
phone
}
的密码为默认密码`
,
content
:
`将重置患
者
${
record
.
real_name
||
record
.
phone
}
的密码为默认密码`
,
onOk
:
async
()
=>
{
try
{
await
adminApi
.
resetPatientPassword
(
record
.
id
);
...
...
web/src/pages/admin/Pharmacy/index.tsx
View file @
e15d3424
...
...
@@ -172,7 +172,7 @@ const AdminPharmacyPage: React.FC = () => {
</Row>
{(stats.low_stock_count > 0 || stats.out_of_stock_count > 0) && (
<Alert message={`当前有
?
${stats.low_stock_count} 种药品库存不足,${stats.out_of_stock_count} 种药品缺货,请及时补货}
<Alert message={`当前有
${stats.low_stock_count} 种药品库存不足,${stats.out_of_stock_count} 种药品缺货,请及时补货}
type="warning" showIcon className="text-xs!" />
)}
...
...
@@ -195,7 +195,7 @@ const AdminPharmacyPage: React.FC = () => {
<Button type="primary" size="small" icon={<PlusOutlined />} onClick={handleAddDrug}>添加鑽搧</Button>
</div>
<Table columns={drugColumns} dataSource={drugs} rowKey="id" loading={loading} size="small"
pagination={{ current: page, total, pageSize: 10, size:
'
small
'
, onChange: setPage, showTotal: (t) => `共
?${t} 种峘
}} />
pagination={{ current: page, total, pageSize: 10, size:
'
small
'
, onChange: setPage, showTotal: (t) => `共
${t} 种
}} />
</Card>
),
},
...
...
web/src/pages/admin/Prescription/index.tsx
View file @
e15d3424
...
...
@@ -159,7 +159,7 @@ const AdminPrescriptionPage: React.FC = () => {
children
:
(
<
Card
size=
"small"
>
<
div
className=
"flex flex-wrap items-center gap-2 mb-2"
>
<
Input
prefix=
{
<
SearchOutlined
/>
}
placeholder=
"搜索处方/患
?
医生"
style=
{
{
width
:
180
}
}
size=
"small"
allowClear
<
Input
prefix=
{
<
SearchOutlined
/>
}
placeholder=
"搜索处方/患
者/
医生"
style=
{
{
width
:
180
}
}
size=
"small"
allowClear
value=
{
keyword
}
onChange=
{
(
e
)
=>
setKeyword
(
e
.
target
.
value
)
}
onPressEnter=
{
()
=>
{
setPage
(
1
);
fetchList
();
}
}
/>
<
RangePicker
size=
"small"
/>
<
Button
type=
"primary"
size=
"small"
ghost
onClick=
{
()
=>
{
setPage
(
1
);
fetchList
();
}
}
>
查询
</
Button
>
...
...
web/src/pages/admin/Statistics/index.tsx
View file @
e15d3424
...
...
@@ -41,14 +41,14 @@ const AdminStatisticsPage: React.FC = () => {
<
Col
span=
{
12
}
>
<
Card
title=
{
<
span
className=
"text-xs font-semibold"
>
问诊趋势
</
span
>
}
size=
"small"
style=
{
{
minHeight
:
280
}
}
>
<
div
className=
"flex justify-center items-center h-52 rounded-lg"
style=
{
{
background
:
'
linear-gradient(135deg, #f5f7fa 0%, #e8edf5 100%)
'
}
}
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
问诊趋势图表 (待集
?
ECharts)
</
Text
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
问诊趋势图表 (待集
成
ECharts)
</
Text
>
</
div
>
</
Card
>
</
Col
>
<
Col
span=
{
12
}
>
<
Card
title=
{
<
span
className=
"text-xs font-semibold"
>
收入趋势
</
span
>
}
size=
"small"
style=
{
{
minHeight
:
280
}
}
>
<
div
className=
"flex justify-center items-center h-52 rounded-lg"
style=
{
{
background
:
'
linear-gradient(135deg, #f5f7fa 0%, #e8edf5 100%)
'
}
}
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
收入趋势图表 (待集
?
ECharts)
</
Text
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
收入趋势图表 (待集
成
ECharts)
</
Text
>
</
div
>
</
Card
>
</
Col
>
...
...
@@ -58,14 +58,14 @@ const AdminStatisticsPage: React.FC = () => {
<
Col
span=
{
12
}
>
<
Card
title=
{
<
span
className=
"text-xs font-semibold"
>
科室问诊分布
</
span
>
}
size=
"small"
style=
{
{
minHeight
:
280
}
}
>
<
div
className=
"flex justify-center items-center h-52 rounded-lg"
style=
{
{
background
:
'
linear-gradient(135deg, #f5f7fa 0%, #e8edf5 100%)
'
}
}
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
科室分布饼图 (待集
?
ECharts)
</
Text
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
科室分布饼图 (待集
成
ECharts)
</
Text
>
</
div
>
</
Card
>
</
Col
>
<
Col
span=
{
12
}
>
<
Card
title=
{
<
span
className=
"text-xs font-semibold"
>
医生排名
</
span
>
}
size=
"small"
style=
{
{
minHeight
:
280
}
}
>
<
div
className=
"flex justify-center items-center h-52 rounded-lg"
style=
{
{
background
:
'
linear-gradient(135deg, #f5f7fa 0%, #e8edf5 100%)
'
}
}
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
医生排名列表 (待集
?
ECharts)
</
Text
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
医生排名列表 (待集
成
ECharts)
</
Text
>
</
div
>
</
Card
>
</
Col
>
...
...
web/src/pages/admin/Users/index.tsx
View file @
e15d3424
...
...
@@ -57,7 +57,7 @@ const AdminUsersPage: React.FC = () => {
const
handleResetPassword
=
(
userId
:
string
)
=>
{
Modal
.
confirm
({
title
:
'
确认重置
'
,
content
:
'
确定要重置该用户的密码吗?重置后密码
?
23456
'
,
content
:
'
确定要重置该用户的密码吗?重置后密码
为 1
23456
'
,
onOk
:
async
()
=>
{
try
{
await
adminApi
.
resetUserPassword
(
userId
);
...
...
@@ -74,7 +74,7 @@ const AdminUsersPage: React.FC = () => {
const newStatus = currentStatus ===
'
active
'
?
'
disabled
'
:
'
active
'
;
Modal.confirm({
title: `确认${action}`,
content: `确定
?
{action}该用户吗?`,
content: `确定
$
{action}该用户吗?`,
onOk: async () => {
try {
await adminApi.updateUserStatus(userId, newStatus);
...
...
@@ -162,7 +162,7 @@ const AdminUsersPage: React.FC = () => {
<
Card
size=
"small"
>
<
div
className=
"flex flex-wrap items-center gap-2"
>
<
Input
placeholder=
"搜索用户
?手机构
prefix={<SearchOutlined />} value={keyword}
<
Input
placeholder=
"搜索用户
名/手机号
prefix={<SearchOutlined />} value={keyword}
onChange={(e) => setKeyword(e.target.value)} style={{ width: 180 }} size="
small
"
/>
<
Select
placeholder=
"角色"
style=
{
{
width
:
100
}
}
size=
"small"
allowClear
value=
{
roleFilter
}
onChange=
{
(
v
)
=>
setRoleFilter
(
v
)
}
options=
{
[
...
...
web/src/pages/doctor/AIAssist/index.tsx
View file @
e15d3424
...
...
@@ -45,23 +45,23 @@ const AIAssistPage: React.FC = () => {
const
handleAnalyze
=
async
()
=>
{
if
(
!
symptoms
.
trim
())
return
;
setLoading
(
true
);
// TODO:
璋冪敤鍚庣 AI 诊断鎺ュ彛
// TODO:
调用后端 AI 诊断接口
// 模拟数据
setTimeout
(()
=>
{
setDiagnoses
([
{
disease
:
'
急性上呼吸道感染
, probability: 85, description:
'
男辩梾姣掑紩璧风殑涓婂懠鍚搁亾鐐庣棁锛屽父瑙佺棁鐘朵负榧诲銆佹祦娑曘€佸捊鐥涖€佸挸鍡界瓑
'
, icd_code:
'
J06
.
9
'
},
{ disease:
'
鎬ユ€ф敮姘旂鐐
,
probability
:
45
,
description
:
'
支皵绠¢粡鑶滅殑鎬ユ€х値鐥囷紝甯哥户号戜簬涓婂懠鍚搁亾鎰熸煋
'
,
icd_code
:
'
J20.9
'
},
{
disease
:
'
过敏性鼻炎, probability: 30, description:
'
鼻黏膜非感染性炎性疾病,以阵发性喷嚏、清水样鼻涕等为特征
'
, icd_code:
'
J30
.
4
'
},
{
disease
:
'
急性上呼吸道感染
'
,
probability
:
85
,
description
:
'
鼻黏膜非感染性炎性疾病,以阵发性喷嚏、清水样鼻涕等为特征
'
,
icd_code
:
'
J06.9
'
},
{
disease
:
'
急性支气管炎
'
,
probability
:
45
,
description
:
'
支气管黏膜非感染性炎性疾病,以咳嗽、咳痰等为特征
'
,
icd_code
:
'
J20.9
'
},
{
disease
:
'
过敏性鼻炎
'
,
probability
:
30
,
description
:
'
鼻黏膜非感染性炎性疾病,以阵发性喷嚏、清水样鼻涕等为特征
'
,
icd_code
:
'
J30.4
'
},
]);
setSimilarCases
([
{ id:
'
1
'
, summary:
'
患者,男,
35
岁,咳嗽、流涕
5
天,体温
37.8
°
C
'
, diagnosis:
'
急性上呼吸道感染
,
treatment
:
'
瀵圭棁治疗:屽彛鏈嶅竷娲涜姮閫€鐑紝姘ㄦ捍绱㈠寲鐥
, similarity: 92 },
{ id:
'
2
'
, summary:
'
患者
咃紝女筹紝
28
岁侊紝鍜界棝銆侀蓟濉
?
澶╋紝浼磋交寰挸鍡
,
diagnosis
:
'
急性上呼吸道感染, treatment:
'
口服娓呭紑鐏甸绮掞紝鍚湇瑗跨摐闇滃惈鐗
,
similarity
:
87
},
{
id
:
'
3
'
,
summary
:
'
患者
咃紝男凤紝42岁侊紝鍜冲椊1鍛紝鐥版恫榛勮壊
'
,
diagnosis
:
'
鎬ユ€ф敮姘旂鐐, treatment:
'
口服闃胯帿瑗挎灄
+
姘ㄦ捍绱紝雾化吸入甯冨湴女堝痉
'
, similarity: 75 },
{
id
:
'
1
'
,
summary
:
'
患者,男,35岁,咳嗽、流涕5天,体温37.8°C
'
,
diagnosis
:
'
急性上呼吸道感染
'
,
treatment
:
'
对症治疗,口服布洛芬退热,氨溴索化痰
'
,
similarity
:
92
},
{
id
:
'
2
'
,
summary
:
'
患者
,女,28岁,咽痛、鼻塞3天,伴轻微咳嗽
'
,
diagnosis
:
'
急性上呼吸道感染
'
,
treatment
:
'
口服清开灵颗粒,含服西瓜霜含片
'
,
similarity
:
87
},
{
id
:
'
3
'
,
summary
:
'
患者
,男,42岁,咳嗽1周,痰液黄色
'
,
diagnosis
:
'
急性支气管炎
'
,
treatment
:
'
口服阿莫西林+氨溴索,雾化吸入布地奈德
'
,
similarity
:
75
},
]);
setDrugSuggestions
([
{ name:
'
甯冩礇鑺紦閲婅兌鍥
,
specification
:
'
0.3g/粒, usage:
'
口服
'
, dosage:
'
姣忔
1
绮掞紝每日
2
次
,
caution
:
'
胃溃疡患者慎用
},
{ name:
'
氨溴索口服液
'
, specification:
'
100
ml
/
瓶
,
usage
:
'
口服
'
,
dosage
:
'
姣忔10ml锛屾瘡无娆, caution:
'
瀛曟棭鏈熺男
},
{
name
:
'
瀵逛箼閰版皑鍩洪厷鐗, specification:
'
0.5
g
/
片
,
usage
:
'
口服
'
,
dosage
:
'
姣忔1鐗囷紝号戠儹无舵湇男紝闂撮殧4-6小时
'
,
caution
:
'
肝功能不全者慎用
},
{
name
:
'
布洛芬缓释胶囊
'
,
specification
:
'
0.3g/粒
'
,
usage
:
'
口服
'
,
dosage
:
'
每次1粒,每日2次
'
,
caution
:
'
胃溃疡患者慎用
'
},
{
name
:
'
氨溴索口服液
'
,
specification
:
'
100ml/瓶
'
,
usage
:
'
口服
'
,
dosage
:
'
每次10ml,每日3次
'
,
caution
:
'
孕早期禁用
'
},
{
name
:
'
对乙酰氨基酚片
'
,
specification
:
'
0.5g/片
'
,
usage
:
'
口服
'
,
dosage
:
'
每次1片,发热时服用,间隔4-6小时
'
,
caution
:
'
肝功能不全者慎用
'
},
]);
setLoading
(
false
);
},
1500
);
...
...
@@ -79,7 +79,7 @@ const AIAssistPage: React.FC = () => {
<
RobotOutlined
className=
"mr-1"
/>
AI 辅助诊断
</
h4
>
<Alert message="AI 辅助诊断
浠呬緵号傝€冿紝鏈€缁堣瘖鏂浠ュ尰男熶复搴婂垽鏂负鍑
type="info" showIcon className="text-xs!" />
<
Alert
message=
"AI 辅助诊断
仅供参考,最终诊断以医生临床判断为准"
type=
"info"
showIcon
className=
"text-xs!"
/>
<
Card
size=
"small"
>
<
div
className=
"text-xs font-semibold mb-1"
>
症状描述
</
div
>
...
...
@@ -116,7 +116,7 @@ const AIAssistPage: React.FC = () => {
</
Row
>
</
Card
>
)
}
/>
) : <Empty description="请输入
ョ棁鐘舵弿杩板悗杩涜鍒嗘瀽
" />,
)
:
<
Empty
description=
"请输入
症状描述后进行分析
"
/>,
},
{
key
:
'
cases
'
,
...
...
@@ -126,7 +126,7 @@ const AIAssistPage: React.FC = () => {
<
Card
size=
"small"
className=
"mb-2"
hoverable
>
<
Row
gutter=
{
8
}
>
<
Col
flex=
"auto"
>
<div className="text-xs mb-0.5"><Text strong>
鎽樿锛
</Text>{item.summary}</div>
<
div
className=
"text-xs mb-0.5"
><
Text
strong
>
摘要:
</
Text
>
{
item
.
summary
}
</
div
>
<
div
className=
"text-xs mb-0.5"
><
Text
strong
>
诊断:
</
Text
><
Tag
color=
"blue"
>
{
item
.
diagnosis
}
</
Tag
></
div
>
<
div
className=
"text-xs"
><
Text
strong
>
治疗:
</
Text
>
{
item
.
treatment
}
</
div
>
</
Col
>
...
...
@@ -136,11 +136,11 @@ const AIAssistPage: React.FC = () => {
</
Row
>
</
Card
>
)
}
/>
) : <Empty description="请输入
ョ棁鐘舵弿杩板悗杩涜鍒嗘瀽
" />,
)
:
<
Empty
description=
"请输入
症状描述后进行分析
"
/>,
},
{
key
:
'
drugs
'
,
label: <span><MedicineBoxOutlined /> 用药建议
</span>,
label
:
<
span
><
MedicineBoxOutlined
/>
用药建议
</
span
>,
children
:
drugSuggestions
.
length
>
0
?
(
<
List
size=
"small"
dataSource=
{
drugSuggestions
}
renderItem=
{
(
item
)
=>
(
<
Card
size=
"small"
className=
"mb-2"
hoverable
>
...
...
@@ -154,7 +154,7 @@ const AIAssistPage: React.FC = () => {
</
Row
>
</
Card
>
)
}
/>
) : <Empty description="请输入
ョ棁鐘舵弿杩板悗杩涜鍒嗘瀽
" />,
)
:
<
Empty
description=
"请输入
症状描述后进行分析
"
/>,
},
]
}
/>
)
}
...
...
web/src/pages/doctor/Certification/index.tsx
View file @
e15d3424
...
...
@@ -96,7 +96,7 @@ const DoctorCertificationPage: React.FC = () => {
</
div
>
<
h3
className=
"text-base font-bold mb-2"
>
认证申请已提
</
h3
>
<
Paragraph
type=
"secondary"
className=
"text-xs!"
>
您的资质认证申请已成功提交,管理员将
?1-3 个工作日内完成审核实
</
Paragraph
>
您的资质认证申请已成功提交,管理员将
在 1-3 个工作日内完成审核。
</
Paragraph
>
<
Button
type=
"primary"
size=
"small"
onClick=
{
()
=>
router
.
push
(
'
/doctor/workbench
'
)
}
>
返回工作台
</
Button
>
</
div
>
</
Card
>
...
...
web/src/pages/doctor/Consult/index.tsx
View file @
e15d3424
...
...
@@ -108,7 +108,7 @@ const ConsultPage: React.FC = () => {
status
:
'
in_progress
'
,
});
setPreConsultReport
(
null
);
message
.
success
(
`已接诊患
?
${
patient
.
patient_name
||
'
患
}`);
message
.
success
(
`已接诊患
者
${
patient
.
patient_name
||
'
未知
'
}
`
);
fetchPreConsultReport
(
patient
.
consult_id
,
patient
.
patient_id
);
fetchPatientList
();
fetchMessages
(
patient
.
consult_id
);
...
...
@@ -140,7 +140,7 @@ const ConsultPage: React.FC = () => {
console.log(
'
handleReject
被调整
,
patient
);
Modal
.
confirm
({
title
:
'
确认拒诊
'
,
content: `
确定拒绝接诊患
?
$
{
patient
.
patient_name
||
'
患
}?`,
content
:
`确定拒绝接诊患
者
${
patient
.
patient_name
||
'
未知
'
}
?`
,
onOk
:
async
()
=>
{
try
{
console
.
log
(
'
调用 rejectConsult API
'
,
patient
.
consult_id
);
...
...
web/src/pages/doctor/ConsultRoom/index.tsx
View file @
e15d3424
...
...
@@ -23,7 +23,7 @@ const DoctorConsultRoomPage: React.FC = () => {
type
:
'
text
'
,
status
:
'
in_progress
'
,
chief_complaint
:
'
头痛、乏力,持续3天数,
medical_history:
'
高血压病
?
年,长期服用降压药物
,
medical_history:
'
高血压病
史
5
年,长期服用降压药物
'
,
};
const messages = [
...
...
@@ -52,7 +52,7 @@ const DoctorConsultRoomPage: React.FC = () => {
age
:
45
,
gender
:
'
?,
phone:
'
138
****
8888
'
,
medical_history
:
'
高血压病
??
,
medical_history:
'
高血压病
史
5
年
,
allergy_history
:
'
青霉素过敏,
consultation_count: 3,
};
...
...
web/src/pages/doctor/Prescription/index.tsx
View file @
e15d3424
...
...
@@ -162,9 +162,9 @@ const PrescriptionModal: React.FC<PrescriptionModalProps> = ({
),
},
{
title:
'
鍗曟鍓傞噺
'
, dataIndex:
'
dosage
'
, key:
'
dosage
'
, width: 110,
title:
'
单次剂量
'
, dataIndex:
'
dosage
'
, key:
'
dosage
'
, width: 110,
render: (_: string, r: DrugItem) => (
<Input size="small" placeholder="如 1粒 value={r.dosage}
<Input size="small" placeholder="如 1粒
"
value={r.dosage}
onChange={(e) => handleDrugChange(r.key,
'
dosage
'
, e.target.value)} />
),
},
...
...
@@ -270,16 +270,16 @@ const PrescriptionModal: React.FC<PrescriptionModalProps> = ({
</
div
>
<
/
Modal
>
<
Modal
title=
"CA 签名确认
"
open=
{
signModalVisible
}
onOk=
{
handleConfirmSign
}
onCancel=
{
()
=>
setSignModalVisible
(
false
)
}
okText=
"确
绛惧悕
"
cancelText=
"取消"
<
Modal
title=
"CA 签名确认"
open=
{
signModalVisible
}
onOk=
{
handleConfirmSign
}
onCancel=
{
()
=>
setSignModalVisible
(
false
)
}
okText=
"确
认签名
"
cancelText=
"取消"
confirmLoading=
{
submitting
}
width=
{
380
}
>
<
div
className=
"text-center py-3"
>
<
SafetyCertificateOutlined
className=
"text-3xl text-green-500 mb-2"
/>
<
div
className=
"text-xs font-semibold mb-1"
>
确
浣跨敤 CA 鏁板瓧璇佷功绛剧讲姝ゅ鏂癸紵
</
div
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
绛剧讲鍚庡鏂瑰皢共锋湁娉曞緥鏁堝姏
</
Text
>
<
div
className=
"text-xs font-semibold mb-1"
>
确
认使用 CA 数字证书签署此处方?
</
div
>
<
Text
type=
"secondary"
className=
"text-xs!"
>
签署后处方将具有法律效力
</
Text
>
<
div
className=
"mt-3 p-2 bg-green-50 rounded text-xs"
>
<
div
>
鑽搧锛?
Text strong
>
{
drugs
.
length
}
</
Text
>
种
</
div
>
<
div
>
閲戦锛?Text strong className="text-red-500!"
>
楼
{
(
totalAmount
/
100
).
toFixed
(
2
)
}
</
Text
></
div
>
<
div
>
药品:
<
Text
strong
>
{
drugs
.
length
}
</
Text
>
种
</
div
>
<
div
>
金额:
<
Text
strong
className=
"text-red-500!"
>
¥
{
(
totalAmount
/
100
).
toFixed
(
2
)
}
</
Text
></
div
>
</
div
>
</
div
>
</
Modal
>
...
...
web/src/pages/patient/Doctors/index.tsx
View file @
e15d3424
...
...
@@ -95,7 +95,7 @@ const PatientDoctorsPage: React.FC = () => {
<
div
className=
"text-xs text-gray-400 mb-1"
>
{
doctor
.
hospital
}
</
div
>
<
div
className=
"flex items-center gap-2 mb-1"
>
<
Rate
disabled
defaultValue=
{
doctor
.
rating
}
className=
"text-xs!"
/>
<
span
className=
"text-[11px] text-gray-400"
>
问诊
?
{
doctor
.
consult_count
}
</
span
>
<
span
className=
"text-[11px] text-gray-400"
>
问诊
{
doctor
.
consult_count
}
</
span
>
</
div
>
<
div
className=
"text-xs text-gray-500 line-clamp-1"
>
擅长:
{
doctor
.
specialties
?.
join
(
'
?) || doctor.introduction
}
...
...
web/src/pages/patient/PreConsult/index.tsx
View file @
e15d3424
...
...
@@ -175,7 +175,7 @@ const PatientPreConsultPage: React.FC = () => {
const welcomeMsg: DisplayMessage = {
id:
'
welcome
'
,
role:
'
assistant
'
,
content: `您好
?{patientInfo.name}!我是AI预问诊助手。
\n\n
请告诉我您目前的主要症状或不适,我会根据您的描述进一步了解情况,帮助您更好地就诊。
\n\n
比如您可以说?
我头痛三天了,还有点恶心"`,
content: `您好
,{patientInfo.name}!我是AI预问诊助手。
\n\n
请告诉我您目前的主要症状或不适,我会根据您的描述进一步了解情况,帮助您更好地就诊。
\n\n
比如您可以说:"
我头痛三天了,还有点恶心"`,
timestamp: Date.now(),
};
setMessages([welcomeMsg]);
...
...
@@ -253,7 +253,7 @@ const PatientPreConsultPage: React.FC = () => {
onError: (error) => {
setMessages(prev => prev.map(m =>
m.id === aiMsgId
? { ...m, content: `抱歉,出现了错误
?
{error}`, loading: false }
? { ...m, content: `抱歉,出现了错误
:
{error}`, loading: false }
: m
));
setSending(false);
...
...
@@ -494,7 +494,7 @@ const PatientPreConsultPage: React.FC = () => {
<div style={{ fontSize: 16, fontWeight: 600 }}>AI 智能预问诊</div>
<div style={{ fontSize: 12, opacity: 0.85 }}>
{sessionStatus ===
'
completed
'
?
'
问诊已完成
:
sessionStatus
===
'
in_progress
'
?
`对话
?· ?
{turnCount}轮`
:
sessionStatus
===
'
in_progress
'
?
`对话
中 · 第
{turnCount}轮`
:
'
请先填写基本信息
'
}
<
/
div
>
</
div
>
...
...
@@ -582,7 +582,7 @@ const PatientPreConsultPage: React.FC = () => {
const
welcomeMsg
:
DisplayMessage
=
{
id
:
'
welcome
'
,
role
:
'
assistant
'
,
content
:
`您好
?{patientInfo.name}!我是AI预问诊助手。\n\n请告诉我您目前的主要症状或不适,我会根据您的描述进一步了解情况,帮助您更好地就诊。\n\n比如您可以说?
我头痛三天了,还有点恶心"`
,
content
:
`您好
,{patientInfo.name}!我是AI预问诊助手。\n\n请告诉我您目前的主要症状或不适,我会根据您的描述进一步了解情况,帮助您更好地就诊。\n\n比如您可以说:"
我头痛三天了,还有点恶心"`
,
timestamp
:
Date
.
now
(),
};
setMessages
([
welcomeMsg
]);
...
...
@@ -610,7 +610,7 @@ const PatientPreConsultPage: React.FC = () => {
>
{
turnCount
>=
2
&&
!
generating
&&
!
reportDone
&&
(
<
Alert
message=
"对话信息已足够,您可以点击右上角「结束对
?
· 生成报告」获取分析结果,或继续补充信息。
message=
"对话信息已足够,您可以点击右上角「结束对
话
· 生成报告」获取分析结果,或继续补充信息。
type="
info
"
showIcon
style=
{
{
marginBottom
:
12
,
borderRadius
:
8
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment