Commit e15d3424 authored by yuguo's avatar yuguo

fix

parent 7880e29e
This diff is collapsed.
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('\nFixed: ' + 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 (/[\u4e00-\u9fff]\?/.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('\nSTILL BROKEN: ' + path.relative(__dirname, f) + ' (lines:' + issues.length + ' gbk:' + gbkCount + ')');
issues.slice(0, 5).forEach(x => console.log(' ' + x));
}
}
console.log('\n\nTotal still broken: ' + broken);
......@@ -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}
......
......@@ -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>
......
......@@ -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"
......
......@@ -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!">
......
......@@ -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 {
......
......@@ -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);
......
......@@ -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>
),
},
......
......@@ -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>
......
......@@ -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>
......
......@@ -57,7 +57,7 @@ const AdminUsersPage: React.FC = () => {
const handleResetPassword = (userId: string) => {
Modal.confirm({
title: '确认重置',
content: '确定要重置该用户的密码吗?重置后密码?23456',
content: '确定要重置该用户的密码吗?重置后密码为 123456',
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={[
......
......@@ -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: '100ml/, usage: '口服', dosage: '姣忔10ml锛屾瘡无娆, caution: '瀛曟棭鏈熺男 },
{ name: '瀵逛箼閰版皑鍩洪厷鐗, specification: '0.5g/, 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="请输入症状描述后进行分析" />,
},
]} />
)}
......
......@@ -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>
......
......@@ -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);
......
......@@ -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,
};
......
......@@ -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>
......
......@@ -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}
......
......@@ -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 }}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment