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
Expand all
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
This diff is collapsed.
Click to expand it.
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