const fs = require('fs'); const path = require('path'); const BASE = path.join(__dirname, '..', 'src', 'views', 'medicationmanagement'); const ZHCN_PATH = path.join(__dirname, '..', 'src', 'i18n', 'locales', 'zhCN.json'); const zhcn = JSON.parse(fs.readFileSync(ZHCN_PATH, 'utf8')); function walkDir(dir) { const files = []; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) files.push(...walkDir(fullPath)); else if (entry.name.endsWith('.vue')) files.push(fullPath); } return files; } const ATTR_PATTERNS = [ { attr: 'placeholder', regex: /(\s+)placeholder="([^"]*[\u4e00-\u9fff][^"]*)"/g }, { attr: 'label', regex: /(\s+)label="([^"]*[\u4e00-\u9fff][^"]*)"/g }, { attr: 'title', regex: /(\s+)title="([^"]*[\u4e00-\u9fff][^"]*)"/g }, { attr: 'start-placeholder', regex: /(\s+)start-placeholder="([^"]*[\u4e00-\u9fff][^"]*)"/g }, { attr: 'end-placeholder', regex: /(\s+)end-placeholder="([^"]*[\u4e00-\u9fff][^"]*)"/g }, ]; const COMMON_MAP = { '操作': 'common.operation', '状态': 'common.status', '创建时间': 'common.createTime', '更新时间': 'common.updateTime', '备注': 'common.remark', '请选择': 'common.pleaseSelect', '请输入': 'common.pleaseEnter', '查询': 'common.search', '重置': 'common.reset', '确认': 'common.confirm', '取消': 'common.cancel', '保存': 'common.save', '删除': 'common.delete', '编辑': 'common.edit', '添加': 'common.add', '查看': 'common.view', '详情': 'common.detail', '导出': 'common.export', '导入': 'common.import', '关闭': 'common.close', '提交': 'common.submit', '刷新': 'common.refresh', '男': 'common.male', '女': 'common.female', '是': 'common.yes', '否': 'common.no', '正常': 'common.normal', '停用': 'common.stop', '提示': 'common.tip', '警告': 'common.warning', '错误': 'common.error', '成功': 'common.success', '消息': 'common.message', '开始时间': 'common.startTime', '结束时间': 'common.endTime', '开始日期': 'common.startDate', '结束日期': 'common.endDate', '性别': 'common.gender', '年龄': 'common.age', '姓名': 'common.name', '编码': 'common.code', '门诊号': 'common.outpatientNo', '患者姓名': 'common.patientName', '无数据': 'common.noData', }; // Comprehensive term mapping const TERM = { '药品名称': 'drugName', '药品编码': 'drugCode', '药品类型': 'drugType', '药品规格': 'drugSpec', '药品批号': 'drugBatchNo', '药品库存': 'drugStock', '药品单价': 'drugUnitPrice', '药品数量': 'drugQty', '药品金额': 'drugAmount', '药品单位': 'drugUnit', '药品分类': 'drugCategory', '药品信息': 'drugInfo', '药品列表': 'drugList', '药品详情': 'drugDetail', '药品汇总': 'drugSummary', '品名': 'itemName', '项目名称': 'itemName', '项目编码': 'itemCode', '项目类型': 'itemType', '项目类别': 'itemCategory', '项目总价': 'itemTotalPrice', '项目信息': 'itemInfo', '项目列表': 'itemList', '项目详情': 'itemDetail', '项目汇总': 'itemSummary', '项目明细': 'itemDetail', '项目': 'item', '规格型号': 'specModel', '规格': 'spec', '单位': 'unit', '数量': 'qty', '单价': 'unitPrice', '金额': 'amount', '总价': 'totalPrice', '合计金额': 'totalAmount', '小计': 'subtotal', '总计': 'grandTotal', '合计': 'total', '差额': 'diff', '盈亏': 'profitLoss', '盘盈': 'surplus', '盘亏': 'shortage', '盈亏数量': 'profitLossQty', '盈亏金额': 'profitLossAmount', '已审核': 'approved', '未审核': 'pending', '待审核': 'pendingApproval', '已退回': 'returned', '已作废': 'voided', '已结清': 'settled', '未结清': 'unsettled', '全部': 'all', '西药': 'westernMedicine', '中成药': 'chinesePatentMedicine', '中草药': 'chineseHerbalMedicine', '中药': 'chineseMedicine', '耗材': 'consumable', '医疗器械': 'medicalDevice', '类别': 'category', '分类': 'classification', '剂型': 'dosageForm', '用法': 'usage', '用量': 'dosage', '频次': 'frequency', '天数': 'days', '总量': 'totalQty', '单量': 'dosePerTime', '库存下限': 'minStock', '库存上限': 'maxStock', '安全库存': 'safeStock', '批号': 'batchNo', '有效期': 'expiryDate', '有效期至': 'expiryDate', '产地': 'origin', '生产厂家': 'manufacturer', '厂家/产地': 'manufacturer', '商品名': 'tradeName', '通用名': 'genericName', '拼音码': 'pinyinCode', '五笔码': 'wubiCode', '医保编码': 'insuranceCode', '自费': 'selfPay', '甲类': 'classA', '乙类': 'classB', '丙类': 'classC', '处方量': 'prescriptionQty', '发药量': 'dispenseQty', '采购量': 'purchaseQty', '入库量': 'inboundQty', '出库量': 'outboundQty', '退货量': 'returnQty', '调拨量': 'transferQty', '盘点量': 'stocktakeQty', '报损量': 'lossQty', '消耗量': 'consumptionQty', '收费员': 'cashier', '挂号员': 'registrar', '医师': 'doctor', '药房': 'pharmacy', '药库': 'drugStorehouse', '门诊': 'outpatient', '住院': 'inpatient', '急诊': 'emergency', '日期': 'date', '时间': 'time', '年': 'year', '月': 'month', '日': 'day', '明细': 'detail', '汇总': 'summary', '清单': 'list', '报表': 'report', '统计': 'statistics', '分析': 'analysis', '预警': 'warning', '提醒': 'reminder', '通知': 'notification', '设置': 'settings', '配置': 'config', '管理': 'management', '维护': 'maintenance', '审核': 'audit', '审批': 'approval', '签收': 'sign', '确认': 'confirm', '退回': 'return', '作废': 'void', '打印': 'print', '预览': 'preview', '下载': 'download', '上传': 'upload', '新增': 'add', '修改': 'edit', '仓库': 'warehouse', '货位': 'location', '请求日期': 'requestDate', '单据号': 'docNo', '单据日期': 'docDate', '药品类型': 'drugType', '源仓库类型': 'sourceWarehouseType', '源仓库': 'sourceWarehouse', '源货位': 'sourceLocation', '目的仓库类型': 'targetWarehouseType', '目的仓库': 'targetWarehouse', '目的货位': 'targetLocation', '产品批号': 'batchNo', '单价(元)': 'unitPriceYuan', '发放数量': 'issueQty', '库存数量': 'stockQty', '实盘数量': 'actualQty', '最小单位': 'minUnit', '拆零比': 'splitRatio', '序号': 'seqNo', '调拨单位': 'transferUnit', '源仓库库存数量': 'sourceStockQty', '目的仓库库存数量': 'targetStockQty', '调拨数量': 'transferQty', '调拨单价': 'transferPrice', '生产日期': 'productionDate', '调拨单据明细': 'transferDocDetail', '收费时间': 'chargeTime', '统计类型': 'statisticsType', '科室': 'department', '开单人': 'orderingDoctor', '收费人': 'cashier', '项目类型': 'itemType', '选择项目类型': 'selectItemType', '医保号': 'medicalInsuranceNo', '药品项目': 'drugItem', '医保码': 'medicalInsuranceCode', '医保类别': 'medicalInsuranceCategory', '医保等级': 'medicalInsuranceLevel', '科室名': 'deptName', '耗材和药品总金额': 'consumableDrugTotal', '产品型号': 'productModel', '所属科室': 'belongingDept', '发放时间': 'issueTime', '版本号': 'versionNo', '搜索': 'search', '请输入项目名称': 'inputItemName', '请选择开始时间': 'selectStartTime', '请选择结束时间': 'selectEndTime', '请选择目的货位': 'selectTargetLocation', '盘点仓库': 'checkWarehouse', '盘点单号': 'checkDocNo', '盘点日期': 'checkDate', '盘点类型': 'checkType', '盘点状态': 'checkStatus', '盘点人': 'stocktaker', '盘点时间': 'stocktakeTime', '审核状态': 'approvalStatus', '审核人': 'approver', '审核时间': 'approvalTime', '制单人': 'maker', '制单时间': 'makeTime', '调拨单号': 'transferDocNo', '调拨日期': 'transferDate', '调拨类型': 'transferType', '调拨状态': 'transferStatus', '申请人': 'applicant', '申请时间': 'applyTime', '审批人': 'approver', '审批时间': 'approvalTime', '批准文号': 'approvalNo', '供应商': 'supplier', '采购价': 'purchasePrice', '零售价': 'retailPrice', '进价': 'purchasePrice', '售价': 'sellingPrice', '库存': 'stock', '当前库存': 'currentStock', '可用库存': 'availableStock', '报损数量': 'lossQty', '报损原因': 'lossReason', '报损单号': 'lossDocNo', '报损日期': 'lossDate', '原价': 'originalPrice', '新价': 'newPrice', '差价': 'priceDiff', '入库单号': 'inboundNo', '入库日期': 'inboundDate', '入库类型': 'inboundType', '出库单号': 'outboundNo', '出库日期': 'outboundDate', '出库类型': 'outboundType', '退货单号': 'returnNo', '退货日期': 'returnDate', '退货状态': 'returnStatus', '对账日期': 'reconciliationDate', '对账状态': 'reconciliationStatus', '结余金额': 'balanceAmount', '期初金额': 'initialAmount', '期末金额': 'finalAmount', '入库金额': 'inboundAmount', '出库金额': 'outboundAmount', '科室汇总': 'deptSummary', '月结年月': 'settlementMonth', '月结状态': 'settlementStatus', '月结日期': 'settlementDate', '门诊收费统计': 'outpatientChargeStats', '收费项目': 'chargeItem', '收费金额': 'chargeAmount', '收费明细': 'chargeDetail', '收费汇总': 'chargeSummary', '患者': 'patient', '就诊日期': 'visitDate', '诊断': 'diagnosis', '处方号': 'prescriptionNo', '处方日期': 'prescriptionDate', '处方状态': 'prescriptionStatus', '发药窗口': 'dispenseWindow', '发药人': 'dispenser', '发药时间': 'dispenseTime', '配药人': 'preparer', '配药时间': 'prepareTime', '退药': 'returnDrug', '退药数量': 'returnDrugQty', '退药原因': 'returnDrugReason', '退药时间': 'returnDrugTime', '请选择仓库': 'selectWarehouse', '请选择货位': 'selectLocation', '请选择药品类型': 'selectDrugType', '请选择源仓库类型': 'selectSourceWarehouseType', '请选择源仓库': 'selectSourceWarehouse', '请选择源货位': 'selectSourceLocation', '请选择目的仓库类型': 'selectTargetWarehouseType', '请选择目的仓库': 'selectTargetWarehouse', '请选择目的货位': 'selectTargetLocation', '请输入单据号': 'inputDocNo', '请输入药品名称': 'inputDrugName', '请输入药品编码': 'inputDrugCode', '请输入批号': 'inputBatchNo', '请输入数量': 'inputQty', '请输入单价': 'inputUnitPrice', '请输入备注': 'inputRemark', '请输入规格': 'inputSpec', '请输入供应商': 'inputSupplier', '请输入生产厂家': 'inputManufacturer', '请输入批准文号': 'inputApprovalNo', '请选择日期': 'selectDate', '请选择状态': 'selectStatus', '请选择类型': 'selectType', '请选择分类': 'selectCategory', '请输入关键词搜索': 'inputKeyword', '输入项目名/拼音搜索': 'searchByItemName', '请选择审核状态': 'selectApprovalStatus', '请选择盘点类型': 'selectCheckType', '请选择盘点仓库': 'selectCheckWarehouse', '请选择结算类型': 'selectSettlementType', '请选择对账状态': 'selectReconciliationStatus', '请输入项目编码': 'inputItemCode', '请输入患者姓名': 'inputPatientName', '请输入门诊号': 'inputOutpatientNo', '请输入医保号': 'inputMedicalInsuranceNo', '请选择科室': 'selectDepartment', '请选择开单人': 'selectOrderingDoctor', '请选择收费人': 'selectCashier', '请选择发药窗口': 'selectDispenseWindow', '请选择发药人': 'selectDispenser', '请选择处方状态': 'selectPrescriptionStatus', '请选择退药原因': 'selectReturnReason', '请选择报损原因': 'selectLossReason', '请选择调价原因': 'selectPriceAdjReason', '请选择入库类型': 'selectInboundType', '请选择出库类型': 'selectOutboundType', '请选择退货状态': 'selectReturnStatus', '请选择供应商': 'selectSupplier', '请选择药品': 'selectDrug', '请选择剂型': 'selectDosageForm', '请选择用法': 'selectUsage', '请选择频次': 'selectFrequency', '请选择医师': 'selectDoctor', '请选择药房': 'selectPharmacy', '请选择药库': 'selectDrugStorehouse', '请选择门诊': 'selectOutpatient', '请选择住院': 'selectInpatient', '请选择急诊': 'selectEmergency', '请输入拼音码搜索': 'searchByPinyin', '请输入通用名搜索': 'searchByGenericName', '请输入商品名搜索': 'searchByTradeName', '请选择日期范围': 'selectDateRange', '请输入库存下限': 'inputMinStock', '请输入库存上限': 'inputMaxStock', '请输入安全库存': 'inputSafeStock', '请输入有效期': 'inputExpiryDate', '请输入产地': 'inputOrigin', '请输入用量': 'inputDosage', '请输入天数': 'inputDays', '请输入总量': 'inputTotalQty', '请输入单量': 'inputDosePerTime', '请输入金额': 'inputAmount', '请输入价格': 'inputPrice', '请输入差价': 'inputPriceDiff', '请输入原因': 'inputReason', '请输入说明': 'inputDescription', '请输入内容': 'inputContent', '请输入标题': 'inputTitle', '请输入名称': 'inputName', '请输入编号': 'inputCode', '请输入电话': 'inputPhone', '请输入地址': 'inputAddress', '请选择操作': 'selectOperation', '请选择时间': 'selectTime', '请选择年月': 'selectYearMonth', '请选择年份': 'selectYear', '请选择月份': 'selectMonth', '请输入单据号': 'inputDocNo', '单据类型': 'docType', '单据状态': 'docStatus', '采购单号': 'purchaseNo', '采购日期': 'purchaseDate', '采购类型': 'purchaseType', '采购状态': 'purchaseStatus', '采购人': 'buyer', '采购时间': 'purchaseTime', '采购数量': 'purchaseQty', '采购金额': 'purchaseAmount', '采购单价': 'purchaseUnitPrice', '采购单位': 'purchaseUnit', '采购批号': 'purchaseBatchNo', '采购有效期': 'purchaseExpiryDate', '采购产地': 'purchaseOrigin', '采购生产厂家': 'purchaseManufacturer', '采购商品名': 'purchaseTradeName', '采购通用名': 'purchaseGenericName', '请领单号': 'requisitionNo', '请领日期': 'requisitionDate', '请领类型': 'requisitionType', '请领状态': 'requisitionStatus', '请领人': 'requisitioner', '请领时间': 'requisitionTime', '请领数量': 'requisitionQty', '请领金额': 'requisitionAmount', '请领单价': 'requisitionUnitPrice', '请领单位': 'requisitionUnit', '退回单号': 'returnNo', '退回日期': 'returnDate', '退回类型': 'returnType', '退回状态': 'returnStatus', '退回人': 'returnPerson', '退回时间': 'returnTime', '退回原因': 'returnReason', '退回数量': 'returnQty', '退回金额': 'returnAmount', '退回单价': 'returnUnitPrice', '退回单位': 'returnUnit', '退回批号': 'returnBatchNo', '调拨单号': 'transferNo', '调拨日期': 'transferDate', '调拨类型': 'transferType', '调拨状态': 'transferStatus', '调拨人': 'transferPerson', '调拨时间': 'transferTime', '调拨原因': 'transferReason', '调拨数量': 'transferQty', '调拨金额': 'transferAmount', '调拨单价': 'transferUnitPrice', '调拨单位': 'transferUnit', '调拨批号': 'transferBatchNo', '对账日期': 'reconciliationDate', '对账类型': 'reconciliationType', '对账状态': 'reconciliationStatus', '对账人': 'reconciler', '对账时间': 'reconciliationTime', '对账数量': 'reconciliationQty', '对账金额': 'reconciliationAmount', '对账单价': 'reconciliationUnitPrice', '对账单位': 'reconciliationUnit', '对账批号': 'reconciliationBatchNo', '结余金额': 'balanceAmount', '期初金额': 'initialAmount', '期末金额': 'finalAmount', '入库金额': 'inboundAmount', '出库金额': 'outboundAmount', '月结年月': 'settlementMonth', '月结状态': 'settlementStatus', '月结日期': 'settlementDate', '门诊收费统计': 'outpatientChargeStats', '收费项目': 'chargeItem', '收费金额': 'chargeAmount', '收费明细': 'chargeDetail', '收费汇总': 'chargeSummary', '就诊日期': 'visitDate', '诊断': 'diagnosis', '处方号': 'prescriptionNo', '处方日期': 'prescriptionDate', '处方状态': 'prescriptionStatus', '发药窗口': 'dispenseWindow', '发药人': 'dispenser', '发药时间': 'dispenseTime', '配药人': 'preparer', '配药时间': 'prepareTime', '退药': 'returnDrug', '退药数量': 'returnDrugQty', '退药原因': 'returnDrugReason', '退药时间': 'returnDrugTime', '盘点人': 'stocktaker', '盘点时间': 'stocktakeTime', '请选择仓库': 'selectWarehouse', '请选择货位': 'selectLocation', '请选择药品类型': 'selectDrugType', '请选择源仓库类型': 'selectSourceWarehouseType', '请选择源仓库': 'selectSourceWarehouse', '请选择源货位': 'selectSourceLocation', '请选择目的仓库类型': 'selectTargetWarehouseType', '请选择目的仓库': 'selectTargetWarehouse', '请选择目的货位': 'selectTargetLocation', '请输入单据号': 'inputDocNo', '请输入药品名称': 'inputDrugName', '请输入药品编码': 'inputDrugCode', '请输入批号': 'inputBatchNo', '请输入数量': 'inputQty', '请输入单价': 'inputUnitPrice', '请输入备注': 'inputRemark', '请输入规格': 'inputSpec', '请输入供应商': 'inputSupplier', '请输入生产厂家': 'inputManufacturer', '请输入批准文号': 'inputApprovalNo', '请选择日期': 'selectDate', '请选择状态': 'selectStatus', '请选择类型': 'selectType', '请选择分类': 'selectCategory', '请输入关键词搜索': 'inputKeyword', '输入项目名/拼音搜索': 'searchByItemName', '请选择审核状态': 'selectApprovalStatus', '请选择盘点类型': 'selectCheckType', '请选择盘点仓库': 'selectCheckWarehouse', '请选择结算类型': 'selectSettlementType', '请选择对账状态': 'selectReconciliationStatus', '请输入项目编码': 'inputItemCode', '请输入患者姓名': 'inputPatientName', '请输入门诊号': 'inputOutpatientNo', '请输入医保号': 'inputMedicalInsuranceNo', '请选择科室': 'selectDepartment', '请选择开单人': 'selectOrderingDoctor', '请选择收费人': 'selectCashier', '请选择发药窗口': 'selectDispenseWindow', '请选择发药人': 'selectDispenser', '请选择处方状态': 'selectPrescriptionStatus', '请选择退药原因': 'selectReturnReason', '请选择报损原因': 'selectLossReason', '请选择调价原因': 'selectPriceAdjReason', '请选择入库类型': 'selectInboundType', '请选择出库类型': 'selectOutboundType', '请选择退货状态': 'selectReturnStatus', '请选择供应商': 'selectSupplier', '请选择药品': 'selectDrug', '请选择剂型': 'selectDosageForm', '请选择用法': 'selectUsage', '请选择频次': 'selectFrequency', '请选择医师': 'selectDoctor', '请选择药房': 'selectPharmacy', '请选择药库': 'selectDrugStorehouse', '请选择门诊': 'selectOutpatient', '请选择住院': 'selectInpatient', '请选择急诊': 'selectEmergency', '请输入拼音码搜索': 'searchByPinyin', '请输入通用名搜索': 'searchByGenericName', '请输入商品名搜索': 'searchByTradeName', '请选择日期范围': 'selectDateRange', '请输入库存下限': 'inputMinStock', '请输入库存上限': 'inputMaxStock', '请输入安全库存': 'inputSafeStock', '请输入有效期': 'inputExpiryDate', '请输入产地': 'inputOrigin', '请输入用量': 'inputDosage', '请输入天数': 'inputDays', '请输入总量': 'inputTotalQty', '请输入单量': 'inputDosePerTime', '请输入金额': 'inputAmount', '请输入价格': 'inputPrice', '请输入差价': 'inputPriceDiff', '请输入原因': 'inputReason', '请输入说明': 'inputDescription', '请输入内容': 'inputContent', '请输入标题': 'inputTitle', '请输入名称': 'inputName', '请输入编号': 'inputCode', '请输入电话': 'inputPhone', '请输入地址': 'inputAddress', '请选择操作': 'selectOperation', '请选择时间': 'selectTime', '请选择年月': 'selectYearMonth', '请选择年份': 'selectYear', '请选择月份': 'selectMonth', '调价单号': 'priceAdjNo', '调价日期': 'priceAdjDate', '调价类型': 'priceAdjType', '调价状态': 'priceAdjStatus', '调价人': 'priceAdjuster', '调价时间': 'priceAdjTime', '调价原因': 'priceAdjReason', '调价数量': 'priceAdjQty', '调价金额': 'priceAdjAmount', '调价单价': 'priceAdjUnitPrice', '调价单位': 'priceAdjUnit', '调价批号': 'priceAdjBatchNo', '报损单号': 'lossDocNo', '报损日期': 'lossDate', '报损类型': 'lossType', '报损状态': 'lossStatus', '报损人': 'lossPerson', '报损时间': 'lossTime', '报损原因': 'lossReason', '报损数量': 'lossQty', '报损金额': 'lossAmount', '报损单价': 'lossUnitPrice', '报损单位': 'lossUnit', '报损批号': 'lossBatchNo', '入库单号': 'inboundNo', '入库日期': 'inboundDate', '入库类型': 'inboundType', '入库人': 'inboundPerson', '入库时间': 'inboundTime', '入库数量': 'inboundQty', '入库金额': 'inboundAmount', '入库单价': 'inboundUnitPrice', '出库单号': 'outboundNo', '出库日期': 'outboundDate', '出库类型': 'outboundType', '出库人': 'outboundPerson', '出库时间': 'outboundTime', '出库数量': 'outboundQty', '出库金额': 'outboundAmount', '出库单价': 'outboundUnitPrice', '退货单号': 'returnNo', '退货日期': 'returnDate', '退货类型': 'returnType', '退货状态': 'returnStatus', '退货人': 'returnPerson', '退货时间': 'returnTime', '退货原因': 'returnReason', '退货数量': 'returnQty', '退货金额': 'returnAmount', '退货单价': 'returnUnitPrice', '退货单位': 'returnUnit', '退货批号': 'returnBatchNo', '请选择开始日期': 'selectStartDate', '请选择结束日期': 'selectEndDate', '请输入项目名称': 'inputItemName', '请输入仓库': 'inputWarehouse', '请输入货位': 'inputLocation', '请输入药品类型': 'inputDrugType', '请输入源仓库类型': 'inputSourceWarehouseType', '请输入源仓库': 'inputSourceWarehouse', '请输入源货位': 'inputSourceLocation', '请输入目的仓库类型': 'inputTargetWarehouseType', '请输入目的仓库': 'inputTargetWarehouse', '请输入目的货位': 'inputTargetLocation', '请输入调拨单号': 'inputTransferDocNo', '请输入调拨日期': 'inputTransferDate', '请输入调拨类型': 'inputTransferType', '请输入调拨状态': 'inputTransferStatus', '请输入调拨人': 'inputTransferPerson', '请输入调拨时间': 'inputTransferTime', '请输入调拨原因': 'inputTransferReason', '请输入调拨数量': 'inputTransferQty', '请输入调拨金额': 'inputTransferAmount', '请输入调拨单价': 'inputTransferUnitPrice', '请输入调拨单位': 'inputTransferUnit', '请输入调拨批号': 'inputTransferBatchNo', '请输入对账日期': 'inputReconciliationDate', '请输入对账类型': 'inputReconciliationType', '请输入对账状态': 'inputReconciliationStatus', '请输入对账人': 'inputReconciler', '请输入对账时间': 'inputReconciliationTime', '请输入对账数量': 'inputReconciliationQty', '请输入对账金额': 'inputReconciliationAmount', '请输入对账单价': 'inputReconciliationUnitPrice', '请输入对账单位': 'inputReconciliationUnit', '请输入对账批号': 'inputReconciliationBatchNo', '请输入结余金额': 'inputBalanceAmount', '请输入期初金额': 'inputInitialAmount', '请输入期末金额': 'inputFinalAmount', '请输入入库金额': 'inputInboundAmount', '请输入出库金额': 'inputOutboundAmount', '请输入月结年月': 'inputSettlementMonth', '请输入月结状态': 'inputSettlementStatus', '请输入月结日期': 'inputSettlementDate', '请输入门诊收费统计': 'inputOutpatientChargeStats', '请输入收费项目': 'inputChargeItem', '请输入收费金额': 'inputChargeAmount', '请输入收费明细': 'inputChargeDetail', '请输入收费汇总': 'inputChargeSummary', '请输入患者': 'inputPatient', '请输入就诊日期': 'inputVisitDate', '请输入诊断': 'inputDiagnosis', '请输入处方号': 'inputPrescriptionNo', '请输入处方日期': 'inputPrescriptionDate', '请输入处方状态': 'inputPrescriptionStatus', '请输入发药窗口': 'inputDispenseWindow', '请输入发药人': 'inputDispenser', '请输入发药时间': 'inputDispenseTime', '请输入配药人': 'inputPreparer', '请输入配药时间': 'inputPrepareTime', '请输入退药': 'inputReturnDrug', '请输入退药数量': 'inputReturnDrugQty', '请输入退药原因': 'inputReturnDrugReason', '请输入退药时间': 'inputReturnDrugTime', '请输入盘点人': 'inputStocktaker', '请输入盘点时间': 'inputStocktakeTime', '请输入请选择仓库': 'inputSelectWarehouse', '请输入请选择货位': 'inputSelectLocation', '请输入请选择药品类型': 'inputSelectDrugType', '请输入请选择源仓库类型': 'inputSelectSourceWarehouseType', '请输入请选择源仓库': 'inputSelectSourceWarehouse', '请输入请选择源货位': 'inputSelectSourceLocation', '请输入请选择目的仓库类型': 'inputSelectTargetWarehouseType', '请输入请选择目的仓库': 'inputSelectTargetWarehouse', '请输入请选择目的货位': 'inputSelectTargetLocation', '请输入请选择日期': 'inputSelectDate', '请输入请选择状态': 'inputSelectStatus', '请输入请选择类型': 'inputSelectType', '请输入请选择分类': 'inputSelectCategory', '请输入请输入关键词搜索': 'inputKeyword', '请输入输入项目名/拼音搜索': 'searchByItemName', '请输入请选择审核状态': 'inputSelectApprovalStatus', '请输入请选择盘点类型': 'inputSelectCheckType', '请输入请选择盘点仓库': 'inputSelectCheckWarehouse', '请输入请选择结算类型': 'inputSelectSettlementType', '请输入请选择对账状态': 'inputSelectReconciliationStatus', '请输入请选择科室': 'inputSelectDepartment', '请输入请选择开单人': 'inputSelectOrderingDoctor', '请输入请选择收费人': 'inputSelectCashier', '请输入请选择发药窗口': 'inputSelectDispenseWindow', '请输入请选择发药人': 'inputSelectDispenser', '请输入请选择处方状态': 'inputSelectPrescriptionStatus', '请输入请选择退药原因': 'inputSelectReturnReason', '请输入请选择报损原因': 'inputSelectLossReason', '请输入请选择调价原因': 'inputSelectPriceAdjReason', '请输入请选择入库类型': 'inputSelectInboundType', '请输入请选择出库类型': 'inputSelectOutboundType', '请输入请选择退货状态': 'inputSelectReturnStatus', '请输入请选择供应商': 'inputSelectSupplier', '请输入请选择药品': 'inputSelectDrug', '请输入请选择剂型': 'inputSelectDosageForm', '请输入请选择用法': 'inputSelectUsage', '请输入请选择频次': 'inputSelectFrequency', '请输入请选择医师': 'inputSelectDoctor', '请输入请选择药房': 'inputSelectPharmacy', '请输入请选择药库': 'inputSelectDrugStorehouse', '请输入请选择门诊': 'inputSelectOutpatient', '请输入请选择住院': 'inputSelectInpatient', '请输入请选择急诊': 'inputSelectEmergency', '请输入请输入拼音码搜索': 'searchByPinyin', '请输入请输入通用名搜索': 'searchByGenericName', '请输入请输入商品名搜索': 'searchByTradeName', '请输入请选择日期范围': 'inputSelectDateRange', '请输入请选择开始时间': 'inputSelectStartTime', '请输入请选择结束时间': 'inputSelectEndTime', }; function getModuleName(filePath) { const rel = path.relative(BASE, filePath); const parts = rel.split(path.sep); if (parts.length === 1) return path.basename(filePath, '.vue'); if (parts.length === 2 && parts[1] === 'index.vue') return parts[0]; if (parts.length >= 3) { if (parts[1] === 'components') return parts[0] + '_' + path.basename(filePath, '.vue'); return parts[1]; } return parts[0]; } function chineseToKey(text, moduleName) { let clean = text.replace(/[::]\s*$/, '').trim(); if (COMMON_MAP[clean]) return COMMON_MAP[clean]; if (TERM[clean]) return `medication.${moduleName}.${TERM[clean]}`; // Try prefix decomposition let prefix = ''; let remaining = clean; for (const [zh, en] of [['请输入', 'input'], ['请选择', 'select']]) { if (remaining.startsWith(zh)) { prefix = en; remaining = remaining.substring(zh.length); break; } } if (remaining && TERM[remaining]) { const base = TERM[remaining]; return `medication.${moduleName}.${prefix}${base.charAt(0).toUpperCase()}${base.slice(1)}`; } // Try partial match for (const [zh, en] of Object.entries(TERM)) { if (remaining.includes(zh)) { return `medication.${moduleName}.${prefix}${en.charAt(0).toUpperCase()}${en.slice(1)}`; } } // Fallback: use hex of Chinese const hex = Buffer.from(clean, 'utf8').toString('hex').substring(0, 16); return `medication.${moduleName}.${prefix || 'val'}_${hex}`; } // Global translation collection const allTranslations = {}; function processFile(filePath) { let content = fs.readFileSync(filePath, 'utf8'); const original = content; const moduleName = getModuleName(filePath); let hasChanges = false; const hasI18n = content.includes('useI18n'); for (const { attr, regex } of ATTR_PATTERNS) { const re = new RegExp(regex.source, regex.flags); let match; const replacements = []; while ((match = re.exec(content)) !== null) { const [fullMatch, whitespace, chineseText] = match; const key = chineseToKey(chineseText, moduleName); allTranslations[key] = chineseText; const replacement = `${whitespace}:${attr}="t('${key}')"`; replacements.push({ fullMatch, replacement, index: match.index }); } for (let i = replacements.length - 1; i >= 0; i--) { const { fullMatch, replacement, index } = replacements[i]; content = content.substring(0, index) + replacement + content.substring(index + fullMatch.length); hasChanges = true; } } if (!hasChanges) return; if (!hasI18n) { const scriptSetupMatch = content.match(/(]*>)/); if (scriptSetupMatch) { const scriptTag = scriptSetupMatch[1]; const insertPos = content.indexOf(scriptTag) + scriptTag.length; const afterScript = content.substring(insertPos); const firstImportMatch = afterScript.match(/\n(import\s)/); if (firstImportMatch) { const importPos = insertPos + afterScript.indexOf(firstImportMatch[1]); content = content.substring(0, importPos) + "import { useI18n } from 'vue-i18n';\n" + content.substring(importPos); } else { content = content.substring(0, insertPos) + "\nimport { useI18n } from 'vue-i18n';" + content.substring(insertPos); } const importSection = content.match(/(]*>[\s\S]*?)(?=\n(?:const|let|var|function|\/\/|\/\*|\*|<\/script>))/); if (importSection) { const lastImportEnd = importSection[0].length + content.indexOf(importSection[0]); const afterImports = content.substring(lastImportEnd); const firstCodeMatch = afterImports.match(/\n(const|let|var|function|\/\/|\/\*|\*)/); if (firstCodeMatch) { const codePos = lastImportEnd + afterImports.indexOf(firstCodeMatch[1]); content = content.substring(0, codePos) + "const { t } = useI18n();\n" + content.substring(codePos); } } } } if (content !== original) { fs.writeFileSync(filePath, content, 'utf8'); } } // Main const vueFiles = walkDir(BASE); console.log(`Processing ${vueFiles.length} Vue files...`); for (const file of vueFiles) { processFile(file); } console.log(`Collected ${Object.keys(allTranslations).length} translation keys`); // Write flat keys const flatPath = path.join(__dirname, '..', 'src', 'i18n', 'locales', 'medication_keys_flat.json'); fs.writeFileSync(flatPath, JSON.stringify(allTranslations, null, 2), 'utf8'); // Write structured keys const outputPath = path.join(__dirname, '..', 'src', 'i18n', 'locales', 'medication_keys.json'); const output = {}; for (const [key, value] of Object.entries(allTranslations)) { const parts = key.split('.'); let current = output; for (let i = 0; i < parts.length - 1; i++) { if (!current[parts[i]]) current[parts[i]] = {}; current = current[parts[i]]; } current[parts[parts.length - 1]] = value; } fs.writeFileSync(outputPath, JSON.stringify(output, null, 2), 'utf8'); console.log(`Wrote ${flatPath}`); console.log(`Wrote ${outputPath}`);