Commit e15d3424 authored by yuguo's avatar yuguo

fix

parent 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(/([\u4e00-\u9fff])\?([,}'"\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(/([\u4e00-\u9fff]+)\?\/([\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(/[\u0200-\u06FF]{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('\nTotal fixed: ' + fixedCount);
// Verify
let broken = 0;
for (const f of files) {
const text = fs.readFileSync(f, 'utf8');
const p1 = text.match(/[\u4e00-\u9fff]\?[,}'"\s\/)<>;]/g) || [];
const p2 = text.match(/[\u0200-\u06FF]{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('\nStill broken: ' + broken);
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