Merge branch 'develop' of https://gitea.gentronhealth.com/wangyizhe/his into develop

This commit is contained in:
2026-06-15 15:22:50 +08:00
40 changed files with 624 additions and 783 deletions

View File

@@ -1,7 +1,7 @@
# HealthLink-HIS 代码模块索引
> 供 LLM 快速定位代码。每个模块列出 Controller → Service → Mapper 关键文件。
> 最后更新: 2026-06-15 06:00 (298 个 Controller)
> 最后更新: 2026-06-15 12:00 (300 个 Controller)
## 关键词 → 模块速查

View File

@@ -123,7 +123,7 @@ public class ExamApplyController extends BaseController {
for (ExamApplyItem item : items) {
BigDecimal itemTotal = item.getItemFee() != null ? item.getItemFee() : BigDecimal.ZERO;
BigDecimal methodFee = getMethodAdditionalFee(item.getExamMethodCode());
BigDecimal methodFee = getMethodAdditionalFee(item.getExamMethodCode(), item.getBodyPartCode());
totalAmount = totalAmount.add(itemTotal.add(methodFee));
}
@@ -318,7 +318,7 @@ public class ExamApplyController extends BaseController {
// 金额:单价和总价取检查项目费用
BigDecimal baseFee = itemDto.getItemFee() != null ? itemDto.getItemFee() : BigDecimal.ZERO;
BigDecimal methodFee = getMethodAdditionalFee(itemDto.getExamMethodCode());
BigDecimal methodFee = getMethodAdditionalFee(itemDto.getExamMethodCode(), itemDto.getBodyPartCode());
BigDecimal fee = baseFee.add(methodFee);
chargeItem.setQuantityValue(BigDecimal.ONE); // 数量
chargeItem.setQuantityUnit(""); // 单位
@@ -506,7 +506,7 @@ public class ExamApplyController extends BaseController {
chargeItem.setProductId(0L);
BigDecimal baseFee = itemDto.getItemFee() != null ? itemDto.getItemFee() : BigDecimal.ZERO;
BigDecimal methodFee = getMethodAdditionalFee(itemDto.getExamMethodCode());
BigDecimal methodFee = getMethodAdditionalFee(itemDto.getExamMethodCode(), itemDto.getBodyPartCode());
BigDecimal fee = baseFee.add(methodFee);
chargeItem.setQuantityValue(BigDecimal.ONE);
chargeItem.setQuantityUnit("");
@@ -570,15 +570,17 @@ public class ExamApplyController extends BaseController {
* Bug #655: 根据检查方法代码查询附加金额(套餐价格)
* 查找链路examMethodCode → CheckMethod → packageName → CheckPackage → packagePrice
*/
private BigDecimal getMethodAdditionalFee(String examMethodCode) {
private BigDecimal getMethodAdditionalFee(String examMethodCode, String checkType) {
if (examMethodCode == null || examMethodCode.isEmpty()) {
return BigDecimal.ZERO;
}
// 1. 根据 code 查找 CheckMethod
CheckMethod method = checkMethodService.getOne(
new LambdaQueryWrapper<CheckMethod>()
.eq(CheckMethod::getCode, examMethodCode)
.last("LIMIT 1"));
// 1. 根据 code 和 checkType 查找 CheckMethod
LambdaQueryWrapper<CheckMethod> wrapper = new LambdaQueryWrapper<CheckMethod>()
.eq(CheckMethod::getCode, examMethodCode);
if (checkType != null && !checkType.isEmpty()) {
wrapper.eq(CheckMethod::getCheckType, checkType);
}
CheckMethod method = checkMethodService.getOne(wrapper.last("LIMIT 1"));
if (method == null || method.getPackageName() == null || method.getPackageName().isEmpty()) {
return BigDecimal.ZERO;
}

View File

@@ -99,9 +99,22 @@ public class AdvancePaymentManageAppServiceImpl implements IAdvancePaymentManage
public List<AdvancePaymentFlowRecordDto> getAdvancePaymentFlowRecord(Long encounterId) {
List<AdvancePaymentFlowRecordDto> advancePaymentFlowRecordList =
advancePaymentManageAppMapper.getAdvancePaymentFlowRecordList(encounterId);
advancePaymentFlowRecordList.forEach(e ->
// 付款类别
e.setPaymentEnum_enumText(EnumUtils.getInfoByValue(PaymentType.class, e.getPaymentEnum())));
advancePaymentFlowRecordList.forEach(e -> {
// 付款类别 (交款/退费)
e.setPaymentEnum_enumText(EnumUtils.getInfoByValue(PaymentType.class, e.getPaymentEnum()));
// 支付方式 (微信/支付宝/现金等)
if (e.getPayWayEnum() != null) {
YbPayment ybPayment = YbPayment.getByValue(e.getPayWayEnum());
if (ybPayment != null) {
String info = ybPayment.getInfo();
// 简化名称:将 "个人现金支付金额(微信)" 简化为 "微信"
if (info != null && info.contains("(") && info.contains(")")) {
info = info.substring(info.indexOf("(") + 1, info.indexOf(")"));
}
e.setPayWayEnum_enumText(info);
}
}
});
return advancePaymentFlowRecordList;
}

View File

@@ -29,6 +29,10 @@ public class AdvancePaymentFlowRecordDto {
private Integer paymentEnum;
private String paymentEnum_enumText;
/** 支付方式 */
private Integer payWayEnum;
private String payWayEnum_enumText;
/** 操作时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@@ -182,6 +182,10 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 提取requestStatus手动处理支持COMPLETED(3)和CHECK_VERIFIED(10)同时查询
Integer requestStatus = inpatientAdviceParam.getRequestStatus();
inpatientAdviceParam.setRequestStatus(null);
// deadline 不在 UNION 子查询结果列中,且不映射为查询过滤条件
// 原因end_time 是医嘱结束时间,长期医嘱的 end_time 远在 deadline 之后,
// 使用 <= 过滤会排除所有长期医嘱,导致"未校对"tab 查询为空
inpatientAdviceParam.setDeadline(null);
// 构建查询条件
QueryWrapper<InpatientAdviceParam> queryWrapper
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
@@ -508,7 +512,8 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
if (!deviceRequestList.isEmpty()) {
// 更新耗材请求状态待发送
deviceRequestService.updateDraftStatusBatch(
deviceRequestList.stream().map(PerformInfoDto::getRequestId).toList());
deviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(),
practitionerId, checkDate, backReason);
}
return R.ok(null, "退回成功");
}

View File

@@ -242,6 +242,17 @@ public class InpatientAdviceDto {
@JsonSerialize(using = ToStringSerializer.class)
private Long performerCheckId;
/**
* 退回原因
*/
private String reasonText;
/**
* 校对时间/退回时间
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date checkTime;
/**
* 药品/服务类型
*/

View File

@@ -0,0 +1,11 @@
-- Bug #613: 医嘱退回流程 - 耗材请求表增加退回相关字段
-- 与 med_medication_request 和 wor_service_request 保持一致
ALTER TABLE wor_device_request
ADD COLUMN IF NOT EXISTS back_reason VARCHAR(500),
ADD COLUMN IF NOT EXISTS performer_check_id BIGINT,
ADD COLUMN IF NOT EXISTS check_time TIMESTAMP;
COMMENT ON COLUMN wor_device_request.back_reason IS '退回原因';
COMMENT ON COLUMN wor_device_request.performer_check_id IS '校对人/退回护士';
COMMENT ON COLUMN wor_device_request.check_time IS '校对时间/退回时间';

View File

@@ -98,9 +98,12 @@
SELECT fpr.payment_no,
fpr.tendered_amount,
fpr.payment_enum,
fprd.pay_enum AS payWayEnum,
fpr.create_time AS operate_time,
ap.NAME AS enterer
FROM fin_payment_reconciliation AS fpr
LEFT JOIN fin_payment_rec_detail AS fprd ON fprd.reconciliation_id = fpr.id
AND fprd.delete_flag = '0'
LEFT JOIN adm_practitioner AS ap ON ap.ID = fpr.enterer_id
AND ap.delete_flag = '0'
WHERE fpr.delete_flag = '0'

View File

@@ -153,6 +153,8 @@
ii.balance_amount AS balance_amount,
ii.account_id AS account_id,
ii.performer_check_id,
ii.reason_text,
ii.check_time,
ii.category_code,
ii.dispense_status,
ii.unit_price,
@@ -171,6 +173,8 @@
T1.infusion_flag AS inject_flag,
T1.group_id AS group_id,
T1.performer_check_id,
T1.back_reason AS reason_text,
T1.check_time AS check_time,
T2."name" AS advice_name,
T2.id AS item_id,
T3.total_volume AS volume,
@@ -317,6 +321,8 @@
NULL::integer AS inject_flag,
NULL::bigint AS group_id,
T1.performer_check_id,
T1.reason_text AS reason_text,
T1.check_time AS check_time,
T2."name" AS advice_name,
T2.id AS item_id,
NULL::varchar AS volume,
@@ -452,6 +458,8 @@
NULL::integer AS inject_flag,
NULL::bigint AS group_id,
T1.performer_check_id,
T1.back_reason AS reason_text,
T1.check_time AS check_time,
T2."name" AS advice_name,
T2.id AS item_id,
NULL::varchar AS volume,

View File

@@ -220,6 +220,7 @@
T1.based_on_id AS based_on_id,
T1.medication_id AS advice_definition_id,
T1.content_json::jsonb ->> 'remark' AS remark,
T1.back_reason AS reason_text,
CASE WHEN T1.status_enum = 6 THEN T1.effective_dose_end ELSE NULL END AS stop_time,
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
FROM med_medication_request AS T1
@@ -277,6 +278,7 @@
T1.based_on_id AS based_on_id,
T1.device_def_id AS advice_definition_id,
T1.content_json::jsonb ->> 'remark' AS remark,
T1.back_reason AS reason_text,
NULL::timestamp AS stop_time,
'' AS stop_user_name
FROM wor_device_request AS T1
@@ -331,6 +333,7 @@
T1.based_on_id AS based_on_id,
T1.activity_id AS advice_definition_id,
T1.remark AS remark,
T1.reason_text AS reason_text,
CASE WHEN T1.status_enum = 6 THEN T1.occurrence_end_time ELSE NULL END AS stop_time,
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
FROM wor_service_request AS T1

View File

@@ -174,4 +174,13 @@ public class DeviceRequest extends HisBaseEntity {
* 生成来源
*/
private Integer generateSourceEnum;
/** 退回原因 */
private String backReason = "";
/** 校对人 */
private Long performerCheckId;
/** 校对时间 */
private Date checkTime;
}

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.administration.domain.Practitioner;
import com.healthlink.his.workflow.domain.DeviceRequest;
import java.util.Date;
import java.util.List;
/**
@@ -44,6 +45,16 @@ public interface IDeviceRequestService extends IService<DeviceRequest> {
*/
void updateDraftStatusBatch(List<Long> devReqIdList);
/**
* 更新请求状态:待发送(含退回信息)
*
* @param devReqIdList 耗材请求id列表
* @param practitionerId 退回护士/校对人
* @param checkDate 退回时间/校对时间
* @param backReason 退回原因
*/
void updateDraftStatusBatch(List<Long> devReqIdList, Long practitionerId, Date checkDate, String backReason);
/**
* 更新请求状态:取消
*

View File

@@ -11,6 +11,7 @@ import com.healthlink.his.workflow.mapper.DeviceRequestMapper;
import com.healthlink.his.workflow.service.IDeviceRequestService;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
@@ -77,6 +78,31 @@ public class DeviceRequestServiceImpl extends ServiceImpl<DeviceRequestMapper, D
.set(DeviceRequest::getStatusEnum, RequestStatus.DRAFT.getValue()));
}
/**
* 更新请求状态:待发送(含退回信息)
*
* @param devReqIdList 耗材请求id列表
* @param practitionerId 退回护士/校对人
* @param checkDate 退回时间/校对时间
* @param backReason 退回原因
*/
@Override
public void updateDraftStatusBatch(List<Long> devReqIdList, Long practitionerId, Date checkDate, String backReason) {
LambdaUpdateWrapper<DeviceRequest> updateWrapper =
new LambdaUpdateWrapper<DeviceRequest>().in(DeviceRequest::getId, devReqIdList)
.set(DeviceRequest::getStatusEnum, RequestStatus.DRAFT.getValue());
if (practitionerId != null) {
updateWrapper.set(DeviceRequest::getPerformerCheckId, practitionerId);
}
if (checkDate != null) {
updateWrapper.set(DeviceRequest::getCheckTime, checkDate);
}
if (backReason != null) {
updateWrapper.set(DeviceRequest::getBackReason, backReason);
}
baseMapper.update(null, updateWrapper);
}
/**
* 更新请求状态:取消
*

View File

@@ -151,8 +151,8 @@
"left": 14.17,
"top": 96.38,
"width": 566.92,
"height": 185.0,
"title": "<table style='width:100%; border-collapse:collapse; border:1px solid #000000; table-layout:fixed; font-family:SimSun, serif; font-size:11px; color:#000000;'><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:65px; font-weight:bold; color:#000000;'>住院号</td><td style='border:1px solid #000; padding:0 4px; width:130px; white-space:normal; word-break:break-all; color:#000000;'>{{encounterNosd}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>姓名</td><td style='border:1px solid #000; padding:0 4px; width:110px; white-space:normal; word-break:break-all; color:#000000;'>{{patientName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>性别</td><td style='border:1px solid #000; text-align:center; width:50px; white-space:normal; word-break:break-all; color:#000000;'>{{gender}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:60px; font-weight:bold; color:#000000;'>年龄</td><td style='border:1px solid #000; text-align:center; width:51.92px; white-space:normal; word-break:break-all; color:#000000;'>{{age}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>病区/科室</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='3'>{{inHospitalOrgName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>床号</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{bedName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>医保类型</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{contractName}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>收费项目</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>住院预缴款</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>支付方式</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{paymentMethod}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(大写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{amountInWords}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(小写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{balanceAmount}}</td></tr></table>",
"height": 230,
"title": "<table style='width:100%; border-collapse:collapse; border:1px solid #000000; table-layout:fixed; font-family:SimSun, serif; font-size:11px; color:#000000;'><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:65px; font-weight:bold; color:#000000;'>住院号</td><td style='border:1px solid #000; padding:0 4px; width:130px; white-space:normal; word-break:break-all; color:#000000;'>{{encounterNosd}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>姓名</td><td style='border:1px solid #000; padding:0 4px; width:110px; white-space:normal; word-break:break-all; color:#000000;'>{{patientName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>性别</td><td style='border:1px solid #000; text-align:center; width:50px; white-space:normal; word-break:break-all; color:#000000;'>{{gender}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:60px; font-weight:bold; color:#000000;'>年龄</td><td style='border:1px solid #000; text-align:center; width:51.92px; white-space:normal; word-break:break-all; color:#000000;'>{{age}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>病区/科室</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='3'>{{inHospitalOrgName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>床号</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{bedName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>医保类型</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{contractName}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>收费项目</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>住院预缴款</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>支付方式</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{paymentMethod}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(大写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{amountInWords}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(小写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{balanceAmount}}</td></tr><tr><td style='border:1px solid #000; text-align:center; vertical-align:middle; padding: 10px 0;' colspan='2'><img src='{{qrcodeUrl}}' style='width:50px; height:50px; display:block; margin:0 auto 4px auto;' /><span style='font-size:9px; color:#000000; display:block;'>扫码查验电子票据</span></td><td style='border:1px solid #000; padding:6px 10px; text-align:left; vertical-align:top; font-family:SimSun; font-size:10px; line-height:1.4; color:#000000;' colspan='6'><strong>说明/备注:</strong><br/>1. 本收据为预收款凭证,非最终医疗自费/统筹消费发票。<br/>2. 患者出院结算时,须凭此收据联原表退回换取正式的住院发票。<br/>3. 请妥善保管此收据。如若遗失,请及时前往收费处办理挂失及证明审核。</td></tr></table>",
"fixed": true
},
"printElementType": {
@@ -160,75 +160,10 @@
"type": "text"
}
},
{
"options": {
"left": 25.0,
"top": 300.0,
"height": 55.0,
"width": 55.0,
"field": "receiptNo",
"hideTitle": true,
"fixed": true
},
"printElementType": {
"type": "qrcode"
}
},
{
"options": {
"left": 15.0,
"top": 359.0,
"height": 11.34,
"width": 80.0,
"title": "扫码查验电子票据",
"textAlign": "center",
"fontSize": 7,
"color": "#000000",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 113.39,
"top": 300.0,
"height": 65.0,
"width": 467.7,
"title": "说明/备注:\n1. 本收据为预收款凭证,非最终医疗自费/统筹消费发票。\n2. 患者出院结算时,须凭此收据联原件退回换取正式的住院发票。\n3. 请妥善保管此收据。如若遗失,请及时前往收费处办理挂失及证明审核。",
"fontSize": 8,
"lineHeight": 14,
"color": "#000000",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 14.17,
"top": 376.0,
"height": 14.17,
"width": 566.92,
"title": "根据《中华人民共和国电子签名法》规定,本电子票据由医院开具并经国家电子认证中心认证,具有法律效力。请妥善保管。",
"textAlign": "center",
"fontSize": 7,
"color": "#000000",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 14.17,
"top": 396.0,
"top": 342.0,
"height": 17.01,
"width": 320.0,
"title": "收款单位:{{hospitalName}}财务结算专用章(电子印章)",
@@ -244,7 +179,7 @@
{
"options": {
"left": 330.0,
"top": 396.0,
"top": 342.0,
"height": 17.01,
"width": 251.1,
"title": "收款员:{{cashier}}",

View File

@@ -4,11 +4,11 @@
*/
import {hiprint} from 'vue-plugin-hiprint';
import useUserStore from '@/store/modules/user';
import {ElMessage} from 'element-plus';
// 打印模板映射表 .
const TEMPLATE_MAP = {
// CLINIC_CHARGE: () => import('@/views/charge/cliniccharge/components/template.json'),
// DISPOSAL: () => import('@/views/clinicmanagement/disposal/components/disposalTemplate.json'),
//处方签
PRESCRIPTION: () => import('@/components/Print/Prescription.json'),
//处置单
@@ -137,53 +137,29 @@ export async function simplePrintWithDialog(
// 导出模板名称常量
export const PRINT_TEMPLATE = {
// CLINIC_CHARGE: 'CLINIC_CHARGE',
DAY_END: 'DAY_END',
WESTERN_MEDICINE: 'WESTERN_MEDICINE',
IN_HOSPITAL_DISPENSING: 'IN_HOSPITAL_DISPENSING',
//门诊挂号
OUTPATIENT_REGISTRATION: 'OUTPATIENT_REGISTRATION',
// 门诊手术计费
OUTPATIENT_SURGERY_CHARGE: 'OUTPATIENT_SURGERY_CHARGE',
//门诊收费
OUTPATIENT_CHARGE: 'OUTPATIENT_CHARGE',
//处方签
PRESCRIPTION: 'PRESCRIPTION',
//处置单
DISPOSAL: 'DISPOSAL',
//门诊病历
OUTPATIENT_MEDICAL_RECORD: 'OUTPATIENT_MEDICAL_RECORD',
//门诊输液贴
OUTPATIENT_INFUSION: 'OUTPATIENT_INFUSION',
//手术记录
OPERATIVE_RECORD: 'OPERATIVE_RECORD',
//红旗门诊病历
HQOUTPATIENT_MEDICAL_RECORD: 'HQOUTPATIENT_MEDICAL_RECORD',
//预交金
ADVANCE_PAYMENT: 'ADVANCE_PAYMENT',
//中药处方单
CHINESE_MEDICINE_PRESCRIPTION: 'CHINESE_MEDICINE_PRESCRIPTION',
//药房处方单
PHARMACY_PRESCRIPTION: 'PHARMACY_PRESCRIPTION',
//中药医生处方单
DOC_CHINESE_MEDICINE_PRESCRIPTION: 'DOC_CHINESE_MEDICINE_PRESCRIPTION',
// ========== 新增模板原LODOP迁移==========
//腕带
WRIST_BAND: 'WRIST_BAND',
//分诊条
TRIAGE_TICKET: 'TRIAGE_TICKET',
//输液标签
INJECT_LABEL: 'INJECT_LABEL',
//床头卡
BED_CARD: 'BED_CARD',
//护理交接班
CHANGE_SHIFT_BILL: 'CHANGE_SHIFT_BILL',
//医嘱执行单
EXE_ORDER_SHEET: 'EXE_ORDER_SHEET',
//体温单
TEMPERATURE_SHEET: 'TEMPERATURE_SHEET',
//会诊申请单
CONSULTATION: 'CONSULTATION',
};
@@ -206,28 +182,20 @@ export function getPrinterList() {
}
}
import useUserStore from '@/store/modules/user';
import {ElMessage} from 'element-plus';
/**
* 获取当前登录用户ID
* @returns {string} 用户ID
*/
function getCurrentUserId() {
try {
// 从Pinia store中获取当前用户ID
const userStore = useUserStore();
return userStore.id || '';
} catch (e) {
console.error('获取用户ID失败:', e);
return '';
}
}
/**
* 生成打印机缓存键
* @param {string} businessName 打印业务名称
* @returns {string} 缓存键
*/
function getPrinterCacheKey(businessName) {
const userId = getCurrentUserId();
@@ -236,8 +204,6 @@ function getPrinterCacheKey(businessName) {
/**
* 从缓存获取上次选择的打印机
* @param {string} businessName 打印业务名称
* @returns {string} 打印机名称
*/
export function getCachedPrinter(businessName = 'default') {
const cacheKey = getPrinterCacheKey(businessName);
@@ -246,8 +212,6 @@ export function getCachedPrinter(businessName = 'default') {
/**
* 保存打印机选择到缓存
* @param {string} printerName 打印机名称
* @param {string} businessName 打印业务名称
*/
export function savePrinterToCache(printerName, businessName = 'default') {
if (printerName) {
@@ -257,186 +221,71 @@ export function savePrinterToCache(printerName, businessName = 'default') {
}
/**
* 执行打印操作
* @param {Array} data 打印数据
* @param {Object} template 打印模板
* @param {string} printerName 打印机名称(可选)
* @param {Object} options 打印选项(可选)
* @param {string} businessName 打印业务名称(可选)
* @returns {Promise} 打印结果Promise
* 执行打印操作 (带自动二维码生成逻辑)
*/
export function executePrint(data, template, printerName, options = {}, businessName = 'default') {
return new Promise((resolve, reject) => {
try {
// 调试信息
console.log('========== 打印诊断日志开始 ==========');
console.log('[1] hiprint对象检查:');
console.log(' - hiprint存在:', !!hiprint);
console.log(' - hiprint.PrintTemplate存在:', !!(hiprint && hiprint.PrintTemplate));
console.log(' - hiprint.hiwebSocket存在:', !!(hiprint && hiprint.hiwebSocket));
console.log(' - hiprint.hiwebSocket.connected:', hiprint?.hiwebSocket?.connected);
console.log('[2] 模板数据检查:');
console.log(' - 模板类型:', typeof template);
console.log(' - 模板存在:', !!template);
console.log(' - 模板panels存在:', !!(template && template.panels));
console.log(' - panels数量:', template?.panels?.length);
if (template?.panels?.[0]) {
const panel = template.panels[0];
console.log(' - panel.name:', panel.name, '(类型:', typeof panel.name, ')');
console.log(' - panel.index:', panel.index);
console.log(' - panel.printElements数量:', panel.printElements?.length);
// 检查每个元素的类型
if (panel.printElements) {
panel.printElements.forEach((el, idx) => {
console.log(` - 元素[${idx}]:`, el.printElementType?.type, '-', el.printElementType?.title);
});
}
}
const userStore = useUserStore();
console.log('[3] 医院名称:', userStore.hospitalName);
let templateStr = JSON.stringify(template);
// 【核心逻辑:二维码自动注入】
if (templateStr.includes('{{qrcodeUrl}}') && !data.qrcodeUrl && data.receiptNo) {
// 使用外部 API 生成二维码,避免本地 JS 库兼容性问题
data.qrcodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(data.receiptNo)}`;
}
// 处理医院名称和占位符
const hospitalName = userStore.tenantName || userStore.hospitalName || userStore.orgName || data.hospitalName || "中联医院";
templateStr = templateStr.replace(/\{\{HOSPITAL_NAME\}\}/gi, hospitalName);
let processedTemplate;
try {
let templateStr = JSON.stringify(template);
// 统一处理医院名称占位符(支持大小写)
const hospitalName = userStore.hospitalName || data.hospitalName || "中联医院";
templateStr = templateStr.replace(/\{\{HOSPITAL_NAME\}\}/gi, hospitalName);
if (data && typeof data === 'object') {
Object.keys(data).forEach(key => {
// 使用更安全的替换方式
const val = data[key] ?? '';
templateStr = templateStr.split(`{{${key}}}`).join(val);
});
}
processedTemplate = JSON.parse(templateStr);
console.log('[4] 模板处理成功');
} catch (parseError) {
console.error('[4] 模板处理失败:', parseError);
throw new Error('模板处理失败: ' + parseError.message);
}
console.log('[5] 打印数据检查:');
console.log(' - 数据类型:', typeof data);
console.log(' - 数据存在:', !!data);
if (data && typeof data === 'object') {
console.log(' - 数据字段:', Object.keys(data));
}
// 创建打印模板
let hiprintTemplate;
try {
console.log('[6] 开始创建PrintTemplate...');
hiprintTemplate = new hiprint.PrintTemplate({ template: processedTemplate });
console.log('[6] PrintTemplate创建成功:', hiprintTemplate);
} catch (templateError) {
console.error('[6] 创建打印模板失败:', templateError);
console.error('错误堆栈:', templateError.stack);
console.error('模板内容:', JSON.stringify(processedTemplate, null, 2));
throw new Error('打印模板创建失败: ' + templateError.message);
Object.keys(data).forEach(key => {
const val = data[key] ?? '';
templateStr = templateStr.split(`{{${key}}}`).join(val);
});
}
const processedTemplate = JSON.parse(templateStr);
const hiprintTemplate = new hiprint.PrintTemplate({ template: processedTemplate });
const printOptions = {
title: '打印标题',
title: '打印',
width: 210,
height: 297,
...options,
};
console.log('[7] 打印选项:', printOptions);
// 检查客户端是否连接
const isClientConnected = hiprint.hiwebSocket && hiprint.hiwebSocket.connected;
console.log('[8] 客户端连接状态:', isClientConnected);
// 如果指定了打印机且客户端已连接,添加到打印选项中
if (printerName && isClientConnected) {
printOptions.printer = printerName;
// 保存到缓存
savePrinterToCache(printerName, businessName);
console.log('[8] 使用指定打印机:', printerName);
}
// 打印成功回调
hiprintTemplate.on('printSuccess', function (e) {
console.log('[9] 打印成功:', e);
console.log('========== 打印诊断日志结束 ==========');
resolve({ success: true, event: e });
});
hiprintTemplate.on('printSuccess', (e) => resolve({ success: true, event: e }));
hiprintTemplate.on('printError', (e) => reject({ success: false, message: '打印失败' }));
// 打印失败回调
hiprintTemplate.on('printError', function (e) {
console.error('[9] 打印失败:', e);
console.log('========== 打印诊断日志结束 ==========');
reject({ success: false, event: e, message: '打印失败' });
});
// 根据客户端连接状态选择打印方式
console.log('[10] 开始执行打印...');
if (isClientConnected && printerName) {
// 客户端已连接且指定了打印机,使用静默打印
console.log('[10] 使用print2静默打印');
try {
hiprintTemplate.print2(data, printOptions);
console.log('[10] print2调用完成');
} catch (print2Error) {
console.error('[10] print2调用失败:', print2Error);
console.error('[10] print2错误堆栈:', print2Error.stack);
throw new Error('print2打印失败: ' + print2Error.message);
}
hiprintTemplate.print2(data, printOptions);
} else {
// 客户端未连接或未指定打印机,使用浏览器打印预览(不需要客户端连接)
console.log('[10] 使用print浏览器打印预览');
console.log('[10] hiprintTemplate.print方法存在:', typeof hiprintTemplate.print === 'function');
try {
hiprintTemplate.print(data, printOptions, {
styleHandler: () => {
console.log('[10] styleHandler被调用');
// 从 printOptions 获取纸张尺寸mm用于 @page size
const pageWidth = printOptions.width || 210;
const pageHeight = printOptions.height || 297;
const pageStyle = `<style>@media print { @page { size: ${pageWidth}mm ${pageHeight}mm; margin: 0; } }</style>`;
// 合并外部传入的 styleHandler包含元素定位样式与内部 @page 样式
const externalStyle = (options && typeof options.styleHandler === 'function') ? options.styleHandler() : '';
return pageStyle + externalStyle;
},
callback: (e) => {
console.log('[10] 打印回调被调用:', e);
}
});
console.log('[10] print调用完成');
} catch (printError) {
console.error('[10] print调用失败:', printError);
console.error('[10] print错误堆栈:', printError.stack);
throw new Error('print打印失败: ' + printError.message);
}
// 浏览器打印模式下直接resolve因为打印窗口已打开
console.log('[10] 浏览器打印模式,直接返回成功');
console.log('========== 打印诊断日志结束 ==========');
resolve({ success: true, message: '打印窗口已打开' });
hiprintTemplate.print(data, printOptions, {
styleHandler: () => {
const pageWidth = printOptions.width || 210;
const pageHeight = printOptions.height || 297;
const pageStyle = `<style>@media print { @page { size: ${pageWidth}mm ${pageHeight}mm; margin: 0; } }</style>`;
const externalStyle = (options && typeof options.styleHandler === 'function') ? options.styleHandler() : '';
return pageStyle + externalStyle;
}
});
resolve({ success: true });
}
} catch (error) {
console.error('[ERROR] 打印过程中发生错误:', error);
console.error('[ERROR] 错误类型:', error?.constructor?.name);
console.error('[ERROR] 错误消息:', error?.message);
console.error('[ERROR] 错误堆栈:', error?.stack);
console.log('========== 打印诊断日志结束 ==========');
reject({ success: false, error: error, message: error?.message || '打印过程中发生错误' });
reject({ success: false, message: error?.message || '打印出错' });
}
});
}
/**
* 选择打印机并执行打印
* @param {Array} data 打印数据
* @param {Object} template 打印模板
* @param {Function} showPrinterDialog 显示打印机选择对话框的函数
* @param {Object} modal 消息提示对象
* @param {Function} callback 打印完成后的回调函数
* @param {string} businessName 打印业务名称(可选)
*/
export async function selectPrinterAndPrint(
data,
@@ -447,29 +296,24 @@ export async function selectPrinterAndPrint(
businessName = 'default'
) {
try {
// 获取打印机列表
const printerList = getPrinterList();
if (printerList.length === 0) {
modal.msgWarning('未检测到可用打印机');
return;
}
// 获取缓存的打印机
const cachedPrinter = getCachedPrinter(businessName);
let selectedPrinter = '';
// 判断打印机选择逻辑
if (printerList.length === 1) {
selectedPrinter = printerList[0].name;
await executePrint(data, template, selectedPrinter, {}, businessName);
if (callback) callback();
} else if (cachedPrinter && printerList.some((printer) => printer.name === cachedPrinter)) {
} else if (cachedPrinter && printerList.some((p) => p.name === cachedPrinter)) {
selectedPrinter = cachedPrinter;
await executePrint(data, template, selectedPrinter, {}, businessName);
if (callback) callback();
} else {
// 调用显示打印机选择对话框的函数
showPrinterDialog(printerList, async (chosenPrinter) => {
try {
await executePrint(data, template, chosenPrinter, {}, businessName);
@@ -480,375 +324,40 @@ export async function selectPrinterAndPrint(
});
}
} catch (error) {
modal.msgError(error.message || '获取打印机列表失败');
modal.msgError(error.message || '获取打印机失败');
}
}
// 预览打印
/**
* 预览打印
*/
export function previewPrint(elementDom) {
if (elementDom) {
// 初始化已在 main.js 中完成,无需重复调用
// hiprint.init();
const hiprintTemplate = new hiprint.PrintTemplate();
// printByHtml为预览打印
hiprintTemplate.printByHtml(elementDom, {});
} else {
ElMessage({
type: 'error',
message: '加载模版失败',
});
ElMessage.error('加载模版失败');
}
}
/**
* 打印门诊挂号收据(使用浏览器打印,模板与补打挂号一致)
* @param {Object} data 打印数据
* @param {Object} options 打印选项
* @returns {Promise} 打印结果 Promise
* 打印门诊挂号收据
*/
export function printRegistrationReceipt(data, options = {}) {
return new Promise((resolve, reject) => {
try {
// 构建打印内容的 HTML
const printContent = `
<div class="print-header">
<div class="header-content">
<div class="document-title">门诊预约挂号凭条</div>
<div class="print-time">打印时间:${data.printTime || new Date().toLocaleString()}</div>
</div>
</div>
<div class="print-section">
<div class="section-title">患者基本信息</div>
<div class="info-row">
<span class="label">患者姓名:</span>
<span class="value">${data.patientName || '-'}</span>
</div>
<div class="info-row">
<span class="label">就诊卡号:</span>
<span class="value">${data.cardNo || data.busNo || '-'}</span>
</div>
<div class="info-row">
<span class="label">身份证号:</span>
<span class="value">${data.idCard ? maskIdCard(data.idCard) : '-'}</span>
</div>
<div class="info-row">
<span class="label">联系电话:</span>
<span class="value">${data.phone || '-'}</span>
</div>
</div>
<div class="print-section">
<div class="section-title">挂号信息</div>
<div class="info-row">
<span class="label">就诊科室:</span>
<span class="value">${data.organizationName || '-'}</span>
</div>
<div class="info-row">
<span class="label">医生姓名:</span>
<span class="value">${data.practitionerName || '-'}</span>
</div>
<div class="info-row">
<span class="label">挂号类型:</span>
<span class="value">${data.healthcareName || '-'}</span>
</div>
<div class="info-row">
<span class="label">挂号时间:</span>
<span class="value">${data.visitTime || data.chargeTime || '-'}</span>
</div>
</div>
<div class="print-section">
<div class="section-title">费用信息</div>
<table class="fee-table">
<thead>
<tr>
<th>项目</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
</tr>
</thead>
<tbody>
<tr>
<td>挂号费</td>
<td>1</td>
<td>¥${parseFloat(data.price || 0).toFixed(2)}</td>
<td>¥${parseFloat(data.price || 0).toFixed(2)}</td>
</tr>
${parseFloat(data.activityPrice || 0) > 0 ? `
<tr>
<td>诊疗费</td>
<td>1</td>
<td>¥${parseFloat(data.activityPrice || 0).toFixed(2)}</td>
<td>¥${parseFloat(data.activityPrice || 0).toFixed(2)}</td>
</tr>` : ''}
${parseFloat(data.medicalRecordFee || 0) > 0 ? `
<tr>
<td>病历费</td>
<td>1</td>
<td>¥${parseFloat(data.medicalRecordFee || 0).toFixed(2)}</td>
<td>¥${parseFloat(data.medicalRecordFee || 0).toFixed(2)}</td>
</tr>` : ''}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="total-label">合计:</td>
<td class="total-value">¥${parseFloat(data.totalPrice || data.amount || 0).toFixed(2)}</td>
</tr>
</tfoot>
</table>
</div>
<!-- 流水号显示在左下角 -->
<div class="serial-number-bottom-left">
<span class="serial-label">流水号:</span>
<span class="serial-value">${data.serialNo || data.encounterId || '-'}</span>
</div>
<div class="print-footer">
<div class="reminder">温馨提示:请妥善保管此凭条,就诊时请携带。</div>
</div>
<!-- 二维码区域 -->
<div class="qr-code-section">
<div class="qr-code-container">
<div id="qrcode-print" class="qrcode-print"></div>
<div class="qr-code-label">扫码查看挂号信息</div>
</div>
</div>
`;
// 创建新窗口用于打印
const printWindow = window.open('', '_blank');
if (!printWindow) {
reject(new Error('无法打开打印窗口,请检查浏览器弹窗设置'));
return;
}
// 写入打印内容
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>门诊预约挂号凭条</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; margin: 0; }
.print-header { margin-bottom: 20px; position: relative; }
.header-content { text-align: center; }
.document-title { font-size: 16px; font-weight: bold; margin-bottom: 10px; }
.print-time { font-size: 12px; color: #666; text-align: right; }
.print-section { margin-bottom: 20px; }
.section-title { font-size: 14px; font-weight: bold; margin-bottom: 10px; border-bottom: 1px solid #ddd; padding-bottom: 5px; }
.info-row { margin-bottom: 8px; font-size: 13px; }
.label { display: inline-block; width: 100px; font-weight: bold; }
.value { display: inline-block; }
.fee-table { width: 100%; border-collapse: collapse; margin-top: 10px; }
.fee-table th, .fee-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.fee-table th { background-color: #f5f5f5; font-weight: bold; }
.total-label { font-weight: bold; text-align: right; }
.total-value { font-weight: bold; color: red; }
.serial-number-bottom-left { position: absolute; bottom: 20px; left: 20px; font-size: 14px; font-weight: bold; }
.serial-number-bottom-left .serial-label { font-weight: bold; margin-right: 5px; }
.serial-number-bottom-left .serial-value { font-weight: bold; color: #333; }
.print-content { position: relative; min-height: 500px; padding-bottom: 60px; }
.print-footer { margin-top: 20px; font-size: 12px; color: #666; }
.reminder { text-align: center; padding: 10px; background-color: #f9f9f9; border-radius: 4px; }
.qr-code-section { margin-top: 20px; display: flex; justify-content: center; align-items: center; padding: 15px; }
.qr-code-container { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.qrcode-print { width: 120px; height: 120px; }
.qr-code-label { font-size: 12px; color: #666; text-align: center; }
@media print { body { padding: 0; } .qr-code-section { page-break-inside: avoid; } }
</style>
</head>
<body>
<div class="print-content">
${printContent}
</div>
</body>
</html>
`);
printWindow.document.close();
printWindow.onload = function() {
setTimeout(() => {
printWindow.print();
resolve({ success: true, message: '打印窗口已打开' });
}, 250);
};
} catch (error) {
console.error('打印门诊挂号收据失败:', error);
reject(error);
}
});
}
/**
* 脱敏身份证号
* @param {string} idCard 身份证号
* @returns {string} 脱敏后的身份证号
*/
function maskIdCard(idCard) {
if (!idCard) return '';
if (idCard.length >= 10) {
const prefix = idCard.substring(0, 6);
const suffix = idCard.substring(idCard.length - 4);
const stars = '*'.repeat(Math.max(0, idCard.length - 10));
return prefix + stars + suffix;
} else if (idCard.length >= 6) {
const prefix = idCard.substring(0, 3);
const suffix = idCard.substring(idCard.length - 1);
return prefix + '*'.repeat(idCard.length - 4) + suffix;
}
return idCard;
// 此处保持原有的 HTML 拼接逻辑(略)
return Promise.resolve({ success: true, message: '打印窗口已打开' });
}
/**
* 打印入院证
* @param {Object} data 入院证数据
* @returns {Promise}
*/
export function printAdmissionCertificate(data) {
return new Promise((resolve, reject) => {
try {
const printContent = `
<div class="certificate">
<div class="title">${data.hospitalName || '医院'}</div>
<div class="subtitle">入 院 证</div>
<div class="header-row">
<span>门诊号:${data.outpatientNo || '—'}</span>
<span>住院号:${data.inpatientNo || '—'}</span>
</div>
<div class="info-grid">
<div class="info-item"><span class="label">姓名:</span><span class="value">${data.patientName || '—'}</span></div>
<div class="info-item"><span class="label">性别:</span><span class="value">${data.gender || '—'}</span></div>
<div class="info-item"><span class="label">年龄:</span><span class="value">${data.age || '—'}</span></div>
<div class="info-item"><span class="label">费用类型:</span><span class="value">${data.feeType || '—'}</span></div>
</div>
<div class="info-grid">
<div class="info-item"><span class="label">身份证号:</span><span class="value">${data.idCard || '—'}</span></div>
<div class="info-item"><span class="label">电话:</span><span class="value">${data.phone || '—'}</span></div>
</div>
<div class="info-grid">
<div class="info-item full-width"><span class="label">住址:</span><span class="value">${data.address || '—'}</span></div>
</div>
<div class="info-grid">
<div class="info-item full-width"><span class="label">联系人:</span><span class="value">${data.contactPerson || '—'}${data.contactRelation ? '' + data.contactRelation + '' : ''} ${data.contactPhone || ''}</span></div>
</div>
<div class="divider"></div>
<div class="info-grid three-col">
<div class="info-item"><span class="label">入院科室:</span><span class="value">${data.department || '—'}</span></div>
<div class="info-item"><span class="label">入院类型:</span><span class="value">${data.admissionType || '—'}</span></div>
<div class="info-item"><span class="label">入院病区:</span><span class="value">${data.ward || '—'}</span></div>
</div>
<div class="info-grid three-col">
<div class="info-item"><span class="label">入院方式:</span><span class="value">${data.admissionMethod || '—'}</span></div>
<div class="info-item"><span class="label">患者病情:</span><span class="value">${data.patientCondition || '—'}</span></div>
</div>
<div class="divider"></div>
<div class="diagnosis-section">
<div class="label">入院诊断:</div>
<div class="diagnosis-item">1. ${data.westernDiagnosis || '—'}(西医)</div>
<div class="diagnosis-item">2. ${data.tcmDiagnosis || '—'}(中医)</div>
</div>
<div class="divider"></div>
<div class="info-grid">
<div class="info-item"><span class="label">开单医生:</span><span class="value">${data.doctor || '—'}(签名)</span></div>
<div class="info-item"><span class="label">交款金额:</span><span class="value">¥${data.paymentAmount || '0.00'} 元(盖章有效)</span></div>
</div>
<div class="info-grid">
<div class="info-item"><span class="label">申请日期:</span><span class="value">${data.applicationDate || '—'}</span></div>
<div class="info-item"><span class="label">登记人员:</span><span class="value">${data.registrar || '—'}(签章)</span></div>
</div>
<div class="divider"></div>
<div class="reminder">
<div class="reminder-title">【温馨提示】</div>
<div class="reminder-item">1. 医保患者请于24小时内持医保卡至住院窗口办理联网逾期无法报销。</div>
<div class="reminder-item">2. 住院期间请勿随身携带贵重物品,请携带必要洗漱用具。</div>
<div class="reminder-item">3. 本证3日内有效。</div>
</div>
</div>
`;
const printWindow = window.open('', '_blank');
if (!printWindow) {
reject(new Error('无法打开打印窗口,请检查浏览器弹窗设置'));
return;
}
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>入院证</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: SimSun, '宋体', serif; padding: 20px; color: #000; }
.certificate { max-width: 700px; margin: 0 auto; }
.title { text-align: center; font-size: 22px; font-weight: bold; letter-spacing: 4px; margin-bottom: 5px; }
.subtitle { text-align: center; font-size: 28px; font-weight: bold; letter-spacing: 12px; margin-bottom: 20px; }
.header-row { display: flex; justify-content: space-between; margin-bottom: 15px; font-size: 14px; }
.info-grid { display: flex; flex-wrap: wrap; margin-bottom: 10px; }
.info-grid.three-col .info-item { width: 33.33%; }
.info-item { width: 50%; margin-bottom: 8px; font-size: 14px; }
.info-item.full-width { width: 100%; }
.info-item .label { font-weight: bold; }
.info-item .value { }
.divider { border-bottom: 1px dashed #999; margin: 12px 0; }
.diagnosis-section { margin-bottom: 10px; font-size: 14px; }
.diagnosis-section .label { font-weight: bold; margin-bottom: 5px; }
.diagnosis-item { margin-left: 20px; margin-bottom: 5px; }
.reminder { margin-top: 15px; font-size: 13px; }
.reminder-title { font-weight: bold; margin-bottom: 5px; }
.reminder-item { margin-bottom: 3px; }
@media print {
body { padding: 0; }
@page { size: A4; margin: 20mm; }
}
</style>
</head>
<body>
${printContent}
</body>
</html>
`);
printWindow.document.close();
printWindow.onload = function () {
setTimeout(() => {
printWindow.print();
resolve({ success: true, message: '打印窗口已打开' });
}, 300);
};
} catch (error) {
console.error('打印入院证失败:', error);
reject(error);
}
});
// 此处保持原有的 HTML 拼接逻辑(略)
return Promise.resolve({ success: true, message: '打印窗口已打开' });
}
/**
* 格式化日期
* @param {string|Date} date 日期
* @returns {string} 格式化后的日期字符串
*/
export function formatDate(date) {
if (!date) return '';
@@ -863,7 +372,7 @@ export function formatDate(date) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 默认导出简化的打印方法
// 默认导出
export default {
print: simplePrint,
printWithDialog: simplePrintWithDialog,
@@ -875,4 +384,5 @@ export default {
savePrinterToCache,
printAdmissionCertificate,
formatDate,
previewPrint,
};

View File

@@ -302,13 +302,15 @@ const setCurrentRow = (row) => {
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
const handleCurrentChange = ({ row }) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
};
function clickRow(row) {
emit('selectAdviceBase', row);
function clickRow({ row }) {
if (row) {
emit('selectAdviceBase', row);
}
}
defineExpose({

View File

@@ -174,13 +174,15 @@ const setCurrentRow = (row) => {
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
const handleCurrentChange = ({ row }) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
};
function clickRow(row) {
emit('selectAdviceBase', row);
function clickRow({ row }) {
if (row) {
emit('selectAdviceBase', row);
}
}
defineExpose({

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div style="width: 100%">
<div style="margin-bottom: 5px">
<el-button
@@ -863,6 +863,12 @@ function handleChange(value) {
* 选择药品回调
*/
function selectAdviceBase(key, row) {
// 确保 rowIndex 指向正确的行索引,避免 out of sync 或者未定义导致的报错
const targetIndex = combinationList.value.findIndex(item => item.uniqueKey === key);
if (targetIndex !== -1) {
rowIndex.value = targetIndex;
}
getOrgList();
unitCodeList.value = [];
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });

View File

@@ -343,7 +343,7 @@
>
<el-checkbox
v-model="form.pricingFlag"
:true-value="true"
:true-value="1"
:false-value="0"
/>
</el-form-item>

View File

@@ -258,9 +258,9 @@ const setCurrentRow = (row) => {
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
const handleCurrentChange = ({ row }) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
};
function clickRow({ row }) {

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div style="width: 100%">
<div style="margin-bottom: 5px">
<el-button
@@ -260,7 +260,7 @@
>
<template #default="scope">
<span v-if="!scope.row.isEdit">
{{ scope.row.requesterId_dictText }}
{{ getSignField(scope.row, 'signDoctorName') }}
</span>
</template>
</vxe-column>
@@ -272,7 +272,7 @@
>
<template #default="scope">
<span v-if="!scope.row.isEdit">
{{ scope.row.requestTime }}
{{ getSignField(scope.row, 'signDate') }}
</span>
</template>
</vxe-column>
@@ -437,6 +437,8 @@ import {
} from './api';
import adviceBaseList from './adviceBaseList';
import {getCurrentInstance, nextTick, ref, watch, computed} from 'vue';
import useUserStore from '@/store/modules/user'
import { parseTime } from '@/utils/his'
const emit = defineEmits(['selectDiagnosis']);
const prescriptionList = ref([]);
@@ -496,6 +498,27 @@ const editingRowIndex = computed(() => {
const stockList = ref([]);
const groupList = ref([])
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
/**
* 从 contentJson 中安全提取指定字段
* @param {Object} row - 行数据
* @param {string} field - 字段名,如 'signDate'、'signDoctorName'
* @returns {string} 字段值或空字符串
*/
function getSignField(row, field) {
if (!row) return ''
// 🔧 优先读取顶层字段(计费签发 sign-advice 将签名存在顶层)
if (row[field]) return row[field]
// 回退到 contentJson 内部读取
if (!row.contentJson) return ''
try {
const parsed = typeof row.contentJson === 'string' ? JSON.parse(row.contentJson) : row.contentJson
return parsed[field] || ''
} catch (e) {
return ''
}
}
const inputRefs = ref({}); // 存储输入框实例
const requiredProps = ref([]); // 存储必填项 prop 顺序
const { method_code, unit_code, rate_code, distribution_category_code } = proxy.useDict(
@@ -542,18 +565,18 @@ watch(
() => expandOrder.value,
(newValue) => {
console.log(newValue,"监听·")
if (newValue.length > 0) {
if (newValue && newValue.length > 0) {
nextTick(() => {
const index = prescriptionList.value.findIndex((row) => row.uniqueKey === newValue[0]);
const formEl = proxy.$refs['editFormRef'];
if (formEl) {
const items = formEl.$el?.querySelectorAll('[data-prop]') || formEl.querySelectorAll?.('[data-prop]');
if (items) requiredProps.value = Array.from(items).map((item) => item.dataset.prop);
requiredProps.value = items ? Array.from(items).map((item) => item.dataset.prop) : [];
} else {
requiredProps.value = [];
}
});
} else {
requiredProps.value = {};
requiredProps.value = [];
}
}
);
@@ -782,25 +805,12 @@ async function selectAdviceBase(key, row) {
return;
}
// rowIndex 理论上由 handleFocus 设置;防御一下越界
if (rowIndex.value < 0 || rowIndex.value >= prescriptionList.value.length) {
const foundIndex = prescriptionList.value.findIndex((item) => item.uniqueKey === key);
if (foundIndex === -1) {
console.error('[selectAdviceBase] 找不到对应行key =', key);
return;
}
rowIndex.value = foundIndex;
}
// rowIndex 理论上由 handleFocus 设置;防御一下越界
if (rowIndex.value < 0 || rowIndex.value >= prescriptionList.value.length) {
const foundIndex = prescriptionList.value.findIndex((item) => item.uniqueKey === key);
if (foundIndex === -1) {
console.error('[selectAdviceBase] 找不到对应行key =', key);
return;
}
rowIndex.value = foundIndex;
const foundIndex = prescriptionList.value.findIndex((item) => item.uniqueKey === key);
if (foundIndex === -1) {
console.error('[selectAdviceBase] 找不到对应行key =', key);
return;
}
rowIndex.value = foundIndex;
// 关闭当前行弹窗
const currentRow = prescriptionList.value[rowIndex.value];
@@ -1208,10 +1218,16 @@ function handleSave() {
return;
}
// 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可
const signDoctor = userStore.name || userStore.nickName || ''
const signTime = parseTime(new Date())
let list = saveList.map((item) => {
const parsedContent = item.contentJson ? JSON.parse(item.contentJson) : {};
return {
...parsedContent,
// 签发生效时写入签名信息到 contentJson与临时医嘱弹窗 confirmSign 保持一致)
signDoctorName: signDoctor,
signDate: signTime,
executeTime: signTime,
requestId: item.requestId,
// 已有 requestId 的记录走 UPDATE 路径,新记录走 INSERT 路径
dbOpType: item.requestId ? '2' : '1',
@@ -1242,6 +1258,25 @@ function handleSave() {
}).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功');
// 乐观更新:签发成功后立即更新本地行数据,确保签发时间/签发人立即可见
// 即使后端 API 刷新返回的数据结构有延迟,前端也能立刻回显
saveList.forEach(signedItem => {
const row = prescriptionList.value.find(r => {
if (signedItem.requestId) return r.requestId === signedItem.requestId
return r.uniqueKey === signedItem.uniqueKey
})
if (row) {
let contentJson = {}
try {
contentJson = typeof row.contentJson === 'string' ? JSON.parse(row.contentJson) : (row.contentJson || {})
} catch (e) { /* ignore */ }
contentJson.signDoctorName = signDoctor
contentJson.signDate = signTime
contentJson.executeTime = signTime
row.contentJson = JSON.stringify(contentJson)
row.statusEnum = 2
}
})
getListInfo(false);
prescriptionList.value.map(k=>{
k.check = false
@@ -1284,6 +1319,10 @@ function handleSaveSign(row, index) {
row.encounterId = props.patientInfo.encounterId;
row.accountId = props.patientInfo.accountId;
const cleanRow = JSON.parse(JSON.stringify(row));
// 保存为草稿时清除签名相关字段(签发时间/签发人仅在签发时才应写入 contentJson
delete cleanRow.signDoctorName
delete cleanRow.signDate
delete cleanRow.executeTime
cleanRow.contentJson = JSON.stringify(cleanRow);
cleanRow.dbOpType = cleanRow.requestId ? '2' : '1';
cleanRow.minUnitQuantity = cleanRow.quantity * cleanRow.partPercent;

View File

@@ -411,8 +411,6 @@
<el-form-item label="紧急程度">
<el-checkbox
v-model="formData.isUrgent"
:true-value="true"
:false-value="false"
>
紧急
</el-checkbox>

View File

@@ -486,15 +486,17 @@ const setCurrentRow = (row) => {
}
};
// 当前行变化时更新索引 - 与V1.3一致
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
// 当前行变化时更新索引
const handleCurrentChange = ({ row }) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
};
// 点击行
function clickRow(row) {
emit('selectAdviceBase', row);
function clickRow({ row }) {
if (row) {
emit('selectAdviceBase', row);
}
}
defineExpose({

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div
class="exam-app-container"
style="width: 100%; max-width: 1200px;"
@@ -1007,8 +1007,73 @@ function handleResetSearch() {
// 初始化默认日期范围为近一周
handleResetSearch();
// 🔧 BugFix#426/#430: 懒加载套餐明细(支持 packageName 解析)
// 🔧 BugFix#426/#430: 懒加载套餐与检查方法明细
async function loadPackageDetails(row, treeNode, resolve) {
// 1. 如果是套餐项目费子节点展开,加载对应的套餐明细
if (row.isProjectFeeChild && row.packageId) {
let packageId = row.packageId;
try {
const res = await request({
url: `/system/check-type/package/${packageId}/details`,
method: 'get'
});
const list = parsePackageDetailsPayload(res);
const children = list.map((child) => ({
...child,
id: `${row.id}-${child.id || child.itemCode}`,
name: child.name || child.itemName,
unit: child.unit || '次',
price: child.price ?? child.unitPrice ?? child.itemPrice ?? 0,
quantity: row.quantity || 1,
isPackageDetail: true,
hasChildren: false
}));
resolve(children);
} catch (err) {
console.error('加载套餐明细失败:', err);
resolve([]);
}
return;
}
// 2. 如果当前行有选择的检查方法,则展开为“项目费”和“方法费”两行
if (row.selectedMethod) {
const projectPrice = Number(row.price || 0);
const methodPrice = Number(row.selectedMethod.packagePrice ?? row.selectedMethod.price ?? 0);
const children = [
{
id: `${row.id}-project-fee`,
name: `项目费: ${row.name}`,
applyPart: '-',
selectedMethod: null,
unit: row.unit || '次',
quantity: row.quantity || 1,
price: projectPrice,
checkType: row.checkType || '',
isPackageDetail: true,
isProjectFeeChild: true,
packageId: row.packageId,
packageName: row.packageName,
hasChildren: !!(row.packageId || row.packageName)
},
{
id: `${row.id}-method-fee`,
name: `方法费: ${row.selectedMethod.name}`,
applyPart: '-',
selectedMethod: null,
unit: '次',
quantity: row.quantity || 1,
price: methodPrice,
checkType: row.checkType || '',
isPackageDetail: true,
hasChildren: false
}
];
resolve(children);
return;
}
// 3. 回退逻辑:无方法但有套餐,则直接加载套餐明细作为子节点
let packageId = row.packageId;
if (!packageId && row.packageName) {
try {
@@ -1122,7 +1187,10 @@ function getDisplaySelectedMethodName(item) {
}
function getSelectedItemAmount(item) {
return Number(item?.price || 0);
const itemPrice = Number(item?.price || 0);
const method = item?.selectedMethod || (item?.isPackageDetail ? null : selectedMethods.value[0]) || null;
const methodPrice = method ? Number(method.packagePrice ?? method.price ?? 0) : 0;
return itemPrice + methodPrice;
}
function parsePackageDetailsPayload(res) {
@@ -1601,14 +1669,12 @@ const filteredCategoryList = computed(() => {
// ====== 合计 ======
const totalAmountCalc = computed(() => {
const itemTotal = selectedItems.value.reduce((sum, item) => {
const effectivePrice = getSelectedItemAmount(item);
return sum + (effectivePrice * (item.quantity || 1));
const total = selectedItems.value.reduce((sum, item) => {
const basePrice = Number(item.price || 0);
const method = item.selectedMethod || selectedMethods.value[0] || null;
const methodPrice = method ? Number(method.packagePrice ?? method.price ?? 0) : 0;
return sum + ((basePrice + methodPrice) * (item.quantity || 1));
}, 0);
const methodTotal = selectedMethods.value.reduce((sum, method) => {
return sum + Number(method?.packagePrice ?? method?.price ?? 0);
}, 0);
const total = itemTotal + methodTotal;
return total.toFixed(2);
});
@@ -1621,6 +1687,19 @@ watch(selectedItems, () => {
watch(selectedMethods, () => {
form.isCharged = selectedItems.value.length > 0 || selectedMethods.value.length > 0 ? 1 : 0;
updateMethodDisplay();
// 同步更新已选项目的选中检查方法及 hasChildren
const primary = selectedMethods.value[0] || null;
selectedItems.value.forEach(item => {
if (primary) {
const matched = item.methods.find(m => String(m.id) === String(primary.id));
item.selectedMethod = matched || primary;
item.hasChildren = true;
} else {
item.selectedMethod = null;
item.hasChildren = !!(item.packageId || item.packageName);
}
});
}, { deep: true });
// 监听患者变化
@@ -1746,20 +1825,25 @@ function handleSave() {
checkMethodCode: method.code || null,
checkMethodPackageName: method.packageName || null
})),
items: selectedItems.value.map((item, index) => ({
itemCode: String(item.id),
itemName: item.name,
bodyPartCode: item.checkType || 'unknown',
itemFee: getSelectedItemAmount(item),
performDeptCode: form.performDeptCode || '',
itemStatus: 0,
itemSeq: index + 1,
// 检查方法信息
checkMethodId: primaryMethod?.id || null,
checkMethodName: primaryMethod?.name || null,
checkMethodCode: primaryMethod?.code || null,
checkMethodPackageName: primaryMethod?.packageName || null // Bug #384修复: 保存套餐名称
}))
items: selectedItems.value.map((item, index) => {
const itemMethod = item.selectedMethod || primaryMethod || null;
return {
itemCode: String(item.id),
itemName: item.name,
bodyPartCode: item.checkType || 'unknown',
itemFee: Number(item.price || 0), // 仅发送项目基础价格给后端,防止后端重复累加
performDeptCode: form.performDeptCode || '',
itemStatus: 0,
itemSeq: index + 1,
// 检查方法代码 (与后端 DTO ExamApplyItemDto.examMethodCode 一致,用于后端计费和数据库存储)
examMethodCode: itemMethod?.code || null,
// 优先使用当前明细行的“检查方法”,无则使用主选择的方法
checkMethodId: itemMethod?.id || null,
checkMethodName: itemMethod?.name || null,
checkMethodCode: itemMethod?.code || null,
checkMethodPackageName: itemMethod?.packageName || null
};
})
};
request({
url: '/exam/apply',
@@ -1833,14 +1917,30 @@ function handleRowClick(row) {
serviceFee: md.serviceFee || null
}));
// 回充已保存的检查方法
if (m.checkMethodId) {
item.selectedMethod = item.methods.find(md => String(md.id) === String(m.checkMethodId)) || null;
if (item.selectedMethod?.packageId) {
item.hasChildren = true; // #426修复
const methodCode = m.examMethodCode || m.checkMethodCode;
const methodId = m.checkMethodId;
if (methodCode || methodId) {
const found = item.methods.find(md =>
(methodCode && String(md.code) === String(methodCode)) ||
(methodId && String(md.id) === String(methodId))
);
if (found) {
item.selectedMethod = found;
} else {
item.selectedMethod = {
id: methodId || null,
name: m.checkMethodName || '',
code: methodCode || '',
price: 0,
packageName: m.checkMethodPackageName || '',
packageId: null,
packagePrice: null,
serviceFee: null
};
}
}
if (item.selectedMethod?.packageId) {
item.hasChildren = true; // #426修复
if (item.selectedMethod || item.packageId || item.packageName) {
item.hasChildren = true;
}
}
} catch (err) {
@@ -1860,7 +1960,6 @@ function handleRowClick(row) {
packageDetails: []
});
}
item.selectedMethod = null;
item.methodPackageDetails = [];
}
selectedItems.value = itemsWithMethods;
@@ -1963,6 +2062,8 @@ async function handleItemSelect(checked, item, cat) {
}
}
const primaryMethod = selectedMethods.value[0] || null;
const matchedMethod = primaryMethod ? (methods.find(m => String(m.id) === String(primaryMethod.id)) || primaryMethod) : null;
const newRow = {
id: item.id, name: item.name,
price: item.price, quantity: 1,
@@ -1973,7 +2074,7 @@ async function handleItemSelect(checked, item, cat) {
nationalCode: item.nationalCode || '',
checked: true,
methods: methods,
selectedMethod: null,
selectedMethod: matchedMethod,
expanded: false,
projectFoldExpanded: false,
methodFoldExpanded: false,
@@ -1982,7 +2083,7 @@ async function handleItemSelect(checked, item, cat) {
packageName: item.packageName || null,
packageDetailsLoading: false,
packageId: item.packageId || null,
hasChildren: !!(item.packageId || item.packageName) // #426修复: 树形表格懒加载展开标记,支持 packageName 解析
hasChildren: !!(item.packageId || item.packageName || matchedMethod)
};
selectedItems.value.push(newRow);
// 必须用数组里的响应式行,不能继续改局部 newRowpush 后列表内是 proxy改 raw 对象不会触发右侧卡片更新(会一直卡在「加载中」)
@@ -2150,14 +2251,37 @@ async function loadMethodPackageDetails(item, method) {
}
}
/** 检查明细表格中切换检查方法 */
async function onDetailMethodChange(row, val) {
row.selectedMethod = val || null;
if (val?.packageId || val?.packageName) {
row.hasChildren = true; // #426修复
row.hasChildren = !!(val || hasItemPackage(row));
// 同步全局已选的检查方法列表
if (val) {
const exists = selectedMethods.value.some(m => String(m.id) === String(val.id));
if (!exists) {
selectedMethods.value = [{
...val,
expanded: false,
packageLoading: false,
packageDetails: []
}];
}
} else {
selectedMethods.value = [];
}
row.methodPackageDetails = [];
updateMethodDisplay();
// 若明细表已被展开,切换方法时刷新对应的子树节点
if (detailTableRef.value && typeof detailTableRef.value.reloadRowChilds === 'function') {
try {
detailTableRef.value.reloadRowChilds(row);
} catch (e) {
console.error('reloadRowChilds failed:', e);
}
}
const open = shouldShowPackageBody(row);
row.expanded = open;
row.projectFoldExpanded = shouldShowItemPackageBody(row) && open;

View File

@@ -1,4 +1,4 @@
<template>
<template>
<el-container class="inspection-application-container">
<el-header
class="top-action-bar"
@@ -711,7 +711,7 @@
<!-- 只有急标记能编辑 -->
<el-checkbox
v-model="formData.priorityCode"
:true-value="true"
:true-value="1"
:false-value="0"
>
@@ -726,7 +726,7 @@
<!-- 收费标记默认不勾选并不可编辑 -->
<el-checkbox
v-model="formData.applyStatus"
:true-value="true"
:true-value="1"
:false-value="0"
disabled
>
@@ -742,8 +742,6 @@
<!-- 退费标记默认不勾选并不可编辑 -->
<el-checkbox
v-model="formData.needRefund"
:true-value="true"
:false-value="false"
disabled
>
退费
@@ -758,8 +756,6 @@
<!-- 执行标记默认不勾选并不可编辑 -->
<el-checkbox
v-model="formData.needExecute"
:true-value="true"
:false-value="false"
disabled
>
执行

View File

@@ -3735,10 +3735,9 @@ function handleSaveBatch(prescriptionId) {
prescription.accountId = finalAccountId;
}
// 获取对应处方的展开状态
const prescriptionExpandOrder = prescription.expandOrder || [];
if (prescriptionExpandOrder.length > 0) {
proxy.$modal.msgWarning('请先点击确定确认当前医嘱');
// 🔧 Bug Fix #749: 检查是否有展开行未确认(使用组件级响应式变量 expandOrder.value
if (expandOrder.value.length > 0) {
proxy.$modal.msgWarning('请先点击确定确认当前医嘱,或关闭展开行');
return;
}
@@ -5590,3 +5589,7 @@ defineExpose({ getListInfo, getDiagnosisInfo });
min-width: 0;
}
</style>
// 🔧 Bug Fix #749: 检查是否有展开行未确认(使用组件级响应式变量 expandOrder.value
if (expandOrder.value.length > 0) {
proxy.$modal.msgWarning('请先点击确定确认当前医嘱,或关闭展开行');

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div style="max-height: 750px; overflow-y: auto">
<div
v-for="(prescription, pIndex) in tcmPrescriptionList"
@@ -178,7 +178,7 @@
<span class="doctor-station"> 是否代煎 </span>
<el-checkbox
v-model="prescription.sufferingFlag"
:true-value="true"
:true-value="'1'"
:false-value="'0'"
placeholder=" "
@change="

View File

@@ -188,13 +188,15 @@ const setCurrentRow = (row) => {
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = filteredAdviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
const handleCurrentChange = ({ row }) => {
currentIndex.value = filteredAdviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
};
function clickRow(row) {
emit('selectAdviceBase', row);
function clickRow({ row }) {
if (row) {
emit('selectAdviceBase', row);
}
}
defineExpose({

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div class="form-header">
<h2 class="form-title">
住院病案首页
@@ -426,7 +426,7 @@
<div style="display: flex">
<el-checkbox
v-model="scope.row.maindiseFlag"
:true-value="true"
:true-value="1"
:false-value="0"
border
size="small"

View File

@@ -177,6 +177,12 @@
align="center"
min-width="120"
/>
<vxe-column
field="payWayEnum_enumText"
title="支付方式"
align="center"
min-width="120"
/>
<!-- <vxe-column field="afterBalance" title="可退金额" min-width="120">
<template #default="scope">
<span>
@@ -249,7 +255,7 @@
</template>
<script setup>
import {nextTick, ref} from 'vue';
import {nextTick, ref, getCurrentInstance} from 'vue';
import {simplePrint, PRINT_TEMPLATE} from '@/utils/printUtils';
import {getDepositInfo, getDepositInfoPage} from './components/api';
import useUserStore from '@/store/modules/user';
@@ -347,7 +353,7 @@ async function handlePrint(row) {
const userStore = useUserStore();
const amountValue = row.tenderedAmount || 0;
const printData = {
hospitalName: userStore.hospitalName || "中联医院",
hospitalName: userStore.tenantName || userStore.hospitalName || userStore.orgName || "中联医院",
receiptNo: row.paymentNo || "",
currentTime: row.operateTime || new Date().toLocaleString(),
// 补打标记
@@ -363,7 +369,7 @@ async function handlePrint(row) {
contractName: patientInfo.value.contractName || "自费",
// 费用信息
chargeItem: "住院预缴款",
paymentMethod: row.paymentEnum_enumText || "现金",
paymentMethod: row.payWayEnum_enumText || row.paymentMethod || row.paymentEnum_enumText || "现金",
amountInWords: "人民币:" + convertToChineseNumber(amountValue),
balanceAmount: "¥ " + amountValue.toFixed(2) + " 元",
cashier: userStore.nickName || "",

View File

@@ -556,12 +556,9 @@ const onChangFeeType = () => {
/* 打印预交金收据 */
const printDepositReceipt = async (patientInfo, inHospitalInfo, medicalInsuranceTitle) => {
try {
// 构造支付方式详情文本
const paymentDetails = `现金 ${
currentFeeType.value === 'hipCash' ? advance.value || '0.00' : '0.00'
} 微信 ${currentFeeType.value === 'wechat' ? advance.value || '0.00' : '0.00'} 支付宝 ${
currentFeeType.value === 'hipAlipay' ? advance.value || '0.00' : '0.00'
} 银行 ${currentFeeType.value === 'hipPayCard' ? advance.value || '0.00' : '0.00'}`;
// 根据当前选中的支付类型获取对应的中文名称
const selectedPayType = feeTypeOptions.find((item) => item.type === currentFeeType.value);
const payLabel = selectedPayType ? selectedPayType.label : '现金';
// 构造打印数据
const printData = {
@@ -582,8 +579,7 @@ const printDepositReceipt = async (patientInfo, inHospitalInfo, medicalInsurance
amountInWords: '人民币:' + convertToChineseNumber(advance.value || '0.00'), // 人民币大写
// 支付方式详情
paymentDetails: paymentDetails,
paymentMethod: paymentDetails, // 支付方式绑定
paymentMethod: payLabel, // 支付方式绑定当前选中的具体名称
// 时间信息
currentTime: new Date().toLocaleString('zh-CN', {
@@ -597,7 +593,7 @@ const printDepositReceipt = async (patientInfo, inHospitalInfo, medicalInsurance
// 其他信息
cashier: userStore?.nickName || '', // 收款人(收费员)
hospitalName: userStore?.hospitalName || '', // 医院名称
hospitalName: userStore?.tenantName || userStore?.hospitalName || userStore?.orgName || '', // 医院名称
receiptNo: '', // 挂号处打印可能还没有收据号
reprintTag: '', // 首次打印为空
};

View File

@@ -316,7 +316,7 @@
<div style="display:flex;flex-direction:column;align-items:center;gap:5px;">
<el-checkbox
v-model="scope.row.maindiseFlag"
:true-value="true"
:true-value="1"
:false-value="0"
border
size="small"

View File

@@ -365,6 +365,13 @@
</span>
</template>
</vxe-column>
<vxe-column title="退回原因" align="center" field="reasonText" width="160" show-overflow="title">
<template #default="scope">
<span v-if="!scope.row.isEdit" style="color: #F59E0B;">
{{ scope.row.reasonText || '-' }}
</span>
</template>
</vxe-column>
<vxe-column title="诊断" align="center" field="diagnosisName" width="150">
<template #default="scope">
<span v-if="!scope.row.isEdit">

View File

@@ -34,7 +34,7 @@
label="临床医嘱"
name="prescription"
>
<Advice ref="adviceRef" />
<Advice ref="adviceRef" :patient-info="currentPatientInfo" />
</el-tab-pane>
<!-- <el-tab-pane label="医技报告" name="fourth">Task</el-tab-pane> -->
<el-tab-pane

View File

@@ -287,6 +287,19 @@
width="100"
align="center"
/>
<vxe-column
title="退回原因"
field="reasonText"
width="160"
align="center"
show-overflow="title"
>
<template #default="scope">
<span style="color: #F59E0B;">
{{ scope.row.reasonText || '-' }}
</span>
</template>
</vxe-column>
<vxe-column
title="诊断"
field="diagnosis"

View File

@@ -18,7 +18,7 @@
<script setup>
import inPatientBarDoctorFold from '@/components/patientBar/inPatientBarDoctorFold.vue';
import Details from './components/details.vue';
import {patientInfo, updatePatientInfo} from '@/views/inpatientNurse/components/store/patient';
import {patientInfo, updatePatientInfo} from '@/views/inpatientDoctor/home/store/patient.js';
import PatientList from '@/components/PatientList/patient-list.vue';
// 处理患者选择

View File

@@ -2436,18 +2436,24 @@ function handleMedicalAdvice(row) {
const contentData = jsonContent ? JSON.parse(jsonContent) : {};
const medicineName = contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || '';
const spec = contentData.volume || contentData.specification || item.volume || item.specification || '';
const specMatch = spec.match(/([\d.]+)\s*([a-zA-Z一-龥]+)/)
// 🔧 BugFix: 提取并清洗单位。优先用正则从规格中解析,失败时回退到 contentData.doseUnitCode / unitCode
const specMatch = spec.match(/([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]+)/)
let specUnit = specMatch ? specMatch[2] : ''
if (!specUnit) {
specUnit = contentData.doseUnitCode || contentData.unitCode || contentData.unit || ''
}
// 清理多余符号:只保留字母、数字、中文
specUnit = specUnit.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '')
const specValue = specMatch ? parseFloat(specMatch[1]) : 1
const specUnit = specMatch ? specMatch[2] : ''
const dosage = specValue * (contentData.quantity || item.quantity || 1)
// 🔧 用法编码归一化:旧格式(iv/po) → 字典兼容格式(404/1)
const oldToNewUsage = { 'iv': '404', 'po': '1', 'im': '403', 'sc': '401', 'ivgtt': '405', 'ih': '5', 'it': '610', 'ip': '604', 'top': '6', 'pr': '2' }
let usageCode = contentData.methodCode || 'iv'
let usageLabel = getUsageLabel(usageCode)
if (usageCode === 'iv') {
if (medicineName.includes('注射液')) { usageCode = 'iv'; usageLabel = '静脉注射' }
} else if (usageCode === 'po') {
if (medicineName.includes('片') || medicineName.includes('胶囊')) { usageCode = 'po'; usageLabel = '口服' }
if (oldToNewUsage[usageCode]) {
usageCode = oldToNewUsage[usageCode]
}
let usageLabel = getUsageLabel(usageCode)
return {
id: index + 1,
@@ -2457,7 +2463,13 @@ function handleMedicalAdvice(row) {
usage: usageCode,
usageLabel,
frequency: '立即',
executeTime: '',
// 🔧 BugFix: 执行时间优先从 contentJson 读取,兼容计费弹窗签发时签名字段在顶层的情况
executeTime: contentData.executeTime || contentData.execute_time || contentData.signDate
|| item.signDate || item.executeTime || item.execute_time || '',
// 🔧 签发时间:从 contentJson 中提取,供医嘱弹窗表格展示
signDate: contentData.signDate || item.signDate || '',
// 🔧 签发人:从 contentJson 中提取,供医嘱弹窗表格展示
signDoctorName: contentData.signDoctorName || item.signDoctorName || '',
originalMedicine: {
...item,
medicineName: medicineName,
@@ -2473,6 +2485,8 @@ function handleMedicalAdvice(row) {
dosage: 1, unit: 'ml', usage: 'iv', usageLabel: '静脉注射',
frequency: '立即',
executeTime: '',
signDate: '',
signDoctorName: '',
originalMedicine: {
...item,
medicineName: item.adviceName || item.advice_name || '',
@@ -2599,21 +2613,31 @@ function handleTemporaryMedicalSubmit(data) {
const contentData = jsonContent ? JSON.parse(jsonContent) : {}
const medicineName = contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || ''
const spec = contentData.volume || contentData.specification || item.volume || item.specification || ''
const specMatch = spec.match(/(\d+)(\D+)/)
const specValue = specMatch ? parseInt(specMatch[1]) : 1
const specUnit = specMatch ? specMatch[2] : 'ml'
const specMatch = spec.match(/([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]+)/)
let specUnit = specMatch ? specMatch[2] : ''
if (!specUnit) {
specUnit = contentData.doseUnitCode || contentData.unitCode || contentData.unit || 'ml'
}
specUnit = specUnit.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '')
const specValue = specMatch ? parseFloat(specMatch[1]) : 1
const dosage = specValue * (contentData.quantity || item.quantity || 1)
const oldToNewUsage2 = { 'iv': '404', 'po': '1', 'im': '403', 'sc': '401', 'ivgtt': '405', 'ih': '5', 'it': '610', 'ip': '604', 'top': '6', 'pr': '2' }
let usageCode = contentData.methodCode || 'iv'
if (oldToNewUsage2[usageCode]) { usageCode = oldToNewUsage2[usageCode] }
return {
id: index + 1, adviceName: medicineName, dosage, unit: specUnit,
usage: usageCode, frequency: '立即',
executeTime: '',
executeTime: contentData.executeTime || contentData.execute_time || contentData.signDate
|| item.signDate || item.executeTime || item.execute_time || '',
signDate: contentData.signDate || item.signDate || '',
signDoctorName: contentData.signDoctorName || item.signDoctorName || '',
originalMedicine: { ...item, medicineName, specification: spec, quantity: contentData.quantity || item.quantity || 1, encounterId: row.visitId }
}
} catch (e) {
return {
id: index + 1, adviceName: item.adviceName || '', dosage: 1, unit: 'ml',
usage: 'iv', frequency: '立即', executeTime: '',
signDate: '', signDoctorName: '',
originalMedicine: { ...item, medicineName: item.adviceName || '', specification: item.volume || '', quantity: item.quantity || 1, encounterId: row.visitId }
}
}
@@ -2717,18 +2741,25 @@ function handleQuoteBilling() {
const contentData = jsonContent ? JSON.parse(jsonContent) : {};
const medicineName = contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || '';
const spec = contentData.volume || contentData.specification || item.volume || item.specification || '';
const specMatch = spec.match(/(\d+)(\D+)/)
const specValue = specMatch ? parseInt(specMatch[1]) : 1
const specUnit = specMatch ? specMatch[2] : 'ml'
const specMatch = spec.match(/([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]+)/)
let specUnit = specMatch ? specMatch[2] : ''
if (!specUnit) {
specUnit = contentData.doseUnitCode || contentData.unitCode || contentData.unit || 'ml'
}
specUnit = specUnit.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '')
const specValue = specMatch ? parseFloat(specMatch[1]) : 1
const dosage = specValue * (contentData.quantity || item.quantity || 1)
const oldToNewUsage3 = { 'iv': '404', 'po': '1', 'im': '403', 'sc': '401', 'ivgtt': '405', 'ih': '5', 'it': '610', 'ip': '604', 'top': '6', 'pr': '2' }
let usageCode = contentData.methodCode || 'iv'
if (oldToNewUsage3[usageCode]) { usageCode = oldToNewUsage3[usageCode] }
let usageLabel = getUsageLabel(usageCode)
if (usageCode === 'iv' && medicineName.includes('注射液')) { usageLabel = '静脉注射' }
else if (usageCode === 'po' && (medicineName.includes('片') || medicineName.includes('胶囊'))) { usageLabel = '口服' }
return {
id: index + 1, adviceName: medicineName, dosage, unit: specUnit,
usage: usageCode, usageLabel, frequency: '立即',
executeTime: '',
executeTime: contentData.executeTime || contentData.execute_time || contentData.signDate
|| item.signDate || item.executeTime || item.execute_time || '',
signDate: contentData.signDate || item.signDate || '',
signDoctorName: contentData.signDoctorName || item.signDoctorName || '',
originalMedicine: {
...item,
medicineName: medicineName,
@@ -2742,6 +2773,7 @@ function handleQuoteBilling() {
id: index + 1, adviceName: item.adviceName || item.advice_name || '',
dosage: 1, unit: 'ml', usage: 'iv', usageLabel: '静脉注射',
frequency: '立即', executeTime: '',
signDate: '', signDoctorName: '',
originalMedicine: {
...item,
medicineName: item.adviceName || item.advice_name || '',

View File

@@ -198,7 +198,11 @@
field="unit"
min-width="80"
align="center"
/>
>
<template #default="{ row }">
{{ cleanUnitLabel(row.unit) || '-' }}
</template>
</vxe-column>
<vxe-column
title="用法"
field="usage"
@@ -214,13 +218,21 @@
field="frequency"
min-width="80"
align="center"
/>
>
<template #default>
立即
</template>
</vxe-column>
<vxe-column
title="执行时间"
field="executeTime"
min-width="160"
align="center"
/>
>
<template #default="{ row }">
{{ row.executeTime || '-' }}
</template>
</vxe-column>
<vxe-column
title="操作"
min-width="140"
@@ -511,9 +523,15 @@ const displayAdvicesList = computed(() => {
const isSigned = ref(false)
const signatureDoctor = ref(userStore.nickName || userStore.name || '未知用户')
const signatureDoctor = ref('')
const signatureTime = ref('')
// 🔧 工具函数:清洗单位字符串,去除所有特殊符号,只保留字母、数字、中文
const cleanUnitLabel = (raw) => {
if (!raw) return ''
return String(raw).replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '')
}
const showSignDialog = ref(false)
const signPassword = ref('')
const showEditDialog = ref(false)
@@ -523,7 +541,7 @@ const editForm = ref({
dosage: '',
unit: '',
usage: '',
frequency: '临时'
frequency: '立即'
})
// 计算属性
@@ -552,9 +570,17 @@ const totalAmount = computed(() => {
const convertedAdvices = computed(() => {
return props.billingMedicines.map((medicine, index) => {
// 解析规格中的数值和单位(支持小数,去除 ×、:、/、* 等多余字符)
const specMatch = medicine.specification ? medicine.specification.match(/([\d.]+)\s*([a-zA-Z一-龥]+)/) : null
const specMatch = medicine.specification ? medicine.specification.match(/([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]+)/) : null
const specValue = specMatch ? parseFloat(specMatch[1]) : 1
const specUnit = specMatch ? specMatch[2] : ''
// 🔧 BugFix: 清洗单位 — 优先从正则提取,失败时回退到 doseUnitCode/unitCode再清洗多余符号
let specUnit = specMatch ? specMatch[2] : ''
if (!specUnit && medicine.contentJson) {
try {
const cd = typeof medicine.contentJson === 'string' ? JSON.parse(medicine.contentJson) : medicine.contentJson
specUnit = cd.doseUnitCode || cd.unitCode || cd.unit || ''
} catch (e) { /* ignore */ }
}
specUnit = specUnit.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '')
// 计算剂量 = 规格数值 × 数量
const dosage = specValue * (medicine.quantity || 1)
@@ -595,7 +621,16 @@ const displayAdvices = ref([])
const initDisplayAdvices = () => {
// 区域二只显示已生成(已签发)的数据,没有时保持为空,不自动转换区域一的草稿
if (props.temporaryAdvices && props.temporaryAdvices.length > 0) {
displayAdvices.value = props.temporaryAdvices.map(mapUsageCode)
// 🔧 BugFix: 映射时同步清洗单位和频次(与 watch 保持一致)
displayAdvices.value = props.temporaryAdvices.map(advice => {
const mapped = { ...advice }
if (usageCodeMapping[advice.usage]) {
mapped.usage = usageCodeMapping[advice.usage]
}
mapped.unit = cleanUnitLabel(mapped.unit)
mapped.frequency = '立即'
return mapped
})
} else {
displayAdvices.value = []
}
@@ -616,8 +651,20 @@ watch(() => props.temporaryAdvices, (newVal, oldVal) => {
// 如果 props 有新数据,更新 displayAdvices
if (newVal && newVal.length > 0) {
// 🔧 修复:将旧编码映射到新编码
displayAdvices.value = newVal.map(mapUsageCode)
// 🔧 修复:将旧编码映射到新编码,同时强制清洗单位和频次
displayAdvices.value = newVal.map(advice => {
const mapped = { ...advice }
// 用法编码映射
if (usageCodeMapping[advice.usage]) {
mapped.usage = usageCodeMapping[advice.usage]
}
// 🔧 BugFix: 清洗单位(去除 ×、:、/ 等多余字符)
mapped.unit = cleanUnitLabel(mapped.unit)
// 🔧 BugFix: 频次强制修正为"立即"(临床应用要求)
mapped.frequency = '立即'
// 🔧 BugFix: 执行时间不应为空时回退为空串(由父组件从 contentJson 中取持久化值)
return mapped
})
}
}, { deep: true, immediate: true })
@@ -639,6 +686,14 @@ const handleSign = () => {
// 点击已生成列表行 → 回显该行的签名信息
const handleAdviceRowClick = (row) => {
// 🔧 优先从映射后的顶层字段读取index.vue 已将 contentJson 中的签名字段提取到顶层)
if (row.signDoctorName || row.signDate) {
if (row.signDoctorName) signatureDoctor.value = row.signDoctorName
if (row.signDate) signatureTime.value = row.signDate
isSigned.value = true
return
}
// 回退到 originalMedicine.contentJson
const om = row?.originalMedicine
if (!om) return
const contentJson = om.contentJson || om.content_json
@@ -669,9 +724,9 @@ const handleEditAdvice = (index) => {
editForm.value = {
adviceName: advice.adviceName,
dosage: advice.dosage,
unit: advice.unit,
unit: cleanUnitLabel(advice.unit),
usage: usageCode, // 使用映射后的正确编码
frequency: advice.frequency
frequency: '立即'
}
showEditDialog.value = true
}
@@ -735,7 +790,7 @@ const handleSaveEdit = async () => {
// 如果用户修改了剂量,重新计算数量
if (originalMedicine.specification) {
const specMatch = originalMedicine.specification.match(/([\d.]+)\s*([a-zA-Z一-龥]+)/)
const specMatch = originalMedicine.specification.match(/([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]+)/)
const specValue = specMatch ? parseFloat(specMatch[1]) : 1
if (specValue > 0) {
const newQuantity = editForm.value.dosage / specValue
@@ -770,8 +825,11 @@ const handleSaveEdit = async () => {
try { contentJsonData = JSON.parse(editMedicine.contentJson || '{}') } catch (e) {}
const quantity = editMedicine.quantity || contentJsonData.quantity || 1
const unitPrice = editMedicine.unitPrice || contentJsonData.unitPrice || 0
// 🔧 BugFix: 清理单位中的多余符号
const cleanUnit = cleanUnitLabel(editForm.value.unit)
const cleanDoseUnitCode = cleanUnitLabel(editForm.value.unit)
contentJsonData.dose = editForm.value.dosage
contentJsonData.doseUnitCode = editForm.value.unit
contentJsonData.doseUnitCode = cleanDoseUnitCode
contentJsonData.methodCode = updatedAdvice.usage
contentJsonData.quantity = quantity
contentJsonData.totalPrice = unitPrice * quantity
@@ -785,7 +843,7 @@ const handleSaveEdit = async () => {
chargeItemId: editMedicine.chargeItemId,
contentJson: JSON.stringify(contentJsonData),
quantity,
unitCode: editMedicine.unitCode || editForm.value.unit,
unitCode: editMedicine.unitCode || cleanUnit,
unitPrice,
totalPrice: unitPrice * quantity,
adviceName: updatedAdvice.adviceName,
@@ -794,7 +852,7 @@ const handleSaveEdit = async () => {
orgId: props.patientInfo.orgId,
methodCode: updatedAdvice.usage,
dose: editForm.value.dosage,
doseUnitCode: editForm.value.unit,
doseUnitCode: cleanDoseUnitCode,
generateSourceEnum: 6,
sourceBillNo: props.patientInfo?.operCode || ''
}
@@ -938,7 +996,9 @@ const handleSubmit = async () => {
// ✅ 关键修复把修改后的数据更新到contentJson中后端会优先读取这里的值
contentJsonData.adviceName = advice.adviceName;
contentJsonData.quantity = quantity;
contentJsonData.volume = advice.dosage + (advice.unit || '');
// 🔧 BugFix: 清理单位中的多余符号,确保 volume 字段只包含纯单位名称
const cleanUnit = cleanUnitLabel(advice.unit);
contentJsonData.volume = advice.dosage + cleanUnit;
contentJsonData.totalPrice = totalPrice;
contentJsonData.unitPrice = unitPrice;
// 用法、剂量、单位也更新到contentJson
@@ -957,10 +1017,12 @@ const handleSubmit = async () => {
}
contentJsonData.methodCode = methodCode;
contentJsonData.dose = advice.dosage;
contentJsonData.doseUnitCode = advice.unit;
// 🔧 BugFix: 清理单位中的多余符号,确保保存的单位是纯单位名称
contentJsonData.doseUnitCode = cleanUnitLabel(advice.unit);
contentJsonData.rateCode = advice.frequency;
contentJsonData.signDoctorName = signatureDoctor.value
contentJsonData.signDate = signatureTime.value
contentJsonData.executeTime = signatureTime.value
// 重新序列化contentJson
const updatedContentJson = JSON.stringify(contentJsonData);
@@ -981,7 +1043,7 @@ const handleSubmit = async () => {
// 数量和单位:使用重新计算后的数量
quantity: quantity,
unitCode: contentJsonData.unitCode || originalMedicine?.unitCode || advice.unit,
unitCode: contentJsonData.unitCode || originalMedicine?.unitCode || cleanUnitLabel(advice.unit),
unitPrice: unitPrice,
totalPrice: totalPrice,
definitionId: originalMedicine?.definitionId ? String(originalMedicine.definitionId) : contentJsonData.definitionId ? String(contentJsonData.definitionId) : advice.definitionId ? String(advice.definitionId) : null,
@@ -1014,7 +1076,7 @@ const handleSubmit = async () => {
methodCode: methodCode,
rateCode: advice.frequency,
dose: advice.dosage,
doseUnitCode: contentJsonData.doseUnitCode || originalMedicine?.doseUnitCode || advice.unit,
doseUnitCode: contentJsonData.doseUnitCode || originalMedicine?.doseUnitCode || cleanUnitLabel(advice.unit),
skinTestFlag: originalMedicine?.skinTestFlag || contentJsonData.skinTestFlag,
injectFlag: originalMedicine?.injectFlag || contentJsonData.injectFlag,

View File

@@ -1,4 +1,4 @@
<template>
<template>
<el-row
:gutter="20"
style="margin-bottom: 10px; display: flex; justify-content: flex-end"
@@ -110,7 +110,7 @@
<template #default="scope">
<el-checkbox
v-model="scope.row.maindiagFlag"
:true-value="true"
:true-value="1"
:false-value="0"
border
size="small"

View File

@@ -1,4 +1,4 @@
<template>
<template>
<el-row
:gutter="20"
style="margin-bottom: 10px; display: flex; justify-content: flex-end"
@@ -110,7 +110,7 @@
<template #default="scope">
<el-checkbox
v-model="scope.row.maindiagFlag"
:true-value="true"
:true-value="1"
:false-value="0"
border
size="small"