diff --git a/healthlink-his-ui/src/i18n/autoTranslate.js b/healthlink-his-ui/src/i18n/autoTranslate.js index 4d1109273..acd709951 100644 --- a/healthlink-his-ui/src/i18n/autoTranslate.js +++ b/healthlink-his-ui/src/i18n/autoTranslate.js @@ -260,7 +260,96 @@ const translationDict = { "ESB": "ESB", "FHIR": "FHIR", "LIS": "LIS", - "PACS": "PACS" + "PACS": "PACS", + // 常见字典标签 + "一类": "Type I", "二类": "Type II", "三类": "Type III", "四类": "Type IV", + "全麻": "General Anesthesia", "局麻": "Local Anesthesia", "椎管内麻醉": "Spinal Anesthesia", + "神经阻滞": "Nerve Block", "复合麻醉": "Combined Anesthesia", + "甲": "Class A", "乙": "Class B", "丙": "Class C", + "自费": "Self-Pay", "公费": "Public Funded", "医保": "Medical Insurance", + "现金": "Cash", "微信": "WeChat", "支付宝": "Alipay", "银行卡": "Bank Card", + "医保卡": "Insurance Card", "扫码支付": "QR Payment", + "初诊": "First Visit", "复诊": "Follow-up Visit", "急诊": "Emergency", + "普通号": "Regular", "专家号": "Specialist", + "门诊": "Outpatient", "住院": "Inpatient", + "待诊": "Waiting", "就诊中": "In Consultation", "已完成": "Completed", + "已签到": "Checked In", "已预约": "Booked", "已退号": "Cancelled", + "男性": "Male", "女性": "Female", + "已婚": "Married", "未婚": "Single", "离异": "Divorced", "丧偶": "Widowed", + "汉族": "Han", "回族": "Hui", "藏族": "Tibetan", "维吾尔族": "Uyghur", + "A型": "Type A", "B型": "Type B", "AB型": "Type AB", "O型": "Type O", + "Rh阳性": "Rh+", "Rh阴性": "Rh-", + "抗生素": "Antibiotic", "非抗生素": "Non-Antibiotic", + "限制使用": "Restricted", "非限制使用": "Unrestricted", "特殊使用": "Special Use", + "口服": "Oral", "注射": "Injection", "外用": "Topical", "吸入": "Inhalation", + "静脉注射": "IV", "肌肉注射": "IM", "皮下注射": "SC", "静脉滴注": "IV Drip", + "每日一次": "Once Daily", "每日两次": "Twice Daily", "每日三次": "Three Times Daily", + "必要时": "As Needed", "立即": "Immediately", "每晚": "Every Night", + "急诊手术": "Emergency Surgery", "择期手术": "Elective Surgery", + "一级手术": "Grade I", "二级手术": "Grade II", "三级手术": "Grade III", "四级手术": "Grade IV", + "Ⅰ级": "Grade I", "Ⅱ级": "Grade II", "Ⅲ级": "Grade III", "Ⅳ级": "Grade IV", + "切口甲级": "Grade A Incision", "切口乙级": "Grade B Incision", "切口丙级": "Grade C Incision", + "甲级愈合": "Grade A Healing", "乙级愈合": "Grade B Healing", "丙级愈合": "Grade C Healing", + "清醒": "Awake", "嗜睡": "Drowsy", "昏迷": "Coma", "模糊": "Confused", + "普通病房": "General Ward", "ICU": "ICU", "手术室": "Operating Room", + "已入院": "Admitted", "已出院": "Discharged", "转科": "Transfer", + "待执行": "Pending", "执行中": "In Progress", "已执行": "Executed", "已停止": "Stopped", + "长期医嘱": "Long-term Order", "临时医嘱": "Temporary Order", + "待签发": "Pending Sign", "已签发": "Signed", "待校对": "Pending Proofread", "已校对": "Proofread", + "阳性": "Positive", "阴性": "Negative", + "合格": "Qualified", "不合格": "Unqualified", + "空气中": "Air", "物体表面": "Surface", "手卫生": "Hand Hygiene", + "消毒液": "Disinfectant", "医疗器械": "Medical Device", + "红细胞悬液": "RBC Suspension", "血浆": "Plasma", "血小板": "Platelet", "冷沉淀": "Cryoprecipitate", "全血": "Whole Blood", + "输血前": "Pre-Transfusion", "输血中": "During Transfusion", "输血后": "Post-Transfusion", + "一级预警": "Level 1 Alert", "二级预警": "Level 2 Alert", "三级预警": "Level 3 Alert", + "黄色预警": "Yellow Alert", "红色预警": "Red Alert", + "呼吸道传染病": "Respiratory", "肠道传染病": "Intestinal", "血源性传染病": "Blood-borne", + "轻度": "Mild", "中度": "Moderate", "重度": "Severe", + "待处理": "Pending", "处理中": "Processing", "已处理": "Handled", "已排除": "Excluded", + "未隔离": "Not Isolated", "已隔离": "Isolated", "已解除": "Released", + "Ⅰ级 濒死": "Level I Critical", "Ⅱ级 危重": "Level II Emergent", + "Ⅲ级 急症": "Level III Urgent", "Ⅳ级 亚急": "Level IV Less Urgent", "Ⅴ级 非紧急": "Level V Non-Urgent", + "抢救区": "Rescue Zone", "留观区": "Observation Zone", "诊室": "Consult Room", "普通区": "General Zone", + "成功": "Success", "失败": "Failed", "进行中": "In Progress", + "住院": "Admit", "出院": "Discharge", "转科": "Transfer", "死亡": "Death", + "达标": "Achieved", "未达标": "Not Achieved", + "空气": "Air", "表面": "Surface", "手": "Hand", + "一级愈合": "Grade A", "二级愈合": "Grade B", "三级愈合": "Grade C", + "已挂号": "Registered", "进行中": "In Progress", "已退号": "Cancelled", + "日班": "Day Shift", "夜班": "Night Shift", "值班": "On Duty", + "主任医师": "Chief Physician", "副主任医师": "Associate Chief Physician", + "主治医师": "Attending Physician", "住院医师": "Resident", + "实习医师": "Intern", "进修医师": "Fellow", + "护士长": "Head Nurse", "主管护师": "Supervisor Nurse", "护师": "Nurse", "护士": "Junior Nurse", + "药品": "Drug", "诊疗": "Treatment", "耗材": "Consumable", "其他": "Other", + "西药": "Western Medicine", "中成药": "Chinese Patent Medicine", "中草药": "Chinese Herbal Medicine", + "注射液": "Injection", "片剂": "Tablet", "胶囊": "Capsule", "颗粒": "Granule", + "口服液": "Oral Liquid", "软膏": "Ointment", "滴眼液": "Eye Drops", + "国产": "Domestic", "进口": "Imported", + "甲类": "Class A", "乙类": "Class B", "丙类": "Class C", + "医保内": "Insurance Covered", "医保外": "Not Covered", + "集中采购": "Centralized Procurement", "自主采购": "Self Procurement", + "紧急采购": "Emergency Procurement", "常规采购": "Routine Procurement", + "入库": "Inbound", "出库": "Outbound", "调拨": "Transfer", "盘点": "Stocktaking", + "盈": "Profit", "亏": "Loss", "损": "Damage", + "门诊收费": "OPD Charge", "住院收费": "IPD Charge", "预交金": "Prepayment", + "已收费": "Charged", "未收费": "Uncharged", "已退费": "Refunded", + "待审批": "Pending Approval", "已审批": "Approved", "已驳回": "Rejected", + "电子处方": "E-Prescription", "纸质处方": "Paper Prescription", + "门诊处方": "OPD Prescription", "住院处方": "IPD Prescription", + "药品不良反应": "Drug ADR", "医疗器械不良事件": "Medical Device Adverse Event", + "新发": "New", "在治": "Under Treatment", "治愈": "Cured", "好转": "Improved", "未愈": "Not Cured", "死亡": "Death", + "门诊会诊": "OPD Consultation", "院内会诊": "In-hospital Consultation", "院际会诊": "Inter-hospital Consultation", + "急会诊": "Urgent Consultation", "普通会诊": "Routine Consultation", + "传染病报告": "Infectious Disease Report", "死亡报告": "Death Report", + "出生报告": "Birth Report", "肿瘤报告": "Tumor Report", + "Ⅰ类切口": "Class I Incision", "Ⅱ类切口": "Class II Incision", "Ⅲ类切口": "Class III Incision", + "无菌物品": "Sterile Items", "消毒物品": "Disinfected Items", + "手卫生依从": "HH Compliance", "手卫生不依从": "HH Non-Compliance", + "接触患者前": "Before Patient Contact", "接触患者后": "After Patient Contact", + "无菌操作前": "Before Aseptic Procedure", "体液暴露后": "After Body Fluid Exposure", + "接触患者周围环境后": "After Contact with Patient Environment" }, 'vi': { "新增": "Thêm", diff --git a/healthlink-his-ui/src/utils/dict.js b/healthlink-his-ui/src/utils/dict.js index 61ecdf49b..86a4ba34c 100755 --- a/healthlink-his-ui/src/utils/dict.js +++ b/healthlink-his-ui/src/utils/dict.js @@ -1,8 +1,10 @@ import useDictStore from '@/store/modules/dict' import {getDicts} from '@/api/system/dict/data' +import {autoTranslate} from '@/i18n/autoTranslate' +import Cookies from 'js-cookie' /** - * 获取字典数据 + * 获取字典数据(支持多语言自动翻译) */ export function useDict(...args) { const res = ref({}); @@ -14,7 +16,14 @@ export function useDict(...args) { res.value[dictType] = dicts; } else { getDicts(dictType).then(resp => { - res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark })) + const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN' + res.value[dictType] = resp.data.map(p => ({ + label: lang === 'zh-CN' ? p.dictLabel : autoTranslate(p.dictLabel), + value: p.dictValue, + elTagType: p.listClass, + elTagClass: p.cssClass, + remark: p.remark + })) useDictStore().setDict(dictType, res.value[dictType]); }) } diff --git a/healthlink-his-ui/src/utils/errorCode.js b/healthlink-his-ui/src/utils/errorCode.js index d2111ee10..bf6de02b4 100755 --- a/healthlink-his-ui/src/utils/errorCode.js +++ b/healthlink-his-ui/src/utils/errorCode.js @@ -4,3 +4,19 @@ export default { '404': '访问资源不存在', 'default': '系统未知错误,请反馈给管理员' } + +// English translations for error codes +export const errorCodeEn = { + '401': 'Authentication failed, cannot access system resources', + '403': 'No permission for current operation', + '404': 'Requested resource not found', + 'default': 'Unknown system error, please contact administrator' +} + +// Vietnamese translations for error codes +export const errorCodeVi = { + '401': 'Xác thực thất bại, không thể truy cập tài nguyên hệ thống', + '403': 'Không có quyền cho thao tác hiện tại', + '404': 'Tài nguyên yêu cầu không tồn tại', + 'default': 'Lỗi hệ thống không xác định, vui lòng liên hệ quản trị viên' +} diff --git a/healthlink-his-ui/src/utils/request.js b/healthlink-his-ui/src/utils/request.js index 72c308f81..fef43fdca 100755 --- a/healthlink-his-ui/src/utils/request.js +++ b/healthlink-his-ui/src/utils/request.js @@ -1,12 +1,14 @@ import axios from 'axios' import {ElLoading, ElMessage, ElMessageBox, ElNotification} from 'element-plus' import {getToken} from '@/utils/auth' -import errorCode from '@/utils/errorCode' +import errorCode, {errorCodeEn, errorCodeVi} from '@/utils/errorCode' import {blobValidate, tansParams} from '@/utils/his' import cache from '@/plugins/cache' import {saveAs} from 'file-saver' import useUserStore from '@/store/modules/user' import JSONBig from 'json-bigint' +import {autoTranslate} from '@/i18n/autoTranslate' +import Cookies from 'js-cookie' // 初始化json-bigint,配置大数字转字符串(关键:storeAsString: true) const jsonBig = JSONBig({ storeAsString: true }) @@ -141,8 +143,14 @@ service.interceptors.request.use(config => { service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; - // 获取错误信息 - const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 获取错误信息(支持多语言) + const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN' + const errorDict = lang === 'en' ? errorCodeEn : lang === 'vi' ? errorCodeVi : errorCode + let msg = errorDict[code] || res.data.msg || errorDict['default'] + // 后端返回的中文错误消息自动翻译 + if (lang !== 'zh-CN' && /[\u4e00-\u9fff]/.test(msg)) { + msg = autoTranslate(msg) + } // 二进制数据则直接返回 if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { return res.data @@ -160,11 +168,12 @@ service.interceptors.request.use(config => { }).catch(() => { isRelogin.show = false; }); - return Promise.reject('登录已过期,请重新登录。') + return Promise.reject(lang === 'zh-CN' ? '登录已过期,请重新登录。' : lang === 'en' ? 'Session expired, please log in again.' : 'Phiên hết hạn, vui lòng đăng nhập lại.') } // 其他页面:显示提示后自动跳转 - ElMessage.warning('登录已过期,正在跳转到登录页面...'); + const expiredMsg = lang === 'zh-CN' ? '登录已过期,正在跳转到登录页面...' : lang === 'en' ? 'Session expired, redirecting to login...' : 'Phiên hết hạn, đang chuyển đến trang đăng nhập...' + ElMessage.warning(expiredMsg); useUserStore().logOut().then(() => { isRelogin.show = false; // 跳转到登录页,保留当前路径作为redirect参数 @@ -204,13 +213,15 @@ service.interceptors.request.use(config => { }, error => { console.log('err' + error) + const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN' let { message } = error; if (message == "Network Error") { - message = "后端接口连接异常"; + message = lang === 'zh-CN' ? '后端接口连接异常' : lang === 'en' ? 'Backend API connection error' : 'Lỗi kết nối API backend'; } else if (message.includes("timeout")) { - message = "系统接口请求超时"; + message = lang === 'zh-CN' ? '系统接口请求超时' : lang === 'en' ? 'API request timeout' : 'Yêu cầu API hết thời gian'; } else if (message.includes("Request failed with status code")) { - message = "系统接口" + message.substr(message.length - 3) + "异常"; + const code = message.substr(message.length - 3) + message = lang === 'zh-CN' ? '系统接口' + code + '异常' : lang === 'en' ? 'API error ' + code : 'Lỗi API ' + code; } // 检查是否需要跳过错误提示 if (!error.config?.skipErrorMsg) { @@ -221,7 +232,9 @@ service.interceptors.request.use(config => { // 通用下载方法 export function download(url, params, filename, config) { - downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) + const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN' + const dlMsg = lang === 'zh-CN' ? '正在下载数据,请稍候' : lang === 'en' ? 'Downloading data, please wait' : 'Đang tải dữ liệu, vui lòng đợi' + downloadLoadingInstance = ElLoading.service({ text: dlMsg, background: "rgba(0, 0, 0, 0.7)", }) return service.post(url, params, { transformRequest: [(params) => { return tansParams(params) }], headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, @@ -235,20 +248,25 @@ export function download(url, params, filename, config) { } else { const resText = await data.text(); const rspObj = JSON.parse(resText); - const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + const errorDict = lang === 'en' ? errorCodeEn : lang === 'vi' ? errorCodeVi : errorCode + let errMsg = errorDict[rspObj.code] || rspObj.msg || errorDict['default'] + if (lang !== 'zh-CN' && /[\u4e00-\u9fff]/.test(errMsg)) errMsg = autoTranslate(errMsg) ElMessage.error(errMsg); } downloadLoadingInstance.close(); }).catch((r) => { console.error(r) - ElMessage.error('下载文件出现错误,请联系管理员!') + const dlErr = lang === 'zh-CN' ? '下载文件出现错误,请联系管理员!' : lang === 'en' ? 'Download error, please contact administrator!' : 'Lỗi tải xuống, vui lòng liên hệ quản trị viên!' + ElMessage.error(dlErr) downloadLoadingInstance.close(); }) } // 添加GET方式下载方法 export function downloadGet(url, params, filename, config) { - downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) + const lang2 = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN' + const dlMsg2 = lang2 === 'zh-CN' ? '正在下载数据,请稍候' : lang2 === 'en' ? 'Downloading data, please wait' : 'Đang tải dữ liệu, vui lòng đợi' + downloadLoadingInstance = ElLoading.service({ text: dlMsg2, background: "rgba(0, 0, 0, 0.7)", }) return service.get(url, { params: params, responseType: 'blob', @@ -261,13 +279,16 @@ export function downloadGet(url, params, filename, config) { } else { const resText = await data.text(); const rspObj = JSON.parse(resText); - const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + const errorDict2 = lang2 === 'en' ? errorCodeEn : lang2 === 'vi' ? errorCodeVi : errorCode + let errMsg = errorDict2[rspObj.code] || rspObj.msg || errorDict2['default'] + if (lang2 !== 'zh-CN' && /[\u4e00-\u9fff]/.test(errMsg)) errMsg = autoTranslate(errMsg) ElMessage.error(errMsg); } downloadLoadingInstance.close(); }).catch((r) => { console.error(r) - ElMessage.error('下载文件出现错误,请联系管理员!') + const dlErr2 = lang2 === 'zh-CN' ? '下载文件出现错误,请联系管理员!' : lang2 === 'en' ? 'Download error, please contact administrator!' : 'Lỗi tải xuống, vui lòng liên hệ quản trị viên!' + ElMessage.error(dlErr2) downloadLoadingInstance.close(); }) }