26 Commits

Author SHA1 Message Date
6d85686b06 测试合并v14 2026-01-15 17:06:32 +08:00
b33cb6f9a1 测试合并v13 2026-01-15 17:04:46 +08:00
072e71b025 测试合并v12 2026-01-15 16:58:14 +08:00
47394de43c Merge pull request 'document' (#2) from document into develop
Reviewed-on: #2
2026-01-15 07:37:39 +00:00
f0f1dde6b6 测试合并 2026-01-15 07:37:39 +00:00
1ab1165697 测试合并 2026-01-15 07:37:39 +00:00
a8f1b1fdfa feat(doctorstation): 添加医嘱类型对应的药品分类筛选功能
- 在处方列表组件中根据医嘱类型自动设置categoryCode筛选条件
- 为西药类型设置categoryCode为'2'
- 为中成药类型设置categoryCode为'1'
- 为耗材和诊疗类型清空categoryCode筛选条件
- 更新基础医嘱列表组件以接收并应用categoryCode查询参数
- 实现医嘱类型改变时的联动筛选逻辑
2026-01-15 15:34:34 +08:00
3b94d19199 Merge remote-tracking branch 'origin/develop' into develop 2026-01-15 15:13:20 +08:00
db1139a14f fix(prescription): 解决处方列表中价格显示的空值异常问题
- 在处方列表组件中添加对unitPrice和totalPrice的空值检查,防止NaN显示
- 优化价格计算逻辑,确保无效价格值被正确处理并显示为默认值
- 更新数据库查询中的条件判断,改进UNION查询的逻辑结构
- 添加对adviceTypes参数的有效性验证,确保查询条件的正确执行
2026-01-15 15:13:09 +08:00
chenjinyang
bea74aeac2 使用element-plus进行提示替换HTML原生弹窗 2026-01-15 15:01:05 +08:00
chenjinyang
634a1f45f9 根据LIS分组开发手册完成功能 2026-01-15 14:36:54 +08:00
8f1ad3307c refactor(doctorstation): 优化医生站医嘱查询SQL逻辑
- 将原有的条件判断逻辑重构为更清晰的choose/when/otherwise结构
- 修复了adviceTypes参数为空或未指定时的SQL执行问题
- 通过trim标签处理UNION ALL连接避免多余关键字
- 添加otherwise分支确保无adviceTypes时返回正确空结果集
- 保持了原有的所有功能逻辑和数据映射关系不变
- 提高了SQL查询的可读性和维护性
2026-01-15 13:42:36 +08:00
d8080fa22d 挂号补单功能的完善 2026-01-14 12:56:39 +08:00
chenjinyang
e8783d9f8f 修复叫号显示屏跳转异常问题 2026-01-14 10:44:35 +08:00
d8c4348341 挂号补单功能的完善 2026-01-14 10:12:25 +08:00
wangjian963
8e61490005 修复门诊医生站检验申请单的就诊卡号无法获取到对应的值的问题 2026-01-13 17:47:51 +08:00
f5f4e3c48e fix(charge): 修复收费模块中的数值计算和空指针异常问题
- 修复金额计算精度问题,使用Number转换和toFixed(2)确保数值准确性
- 添加安全访问操作符(?.)避免空指针异常导致页面崩溃
- 修复数组过滤和查找操作的空值处理逻辑
- 优化错误消息显示,提供更友好的用户提示
- 修复模板文件路径引用问题,确保打印功能正常工作
- 统一金额计算逻辑,避免因数据类型不一致导致的计算错误
2026-01-13 17:30:17 +08:00
0f013715b8 fix(charge): 修复合同列表空值访问导致的门诊登记页面异常 2026-01-13 17:06:52 +08:00
fb9722d328 fix(charge): 修复合同列表空值访问导致的门诊登记页面异常 2026-01-13 17:06:43 +08:00
6f9192d30d fix(charge): 修复医保支付金额计算的安全访问问题
- 在 cliniccharge 组件中为所有金额查询添加可选链操作符防止空指针异常
- 在住院管理收费结算组件中修复金额格式化导致的显示问题
- 统一处理患者信息字段的空值情况避免页面渲染错误
- 修正金额计算逻辑确保数值精度和显示准确性
2026-01-13 17:02:27 +08:00
f2b5b90f34 Merge remote-tracking branch 'origin/develop' into develop 2026-01-13 17:01:29 +08:00
py
a2cbd5e583 测试:科室预约工作时间维护 2026-01-13 17:00:31 +08:00
d3df46858b fix(charge): 修复医保支付计算中的潜在空指针异常
- 在 chargeDialog.vue 中为所有 param.detail.find() 调用添加可选链操作符
- 修复了基金支付总额、个人负担总金额和其他支付类型的空指针风险
- 解决了基本医保统筹基金支出等各项支付类型的潜在运行时错误
- 在微信刷卡支付逻辑中同样应用可选链操作符保护
- 修复了 FULAMT_OWNPAY_AMT 计算中的运算符优先级问题

feat(hospitalRecord): 动态替换打印模板中的医院名称

- 在 MedicationDetails.vue 中引入并使用 userStore 获取医院名称
- 修改处置模板打印逻辑以动态替换 {{HOSPITAL_NAME}} 占位符
- 更新处方模板打印功能以支持医院名称的动态替换
- 激活之前被注释掉的模板文件导入语句
- 移除硬编码的医院名称,实现模板的动态化配置
2026-01-13 16:58:43 +08:00
47a7a945bc Merge remote-tracking branch 'origin/develop' into develop 2026-01-13 15:27:01 +08:00
0a56c0dcf0 feat(store): 更新用户模块状态管理以支持租户配置
- 调整导入语句顺序以符合代码风格规范
- 添加tenantName字段用于存储租户名称
- 添加optionMap字段用于存储租户配置项映射
- 修改getInfo方法以从optionMap优先获取医院名称配置
- 添加tenantName赋值逻辑以支持租户名称显示
- 移除已废弃的用户个人资料相关组件文件
2026-01-13 15:26:46 +08:00
15d32134e2 挂号补单功能的完善 2026-01-13 14:48:18 +08:00
64 changed files with 3139 additions and 2281 deletions

104
check_display_order.sql Normal file
View File

@@ -0,0 +1,104 @@
-- 检查流水号display_order是否按“科室+医生+当天”正确递增
--
-- 说明:
-- 1. display_order 存的是纯数字1, 2, 3...),不带时间戳前缀
-- 2. 时间戳前缀(如 20260109是在前端显示时加上的
-- 3. 后端用 Redis key "ORG-{科室ID}-DOC-{医生ID}" 按天自增
--
-- 如何判断逻辑是否正确:
-- 同一科室、同一医生、同一天的记录display_order 应该递增1, 2, 3...
-- 不同科室、不同医生、不同天的记录,可能都是 1这是正常的
-- ========================================
-- 查询1按“科室+医生+日期”分组,看每组内的 display_order 是否递增
-- ========================================
SELECT
DATE(start_time) AS ,
organization_id AS ID,
registrar_id AS ID,
COUNT(*) AS ,
MIN(display_order) AS ,
MAX(display_order) AS ,
STRING_AGG(display_order::text, ', ' ORDER BY start_time) AS ,
STRING_AGG(id::text, ', ' ORDER BY start_time) AS ID列表
FROM adm_encounter
WHERE delete_flag = '0'
AND start_time >= CURRENT_DATE - INTERVAL '7 days' -- 只看最近7天
AND display_order IS NOT NULL
GROUP BY DATE(start_time), organization_id, registrar_id
ORDER BY DESC, ID, ID;
-- ========================================
-- 查询2详细查看每条记录看同组内的序号是否连续
-- ========================================
SELECT
id AS ID,
DATE(start_time) AS ,
organization_id AS ID,
registrar_id AS ID,
start_time AS ,
display_order AS ,
-- 计算:同组内的序号应该是 1, 2, 3...,看是否有重复或跳号
ROW_NUMBER() OVER (
PARTITION BY DATE(start_time), organization_id, registrar_id
ORDER BY start_time
) AS ,
CASE
WHEN display_order = ROW_NUMBER() OVER (
PARTITION BY DATE(start_time), organization_id, registrar_id
ORDER BY start_time
) THEN '✓ 正常'
ELSE '✗ 异常'
END AS
FROM adm_encounter
WHERE delete_flag = '0'
AND start_time >= CURRENT_DATE - INTERVAL '7 days'
AND display_order IS NOT NULL
ORDER BY DATE(start_time) DESC, organization_id, registrar_id, start_time;
-- ========================================
-- 查询3只看今天的数据最直观
-- ========================================
SELECT
id AS ID,
organization_id AS ID,
registrar_id AS ID,
start_time AS ,
display_order AS
FROM adm_encounter
WHERE delete_flag = '0'
AND DATE(start_time) = CURRENT_DATE
AND display_order IS NOT NULL
ORDER BY organization_id, registrar_id, start_time;
-- ========================================
-- 查询4发现问题 - 找出同组内 display_order 重复的记录
-- ========================================
WITH ranked AS (
SELECT
id,
DATE(start_time) AS reg_date,
organization_id,
registrar_id,
start_time,
display_order,
ROW_NUMBER() OVER (
PARTITION BY DATE(start_time), organization_id, registrar_id
ORDER BY start_time
) AS should_be_order
FROM adm_encounter
WHERE delete_flag = '0'
AND start_time >= CURRENT_DATE - INTERVAL '7 days'
AND display_order IS NOT NULL
)
SELECT
reg_date AS ,
organization_id AS ID,
registrar_id AS ID,
COUNT(*) AS ,
STRING_AGG(id::text || '->' || display_order::text, ', ') AS
FROM ranked
WHERE display_order != should_be_order
GROUP BY reg_date, organization_id, registrar_id
ORDER BY reg_date DESC;

10
md/test.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试合并11112</title>
</head>
<body>
</body>
</html>

View File

@@ -64,6 +64,11 @@
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!-- rabbitMQ --> <!-- rabbitMQ -->
<!-- <dependency> <!-- <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@@ -141,4 +141,9 @@ public class CurrentDayEncounterDto {
*/ */
private String identifierNo; private String identifierNo;
/**
* 流水号(就诊当日序号)
*/
private Integer displayOrder;
} }

View File

@@ -4,7 +4,7 @@ import com.core.common.core.domain.R;
import com.openhis.check.domain.LisGroupInfo; import com.openhis.check.domain.LisGroupInfo;
public interface ILisGroupInfoAppService { public interface ILisGroupInfoAppService {
R<?> getLisGroupInfoList(); R<?> getLisGroupInfoList(Integer pageNum, Integer pageSize);
R<?> add(LisGroupInfo lisGroupInfo); R<?> add(LisGroupInfo lisGroupInfo);

View File

@@ -1,6 +1,7 @@
package com.openhis.web.check.appservice.impl; package com.openhis.web.check.appservice.impl;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R; import com.core.common.core.domain.R;
import com.openhis.check.domain.LisGroupInfo; import com.openhis.check.domain.LisGroupInfo;
import com.openhis.check.service.ILisGroupInfoService; import com.openhis.check.service.ILisGroupInfoService;
@@ -17,11 +18,14 @@ public class LisGroupInfoAppServiceImpl implements ILisGroupInfoAppService {
@Resource @Resource
private ILisGroupInfoService lisGroupInfoService; private ILisGroupInfoService lisGroupInfoService;
@Override @Override
public R<?> getLisGroupInfoList() { public R<?> getLisGroupInfoList(Integer pageNum, Integer pageSize) {
List<LisGroupInfo> list = lisGroupInfoService.list(); Page<LisGroupInfo> page = new Page<>(pageNum, pageSize);
Page<LisGroupInfo> list = lisGroupInfoService.page(page);
return R.ok(list); return R.ok(list);
} }
@Override @Override
public R<?> add(LisGroupInfo lisGroupInfo) { public R<?> add(LisGroupInfo lisGroupInfo) {
if (ObjectUtil.isEmpty(lisGroupInfo)) { if (ObjectUtil.isEmpty(lisGroupInfo)) {

View File

@@ -19,8 +19,8 @@ public class LisGroupInfoController {
* *
* */ * */
@GetMapping("/list") @GetMapping("/list")
public R<?> getLisGroupInfoList(){ public R<?> getLisGroupInfoList(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize){
return R.ok(lisGroupInfoAppService.getLisGroupInfoList()); return R.ok(lisGroupInfoAppService.getLisGroupInfoList(pageNum, pageSize));
} }
/* /*

View File

@@ -401,6 +401,20 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
// 删除 // 删除
List<AdviceSaveDto> deleteList = medicineList.stream() List<AdviceSaveDto> deleteList = medicineList.stream()
.filter(e -> DbOpType.DELETE.getCode().equals(e.getDbOpType())).collect(Collectors.toList()); .filter(e -> DbOpType.DELETE.getCode().equals(e.getDbOpType())).collect(Collectors.toList());
// 校验删除的医嘱是否已经收费
List<Long> delRequestIdList = deleteList.stream().map(AdviceSaveDto::getRequestId).collect(Collectors.toList());
if (!delRequestIdList.isEmpty()) {
List<ChargeItem> chargeItemList = iChargeItemService.getChargeItemInfoByReqId(delRequestIdList);
if (chargeItemList != null && !chargeItemList.isEmpty()) {
for (ChargeItem ci : chargeItemList) {
if (ChargeItemStatus.BILLED.getValue().equals(ci.getStatusEnum())) {
return R.fail("已收费的项目无法删除,请刷新页面后重试");
}
}
}
}
for (AdviceSaveDto adviceSaveDto : deleteList) { for (AdviceSaveDto adviceSaveDto : deleteList) {
iMedicationRequestService.removeById(adviceSaveDto.getRequestId()); iMedicationRequestService.removeById(adviceSaveDto.getRequestId());
// 删除已经产生的药品发放信息 // 删除已经产生的药品发放信息

View File

@@ -22,6 +22,7 @@ import com.openhis.web.doctorstation.dto.PrescriptionInfoBaseDto;
import com.openhis.web.doctorstation.dto.PrescriptionInfoDetailDto; import com.openhis.web.doctorstation.dto.PrescriptionInfoDetailDto;
import com.openhis.web.doctorstation.dto.ReceptionStatisticsDto; import com.openhis.web.doctorstation.dto.ReceptionStatisticsDto;
import com.openhis.web.doctorstation.mapper.DoctorStationMainAppMapper; import com.openhis.web.doctorstation.mapper.DoctorStationMainAppMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -32,6 +33,7 @@ import java.util.stream.Collectors;
/** /**
* 医生站-主页面 应用实现类 * 医生站-主页面 应用实现类
*/ */
//@Slf4j
@Service @Service
public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppService { public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppService {
@@ -95,6 +97,9 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
ParticipantType.REGISTRATION_DOCTOR.getCode(), ParticipantType.ADMITTER.getCode(), userId, ParticipantType.REGISTRATION_DOCTOR.getCode(), ParticipantType.ADMITTER.getCode(), userId,
currentUserOrganizationId, pricingFlag, EncounterStatus.PLANNED.getValue(), currentUserOrganizationId, pricingFlag, EncounterStatus.PLANNED.getValue(),
EncounterActivityStatus.ACTIVE.getValue(), queryWrapper); EncounterActivityStatus.ACTIVE.getValue(), queryWrapper);
//日志输出就诊患者信息,patientInfo
// log.debug("就诊患者信息: 总数={}, 记录数={}, 数据={}",
// patientInfo.getTotal(), patientInfo.getRecords().size(), patientInfo.getRecords());
patientInfo.getRecords().forEach(e -> { patientInfo.getRecords().forEach(e -> {
// 性别 // 性别
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum())); e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));

View File

@@ -123,4 +123,8 @@ public class PatientInfoDto {
* 病历号 * 病历号
*/ */
private String busNo; private String busNo;
/**
* 就诊卡号
*/
private String identifierNo;
} }

View File

@@ -59,6 +59,13 @@ public interface IEleInvoiceService {
*/ */
R<?> invoiceWriteoff(Long paymentId, String reason); R<?> invoiceWriteoff(Long paymentId, String reason);
/**
* 获取发票HTML
* @param busNo 业务流水号
* @return HTML字符串
*/
String getInvoiceHtml(String busNo);
/** /**
* 查询已开发票 * 查询已开发票
* @param invoiceId 主键id * @param invoiceId 主键id

View File

@@ -30,6 +30,7 @@ import com.openhis.financial.domain.PaymentRecDetail;
import com.openhis.financial.domain.PaymentReconciliation; import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.service.IPaymentRecDetailService; import com.openhis.financial.service.IPaymentRecDetailService;
import com.openhis.financial.service.IPaymentReconciliationService; import com.openhis.financial.service.IPaymentReconciliationService;
import com.openhis.web.paymentmanage.appservice.IChargeBillService;
import com.openhis.web.paymentmanage.appservice.IEleInvoiceService; import com.openhis.web.paymentmanage.appservice.IEleInvoiceService;
import com.openhis.web.paymentmanage.dto.*; import com.openhis.web.paymentmanage.dto.*;
import com.openhis.web.paymentmanage.mapper.EleInvoiceMapper; import com.openhis.web.paymentmanage.mapper.EleInvoiceMapper;
@@ -38,6 +39,11 @@ import com.openhis.yb.service.IClinicSettleService;
import com.openhis.yb.service.IRegService; import com.openhis.yb.service.IRegService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
@@ -56,6 +62,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@@ -64,6 +71,7 @@ import java.text.DecimalFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -97,153 +105,180 @@ public class EleInvoiceServiceImpl implements IEleInvoiceService {
@Resource @Resource
IEncounterService encounterService; IEncounterService encounterService;
@Resource @Resource
IChargeBillService chargeBillService;
@Resource
private AssignSeqUtil assignSeqUtil; private AssignSeqUtil assignSeqUtil;
@Autowired @Autowired
private HttpConfig httpConfig; private HttpConfig httpConfig;
public static JSONObject PreInvoicePostForward(JSONObject bill, String endpoint) { static {
String resultString = ""; Properties p = new Properties();
// JSONObject result = new JSONObject(); p.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
// 获取当前租户的option信息 p.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
Velocity.init(p);
}
private static final Map<String, JSONObject> invoiceDataMap = new ConcurrentHashMap<>();
private JSONObject internalRegistration(JSONObject bill) {
return createInternalSuccessResponse(bill, "REG");
}
private JSONObject internalOutpatient(JSONObject bill) {
return createInternalSuccessResponse(bill, "OUT");
}
private JSONObject internalHospitalized(JSONObject bill) {
return createInternalSuccessResponse(bill, "HOS");
}
private JSONObject internalWriteOff(JSONObject bill) {
JSONObject message = new JSONObject();
message.put("eScarletBillBatchCode", "SC" + System.currentTimeMillis());
message.put("eScarletBillNo", UUID.randomUUID().toString().substring(0, 8));
message.put("eScarletRandom", "666888");
message.put("createTime", "20251101143028");
message.put("billQRCode", "QR_DATA_SCARLET");
JSONObject optionJson = SecurityUtils.getLoginUser().getOptionJson(); JSONObject optionJson = SecurityUtils.getLoginUser().getOptionJson();
String baseUrl = optionJson.getString(CommonConstants.Option.URL); String baseUrl = optionJson.getString(CommonConstants.Option.URL);
String appID = optionJson.getString(CommonConstants.Option.APP_ID); message.put("pictureUrl", baseUrl + "/invoice/view?busNo=scarlet");
String appKey = optionJson.getString(CommonConstants.Option.KEY); message.put("pictureNetUrl", baseUrl + "/invoice/view?busNo=scarlet");
EleInvioceBillDto eleInvioceBillDto = new EleInvioceBillDto(); JSONObject result = new JSONObject();
eleInvioceBillDto.setBaseUrl(baseUrl); result.put("result", "S0000");
eleInvioceBillDto.setEndpoint(endpoint); result.put("message", Base64.getEncoder().encodeToString(message.toJSONString().getBytes(StandardCharsets.UTF_8)));
eleInvioceBillDto.setAppKey(appKey); return result;
eleInvioceBillDto.setAppID(appID);
eleInvioceBillDto.setJsonObject(bill);
// 创建Http请求
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000)
.setSocketTimeout(30000).build();
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response = null;
// 发送请求
try {
HttpPost httpPost = new HttpPost(optionJson.getString("invoiceUrl") + "/eleInvoice/forward");
System.out.println(optionJson.getString("invoiceUrl") + "/eleInvoice/forward");
StringEntity stringEntity = new StringEntity(com.alibaba.fastjson2.JSON.toJSONString(eleInvioceBillDto),
ContentType.APPLICATION_JSON);
httpPost.setEntity(stringEntity);
// 执行http请求
response = httpClient.execute(httpPost);
if (response == null) {
throw new ServiceException("Http请求异常未接受返回参数");
} }
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
private JSONObject createInternalSuccessResponse(JSONObject bill, String prefix) {
String busNo = bill.getString("busNo");
JSONObject optionJson = SecurityUtils.getLoginUser().getOptionJson();
String baseUrl = optionJson.getString(CommonConstants.Option.URL);
JSONObject message = new JSONObject();
message.put("billBatchCode", prefix + "BC" + System.currentTimeMillis());
message.put("billNo", UUID.randomUUID().toString().substring(0, 8));
message.put("random", "123456");
message.put("createTime", new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));
message.put("billQRCode", "QR_" + busNo);
message.put("pictureUrl", baseUrl + "/invoice/view?busNo=" + busNo);
message.put("pictureNetUrl", baseUrl + "/invoice/view?busNo=" + busNo);
JSONObject result = new JSONObject();
result.put("result", "S0000");
result.put("message", Base64.getEncoder().encodeToString(message.toJSONString().getBytes(StandardCharsets.UTF_8)));
return result;
}
private JSONObject createInternalErrorResponse(String msg) {
JSONObject result = new JSONObject();
result.put("result", "E0001");
result.put("message", Base64.getEncoder().encodeToString(msg.getBytes(StandardCharsets.UTF_8)));
return result;
}
@Override
public String getInvoiceHtml(String busNo) {
JSONObject bill = invoiceDataMap.get(busNo);
if (bill == null) {
return "<html><body><h2>未找到流水号为 " + busNo + " 的发票数据</h2></body></html>";
}
JSONObject receiptData = bill.getJSONObject("receiptData");
VelocityContext context = new VelocityContext();
context.put("hospitalName", receiptData != null ? receiptData.getString("fixmedinsName") : "HIS 医疗机构");
context.put("patientName", bill.getString("payer"));
context.put("outpatientNo", receiptData != null ? receiptData.getString("regNo") : bill.getString("busNo"));
context.put("idCard", bill.getString("cardNo"));
context.put("tel", bill.getString("tel"));
context.put("deptName", bill.getString("patientCategory"));
context.put("doctorName", receiptData != null ? receiptData.getString("doctor") : "-");
context.put("appointmentTime", bill.getString("consultationDate"));
context.put("totalAmt", bill.getString("totalAmt"));
context.put("busNo", busNo);
context.put("printTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
List<Map<String, Object>> items = new ArrayList<>();
if (receiptData != null && receiptData.containsKey("chargeItem")) {
com.alibaba.fastjson2.JSONArray chargeItems = receiptData.getJSONArray("chargeItem");
for (int i = 0; i < chargeItems.size(); i++) {
JSONObject item = chargeItems.getJSONObject(i);
Map<String, Object> itemMap = new HashMap<>();
itemMap.put("chargeItemName", item.getString("chargeItemName"));
itemMap.put("quantityValue", item.getString("quantityValue"));
itemMap.put("totalPrice", item.getString("totalPrice"));
items.add(itemMap);
}
} else {
Map<String, Object> itemMap = new HashMap<>();
itemMap.put("chargeItemName", "挂号费");
itemMap.put("quantityValue", "1");
itemMap.put("totalPrice", bill.getString("totalAmt"));
items.add(itemMap);
}
context.put("items", items);
try {
Template template = Velocity.getTemplate("vm/invoice/invoice.vm");
StringWriter writer = new StringWriter();
template.merge(context, writer);
return writer.toString();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("渲染发票模板失败", e);
throw new ServiceException("Http请求异常请稍后再试。"); return "<html><body><h2>渲染发票凭条失败:" + e.getMessage() + "</h2></body></html>";
} finally { }
if (response != null) { }
public JSONObject PreInvoicePostForward(JSONObject bill, String endpoint) {
// 参考补打小票逻辑,动态获取小票详细信息
Long paymentId = bill.getLong("paymentId");
if (paymentId != null) {
try { try {
response.close(); Map<String, Object> receiptDetail = chargeBillService.getDetail(paymentId);
} catch (IOException e) { bill.put("receiptData", receiptDetail);
// logger.error("关闭响应异常", e); log.info("已成功获取并注入小票动态数据paymentId: {}", paymentId);
throw new ServiceException("未关闭系统资源:" + e.getStackTrace()); } catch (Exception e) {
log.error("获取小票数据失败paymentId: {}", paymentId, e);
} }
} }
// 内部调用逻辑:不再使用 Http 客户端,直接分发到本地逻辑
String busNo = bill.getString("busNo");
if (busNo != null) {
invoiceDataMap.put(busNo, bill);
} }
return JSONObject.parseObject(resultString);
JSONObject internalResult;
if (endpoint.contains("invEBillRegistration")) {
internalResult = internalRegistration(bill);
} else if (endpoint.contains("invoiceEBillOutpatient")) {
internalResult = internalOutpatient(bill);
} else if (endpoint.contains("invEBillHospitalized")) {
internalResult = internalHospitalized(bill);
} else if (endpoint.contains("writeOffEBill")) {
internalResult = internalWriteOff(bill);
} else {
internalResult = createInternalErrorResponse("未知接口: " + endpoint);
}
JSONObject finalResponse = new JSONObject();
finalResponse.put("success", true);
finalResponse.put("result", internalResult);
return finalResponse;
} }
/** /**
* 发送请求 * 发送请求 (内部调用版本)
* *
* @param bill 请求参数 * @param bill 请求参数
* @param endpoint 请求后缀url * @param endpoint 请求后缀url
* @return 返回值 * @return 返回值
*/ */
public static JSONObject PreInvoicePost(JSONObject bill, String endpoint) { public JSONObject PreInvoicePost(JSONObject bill, String endpoint) {
return PreInvoicePostForward(bill, endpoint);
JSONObject result = new JSONObject();
// 获取当前租户的option信息
JSONObject optionJson = SecurityUtils.getLoginUser().getOptionJson();
String baseUrl = optionJson.getString(CommonConstants.Option.URL);
// 拼接成完整 URL作为路径
String cleanUrl = baseUrl + "/" + endpoint; // 确保用 "/" 分隔
String url = cleanUrl.trim().replaceAll("^\"|\"$", "") // 去除首尾引号
.replaceAll("\\s+", "")// 去除首尾引号
.replaceAll("\"", ""); // 去除中间引号
String appID = optionJson.getString(CommonConstants.Option.APP_ID);
String appKey = optionJson.getString(CommonConstants.Option.KEY);
String data = bill.toJSONString();
String version = "1.0";
// 请求随机标识 noise
String noise = UUID.randomUUID().toString();
data = Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
StringBuilder str = new StringBuilder();
str.append("appid=").append(appID);
str.append("&data=").append(data);
str.append("&noise=").append(noise);
str.append("&key=").append(appKey);
str.append("&version=").append(version);
String sign = DigestUtils.md5Hex(str.toString().getBytes(Charset.forName("UTF-8"))).toUpperCase();
Map<String, String> map = new HashMap<>();
map.put("appid", appID);
map.put("data", data);
map.put("noise", noise);
map.put("sign", sign);
map.put("version", version);
try {
HttpPost httpPost = new HttpPost(url);
CloseableHttpClient client = HttpClients.createDefault();
String respContent = null;
// 请求参数转JOSN字符串
StringEntity entity = new StringEntity(new ObjectMapper().writeValueAsString(map), "utf-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
HttpResponse resp = client.execute(httpPost);
if (resp.getStatusLine().getStatusCode() == 200) {
String rev = EntityUtils.toString(resp.getEntity());
// System.out.println("返回串--》"+rev);
Map resultData = new ObjectMapper().readValue(rev, Map.class);
String rdata = resultData.get("data").toString();
String rnoise = resultData.get("noise").toString();
// 1、拼接返回验签参数
StringBuilder str1 = new StringBuilder();
str1.append("appid=").append(appID);
str1.append("&data=").append(rdata);
str1.append("&noise=").append(rnoise);
str1.append("&key=").append(appKey);
str1.append("&version=").append(version);
// 3.MD5加密 生成sign
String rmd5 = DigestUtils.md5Hex(str1.toString().getBytes(Charset.forName("UTF-8"))).toUpperCase();
String rsign = resultData.get("sign").toString();
System.out.println("验签-》" + (StringUtils.equals(rsign, rmd5)));
String busData
= new String(Base64.getDecoder().decode(resultData.get("data").toString()), StandardCharsets.UTF_8);
System.out.println("返回业务数据--》" + busData);
Map busDataMap = new ObjectMapper().readValue(busData, Map.class);
System.out
.println("业务信息解密--》" + new String(Base64.getDecoder().decode(busDataMap.get("message").toString()),
StandardCharsets.UTF_8));
JSONObject resobj = JSONObject.parseObject(busData);
result.put("success", true);
result.put("result", resobj);
} else {
result.put("msg", "web响应失败!");
result.put("success", false);
}
} catch (Exception e) {
result.put("msg", e.getMessage());
result.put("success", false);
}
return result;
} }
/** /**
@@ -339,6 +374,7 @@ public class EleInvoiceServiceImpl implements IEleInvoiceService {
// --------------------请求业务参数 data--------------------START // --------------------请求业务参数 data--------------------START
JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle); JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle);
bill.put("paymentId", paymentId);
// ------票据信息------ // ------票据信息------
// busType 业务标识 String 20 是 06标识挂号 // busType 业务标识 String 20 是 06标识挂号
@@ -580,6 +616,7 @@ public class EleInvoiceServiceImpl implements IEleInvoiceService {
// --------------------请求业务参数 data--------------------START // --------------------请求业务参数 data--------------------START
JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle); JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle);
bill.put("paymentId", paymentId);
// ------票据信息------ // ------票据信息------
// busType 业务标识 String 20 是 直接填写业务系统内部编码值由医疗平台配置对照例如附录5 业务标识列表 // busType 业务标识 String 20 是 直接填写业务系统内部编码值由医疗平台配置对照例如附录5 业务标识列表
@@ -890,6 +927,7 @@ public class EleInvoiceServiceImpl implements IEleInvoiceService {
// --------------------请求业务参数 data--------------------START // --------------------请求业务参数 data--------------------START
JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle); JSONObject bill = commomSet(patientInfo, paymentInfo, clinicSettle);
bill.put("paymentId", paymentId);
// ------票据信息------ // ------票据信息------
// busType 业务标识 String 20 是 直接填写业务系统内部编码值由医疗平台配置对照例如附录5 业务标识列表 // busType 业务标识 String 20 是 直接填写业务系统内部编码值由医疗平台配置对照例如附录5 业务标识列表

View File

@@ -1818,6 +1818,10 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
// 保存就诊信息 // 保存就诊信息
Encounter encounter = new Encounter(); Encounter encounter = new Encounter();
BeanUtils.copyProperties(encounterFormData, encounter); BeanUtils.copyProperties(encounterFormData, encounter);
// 将挂号医生ID提前塞入 encounter 的 registrarId便于生成“科室+医生+当日”序号
if (encounterParticipantFormData.getPractitionerId() != null) {
encounter.setRegistrarId(encounterParticipantFormData.getPractitionerId());
}
encounter.setBusNo(outpatientRegistrationSettleParam.getBusNo()); encounter.setBusNo(outpatientRegistrationSettleParam.getBusNo());
// 就诊ID // 就诊ID
Long encounterId = iEncounterService.saveEncounterByRegister(encounter); Long encounterId = iEncounterService.saveEncounterByRegister(encounter);

View File

@@ -41,22 +41,23 @@ public class EleInvoiceController {
public R<?> invoiceReissue(@RequestBody MakeInvoiceDto makeInvoiceDto) { public R<?> invoiceReissue(@RequestBody MakeInvoiceDto makeInvoiceDto) {
// 付款成功后,开具发票 // 付款成功后,开具发票
R<?> result = eleInvoiceService.invoiceReissue(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId()); R<?> result = eleInvoiceService.invoiceReissue(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId());
R<?> eleResult = null;
if (result.getCode() == 200) { if (result.getCode() == 200) {
if (result.getData() == YbEncounterClass.REG.getValue()) { if (result.getData() == YbEncounterClass.REG.getValue()) {
// 付款成功后,开具发票 // 付款成功后,开具发票
R<?> eleResult = eleInvoiceService.invoiceRegMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId()); eleResult = eleInvoiceService.invoiceRegMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId());
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
return R.ok(" 挂号电子发票开具失败 :" + eleResult.getMsg()); return R.ok(" 挂号电子发票开具失败 :" + eleResult.getMsg());
} }
} else if (result.getData() == YbEncounterClass.AMB.getValue()) { } else if (result.getData() == YbEncounterClass.AMB.getValue()) {
// 付款成功后,开具发票 // 付款成功后,开具发票
R<?> eleResult = eleInvoiceService.invoiceMZMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId()); eleResult = eleInvoiceService.invoiceMZMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId());
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
return R.ok(" 门诊电子发票开具失败 :" + eleResult.getMsg()); return R.ok(" 门诊电子发票开具失败 :" + eleResult.getMsg());
} }
} else if (result.getData() == YbEncounterClass.IMP.getValue()) { } else if (result.getData() == YbEncounterClass.IMP.getValue()) {
// 付款成功后,开具发票 // 付款成功后,开具发票
R<?> eleResult = eleInvoiceService.invoiceZYMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId()); eleResult = eleInvoiceService.invoiceZYMake(makeInvoiceDto.getPaymentId(), makeInvoiceDto.getEncounterId());
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
return R.ok(" 住院电子发票开具失败 :" + eleResult.getMsg()); return R.ok(" 住院电子发票开具失败 :" + eleResult.getMsg());
} }
@@ -64,7 +65,11 @@ public class EleInvoiceController {
return R.ok("电子发票类型不明确!"); return R.ok("电子发票类型不明确!");
} }
} }
Map detail = iChargeBillService.getDetail(makeInvoiceDto.getPaymentId()); Map<String, Object> detail = iChargeBillService.getDetail(makeInvoiceDto.getPaymentId());
if (eleResult != null && eleResult.getCode() == 200 && eleResult.getData() instanceof Invoice) {
Invoice invoice = (Invoice) eleResult.getData();
detail.put("pictureUrl", invoice.getPictureUrl());
}
return R.ok(detail); return R.ok(detail);
} }
@@ -95,10 +100,18 @@ public class EleInvoiceController {
public R<?> invoiceOpen(@RequestParam("invoiceId") String invoiceId) { public R<?> invoiceOpen(@RequestParam("invoiceId") String invoiceId) {
// 退款成功后,开具发票 // 退款成功后,开具发票
Invoice invoice = eleInvoiceService.getInvoiceById(Long.parseLong(invoiceId)); Invoice invoice = eleInvoiceService.getInvoiceById(Long.parseLong(invoiceId));
if(invoice ==null){ if (invoice == null) {
throw new ServiceException("未查询到发票信息"); throw new ServiceException("未查询到发票信息");
} }
return R.ok(invoice.getPictureUrl()); return R.ok(invoice.getPictureUrl());
} }
/**
* 获取发票HTML凭条预览
*/
@GetMapping(value = "/view", produces = "text/html;charset=UTF-8")
@ResponseBody
public String viewInvoice(@RequestParam("busNo") String busNo) {
return eleInvoiceService.getInvoiceHtml(busNo);
}
} }

View File

@@ -6,6 +6,7 @@ package com.openhis.web.paymentmanage.controller;
import com.core.common.core.domain.R; import com.core.common.core.domain.R;
import com.core.common.enums.TenantOptionDict; import com.core.common.enums.TenantOptionDict;
import com.core.web.util.TenantOptionUtil; import com.core.web.util.TenantOptionUtil;
import com.openhis.administration.domain.Invoice;
import com.openhis.financial.domain.PaymentReconciliation; import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.web.chargemanage.dto.OutpatientRegistrationAddParam; import com.openhis.web.chargemanage.dto.OutpatientRegistrationAddParam;
import com.openhis.web.chargemanage.dto.OutpatientRegistrationSettleParam; import com.openhis.web.chargemanage.dto.OutpatientRegistrationSettleParam;
@@ -92,10 +93,12 @@ public class PaymentReconciliationController {
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
// 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok // 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok
return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg()); return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg());
} else if (eleResult.getData() instanceof Invoice) {
Invoice invoice = (Invoice) eleResult.getData();
detail.put("pictureUrl", invoice.getPictureUrl());
} }
return R.ok(detail);
} }
// Map detail = iChargeBillService.getDetail(paymentRecon.getId());
} }
return result; return result;
} }
@@ -194,7 +197,11 @@ public class PaymentReconciliationController {
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
// 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok // 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok
return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg()); return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg());
} else if (eleResult.getData() instanceof Invoice) {
Invoice invoice = (Invoice) eleResult.getData();
detail.put("pictureUrl", invoice.getPictureUrl());
} }
return R.ok(detail);
} }
} }
return result; return result;
@@ -233,6 +240,9 @@ public class PaymentReconciliationController {
if (eleResult.getCode() != 200) { if (eleResult.getCode() != 200) {
// 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok // 因收费成功前端需要关闭弹窗此处信息仅用于提示所以返回ok
return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg()); return R.ok(detail, " 收费成功,电子发票开具失败 :" + eleResult.getMsg());
} else if (eleResult.getData() instanceof Invoice) {
Invoice invoice = (Invoice) eleResult.getData();
detail.put("pictureUrl", invoice.getPictureUrl());
} }
return R.ok(detail); return R.ok(detail);
} }
@@ -260,8 +270,9 @@ public class PaymentReconciliationController {
// 因取消付款成功前端需要关闭弹窗此处信息仅用于提示所以返回ok // 因取消付款成功前端需要关闭弹窗此处信息仅用于提示所以返回ok
return R.ok(null, " 取消付款成功,电子发票开具失败 :" + eleResult.getMsg()); return R.ok(null, " 取消付款成功,电子发票开具失败 :" + eleResult.getMsg());
} }
return R.ok("取消结算成功");
} }
return R.ok("取消结算失败,请确认"); return R.fail("取消结算失败,请确认");
} }
/** /**

View File

@@ -46,6 +46,7 @@
<select id="getCurrentDayEncounter" resultType="com.openhis.web.chargemanage.dto.CurrentDayEncounterDto"> <select id="getCurrentDayEncounter" resultType="com.openhis.web.chargemanage.dto.CurrentDayEncounterDto">
SELECT T9.tenant_id AS tenantId, SELECT T9.tenant_id AS tenantId,
T9.encounter_id AS encounterId, T9.encounter_id AS encounterId,
T9.display_order AS displayOrder,
T9.organization_id AS organizationId, T9.organization_id AS organizationId,
T9.organization_name AS organizationName, T9.organization_name AS organizationName,
T9.healthcare_name AS healthcareName, T9.healthcare_name AS healthcareName,
@@ -66,10 +67,11 @@
T9.payment_id AS paymentId, T9.payment_id AS paymentId,
T9.picture_url AS pictureUrl, T9.picture_url AS pictureUrl,
T9.birth_date AS birthDate, T9.birth_date AS birthDate,
T9.identifier_no AS identifierNo COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo
from ( from (
SELECT T1.tenant_id AS tenant_id, SELECT T1.tenant_id AS tenant_id,
T1.id AS encounter_id, T1.id AS encounter_id,
T1.display_order AS display_order,
T1.organization_id AS organization_id, T1.organization_id AS organization_id,
T2.NAME AS organization_name, T2.NAME AS organization_name,
T3.NAME AS healthcare_name, T3.NAME AS healthcare_name,
@@ -90,6 +92,7 @@
T13.id AS payment_id, T13.id AS payment_id,
ai.picture_url AS picture_url, ai.picture_url AS picture_url,
T8.birth_date AS birth_date, T8.birth_date AS birth_date,
T8.bus_no AS patient_bus_no,
T18.identifier_no AS identifier_no T18.identifier_no AS identifier_no
FROM adm_encounter AS T1 FROM adm_encounter AS T1
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0' LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
@@ -129,6 +132,8 @@
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
FROM adm_patient_identifier FROM adm_patient_identifier
WHERE delete_flag = '0' WHERE delete_flag = '0'
AND identifier_no IS NOT NULL
AND identifier_no != ''
) t ) t
WHERE rn = 1 WHERE rn = 1
) AS T18 ON T8.id = T18.patient_id ) AS T18 ON T8.id = T18.patient_id

View File

@@ -43,8 +43,11 @@
abi.restricted_scope, abi.restricted_scope,
abi.dosage_instruction, abi.dosage_instruction,
abi.chrgitm_lv abi.chrgitm_lv
from ( FROM (
<if test="adviceTypes == null or adviceTypes.contains(1)"> <!-- 确保至少有一个查询被执行以避免语法错误 -->
<if test="adviceTypes != null and !adviceTypes.isEmpty() and (adviceTypes.contains(1) or adviceTypes.contains(2) or adviceTypes.contains(3))">
<!-- 如果有有效的adviceTypes则执行对应的查询 -->
<if test="adviceTypes.contains(1)">
(SELECT (SELECT
DISTINCT ON (T1.ID) DISTINCT ON (T1.ID)
T1.tenant_id, T1.tenant_id,
@@ -110,14 +113,10 @@
</if> </if>
AND T5.instance_table = #{medicationTableName} AND T5.instance_table = #{medicationTableName}
) )
<if test="adviceTypes.contains(2) or adviceTypes.contains(3)">UNION ALL</if>
</if> </if>
<if test="adviceTypes.contains(2)">
<if test="adviceTypes == null or adviceTypes.contains(1)">
<if test="adviceTypes == null or adviceTypes.contains(2) or adviceTypes.contains(3)">UNION ALL</if>
</if>
<if test="adviceTypes == null or adviceTypes.contains(2)">
(SELECT (SELECT
DISTINCT ON (T1.ID) DISTINCT ON (T1.ID)
T1.tenant_id, T1.tenant_id,
@@ -177,14 +176,10 @@
AND T4.instance_table = #{deviceTableName} AND T4.instance_table = #{deviceTableName}
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
) )
<if test="adviceTypes.contains(3)">UNION ALL</if>
</if> </if>
<if test="adviceTypes.contains(3)">
<if test="adviceTypes == null or adviceTypes.contains(2)">
<if test="adviceTypes == null or adviceTypes.contains(3)">UNION ALL</if>
</if>
<if test="adviceTypes == null or adviceTypes.contains(3)">
(SELECT (SELECT
DISTINCT ON (T1.ID) DISTINCT ON (T1.ID)
T1.tenant_id, T1.tenant_id,
@@ -245,6 +240,51 @@
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
) )
</if> </if>
</if>
<!-- 如果没有有效的adviceTypes提供一个空的默认查询以避免语法错误 -->
<if test="adviceTypes == null or adviceTypes.isEmpty() or (!adviceTypes.contains(1) and !adviceTypes.contains(2) and !adviceTypes.contains(3))">
SELECT
mmd.tenant_id,
CAST(0 AS INTEGER) AS advice_type,
CAST('' AS VARCHAR) AS bus_no,
CAST('' AS VARCHAR) AS category_code,
CAST('' AS VARCHAR) AS pharmacology_category_code,
CAST(0 AS NUMERIC) AS part_percent,
CAST(0 AS NUMERIC) AS unit_conversion_ratio,
CAST(0 AS INTEGER) AS part_attribute_enum,
CAST(0 AS INTEGER) AS tho_part_attribute_enum,
CAST(0 AS INTEGER) AS skin_test_flag,
CAST(0 AS INTEGER) AS inject_flag,
CAST(0 AS BIGINT) AS advice_definition_id,
CAST('' AS VARCHAR) AS advice_name,
CAST('' AS VARCHAR) AS advice_bus_no,
CAST('' AS VARCHAR) AS py_str,
CAST('' AS VARCHAR) AS wb_str,
CAST('' AS VARCHAR) AS yb_no,
CAST('' AS VARCHAR) AS product_name,
CAST(0 AS INTEGER) AS activity_type,
CAST('' AS VARCHAR) AS unit_code,
CAST('' AS VARCHAR) AS min_unit_code,
CAST(0 AS NUMERIC) AS volume,
CAST('' AS VARCHAR) AS method_code,
CAST('' AS VARCHAR) AS rate_code,
CAST(0 AS BIGINT) AS org_id,
CAST(0 AS BIGINT) AS location_id,
CAST('' AS VARCHAR) AS dose,
CAST('' AS VARCHAR) AS dose_unit_code,
CAST('' AS VARCHAR) AS supplier,
CAST(0 AS BIGINT) AS supplier_id,
CAST('' AS VARCHAR) AS manufacturer,
CAST(0 AS BIGINT) AS charge_item_definition_id,
CAST('' AS VARCHAR) AS advice_table_name,
CAST(0 AS BIGINT) AS position_id,
CAST(0 AS INTEGER) AS restricted_flag,
CAST('' AS VARCHAR) AS restricted_scope,
CAST('' AS VARCHAR) AS dosage_instruction,
CAST(0 AS INTEGER) AS chrgitm_lv
FROM med_medication_definition mmd
WHERE 1 = 0 -- 仍然确保不返回任何行,但使用真实表确保类型正确
</if>
) AS abi ) AS abi
${ew.customSqlSegment} ${ew.customSqlSegment}
</select> </select>

View File

@@ -23,7 +23,8 @@
T10.reception_time, T10.reception_time,
T10.practitioner_user_id, T10.practitioner_user_id,
T10.jz_practitioner_user_id, T10.jz_practitioner_user_id,
T10.bus_no T10.bus_no,
T10.identifier_no
from from
( (
SELECT T1.tenant_id AS tenant_id, SELECT T1.tenant_id AS tenant_id,
@@ -48,7 +49,8 @@
T1.create_time AS register_time, T1.create_time AS register_time,
T1.reception_time AS reception_time, T1.reception_time AS reception_time,
T1.organization_id AS org_id, T1.organization_id AS org_id,
T8.bus_no AS bus_no T8.bus_no AS bus_no,
T9.identifier_no AS identifier_no
FROM adm_encounter AS T1 FROM adm_encounter AS T1
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0' LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0' LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
@@ -67,6 +69,20 @@
LEFT JOIN adm_account AS T6 ON T1.ID = T6.encounter_id AND T6.delete_flag = '0' and T6.encounter_flag = '1' LEFT JOIN adm_account AS T6 ON T1.ID = T6.encounter_id AND T6.delete_flag = '0' and T6.encounter_flag = '1'
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0' LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0' LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
LEFT JOIN (
SELECT patient_id,
identifier_no
FROM (
SELECT patient_id,
identifier_no,
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
FROM adm_patient_identifier
WHERE delete_flag = '0'
AND identifier_no IS NOT NULL
AND identifier_no != ''
) t
WHERE rn = 1
) AS T9 ON T8.id = T9.patient_id
WHERE WHERE
T1.delete_flag = '0' T1.delete_flag = '0'
<!-- 当前登录账号ID 和 当前登录账号所属的科室ID 用于控制数据权限 --> <!-- 当前登录账号ID 和 当前登录账号所属的科室ID 用于控制数据权限 -->

View File

@@ -0,0 +1,68 @@
<html>
<head>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; width: 350px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; background: #fff; }
.header { text-align: center; }
.hospital-name { font-size: 20px; font-weight: bold; margin-bottom: 5px; }
.title { font-size: 16px; margin-bottom: 10px; }
.time { font-size: 12px; color: #666; margin-bottom: 15px; }
.section { border-top: 1px solid #000; padding-top: 10px; margin-top: 10px; }
.section-title { font-weight: bold; text-decoration: underline; margin-bottom: 10px; font-size: 14px; }
.item { display: flex; font-size: 13px; margin-bottom: 5px; }
.label { width: 90px; color: #333; }
.value { flex: 1; font-weight: 500; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 13px; }
th { text-align: left; border-bottom: 1px dashed #ccc; padding-bottom: 5px; color: #666; }
td { padding: 5px 0; }
.total { text-align: right; font-weight: bold; border-top: 1px solid #000; padding-top: 10px; margin-top: 10px; font-size: 15px; }
.footer { margin-top: 20px; font-size: 11px; color: #666; line-height: 1.5; }
.qr-code { text-align: center; margin-top: 15px; }
.serial-no { text-align: left; margin-top: 10px; font-size: 12px; font-weight: bold; border-top: 1px dashed #ccc; padding-top: 10px; }
</style>
</head>
<body>
<div class='header'>
<div class='hospital-name'>$hospitalName</div>
<div class='title'>门诊预约挂号凭条</div>
<div class='time'>打印时间:$printTime</div>
</div>
<div class='section'>
<div class='section-title'>患者基本信息</div>
<div class='item'><div class='label'>患者姓名:</div><div class='value'>$patientName</div></div>
<div class='item'><div class='label'>门诊号:</div><div class='value'>$outpatientNo</div></div>
<div class='item'><div class='label'>身份证号:</div><div class='value'>#if($idCard)$idCard#else-#end</div></div>
<div class='item'><div class='label'>联系电话:</div><div class='value'>#if($tel)$tel#else-#end</div></div>
</div>
<div class='section'>
<div class='section-title'>预约详情</div>
<div class='item'><div class='label'>就诊科室:</div><div class='value'>#if($deptName)$deptName#else-#end</div></div>
<div class='item'><div class='label'>医生姓名:</div><div class='value'>$doctorName</div></div>
<div class='item'><div class='label'>预约时间:</div><div class='value'>#if($appointmentTime)$appointmentTime#else-#end</div></div>
<div class='item'><div class='label'>就诊地点:</div><div class='value'>门诊大楼内</div></div>
<div class='item'><div class='label'>预约状态:</div><div class='value'><span style='color:green;'>☑ 已 预</span></div></div>
</div>
<div class='section'>
<div class='section-title'>费用信息</div>
<table>
<tr><th>项目</th><th>数量</th><th>单价</th><th>金额</th></tr>
#foreach($item in $items)
<tr>
<td>$item.chargeItemName</td>
<td>$item.quantityValue</td>
<td>¥$item.totalPrice</td>
<td>¥$item.totalPrice</td>
</tr>
#end
</table>
<div class='total'>合计:¥$totalAmt</div>
<div class='item' style='margin-top:10px;'><div class='label'>支付方式:</div><div class='value'>线上支付 (已支付)</div></div>
</div>
<div class='footer'>
温馨提示请至少提前30分钟到达取号过时自动取消。服务时间8:00-17:00
</div>
<div class='qr-code'>
<img src='https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=$busNo' width='100' height='100' />
</div>
<div class='serial-no'>流水号:$busNo</div>
</body>
</html>

View File

@@ -44,8 +44,16 @@ public class EncounterServiceImpl extends ServiceImpl<EncounterMapper, Encounter
// 生成就诊编码 医保挂号时是先生成码后生成实体 // 生成就诊编码 医保挂号时是先生成码后生成实体
encounter.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.ENCOUNTER_NUM.getPrefix(), 4)); encounter.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.ENCOUNTER_NUM.getPrefix(), 4));
} }
// 生成就诊序号 (患者ID + 科室ID 作为当日就诊号的唯一标识) // 生成就诊序号
String preFix = encounter.getPatientId() + String.valueOf(encounter.getOrganizationId()); // 1) 若挂号医生已传入registrarId 充当挂号医生 ID按“科室+医生+当日”递增
// Key 示例ORG-123-DOC-456 -> 1、2、3...
// 2) 否则按“科室+当日”递增
String preFix;
if (encounter.getRegistrarId() != null) {
preFix = "ORG-" + encounter.getOrganizationId() + "-DOC-" + encounter.getRegistrarId();
} else {
preFix = "ORG-" + encounter.getOrganizationId();
}
encounter.setDisplayOrder(assignSeqUtil.getSeqNoByDay(preFix)); encounter.setDisplayOrder(assignSeqUtil.getSeqNoByDay(preFix));
// 患者ID // 患者ID
Long patientId = encounter.getPatientId(); Long patientId = encounter.getPatientId();

View File

@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>openhis-server</artifactId>
<groupId>com.openhis</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>openhis-einvoiceapp</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- 领域-->
<!-- <dependency>-->
<!-- <groupId>com.openhis</groupId>-->
<!-- <artifactId>openhis-domain</artifactId>-->
<!-- <version>0.0.1-SNAPSHOT</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- 共通-->
<dependency>
<groupId>com.openhis</groupId>
<artifactId>openhis-common</artifactId>
</dependency>
<!-- liteflow-->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>
<!-- pdf依赖-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
</dependency>
</dependencies>
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <configuration>-->
<!-- <fork>true</fork> &lt;!&ndash; 如果没有该配置devtools不会生效 &ndash;&gt;-->
<!-- </configuration>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <goals>-->
<!-- <goal>repackage</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-war-plugin</artifactId>-->
<!-- <version>${maven-war-plugin.version}</version>-->
<!-- <configuration>-->
<!-- <failOnMissingWebXml>false</failOnMissingWebXml>-->
<!-- <warName>${project.artifactId}</warName>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- <finalName>${project.artifactId}</finalName>-->
<!-- </build>-->
</project>

View File

@@ -1,7 +1,7 @@
import { login, logout, getInfo } from '@/api/login' import {getInfo, login, logout} from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth' import {getToken, removeToken, setToken} from '@/utils/auth'
import defAva from '@/assets/images/user.png' import defAva from '@/assets/images/user.png'
import { defineStore } from 'pinia' import {defineStore} from 'pinia'
const useUserStore = defineStore( const useUserStore = defineStore(
'user', 'user',
@@ -19,7 +19,9 @@ const useUserStore = defineStore(
roles: [], roles: [],
permissions: [], permissions: [],
tenantId: '', tenantId: '',
hospitalName:'' tenantName: '', // 租户名称
hospitalName:'',
optionMap: {} // 租户配置项Map从sys_tenant_option表读取
}), }),
actions: { actions: {
// 登录 // 登录
@@ -62,9 +64,10 @@ const useUserStore = defineStore(
this.practitionerId = res.practitionerId this.practitionerId = res.practitionerId
this.fixmedinsCode = res.optionJson.fixmedinsCode this.fixmedinsCode = res.optionJson.fixmedinsCode
this.avatar = avatar this.avatar = avatar
this.hospitalName = res.optionJson.hospitalName this.optionMap = res.optionMap || {}
// 获取tenantId优先从res.user获取否则从res获取 // 优先从optionMap获取配置如果没有则从optionJson获取
this.tenantId = user.tenantId || res.tenantId || this.tenantId this.hospitalName = this.optionMap.hospitalName || res.optionJson.hospitalName || ''
this.tenantName = res.tenantName || ''
resolve(res) resolve(res)
}).catch(error => { }).catch(error => {

View File

@@ -299,236 +299,236 @@ async function printReceipt(param) {
// 金额大于0时显示金额和单位等于0时不显示单位 // 金额大于0时显示金额和单位等于0时不显示单位
YB_FUND_PAY: (() => { YB_FUND_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 100000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 100000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 基金支付总额 })(), // 基金支付总额
SELF_PAY: (() => { SELF_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 200000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 200000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人负担总金额 })(), // 个人负担总金额
OTHER_PAY: (() => { OTHER_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 300000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 其他(如医院负担金额) })(), // 其他(如医院负担金额)
// 基本医保统筹基金支出 // 基本医保统筹基金支出
YB_TC_FUND_AMOUNT: (() => { YB_TC_FUND_AMOUNT: (() => {
const amount = param.detail.find((t) => t.payEnum === 110000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 110000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 基本医保统筹基金支出 })(), // 基本医保统筹基金支出
YB_BC_FUND_AMOUNT: (() => { YB_BC_FUND_AMOUNT: (() => {
const amount = param.detail.find((t) => t.payEnum === 120000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 120000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 补充医疗保险基金支出 })(), // 补充医疗保险基金支出
YB_JZ_FUND_AMOUNT: (() => { YB_JZ_FUND_AMOUNT: (() => {
const amount = param.detail.find((t) => t.payEnum === 130000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 130000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助基金支出 })(), // 医疗救助基金支出
YB_OTHER_AMOUNT: (() => { YB_OTHER_AMOUNT: (() => {
const amount = param.detail.find((t) => t.payEnum === 140000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 140000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 其他支出 })(), // 其他支出
// 职工基本医疗保险 // 职工基本医疗保险
YB_TC_ZG_FUND_VALUE: (() => { YB_TC_ZG_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 110100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 110100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 职工基本医疗保险 })(), // 职工基本医疗保险
YB_TC_JM_FUND_VALUE: (() => { YB_TC_JM_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 110200)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 110200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 居民基本医疗保险 })(), // 居民基本医疗保险
// 补充医疗保险基金支出细分 // 补充医疗保险基金支出细分
YB_BC_JM_DB_VALUE: (() => { YB_BC_JM_DB_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 120100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 120100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 全体参保人的居民大病保险 })(), // 全体参保人的居民大病保险
YB_BC_DE_BZ_VALUE: (() => { YB_BC_DE_BZ_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 120200)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 120200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 大额医疗费用补助 })(), // 大额医疗费用补助
YB_BC_ZG_DE_BZ_VALUE: (() => { YB_BC_ZG_DE_BZ_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 120300)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 120300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 企业职工大额医疗费用补助 })(), // 企业职工大额医疗费用补助
YB_BC_GWY_BZ_VALUE: (() => { YB_BC_GWY_BZ_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 120400)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 120400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 公务员医疗补助 })(), // 公务员医疗补助
// 其他支出细分 // 其他支出细分
OTHER_PAY_DD_FUND_VALUE: (() => { OTHER_PAY_DD_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300001)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300001)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 兜底基金支出 })(), // 兜底基金支出
OTHER_PAY_YW_SH_FUND_VALUE: (() => { OTHER_PAY_YW_SH_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300002)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300002)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 意外伤害基金支出 })(), // 意外伤害基金支出
OTHER_PAY_LX_YL_FUND_VALUE: (() => { OTHER_PAY_LX_YL_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300003)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300003)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员医疗保障金支出 })(), // 离休人员医疗保障金支出
OTHER_PAY_LX_YH_FUND_VALUE: (() => { OTHER_PAY_LX_YH_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300004)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300004)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员优惠金支出 })(), // 离休人员优惠金支出
OTHER_PAY_CZ_FUND_VALUE: (() => { OTHER_PAY_CZ_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300005)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300005)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 财政基金支出 })(), // 财政基金支出
OTHER_PAY_CZ_YZ_FUND_VALUE: (() => { OTHER_PAY_CZ_YZ_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300006)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300006)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 财政预支支出 })(), // 财政预支支出
OTHER_PAY_ZG_DB_FUND_VALUE: (() => { OTHER_PAY_ZG_DB_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300007)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300007)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 职工大病基金支出 })(), // 职工大病基金支出
OTHER_PAY_EY_FUND_VALUE: (() => { OTHER_PAY_EY_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300008)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300008)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 二乙基金支出 })(), // 二乙基金支出
OTHER_PAY_QX_JZ_FUND_VALUE: (() => { OTHER_PAY_QX_JZ_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300009)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300009)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 倾斜救助支出 })(), // 倾斜救助支出
OTHER_PAY_YL_JZ_FUND_VALUE: (() => { OTHER_PAY_YL_JZ_FUND_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 300010)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300010)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助再救助基金 })(), // 医疗救助再救助基金
HOSP_PART_AMT: (() => { HOSP_PART_AMT: (() => {
const amount = param.detail.find((t) => t.payEnum === 300011)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 300011)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 医院负担金额 })(), // 医院负担金额
// 医保结算返回值 // 医保结算返回值
FULAMT_OWNPAY_AMT: (() => { FULAMT_OWNPAY_AMT: (() => {
const amount = param.detail.find((t) => t.payEnum === 1)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 1)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 全自费金额 })(), // 全自费金额
OVERLMT_SELFPAY: (() => { OVERLMT_SELFPAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 3)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 3)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 超限价自费费用 })(), // 超限价自费费用
PRESELFPAY_AMT: (() => { PRESELFPAY_AMT: (() => {
const amount = param.detail.find((t) => t.payEnum === 4)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 4)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 先行自付金额 })(), // 先行自付金额
INSCP_SCP_AMT: (() => { INSCP_SCP_AMT: (() => {
const amount = param.detail.find((t) => t.payEnum === 5)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 5)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 符合政策范围金额 })(), // 符合政策范围金额
ACT_PAY_DEDC: (() => { ACT_PAY_DEDC: (() => {
const amount = param.detail.find((t) => t.payEnum === 6)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 6)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 实际支付起付线 })(), // 实际支付起付线
POOL_PROP_SELFPAY: (() => { POOL_PROP_SELFPAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 7)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 7)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 基本医疗保险统筹基金支付比例 })(), // 基本医疗保险统筹基金支付比例
BALC: (() => { BALC: (() => {
const amount = param.detail.find((t) => t.payEnum === 8)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 8)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 余额 })(), // 余额
// 特殊支付方式 // 特殊支付方式
SELF_YB_ZH_PAY: (() => { SELF_YB_ZH_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 210000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 210000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人医保账户支付 })(), // 个人医保账户支付
SELF_YB_ZH_GJ_VALUE: (() => { SELF_YB_ZH_GJ_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 210100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 210100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 账户共济支付金额 })(), // 账户共济支付金额
SELF_CASH_PAY: (() => { SELF_CASH_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 220000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 220000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额 })(), // 个人现金支付金额
SELF_VX_PAY: (() => { SELF_VX_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 230000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 230000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 微信支付金额 })(), // 微信支付金额
SELF_ALI_PAY: (() => { SELF_ALI_PAY: (() => {
const amount = param.detail.find((t) => t.payEnum === 240000)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 240000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 阿里支付金额 })(), // 阿里支付金额
// 现金支付细分 // 现金支付细分
SELF_CASH_VALUE: (() => { SELF_CASH_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 220400)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(现金) })(), // 个人现金支付金额(现金)
SELF_CASH_VX_VALUE: (() => { SELF_CASH_VX_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 220100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(微信) })(), // 个人现金支付金额(微信)
SELF_CASH_ALI_VALUE: (() => { SELF_CASH_ALI_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 220200)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(支付宝) })(), // 个人现金支付金额(支付宝)
SELF_CASH_UNION_VALUE: (() => { SELF_CASH_UNION_VALUE: (() => {
const amount = param.detail.find((t) => t.payEnum === 220300)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(银联) })(), // 个人现金支付金额(银联)
// 基金类型(扩展) // 基金类型(扩展)
BIRTH_FUND: (() => { BIRTH_FUND: (() => {
const amount = param.detail.find((t) => t.payEnum === 510100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 510100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 生育基金 })(), // 生育基金
RETIREE_MEDICAL: (() => { RETIREE_MEDICAL: (() => {
const amount = param.detail.find((t) => t.payEnum === 340100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 340100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员医疗保障基金 })(), // 离休人员医疗保障基金
URBAN_BASIC_MEDICAL: (() => { URBAN_BASIC_MEDICAL: (() => {
const amount = param.detail.find((t) => t.payEnum === 390100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 390100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民基本医疗保险基金 })(), // 城乡居民基本医疗保险基金
URBAN_SERIOUS_ILLNESS: (() => { URBAN_SERIOUS_ILLNESS: (() => {
const amount = param.detail.find((t) => t.payEnum === 390200)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 390200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民大病医疗保险基金 })(), // 城乡居民大病医疗保险基金
MEDICAL_ASSISTANCE: (() => { MEDICAL_ASSISTANCE: (() => {
const amount = param.detail.find((t) => t.payEnum === 610100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 610100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助基金 })(), // 医疗救助基金
GOVERNMENT_SUBSIDY: (() => { GOVERNMENT_SUBSIDY: (() => {
const amount = param.detail.find((t) => t.payEnum === 640100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 640100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 政府兜底基金 })(), // 政府兜底基金
ACCIDENT_INSURANCE: (() => { ACCIDENT_INSURANCE: (() => {
const amount = param.detail.find((t) => t.payEnum === 390400)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 390400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 意外伤害基金 })(), // 意外伤害基金
CARE_INSURANCE: (() => { CARE_INSURANCE: (() => {
const amount = param.detail.find((t) => t.payEnum === 620100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 620100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 照护保险基金 })(), // 照护保险基金
FINANCIAL_FUND: (() => { FINANCIAL_FUND: (() => {
const amount = param.detail.find((t) => t.payEnum === 360100)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 360100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 财政基金 })(), // 财政基金
HOSPITAL_ADVANCE: (() => { HOSPITAL_ADVANCE: (() => {
const amount = param.detail.find((t) => t.payEnum === 999900)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 999900)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 医院垫付 })(), // 医院垫付
SUPPLEMENTARY_INSURANCE: (() => { SUPPLEMENTARY_INSURANCE: (() => {
const amount = param.detail.find((t) => t.payEnum === 390300)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 390300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民大病补充保险基金 })(), // 城乡居民大病补充保险基金
HEALTHCARE_PREPAYMENT: (() => { HEALTHCARE_PREPAYMENT: (() => {
const amount = param.detail.find((t) => t.payEnum === 360300)?.amount ?? 0; const amount = param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount; return amount > 0 ? amount + ' 元' : amount;
})(), // 保健预支基金 })(), // 保健预支基金
//微信刷卡支付 //微信刷卡支付
SELF_CASH_VX_VALUE: (() => { SELF_CASH_VX_VALUE: (() => {
// const cashValue = param.detail.find((t) => t.payEnum === 220400)?.amount ?? 0; // const cashValue = param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0;
const vxValue = param.detail.find((t) => t.payEnum === 220100)?.amount ?? 0; const vxValue = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
const unionValue = param.detail.find((t) => t.payEnum === 220300)?.amount ?? 0; const unionValue = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
const aliValue = param.detail.find((t) => t.payEnum === 220200)?.amount ?? 0; const aliValue = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
return vxValue + unionValue + aliValue + ' 元'; return (Number(vxValue) + Number(unionValue) + Number(aliValue)).toFixed(2) + ' 元';
})(), })(),
Mr_QR_Code: param.regNo, Mr_QR_Code: param.regNo,

View File

@@ -287,7 +287,7 @@ function getPatientList() {
queryParams.value.receptionTimeETime = undefined; queryParams.value.receptionTimeETime = undefined;
} }
getList(queryParams.value).then((res) => { getList(queryParams.value).then((res) => {
patientList.value = res.data.data.records; patientList.value = res.data?.data?.records || [];
}); });
} }
@@ -363,17 +363,17 @@ function confirmCharge() {
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
chargeItemIds: chargeItemIdList.value, chargeItemIds: chargeItemIdList.value,
}).then((res) => { }).then((res) => {
if (res.code == 200) { if (res.code == 200 && res.data) {
// totalAmount.value = res.data.psnCashPay; // totalAmount.value = res.data.psnCashPay;
paymentId.value = res.data.paymentId; paymentId.value = res.data.paymentId;
chrgBchnoList.value = res.data.chrgBchnoList; chrgBchnoList.value = res.data.chrgBchnoList;
totalAmount.value = res.data.details.find((item) => item.payEnum == 220000).amount; totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details.filter((item) => { details.value = res.data.details?.filter((item) => {
return item.amount > 0; return item.amount > 0;
}); }) || [];
openDialog.value = true; openDialog.value = true;
} else { } else {
proxy.$modal.msgError(res.msg); proxy.$modal.msgError(res?.msg || '预结算失败');
} }
}); });
// console.log(patientInfo) // console.log(patientInfo)
@@ -517,11 +517,11 @@ async function handleReadCard(value) {
ybMdtrtCertType: userCardInfo.psnCertType, ybMdtrtCertType: userCardInfo.psnCertType,
busiCardInfo: userCardInfo.busiCardInfo, busiCardInfo: userCardInfo.busiCardInfo,
}).then((res) => { }).then((res) => {
if (res.code == 200) { if (res.code == 200 && res.data) {
// totalAmount.value = res.data.psnCashPay; // totalAmount.value = res.data.psnCashPay;
paymentId.value = res.data.paymentId; paymentId.value = res.data.paymentId;
totalAmount.value = res.data.details.find((item) => item.payEnum == 220000).amount; totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details; details.value = res.data.details || [];
// chrgBchnoList.value = res.data.chrgBchnoList; // chrgBchnoList.value = res.data.chrgBchnoList;
chargeItemIdList.value = selectRows.map((item) => { chargeItemIdList.value = selectRows.map((item) => {
return item.id; return item.id;
@@ -537,7 +537,7 @@ async function handleReadCard(value) {
}); });
openDialog.value = true; openDialog.value = true;
} else { } else {
proxy.$modal.msgError(res.msg); proxy.$modal.msgError(res?.msg || '预结算失败');
} }
}); });
} }
@@ -648,9 +648,9 @@ function printCharge(row) {
getChargeInfo({ paymentId: row.paymentId }).then((res) => { getChargeInfo({ paymentId: row.paymentId }).then((res) => {
// 设置实收金额 // 设置实收金额
if (res.data && res.data.detail) { if (res.data && res.data.detail) {
const amountDetail = res.data.detail.find((item) => item.payEnum == 220000); const amountDetail = res.data.detail?.find((item) => item.payEnum == 220000);
if (amountDetail) { if (amountDetail) {
totalAmount.value = amountDetail.amount; totalAmount.value = amountDetail.amount || 0;
// 为合并的行设置金额相关字段值 // 为合并的行设置金额相关字段值
rows.forEach((item) => { rows.forEach((item) => {

View File

@@ -302,14 +302,14 @@ function handleRefund(row) {
// return new Decimal(accumulator).add(new Decimal(currentRow.totalPrice || 0)); // return new Decimal(accumulator).add(new Decimal(currentRow.totalPrice || 0));
// }, 0); // }, 0);
getReturnDetail({ id: row.paymentId }).then((res) => { getReturnDetail({ id: row.paymentId }).then((res) => {
if (res.data.length > 0) { if (res.data?.length > 0) {
totalAmount.value = totalAmount.value =
res.data.find((item) => item.payEnum === 220000).amount - (res.data.find((item) => item.payEnum === 220000)?.amount ?? 0) -
(res.data.find((item) => item.payEnum === 220500)?.amount || 0); (res.data.find((item) => item.payEnum === 220500)?.amount ?? 0);
} }
details.value = res.data.filter((item) => { details.value = res.data?.filter((item) => {
return item.amount > 0; return item.amount > 0;
}); }) || [];
}); });
paymentId.value = row.paymentId; paymentId.value = row.paymentId;
patientInfo.value.patientId = row.patientId; patientInfo.value.patientId = row.patientId;

View File

@@ -128,7 +128,7 @@ const getFeeTypeText = computed(() => {
} }
// 如果只有一个选项,直接返回第一个选项的文本 // 如果只有一个选项,直接返回第一个选项的文本
if (props.medfee_paymtd_code.length === 1) { if (props.medfee_paymtd_code.length === 1) {
return props.medfee_paymtd_code[0].label || ''; return props.medfee_paymtd_code[0]?.label || '';
} }
return ''; return '';
}); });
@@ -214,38 +214,38 @@ async function printReceipt(param) {
{ {
...param, ...param,
// 基础支付类型 // 基础支付类型
YB_FUND_PAY: param.detail.find((t) => t.payEnum === 100000)?.amount ?? 0, // 基金支付总额 YB_FUND_PAY: param.detail?.find((t) => t.payEnum === 100000)?.amount ?? 0, // 基金支付总额
SELF_PAY: param.detail.find((t) => t.payEnum === 200000)?.amount ?? 0, // 个人负担总金额 SELF_PAY: param.detail?.find((t) => t.payEnum === 200000)?.amount ?? 0, // 个人负担总金额
OTHER_PAY: param.detail.find((t) => t.payEnum === 300000)?.amount ?? 0, // 其他(如医院负担金额) OTHER_PAY: param.detail?.find((t) => t.payEnum === 300000)?.amount ?? 0, // 其他(如医院负担金额)
// 基本医保统筹基金支出 // 基本医保统筹基金支出
YB_TC_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 110000)?.amount ?? 0, // 基本医保统筹基金支出 YB_TC_FUND_AMOUNT: param.detail?.find((t) => t.payEnum === 110000)?.amount ?? 0, // 基本医保统筹基金支出
YB_BC_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 120000)?.amount ?? 0, // 补充医疗保险基金支出 YB_BC_FUND_AMOUNT: param.detail?.find((t) => t.payEnum === 120000)?.amount ?? 0, // 补充医疗保险基金支出
YB_JZ_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 130000)?.amount ?? 0, // 医疗救助基金支出 YB_JZ_FUND_AMOUNT: param.detail?.find((t) => t.payEnum === 130000)?.amount ?? 0, // 医疗救助基金支出
YB_OTHER_AMOUNT: param.detail.find((t) => t.payEnum === 140000)?.amount ?? 0, // 其他支出 YB_OTHER_AMOUNT: param.detail?.find((t) => t.payEnum === 140000)?.amount ?? 0, // 其他支出
// 职工基本医疗保险 // 职工基本医疗保险
YB_TC_ZG_FUND_VALUE: param.detail.find((t) => t.payEnum === 110100)?.amount ?? 0, // 职工基本医疗保险 YB_TC_ZG_FUND_VALUE: param.detail?.find((t) => t.payEnum === 110100)?.amount ?? 0, // 职工基本医疗保险
YB_TC_JM_FUND_VALUE: param.detail.find((t) => t.payEnum === 110200)?.amount ?? 0, // 居民基本医疗保险 YB_TC_JM_FUND_VALUE: param.detail?.find((t) => t.payEnum === 110200)?.amount ?? 0, // 居民基本医疗保险
// 补充医疗保险基金支出细分 // 补充医疗保险基金支出细分
YB_BC_JM_DB_VALUE: param.detail.find((t) => t.payEnum === 120100)?.amount ?? 0, // 全体参保人的居民大病保险 YB_BC_JM_DB_VALUE: param.detail?.find((t) => t.payEnum === 120100)?.amount ?? 0, // 全体参保人的居民大病保险
YB_BC_DE_BZ_VALUE: param.detail.find((t) => t.payEnum === 120200)?.amount ?? 0, // 大额医疗费用补助 YB_BC_DE_BZ_VALUE: param.detail?.find((t) => t.payEnum === 120200)?.amount ?? 0, // 大额医疗费用补助
YB_BC_ZG_DE_BZ_VALUE: param.detail.find((t) => t.payEnum === 120300)?.amount ?? 0, // 企业职工大额医疗费用补助 YB_BC_ZG_DE_BZ_VALUE: param.detail?.find((t) => t.payEnum === 120300)?.amount ?? 0, // 企业职工大额医疗费用补助
YB_BC_GWY_BZ_VALUE: param.detail.find((t) => t.payEnum === 120400)?.amount ?? 0, // 公务员医疗补助 YB_BC_GWY_BZ_VALUE: param.detail?.find((t) => t.payEnum === 120400)?.amount ?? 0, // 公务员医疗补助
// 其他支出细分 // 其他支出细分
OTHER_PAY_DD_FUND_VALUE: param.detail.find((t) => t.payEnum === 300001)?.amount ?? 0, // 兜底基金支出 OTHER_PAY_DD_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300001)?.amount ?? 0, // 兜底基金支出
OTHER_PAY_YW_SH_FUND_VALUE: param.detail.find((t) => t.payEnum === 300002)?.amount ?? 0, // 意外伤害基金支出 OTHER_PAY_YW_SH_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300002)?.amount ?? 0, // 意外伤害基金支出
OTHER_PAY_LX_YL_FUND_VALUE: param.detail.find((t) => t.payEnum === 300003)?.amount ?? 0, // 离休人员医疗保障金支出 OTHER_PAY_LX_YL_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300003)?.amount ?? 0, // 离休人员医疗保障金支出
OTHER_PAY_LX_YH_FUND_VALUE: param.detail.find((t) => t.payEnum === 300004)?.amount ?? 0, // 离休人员优惠金支出 OTHER_PAY_LX_YH_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300004)?.amount ?? 0, // 离休人员优惠金支出
OTHER_PAY_CZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300005)?.amount ?? 0, // 财政基金支出 OTHER_PAY_CZ_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300005)?.amount ?? 0, // 财政基金支出
OTHER_PAY_CZ_YZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300006)?.amount ?? 0, // 财政预支支出 OTHER_PAY_CZ_YZ_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300006)?.amount ?? 0, // 财政预支支出
OTHER_PAY_ZG_DB_FUND_VALUE: param.detail.find((t) => t.payEnum === 300007)?.amount ?? 0, // 职工大病基金支出 OTHER_PAY_ZG_DB_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300007)?.amount ?? 0, // 职工大病基金支出
OTHER_PAY_EY_FUND_VALUE: param.detail.find((t) => t.payEnum === 300008)?.amount ?? 0, // 二乙基金支出 OTHER_PAY_EY_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300008)?.amount ?? 0, // 二乙基金支出
OTHER_PAY_QX_JZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300009)?.amount ?? 0, // 倾斜救助支出 OTHER_PAY_QX_JZ_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300009)?.amount ?? 0, // 倾斜救助支出
OTHER_PAY_YL_JZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300010)?.amount ?? 0, // 医疗救助再救助基金 OTHER_PAY_YL_JZ_FUND_VALUE: param.detail?.find((t) => t.payEnum === 300010)?.amount ?? 0, // 医疗救助再救助基金
HOSP_PART_AMT: param.detail.find((t) => t.payEnum === 300011)?.amount ?? 0, // 医院负担金额 HOSP_PART_AMT: param.detail?.find((t) => t.payEnum === 300011)?.amount ?? 0, // 医院负担金额
// 医保结算返回值 - 修复运算符优先级问题,添加括号确保正确拼接'元' // 医保结算返回值 - 修复运算符优先级问题,添加括号确保正确拼接'元'
FULAMT_OWNPAY_AMT: (param.detail?.find((t) => t.payEnum === 1)?.amount ?? 0) + '元', // 全自费金额 FULAMT_OWNPAY_AMT: (param.detail?.find((t) => t.payEnum === 1)?.amount ?? 0) + '元', // 全自费金额
@@ -347,11 +347,11 @@ async function printReceipt(param) {
: '', // 保健预支基金 : '', // 保健预支基金
//微信刷卡支付 //微信刷卡支付
SELF_CASH_VX_VALUE: (() => { SELF_CASH_VX_VALUE: (() => {
// const cashValue = param.detail.find((t) => t.payEnum === 220400)?.amount ?? 0; // const cashValue = param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0;
const vxValue = param.detail.find((t) => t.payEnum === 220100)?.amount ?? 0; const vxValue = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
const unionValue = param.detail.find((t) => t.payEnum === 220300)?.amount ?? 0; const unionValue = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
const aliValue = param.detail.find((t) => t.payEnum === 220200)?.amount ?? 0; const aliValue = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
return vxValue + unionValue + aliValue + '元'; return (Number(vxValue) + Number(unionValue) + Number(aliValue)).toFixed(2) + '元';
})(), })(),
// 患者信息 // 患者信息

View File

@@ -196,7 +196,7 @@ watch(
// 计算应退金额并更新表单数据 // 计算应退金额并更新表单数据
const targetPayEnums = [220500, 220400, 220100, 220200, 220300]; const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = res.data const sum = res.data
.filter((item) => targetPayEnums.includes(item.payEnum)) ?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((total, item) => total + (Number(item.amount) || 0), 0); .reduce((total, item) => total + (Number(item.amount) || 0), 0);
if (sum > 0) { if (sum > 0) {
formData.totalAmount = sum; formData.totalAmount = sum;
@@ -313,9 +313,9 @@ const displayAmount = computed(() => {
} }
const targetPayEnums = [220500, 220400, 220100, 220200, 220300]; const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = preCancelData.value const sum = preCancelData.value
.filter((item) => targetPayEnums.includes(item.payEnum)) ?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((sum, item) => sum + (Number(item.amount) || 0), 0); .reduce((sum, item) => sum + (Number(item.amount) || 0), 0);
return sum.toFixed(2); return sum?.toFixed(2) ?? '0.00';
}); });
const returnedAmount = computed(() => { const returnedAmount = computed(() => {
@@ -334,7 +334,7 @@ const calculatedTotalAmount = computed(() => {
const targetPayEnums = [220500, 220400, 220100, 220200, 220300]; const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
// 应退金额 = (amount - returnAmount) 的总和,即剩余应退金额 // 应退金额 = (amount - returnAmount) 的总和,即剩余应退金额
const sum = preCancelData.value const sum = preCancelData.value
.filter((item) => targetPayEnums.includes(item.payEnum)) ?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce( .reduce(
(total, item) => total + ((Number(item.amount) || 0) - (Number(item.returnAmount) || 0)), (total, item) => total + ((Number(item.amount) || 0) - (Number(item.returnAmount) || 0)),
0 0
@@ -348,7 +348,7 @@ const calculatedReturnAmount = computed(() => {
} }
const targetPayEnums = [220500, 220400, 220100, 220200, 220300]; const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = preCancelData.value const sum = preCancelData.value
.filter((item) => targetPayEnums.includes(item.payEnum)) ?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((total, item) => total + (Number(item.returnAmount) || 0), 0); .reduce((total, item) => total + (Number(item.returnAmount) || 0), 0);
return sum || 0; return sum || 0;
}); });
@@ -359,12 +359,12 @@ const refundMethodsFromApi = computed(() => {
return []; return [];
} }
const targetPayEnums = [220500, 220400, 220100, 220200, 220300]; const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
return preCancelData.value.filter( return preCancelData.value?.filter(
(item) => (item) =>
targetPayEnums.includes(item.payEnum) && targetPayEnums.includes(item.payEnum) &&
item.payEnum_dictText && item.payEnum_dictText &&
item.payEnum_dictText.trim() !== '' item.payEnum_dictText.trim() !== ''
); ) || [];
}); });
// 根据 payEnum 获取支付方式标签 // 根据 payEnum 获取支付方式标签
@@ -380,7 +380,7 @@ const getPayMethodLabel = (payEnum) => {
// 计算所有退费方式的总和 // 计算所有退费方式的总和
const totalRefundAmount = computed(() => { const totalRefundAmount = computed(() => {
return refundMethodsFromApi.value.reduce((sum, item) => sum + (Number(item.amount) || 0), 0); return refundMethodsFromApi.value?.reduce((sum, item) => sum + (Number(item.amount) || 0), 0) ?? 0;
}); });
// 计算剩余可输入金额(应退金额 - 已输入的退费金额总和) // 计算剩余可输入金额(应退金额 - 已输入的退费金额总和)

View File

@@ -151,10 +151,6 @@
<span class="label">患者姓名:</span> <span class="label">患者姓名:</span>
<span class="value">{{ form.name || '-' }}</span> <span class="value">{{ form.name || '-' }}</span>
</div> </div>
<div class="info-row">
<span class="label">门诊号:</span>
<span class="value">{{ form.serialNo || '-' }}</span>
</div>
<div class="info-row"> <div class="info-row">
<span class="label">就诊卡号:</span> <span class="label">就诊卡号:</span>
<span class="value">{{ form.cardNo || '-' }}</span> <span class="value">{{ form.cardNo || '-' }}</span>
@@ -233,6 +229,12 @@
</table> </table>
</div> </div>
<!-- 流水号显示在左下角 -->
<div class="serial-number-bottom-left">
<span class="serial-label">流水号:</span>
<span class="serial-value">{{ form.serialNo || '-' }}</span>
</div>
<div class="print-footer"> <div class="print-footer">
<div class="reminder">温馨提示: 请妥善保管此凭条就诊时请携带</div> <div class="reminder">温馨提示: 请妥善保管此凭条就诊时请携带</div>
</div> </div>
@@ -545,29 +547,21 @@ function formatDateTime(date) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} }
// 计算流水号(与 fillForm 中的逻辑一致 // 计算流水号直接使用挂号记录表的主键IDencounterId
function calculateSerialNo(row) { function calculateSerialNo(row) {
if (!row) { if (!row) {
return '-'; return '-';
} }
// 优先使用已有的 serialNo如果存在 // 直接使用主键ID作为流水号
if (row.serialNo) { if (row.encounterId != null && row.encounterId !== undefined) {
return row.serialNo; return String(row.encounterId);
} else if (row.id != null && row.id !== undefined) {
// 兼容其他可能的ID字段名
return String(row.id);
} else {
return '-';
} }
// 使用 encounterId 的后3位计算流水号
if (row.encounterId) {
const idStr = String(row.encounterId);
const lastThree = idStr.slice(-3);
const num = parseInt(lastThree) || 0;
return String((num % 999) + 1).padStart(3, '0');
}
// 如果 encounterId 为空生成一个001-999的序列号
const timestamp = Date.now();
const serial = (timestamp % 999) + 1;
return String(serial).padStart(3, '0');
} }
// 提交补打挂号 // 提交补打挂号
@@ -835,6 +829,26 @@ function handleBrowserPrint() {
font-weight: bold; font-weight: bold;
color: red; 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 { .print-footer {
margin-top: 20px; margin-top: 20px;
font-size: 12px; font-size: 12px;
@@ -1052,6 +1066,31 @@ async function handleConfirmSelect() {
color: red; 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 { .print-footer {
margin-top: 20px; margin-top: 20px;
font-size: 12px; font-size: 12px;

View File

@@ -801,10 +801,10 @@ const { queryParams, form, rules } = toRefs(data);
/** 根据contractNo获取费用性质名称 */ /** 根据contractNo获取费用性质名称 */
function getFeeTypeName(contractNo) { function getFeeTypeName(contractNo) {
if (!contractNo || !medfee_paymtd_code || !Array.isArray(medfee_paymtd_code)) { if (!contractNo || !medfee_paymtd_code?.value || !Array.isArray(medfee_paymtd_code.value)) {
return ''; return '';
} }
const dictItem = medfee_paymtd_code.find(item => item.value === contractNo); const dictItem = medfee_paymtd_code.value.find(item => item.value === contractNo);
return dictItem ? dictItem.label : ''; return dictItem ? dictItem.label : '';
} }
@@ -1185,7 +1185,7 @@ function filterDoctorsByHealthcare() {
} }
// 获取选中的挂号类型信息 // 获取选中的挂号类型信息
const selectedHealthcare = healthcareList.value.find( const selectedHealthcare = healthcareList.value?.find(
(healthcare) => healthcare.id === form.value.serviceTypeId (healthcare) => healthcare.id === form.value.serviceTypeId
); );
@@ -1349,7 +1349,7 @@ function handleAdd() {
genderEnum_enumText: form.value.genderEnum_enumText, genderEnum_enumText: form.value.genderEnum_enumText,
age: form.value.age, age: form.value.age,
contractName: form.value.contractNo contractName: form.value.contractNo
? contractList.value.find((item) => item.busNo === form.value.contractNo)?.contractName || ? contractList.value?.find((item) => item.busNo === form.value.contractNo)?.contractName ||
'自费' '自费'
: '自费', : '自费',
idCard: form.value.idCard, idCard: form.value.idCard,

View File

@@ -198,6 +198,9 @@ watch(
// 直接更新查询参数 // 直接更新查询参数
queryParams.value.searchKey = newValue.searchKey || ''; queryParams.value.searchKey = newValue.searchKey || '';
// 更新categoryCode
queryParams.value.categoryCode = newValue.categoryCode || '';
// 处理类型筛选 // 处理类型筛选
if (newValue.adviceType !== undefined && newValue.adviceType !== null && newValue.adviceType !== '') { if (newValue.adviceType !== undefined && newValue.adviceType !== null && newValue.adviceType !== '') {
// 单个类型 // 单个类型
@@ -215,9 +218,19 @@ getList();
// 从priceList列表中获取价格 // 从priceList列表中获取价格
function getPriceFromInventory(row) { function getPriceFromInventory(row) {
if (row.priceList && row.priceList.length > 0) { if (row.priceList && row.priceList.length > 0) {
const price = row.priceList[0].price || 0; const price = row.priceList[0].price;
// 检查价格是否为有效数字
if (price !== undefined && price !== null && !isNaN(price) && isFinite(price)) {
return Number(price).toFixed(2) + ' 元'; return Number(price).toFixed(2) + ' 元';
} }
// 如果价格无效,尝试从其他可能的字段获取价格
if (row.totalPrice !== undefined && row.totalPrice !== null && !isNaN(row.totalPrice) && isFinite(row.totalPrice)) {
return Number(row.totalPrice).toFixed(2) + ' 元';
}
if (row.price !== undefined && row.price !== null && !isNaN(row.price) && isFinite(row.price)) {
return Number(row.price).toFixed(2) + ' 元';
}
}
return '-'; return '-';
} }
function getList() { function getList() {

View File

@@ -127,8 +127,8 @@
<div style="display:flex;flex-direction:column;align-items:center;gap:5px;"> <div style="display:flex;flex-direction:column;align-items:center;gap:5px;">
<el-checkbox <el-checkbox
label="主诊断" label="主诊断"
:trueLabel="1" :true-value="1"
:falseLabel="0" :false-value="0"
v-model="scope.row.maindiseFlag" v-model="scope.row.maindiseFlag"
border border
size="small" size="small"
@@ -409,10 +409,10 @@ function getTree() {
function handleAddDiagnosis() { function handleAddDiagnosis() {
proxy.$refs.formRef.validate((valid) => { proxy.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
// if (!allowAdd.value) { const maxSortNo = form.value.diagnosisList.length > 0
// proxy.$modal.msgWarning('请先填写病历'); ? Math.max(...form.value.diagnosisList.map(item => item.diagSrtNo || 0))
// return; : 0;
// }
form.value.diagnosisList.push({ form.value.diagnosisList.push({
showPopover: false, showPopover: false,
name: undefined, name: undefined,
@@ -491,6 +491,7 @@ function handleSaveDiagnosis() {
return false; return false;
} else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) { } else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) {
proxy.$modal.msgWarning('至少添加一条主诊断'); proxy.$modal.msgWarning('至少添加一条主诊断');
return false;
} else { } else {
// 保存前按排序号排序 // 保存前按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0)); form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));

View File

@@ -38,7 +38,7 @@ watch(
queryParams.value.searchKey = newValue; queryParams.value.searchKey = newValue;
getList(); getList();
}, },
{ immdiate: true } { immediate: true }
); );
getList(); getList();

View File

@@ -17,7 +17,7 @@
</el-button> </el-button>
</div> </div>
</div> </div>
<!--检验信息-->
<el-table <el-table
ref="inspectionTableRef" ref="inspectionTableRef"
:data="inspectionList" :data="inspectionList"
@@ -88,18 +88,18 @@
<div class="application-form" style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;"> <div class="application-form" style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;">
<div style="margin-bottom: 20px"> <div style="margin-bottom: 20px">
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请单号</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">申请单号</label>
<el-input v-model="formData.applicationNo" disabled size="small" /> <el-input v-model="formData.applicationNo" readonly size="small" />
</div> </div>
<!-- 患者信息行 --> <!-- 患者信息行 -->
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px"> <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px">
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">姓名</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">姓名</label>
<el-input v-model="formData.patientName" disabled size="small" /> <el-input v-model="formData.patientName" readonly size="small" />
</div> </div>
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">就诊卡号</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">就诊卡号</label>
<el-input v-model="formData.cardNo" disabled size="small" /> <el-input v-model="formData.identifierNo" readonly size="small" />
</div> </div>
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">费用性质</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">费用性质</label>
@@ -115,6 +115,7 @@
<!-- 申请信息行 --> <!-- 申请信息行 -->
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px"> <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px">
<!--申请日期-->
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请日期</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">申请日期</label>
<el-date-picker <el-date-picker
@@ -127,6 +128,7 @@
style="width: 100%" style="width: 100%"
/> />
</div> </div>
<!--申请科室-->
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请科室</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">申请科室</label>
<el-select v-model="formData.departmentName" placeholder="请选择申请科室" size="small" style="width: 100%" disabled> <el-select v-model="formData.departmentName" placeholder="请选择申请科室" size="small" style="width: 100%" disabled>
@@ -135,9 +137,11 @@
<el-option label="儿科" value="pediatrics" /> <el-option label="儿科" value="pediatrics" />
</el-select> </el-select>
</div> </div>
<!--申请医生-->
<div> <div>
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请医生</label> <label style="display: block; margin-bottom: 5px; font-weight: bold">申请医生</label>
<el-select v-model="formData.doctorName" placeholder="请选择申请医生" size="small" style="width: 100%" disabled> <el-select v-model="formData.doctorName" placeholder="请选择申请医生" size="small" style="width: 100%">
<el-option label="张医生" value="doctor_zhang" /> <el-option label="张医生" value="doctor_zhang" />
<el-option label="李医生" value="doctor_li" /> <el-option label="李医生" value="doctor_li" />
<el-option label="王医生" value="doctor_wang" /> <el-option label="王医生" value="doctor_wang" />
@@ -164,7 +168,7 @@
<div v-if="validationErrors.executeDepartment" class="error-message">请选择执行科室</div> <div v-if="validationErrors.executeDepartment" class="error-message">请选择执行科室</div>
</div> </div>
<!-- 诊断相关字段 --> <!-- 诊断描述 -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px">
<div :class="{ 'form-item-error': validationErrors.diagnosisDesc }"> <div :class="{ 'form-item-error': validationErrors.diagnosisDesc }">
<label style="display: block; margin-bottom: 5px; font-weight: bold">诊断描述 <span style="color: #f56c6c">*</span></label> <label style="display: block; margin-bottom: 5px; font-weight: bold">诊断描述 <span style="color: #f56c6c">*</span></label>
@@ -248,7 +252,6 @@
</div> </div>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="检验信息" name="inspectionInfo"> <el-tab-pane label="检验信息" name="inspectionInfo">
<div style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;"> <div style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px">
@@ -458,7 +461,7 @@ const formData = reactive({
applicationId: null, applicationId: null,
applicationNo: '202511210001', applicationNo: '202511210001',
patientName: '', patientName: '',
cardNo: '', identifierNo: '',
feeType: 'self', feeType: 'self',
applicationDate: new Date(), applicationDate: new Date(),
departmentName: '', departmentName: '',
@@ -579,11 +582,11 @@ const getFilteredItems = (categoryKey) => {
// 初始化数据 // 初始化数据
function initData() { function initData() {
console.log('检验组件初始化patientInfo:', props.patientInfo) // console.log('检验组件初始化patientInfo:', props.patientInfo)
if (props.patientInfo) { if (props.patientInfo) {
queryParams.encounterId = props.patientInfo.encounterId queryParams.encounterId = props.patientInfo.encounterId
formData.patientName = props.patientInfo.patientName || '' formData.patientName = props.patientInfo.patientName || ''
formData.cardNo = props.patientInfo.cardNo || '' formData.identifierNo = props.patientInfo.identifierNo || ''
formData.departmentName = props.patientInfo.departmentName || '' formData.departmentName = props.patientInfo.departmentName || ''
formData.doctorName = props.patientInfo.doctorName || '' formData.doctorName = props.patientInfo.doctorName || ''
} }
@@ -597,26 +600,26 @@ function initData() {
function getInspectionList() { function getInspectionList() {
// 如果没有encounterId,不调用接口 // 如果没有encounterId,不调用接口
if (!queryParams.encounterId) { if (!queryParams.encounterId) {
console.warn('【检验】encounterId为空,不调用接口') // console.warn('【检验】encounterId为空,不调用接口')
return return
} }
loading.value = true loading.value = true
// 调用真实的API只传递 encounterId 参数 // 调用真实的API只传递 encounterId 参数
console.log('【检验】调用API,encounterId:', queryParams.encounterId) // console.log('【检验】调用API,encounterId:', queryParams.encounterId)
getInspectionApplicationList({ encounterId: queryParams.encounterId }).then((res) => { getInspectionApplicationList({ encounterId: queryParams.encounterId }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
inspectionList.value = res.data || [] inspectionList.value = res.data || []
total.value = res.data?.length || 0 total.value = res.data?.length || 0
console.log('【检验】获取数据成功,数量:', inspectionList.value.length) // console.log('【检验】获取数据成功,数量:', inspectionList.value.length)
} else { } else {
inspectionList.value = [] inspectionList.value = []
total.value = 0 total.value = 0
ElMessage.error('获取检验申请单列表失败') ElMessage.error('获取检验申请单列表失败')
} }
}).catch((error) => { }).catch((error) => {
console.error('获取检验申请单列表异常:', error) // console.error('获取检验申请单列表异常:', error)
inspectionList.value = [] inspectionList.value = []
total.value = 0 total.value = 0
ElMessage.error('获取检验申请单列表异常') ElMessage.error('获取检验申请单列表异常')
@@ -627,7 +630,7 @@ function getInspectionList() {
// 新增申请单 // 新增申请单
function handleNewApplication() { function handleNewApplication() {
console.log('点击新增按钮') // console.log('点击新增按钮')
resetForm() resetForm()
// 生成申请单号 // 生成申请单号
formData.applicationNo = generateApplicationNo() formData.applicationNo = generateApplicationNo()
@@ -737,7 +740,7 @@ function handleSave() {
totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.price + (item.serviceFee || 0), 0) totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.price + (item.serviceFee || 0), 0)
} }
console.log('保存检验申请单数据:', saveData) // console.log('保存检验申请单数据:', saveData)
// 调用真实的API保存 // 调用真实的API保存
saveInspectionApplication(saveData).then((res) => { saveInspectionApplication(saveData).then((res) => {
@@ -751,7 +754,7 @@ function handleSave() {
ElMessage.error(res.message || '保存失败') ElMessage.error(res.message || '保存失败')
} }
}).catch((error) => { }).catch((error) => {
console.error('保存检验申请单异常:', error) // console.error('保存检验申请单异常:', error)
ElMessage.error('保存异常') ElMessage.error('保存异常')
}) })
} }
@@ -761,7 +764,7 @@ function handleFormSave() {
formRef.value?.validate((valid) => { formRef.value?.validate((valid) => {
if (valid) { if (valid) {
formData.inspectionItems = selectedInspectionItems.value.map(item => item.id) formData.inspectionItems = selectedInspectionItems.value.map(item => item.id)
console.log('保存检验申请单表单数据:', formData) // console.log('保存检验申请单表单数据:', formData)
ElMessage.success('保存成功') ElMessage.success('保存成功')
showForm.value = false showForm.value = false
getInspectionList() getInspectionList()
@@ -771,7 +774,7 @@ function handleFormSave() {
// 查看详情 // 查看详情
function handleView(row) { function handleView(row) {
console.log('点击查看按钮,数据:', row) // console.log('点击查看按钮,数据:', row)
// 加载表单数据 // 加载表单数据
Object.assign(formData, row) Object.assign(formData, row)
@@ -811,7 +814,7 @@ function switchCategory(category) {
// 处理搜索 // 处理搜索
function handleSearch() { function handleSearch() {
// 搜索逻辑已在getFilteredItems中实现这里可以添加额外的搜索逻辑 // 搜索逻辑已在getFilteredItems中实现这里可以添加额外的搜索逻辑
console.log('搜索关键词:', searchKeyword.value) // console.log('搜索关键词:', searchKeyword.value)
} }
// 处理项目项点击(排除勾选框点击) // 处理项目项点击(排除勾选框点击)
@@ -866,7 +869,7 @@ function handleSelectionChange(selection) {
// 打印申请单 // 打印申请单
function handlePrint(row) { function handlePrint(row) {
console.log('打印申请单:', row) // console.log('打印申请单:', row)
// 切换到申请单TAB // 切换到申请单TAB
leftActiveTab.value = 'application' leftActiveTab.value = 'application'
@@ -949,7 +952,7 @@ function handleDelete(row) {
confirmButtonClass: 'el-button--danger' confirmButtonClass: 'el-button--danger'
} }
).then(() => { ).then(() => {
console.log('删除申请单:', row) // console.log('删除申请单:', row)
// 调用真实的API删除 // 调用真实的API删除
deleteInspectionApplication(row.applicationId).then((res) => { deleteInspectionApplication(row.applicationId).then((res) => {
if (res.code === 200) { if (res.code === 200) {
@@ -960,7 +963,7 @@ function handleDelete(row) {
ElMessage.error(res.message || '删除失败') ElMessage.error(res.message || '删除失败')
} }
}).catch((error) => { }).catch((error) => {
console.error('删除检验申请单异常:', error) // console.error('删除检验申请单异常:', error)
ElMessage.error('删除异常') ElMessage.error('删除异常')
}) })
}).catch(() => { }).catch(() => {
@@ -987,10 +990,11 @@ watch(() => props.activeTab, (newVal) => {
// 监听patientInfo变化,确保encounterId及时更新 // 监听patientInfo变化,确保encounterId及时更新
watch(() => props.patientInfo, (newVal) => { watch(() => props.patientInfo, (newVal) => {
console.log('【检验】patientInfo变化:', newVal) // console.log('【检验】patientInfo变化:', newVal)
console.log('【检验】接收到的完整patientInfo:', JSON.stringify(newVal, null, 2))
if (newVal && newVal.encounterId) { if (newVal && newVal.encounterId) {
queryParams.encounterId = newVal.encounterId queryParams.encounterId = newVal.encounterId
console.log('【检验】更新encounterId:', queryParams.encounterId) // console.log('【检验】更新encounterId:', queryParams.encounterId)
} }
}, { deep: true, immediate: true }) }, { deep: true, immediate: true })

View File

@@ -96,7 +96,7 @@
' ' + ' ' +
scope.row.volume + scope.row.volume +
' [' + ' [' +
Number(scope.row.unitPrice).toFixed(2) + (scope.row.unitPrice !== undefined && scope.row.unitPrice !== null && !isNaN(scope.row.unitPrice) && isFinite(scope.row.unitPrice) ? Number(scope.row.unitPrice).toFixed(2) : '-') +
' 元' + ' 元' +
'/' + '/' +
scope.row.unitCode_dictText + scope.row.unitCode_dictText +
@@ -145,7 +145,7 @@
<span class="medicine-info"> 注射药品{{ scope.row.injectFlag_enumText }} </span> <span class="medicine-info"> 注射药品{{ scope.row.injectFlag_enumText }} </span>
<span class="total-amount"> <span class="total-amount">
总金额{{ 总金额{{
scope.row.totalPrice (scope.row.totalPrice !== undefined && scope.row.totalPrice !== null && !isNaN(scope.row.totalPrice) && isFinite(scope.row.totalPrice))
? Number(scope.row.totalPrice).toFixed(2) + ' 元' ? Number(scope.row.totalPrice).toFixed(2) + ' 元'
: '0.00 元' : '0.00 元'
}} }}
@@ -631,7 +631,7 @@
" " + " " +
scope.row.volume + scope.row.volume +
" [" + " [" +
Number(scope.row.unitPrice).toFixed(2) + (scope.row.unitPrice !== undefined && scope.row.unitPrice !== null && !isNaN(scope.row.unitPrice) && isFinite(scope.row.unitPrice) ? Number(scope.row.unitPrice).toFixed(2) : '-') +
" " + " " +
"/" + "/" +
scope.row.unitCode_dictText + scope.row.unitCode_dictText +
@@ -687,7 +687,7 @@
</el-form-item> </el-form-item>
<span class="total-amount"> <span class="total-amount">
总金额:{{ 总金额:{{
scope.row.totalPrice (scope.row.totalPrice !== undefined && scope.row.totalPrice !== null && !isNaN(scope.row.totalPrice) && isFinite(scope.row.totalPrice))
? Number(scope.row.totalPrice).toFixed(2) + ' 元' ? Number(scope.row.totalPrice).toFixed(2) + ' 元'
: '0.00 元' : '0.00 元'
}} }}
@@ -702,7 +702,7 @@
<span style="font-size: 16px; font-weight: 600"> <span style="font-size: 16px; font-weight: 600">
{{ scope.row.adviceName }} {{ scope.row.adviceName }}
{{ {{
scope.row.unitPrice (scope.row.unitPrice !== undefined && scope.row.unitPrice !== null && !isNaN(scope.row.unitPrice) && isFinite(scope.row.unitPrice))
? Number(scope.row.unitPrice).toFixed(2) + '/次' ? Number(scope.row.unitPrice).toFixed(2) + '/次'
: '-' + '元' : '-' + '元'
}} }}
@@ -748,7 +748,7 @@
<span class="total-amount"> <span class="total-amount">
总金额: 总金额:
{{ {{
scope.row.totalPrice (scope.row.totalPrice !== undefined && scope.row.totalPrice !== null && !isNaN(scope.row.totalPrice) && isFinite(scope.row.totalPrice))
? Number(scope.row.totalPrice).toFixed(2) + ' 元' ? Number(scope.row.totalPrice).toFixed(2) + ' 元'
: '0.00 元' : '0.00 元'
}} }}
@@ -782,6 +782,19 @@
// 当医嘱类型改变时,清空当前选择的项目名称,因为不同类型项目的数据结构可能不兼容 // 当医嘱类型改变时,清空当前选择的项目名称,因为不同类型项目的数据结构可能不兼容
prescriptionList[scope.$index].adviceName = undefined; prescriptionList[scope.$index].adviceName = undefined;
adviceQueryParams.adviceType = value; adviceQueryParams.adviceType = value;
// 根据选择的类型设置categoryCode用于药品分类筛选
if (value == 1) { // 西药
adviceQueryParams.categoryCode = '2';
} else if (value == 2) { // 中成药
adviceQueryParams.categoryCode = '1';
} else if (value == 3) { // 耗材
adviceQueryParams.categoryCode = ''; // 耗材不需要categoryCode筛选
} else if (value == 4) { // 诊疗
adviceQueryParams.categoryCode = ''; // 诊疗不需要categoryCode筛选
} else {
adviceQueryParams.categoryCode = ''; // 全部类型
}
} }
" "
@clear=" @clear="
@@ -907,7 +920,7 @@
<el-table-column label="总金额" align="right" prop="" header-align="center" width="100"> <el-table-column label="总金额" align="right" prop="" header-align="center" width="100">
<template #default="scope"> <template #default="scope">
<span v-if="!scope.row.isEdit" style="text-align: right"> <span v-if="!scope.row.isEdit" style="text-align: right">
{{ scope.row.totalPrice ? Number(scope.row.totalPrice).toFixed(2) + ' 元' : '-' }} {{ (scope.row.totalPrice !== undefined && scope.row.totalPrice !== null && !isNaN(scope.row.totalPrice) && isFinite(scope.row.totalPrice)) ? Number(scope.row.totalPrice).toFixed(2) + ' 元' : '-' }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
@@ -2472,8 +2485,8 @@ function setValue(row) {
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0)) ? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
: 0; : 0;
prescriptionList.value[targetIndex] = { prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[targetIndex], ...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)), ...JSON.parse(JSON.stringify(row)),
// 确保adviceType为数字类型避免类型不匹配导致的显示问题 // 确保adviceType为数字类型避免类型不匹配导致的显示问题
adviceType: Number(row.adviceType), adviceType: Number(row.adviceType),
@@ -2852,12 +2865,25 @@ function getGroupMarkers() {
function calculateTotalPrice(row, index) { function calculateTotalPrice(row, index) {
nextTick(() => { nextTick(() => {
if (row.adviceType == 3) { if (row.adviceType == 3) {
// 检查价格是否为有效数字
if (row.unitPrice !== undefined && row.unitPrice !== null && !isNaN(row.unitPrice) && isFinite(row.unitPrice)) {
row.totalPrice = (row.unitPrice * row.quantity).toFixed(6); row.totalPrice = (row.unitPrice * row.quantity).toFixed(6);
} else {
row.totalPrice = '0.000000'; // 或者设置为 0
}
} else { } else {
if (row.unitCode == row.minUnitCode) { if (row.unitCode == row.minUnitCode) {
if (row.minUnitPrice !== undefined && row.minUnitPrice !== null && !isNaN(row.minUnitPrice) && isFinite(row.minUnitPrice)) {
row.totalPrice = (row.minUnitPrice * row.quantity).toFixed(6); row.totalPrice = (row.minUnitPrice * row.quantity).toFixed(6);
} else { } else {
row.totalPrice = '0.000000';
}
} else {
if (row.unitPrice !== undefined && row.unitPrice !== null && !isNaN(row.unitPrice) && isFinite(row.unitPrice)) {
row.totalPrice = (row.unitPrice * row.quantity).toFixed(6); row.totalPrice = (row.unitPrice * row.quantity).toFixed(6);
} else {
row.totalPrice = '0.000000';
}
} }
} }
}); });

View File

@@ -775,6 +775,19 @@ function deletePrescription(prescription) {
const index = tcmPrescriptionList.value.findIndex((p) => p.id === prescription.id); const index = tcmPrescriptionList.value.findIndex((p) => p.id === prescription.id);
if (index !== -1) { if (index !== -1) {
const prescriptionData = tcmPrescriptionList.value[index];
if (prescriptionData.prescriptionList && prescriptionData.prescriptionList.length > 0) {
proxy.$modal.msgWarning('该处方单还有药品,请先删除所有药品后再删除处方单');
return;
}
const hasChargedItems = prescriptionData.prescriptionList && prescriptionData.prescriptionList.some(item => item.statusEnum === 2);
if (hasChargedItems) {
proxy.$modal.msgWarning('该处方单已收费,不能删除');
return;
}
tcmPrescriptionList.value.splice(index, 1); tcmPrescriptionList.value.splice(index, 1);
} }
} }

View File

@@ -321,7 +321,10 @@ getPatientList();
function getPatientList() { function getPatientList() {
queryParams.value.statusEnum = 2; queryParams.value.statusEnum = 2;
getList(queryParams.value).then((res) => { getList(queryParams.value).then((res) => {
// console.log('API返回的完整数据:', res); // 添加这行来查看完整返回
// console.log('API返回的数据记录:', res.data.records); // 查看具体数据记录
patientList.value = res.data.records.map((item) => { patientList.value = res.data.records.map((item) => {
// console.log('处理的单个患者数据:', item); // 查看处理的单个患者数据
return { return {
...item, ...item,
active: currentEncounterId.value ? item.encounterId == currentEncounterId.value : false, active: currentEncounterId.value ? item.encounterId == currentEncounterId.value : false,
@@ -431,7 +434,7 @@ function handleClick(tab) {
// 查看本次就诊处方单从医嘱Tab页获取已开立的处方单信息 // 查看本次就诊处方单从医嘱Tab页获取已开立的处方单信息
function getEnPrescription(encounterId) { function getEnPrescription(encounterId) {
getEnPrescriptionInfo({ encounterId: encounterId }).then((res) => { getEnPrescriptionInfo({ encounterId: encounterId }).then((res) => {
console.log('处方单 res', res); // console.log('处方单 res', res);
let dataArr = res.data.records || []; let dataArr = res.data.records || [];
if (dataArr.length <= 0) { if (dataArr.length <= 0) {
ElMessage({ ElMessage({
@@ -461,6 +464,7 @@ function handleCardClick(item, index) {
patient.active = patient.encounterId === item.encounterId; patient.active = patient.encounterId === item.encounterId;
}); });
patientInfo.value = item; patientInfo.value = item;
// console.log('patientInfo.value.cardNo:', patientInfo.value.cardNo)
// 将患者信息保存到store中供hospitalizationEmr组件使用 // 将患者信息保存到store中供hospitalizationEmr组件使用
updatePatientInfo(item); updatePatientInfo(item);
activeTab.value = 'hospitalizationEmr'; activeTab.value = 'hospitalizationEmr';

View File

@@ -291,10 +291,13 @@ import {advicePrint, getAdjustPriceSwitchState, lotNumberMatch} from '@/api/publ
import {debounce} from 'lodash-es'; import {debounce} from 'lodash-es';
import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue'; import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue';
import {hiprint} from 'vue-plugin-hiprint'; import {hiprint} from 'vue-plugin-hiprint';
// import templateJson from './components/templateJson.json'; import templateJson from './templateJson.json';
// import disposalTemplate from './components/disposalTemplate.json'; import disposalTemplate from './disposalTemplate.json';
import {formatInventory} from '@/utils/his.js'; import {formatInventory} from '@/utils/his.js';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const showSearch = ref(true); const showSearch = ref(true);
const total = ref(0); const total = ref(0);
@@ -444,7 +447,9 @@ async function printPrescription() {
}).then((res) => { }).then((res) => {
if (projectTypeCode.value == '3') { if (projectTypeCode.value == '3') {
const result = res.data; const result = res.data;
const printElements = disposalTemplate; const printElements = JSON.parse(
JSON.stringify(disposalTemplate).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
height: 210, height: 210,
@@ -474,7 +479,9 @@ async function printPrescription() {
prescriptionList: item, prescriptionList: item,
}); });
}); });
const printElements = templateJson; const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
height: 210, height: 210,

View File

@@ -0,0 +1,337 @@
{
"panels": [
{
"index": 0,
"name": 1,
"paperType": "A5",
"height": 210,
"width": 148,
"paperHeader": 0,
"paperFooter": 592.4409448818898,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(184, 184, 184, 0.3)",
"fontSize": "14px",
"rotate": 25,
"width": 200,
"height": 200,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 12,
"width": 420,
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"qrCodeLevel": 0,
"textAlign": "center"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 45,
"height": 9.75,
"width": 420,
"title": "处置单",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,
"qrCodeLevel": 0,
"textAlign": "center"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 22.5,
"top": 67.5,
"height": 9.75,
"width": 120,
"title": "姓名",
"field": "patientName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 157.5,
"top": 67.5,
"height": 9.75,
"width": 120,
"title": "年龄",
"field": "age",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 300,
"top": 67.5,
"height": 9.75,
"width": 120,
"title": "性别",
"field": "genderEnum_enumText",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 22.5,
"top": 94.5,
"height": 9.75,
"width": 96,
"title": "科室",
"field": "departmentName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 156,
"top": 94.5,
"height": 9.75,
"width": 118.5,
"title": "费用性质",
"field": "contractName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 300,
"top": 94.5,
"height": 9.75,
"width": 120,
"title": "日期",
"field": "reqTime",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 22.5,
"top": 121.5,
"height": 9.75,
"width": 123,
"title": "门诊号",
"field": "encounterNo",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 156,
"top": 121.5,
"height": 9.75,
"width": 120,
"title": "开单医生",
"field": "doctorName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 18,
"top": 141,
"height": 9,
"width": 393,
"borderWidth": "1.5",
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 22.5,
"top": 156,
"height": 9.75,
"width": 24,
"title": "Rp",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 22.5,
"top": 177,
"height": 36,
"width": 387,
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false,
"field": "adviceItemList",
"columns": [
[
{
"title": "项目名",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 180.11284430829235,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' 元'; }",
"width": 65.15543233883191,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' ' + row.unitCode_dictText; }",
"width": 61.720533008519084,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "执行次数",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 80.01119034435662,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
}
]
}
]
}

View File

@@ -0,0 +1,339 @@
{
"panels": [
{
"index": 0,
"name": 1,
"paperType": "自定义",
"height": 130,
"width": 210,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(184, 184, 184, 0.3)",
"fontSize": "14px",
"rotate": 25,
"width": 200,
"height": 200,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {},
"paperHeader": 0,
"paperFooter": 841.8897637795277,
"printElements": [
{
"options": {
"left": 252,
"top": 13.5,
"height": 12,
"width": 75,
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 19.5,
"top": 33,
"height": 9.75,
"width": 120,
"title": "日期",
"field": "occurrenceTime",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 222,
"top": 33,
"height": 9.75,
"width": 120,
"title": "单据号",
"field": "busNo",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 465,
"top": 33,
"height": 9.75,
"width": 120,
"title": "机构:{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 19.5,
"top": 57,
"height": 9.75,
"width": 322.5,
"title": "供应商",
"field": "supplierName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 19.5,
"top": 84,
"height": 36,
"width": 570,
"title": "undefined+beforeDragIn",
"field": "purchaseinventoryList",
"coordinateSync": false,
"widthHeightSync": false,
"columns": [
[
{
"title": "项目名",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 53.52877835374887,
"field": "name",
"checked": true,
"columnId": "name",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "规格",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 50.3056193642802,
"field": "volume",
"checked": true,
"columnId": "volume",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "厂家/产地",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 84.09807741407441,
"field": "manufacturerText",
"checked": true,
"columnId": "manufacturerText",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' ' + row.unitCode_dictText; }",
"width": 37.02194283284556,
"field": "itemQuantity",
"checked": true,
"columnId": "itemQuantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "采购单价",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' 元'; }",
"width": 45.05495152300426,
"field": "price",
"checked": true,
"columnId": "price",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "金额",
"titleSync": false,
"align": "right",
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' 元'; }",
"width": 39.04544357631049,
"field": "totalPrice",
"checked": true,
"columnId": "totalPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "仓库",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 40.041954542099724,
"field": "purposeLocationName",
"checked": true,
"columnId": "purposeLocationName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "产品批号",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 47.05067091842349,
"field": "lotNumber",
"checked": true,
"columnId": "lotNumber",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "生产日期",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 63.089377997062755,
"field": "startTime",
"checked": true,
"columnId": "startTime",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "有效期至",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 59.05673483929025,
"field": "endTime",
"checked": true,
"columnId": "endTime",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "发票号",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 51.706448638859854,
"field": "invoiceNo",
"checked": true,
"columnId": "invoiceNo",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 480,
"top": 130.5,
"height": 12,
"width": 109.5,
"title": "合计",
"field": "totalAmount",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"formatter": "function(title,value,options,templateData,target,paperNo){\n return value + ' 元'\n}"
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
]
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -1754,7 +1754,11 @@
</template> </template>
<script setup> <script setup>
import {ref} from 'vue'; import {ref, reactive, getCurrentInstance} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
import formDataJs from '../../doctorstation/components/store/medicalpage'; import formDataJs from '../../doctorstation/components/store/medicalpage';
import {patientInfo} from '../../inpatientDoctor/home/store/patient'; import {patientInfo} from '../../inpatientDoctor/home/store/patient';

View File

@@ -260,17 +260,17 @@ function printReceipt(param) {
...param, ...param,
// 基础支付类型 // 基础支付类型
YB_FUND_PAY: YB_FUND_PAY:
param.detail?.find((t) => t.payEnum === 100000)?.amount.toFixed(2) + ' 元' ?? 0, // 基金支付总额 (param.detail?.find((t) => t.payEnum === 100000)?.amount?.toFixed(2) || '0.00') + ' 元',
SELF_PAY: param.detail?.find((t) => t.payEnum === 200000)?.amount.toFixed(2) + ' 元' ?? 0, // 个人负担总金额 SELF_PAY: (param.detail?.find((t) => t.payEnum === 200000)?.amount?.toFixed(2) || '0.00') + ' 元',
OTHER_PAY: param.detail?.find((t) => t.payEnum === 300000)?.amount ?? 0, // 其他(如医院负担金额) OTHER_PAY: param.detail?.find((t) => t.payEnum === 300000)?.amount ?? 0, // 其他(如医院负担金额)
// 基本医保统筹基金支出 // 基本医保统筹基金支出
YB_TC_FUND_AMOUNT: YB_TC_FUND_AMOUNT:
param.detail?.find((t) => t.payEnum === 110000)?.amount.toFixed(2) + ' 元' ?? 0, // 基本医保统筹基金支出 (param.detail?.find((t) => t.payEnum === 110000)?.amount?.toFixed(2) || '0.00') + ' 元',
YB_BC_FUND_AMOUNT: YB_BC_FUND_AMOUNT:
param.detail?.find((t) => t.payEnum === 120000)?.amount.toFixed(2) + ' 元' ?? 0, // 补充医疗保险基金支出 (param.detail?.find((t) => t.payEnum === 120000)?.amount?.toFixed(2) || '0.00') + ' 元',
YB_JZ_FUND_AMOUNT: YB_JZ_FUND_AMOUNT:
param.detail?.find((t) => t.payEnum === 130000)?.amount.toFixed(2) + ' 元' ?? 0, // 医疗救助基金支出 (param.detail?.find((t) => t.payEnum === 130000)?.amount?.toFixed(2) || '0.00') + ' 元',
// YB_OTHER_AMOUNT: param.detail.find((t) => t.payEnum === 140000)?.amount ?? 0, // 其他支出 // YB_OTHER_AMOUNT: param.detail.find((t) => t.payEnum === 140000)?.amount ?? 0, // 其他支出
// 职工基本医疗保险 // 职工基本医疗保险
@@ -298,17 +298,17 @@ function printReceipt(param) {
// 医保结算返回值 // 医保结算返回值
FULAMT_OWNPAY_AMT: FULAMT_OWNPAY_AMT:
param.detail?.find((t) => t.payEnum === 1)?.amount.toFixed(2) + ' 元' ?? 0, // 全自费金额 (param.detail?.find((t) => t.payEnum === 1)?.amount?.toFixed(2) || '0.00') + ' 元',
// OVERLMT_SELFPAY: param.detail.find((t) => t.payEnum === 3)?.amount ?? 0, // 超限价自费费用 // OVERLMT_SELFPAY: param.detail.find((t) => t.payEnum === 3)?.amount ?? 0, // 超限价自费费用
// PRESELFPAY_AMT: param.detail.find((t) => t.payEnum === 4)?.amount ?? 0, // 先行自付金额 // PRESELFPAY_AMT: param.detail.find((t) => t.payEnum === 4)?.amount ?? 0, // 先行自付金额
INSCP_SCP_AMT: param.detail?.find((t) => t.payEnum === 5)?.amount.toFixed(2) + ' 元' ?? 0, // 符合政策范围金额 INSCP_SCP_AMT: (param.detail?.find((t) => t.payEnum === 5)?.amount?.toFixed(2) || '0.00') + ' 元',
// ACT_PAY_DEDC: param.detail.find((t) => t.payEnum === 6)?.amount ?? 0, // 实际支付起付线 // ACT_PAY_DEDC: param.detail.find((t) => t.payEnum === 6)?.amount ?? 0, // 实际支付起付线
// POOL_PROP_SELFPAY: param.detail.find((t) => t.payEnum === 7)?.amount ?? 0, // 基本医疗保险统筹基金支付比例 // POOL_PROP_SELFPAY: param.detail.find((t) => t.payEnum === 7)?.amount ?? 0, // 基本医疗保险统筹基金支付比例
// BALC: param.detail.find((t) => t.payEnum === 8)?.amount ?? 0, // 余额 // BALC: param.detail.find((t) => t.payEnum === 8)?.amount ?? 0, // 余额
// 特殊支付方式 // 特殊支付方式
SELF_YB_ZH_PAY: SELF_YB_ZH_PAY:
param.detail?.find((t) => t.payEnum === 210000)?.amount.toFixed(2) + ' 元' ?? 0, // 个人医保账户支付 (param.detail?.find((t) => t.payEnum === 210000)?.amount?.toFixed(2) || '0.00') + ' 元',
// SELF_YB_ZH_GJ_VALUE: param.detail.find((t) => t.payEnum === 210100)?.amount ?? 0, // 账户共济支付金额 // SELF_YB_ZH_GJ_VALUE: param.detail.find((t) => t.payEnum === 210100)?.amount ?? 0, // 账户共济支付金额
// SELF_CASH_PAY: param.detail.find((t) => t.payEnum === 220000)?.amount ?? 0, // 个人现金支付金额 // SELF_CASH_PAY: param.detail.find((t) => t.payEnum === 220000)?.amount ?? 0, // 个人现金支付金额
// SELF_VX_PAY: param.detail.find((t) => t.payEnum === 230000)?.amount ?? 0, // 微信支付金额 // SELF_VX_PAY: param.detail.find((t) => t.payEnum === 230000)?.amount ?? 0, // 微信支付金额
@@ -334,14 +334,14 @@ function printReceipt(param) {
// SUPPLEMENTARY_INSURANCE: param.detail.find((t) => t.payEnum === 390300)?.amount ?? 0, // 城乡居民大病补充保险基金 // SUPPLEMENTARY_INSURANCE: param.detail.find((t) => t.payEnum === 390300)?.amount ?? 0, // 城乡居民大病补充保险基金
// HEALTHCARE_PREPAYMENT: param.detail.find((t) => t.payEnum === 360300)?.amount ?? 0, // 保健预支基金 // HEALTHCARE_PREPAYMENT: param.detail.find((t) => t.payEnum === 360300)?.amount ?? 0, // 保健预支基金
Mr_QR_Code: param.regNo, Mr_QR_Code: param.regNo,
sex: props.patientInfo.genderEnum_enumText, sex: props.patientInfo?.genderEnum_enumText || '',
age: props.patientInfo.age, age: props.patientInfo?.age || '',
personType: '职工医保', personType: '职工医保',
fixmedinsName: param.fixmedinsName + '门诊收费明细', fixmedinsName: (param.fixmedinsName || '') + '门诊收费明细',
name: props.patientInfo.patientName, // 姓名 name: props.patientInfo?.patientName || '', // 姓名
gender: props.patientInfo.genderEnum_enumText, // 性别 gender: props.patientInfo?.genderEnum_enumText || '', // 性别
age: props.patientInfo.age, // 年龄 age: props.patientInfo?.age || '', // 年龄
encounterBusNo: props.patientInfo.encounterBusNo, // 病例号 encounterBusNo: props.patientInfo?.encounterBusNo || '', // 病例号
currentDate: currentDate.value, // 收费日期 currentDate: currentDate.value, // 收费日期
chargedItems: props.chargedItems, // 收费项目 chargedItems: props.chargedItems, // 收费项目
totalAmount: props.totalAmount.toFixed(2) + ' 元', // 应收金额 totalAmount: props.totalAmount.toFixed(2) + ' 元', // 应收金额

View File

@@ -498,21 +498,21 @@ function confirmCharge() {
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
chargeItemIds: chargeItemIdList.value, chargeItemIds: chargeItemIdList.value,
}).then((res) => { }).then((res) => {
if (res.code == 200) { if (res.code == 200 && res.data) {
const item = res.data.paymentRecDetailDtoList.find((i) => { const item = res.data.paymentRecDetailDtoList?.find((i) => {
return i.payEnum == 220000; return i.payEnum == 220000;
}); });
totalAmount.value = item?.amount; totalAmount.value = item?.amount ?? 0;
paymentId.value = res.data.id; paymentId.value = res.data.id;
newId.value = res.data.newId; newId.value = res.data.newId;
oldId.value = res.data.oldId; oldId.value = res.data.oldId;
chrgBchnoList.value = res.data.chrgBchnoList; chrgBchnoList.value = res.data.chrgBchnoList;
details.value = res.data.paymentRecDetailDtoList?.filter((item) => { details.value = res.data.paymentRecDetailDtoList?.filter((item) => {
return item.amount > 0; return item.amount > 0;
}); }) || [];
openDialog.value = true; openDialog.value = true;
} else { } else {
proxy.$modal.msgError(res.msg); proxy.$modal.msgError(res?.msg || '预结算失败');
} }
}); });
// console.log(patientInfo) // console.log(patientInfo)
@@ -656,12 +656,12 @@ async function handleReadCard(value) {
ybMdtrtCertType: userCardInfo.psnCertType, ybMdtrtCertType: userCardInfo.psnCertType,
busiCardInfo: userCardInfo.busiCardInfo, busiCardInfo: userCardInfo.busiCardInfo,
}).then((res) => { }).then((res) => {
if (res.code == 200) { if (res.code == 200 && res.data) {
paymentId.value = res.data.id; paymentId.value = res.data.id;
totalAmount.value = res.data.paymentRecDetailDtoList.find( totalAmount.value = res.data.paymentRecDetailDtoList?.find(
(item) => item.payEnum == 220000 (item) => item.payEnum == 220000
).amount; )?.amount ?? 0;
details.value = res.data.paymentRecDetailDtoList; details.value = res.data.paymentRecDetailDtoList || [];
// chrgBchnoList.value = res.data.chrgBchnoList; // chrgBchnoList.value = res.data.chrgBchnoList;
chargeItemIdList.value = selectRows.map((item) => { chargeItemIdList.value = selectRows.map((item) => {
return item.id; return item.id;
@@ -680,7 +680,7 @@ async function handleReadCard(value) {
openDialog.value = true; openDialog.value = true;
} else { } else {
proxy.$modal.msgError(res.msg); proxy.$modal.msgError(res?.msg || '预结算失败');
} }
}); });
} }

View File

@@ -24,7 +24,7 @@
<tr <tr
v-for="(item, index) in tableData" v-for="(item, index) in tableData"
:key="item.id || index" :key="item.id || index"
:class="{ 'editing-row': item.editing }" :class="{ 'editing-row': item.editing, 'success-row': item.success, 'deleting-row': item.deleting }"
> >
<td>{{ index + 1 }}</td> <td>{{ index + 1 }}</td>
<td> <td>
@@ -32,10 +32,8 @@
<input <input
type="text" type="text"
v-model="item.healthInstitution" v-model="item.healthInstitution"
placeholder="请输入卫生机构" disabled
:class="{ 'error-input': item.errors?.healthInstitution }"
> >
<span v-if="item.errors?.healthInstitution" class="error-message">{{ item.errors.healthInstitution }}</span>
</template> </template>
<template v-else> <template v-else>
{{ item.healthInstitution }} {{ item.healthInstitution }}
@@ -55,7 +53,7 @@
type="text" type="text"
v-model="item.lisGroupName" v-model="item.lisGroupName"
placeholder="请输入分组名称" placeholder="请输入分组名称"
:class="{ 'error-input': item.errors?.lisGroupName }" :class="{ 'error-input': item.errors?.lisGroupName, 'focus-target': item.isNew }"
> >
<span v-if="item.errors?.lisGroupName" class="error-message">{{ item.errors.lisGroupName }}</span> <span v-if="item.errors?.lisGroupName" class="error-message">{{ item.errors.lisGroupName }}</span>
</template> </template>
@@ -108,21 +106,26 @@
<!-- 分页区域 --> <!-- 分页区域 -->
<div class="pagination"> <div class="pagination">
<button class="pagination-btn"></button> <button class="pagination-btn" @click="prevPage" :disabled="currentPage <= 1"></button>
<span>1</span> <span>{{ currentPage }}</span>
<button class="pagination-btn"></button> <button class="pagination-btn" @click="nextPage" :disabled="currentPage >= totalPages"></button>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {computed, onMounted, reactive, ref} from 'vue' import {computed, nextTick, onMounted, reactive, ref} from 'vue'
import {addLisGroup, delLisGroup, listLisGroup, updateLisGroup} from '@/api/system/checkType' import {addLisGroup, delLisGroup, listLisGroup, updateLisGroup} from '@/api/system/checkType'
import {getDicts} from "@/api/system/dict/data"; import {getDicts} from "@/api/system/dict/data";
import useUserStore from '@/store/modules/user';
import {ElMessage, ElMessageBox} from 'element-plus';
export default { export default {
name: 'LisGroupMaintain', name: 'LisGroupMaintain',
setup() { setup() {
// 获取用户store
const userStore = useUserStore();
// 加载状态 // 加载状态
const loading = ref(false) const loading = ref(false)
@@ -138,7 +141,6 @@ export default {
bloodTubeOptions.value = response.data.map(item => item.label || item.dictLabel); bloodTubeOptions.value = response.data.map(item => item.label || item.dictLabel);
} }
} catch (error) { } catch (error) {
console.error('获取采血管颜色字典失败:', error);
// 如果字典获取失败,使用默认选项作为备选 // 如果字典获取失败,使用默认选项作为备选
bloodTubeOptions.value = ['黄管', '紫管', '蓝管']; bloodTubeOptions.value = ['黄管', '紫管', '蓝管'];
} }
@@ -154,6 +156,11 @@ export default {
return Math.ceil(totalItems.value / pageSize.value) return Math.ceil(totalItems.value / pageSize.value)
}) })
// 获取当前用户的卫生机构名称
const getCurrentHospital = () => {
return userStore.hospitalName || userStore.orgName || '演示医院';
};
// 表格数据 // 表格数据
const tableData = reactive([]) const tableData = reactive([])
@@ -181,172 +188,58 @@ export default {
pageSize: pageSize.value pageSize: pageSize.value
} }
console.log('准备调用接口,查询参数:', query)
// 调用接口获取数据 // 调用接口获取数据
// 注意根据checkType.js中的定义这个接口使用GET方法和params参数
const response = await listLisGroup(query) const response = await listLisGroup(query)
console.log('接口返回结果类型:', typeof response)
console.log('接口返回结果:', response)
console.log('响应是否包含code字段:', response && 'code' in response)
console.log('响应是否包含data字段:', response && 'data' in response)
// 清空现有数据 // 清空现有数据
tableData.splice(0, tableData.length) tableData.splice(0, tableData.length)
totalItems.value = 0 totalItems.value = 0
// 适配可能的不同响应格式 // 处理明确的数据结构
let items = [] if (response && response.code === 200) {
let total = 0 // 清空现有数据
tableData.splice(0, tableData.length);
// 检查响应是否存在 // 获取最内层的数据结构
if (response) { const innerResponse = response.data;
// 处理标准响应格式 if (innerResponse && innerResponse.code === 200) {
if (typeof response === 'object') { const data = innerResponse.data;
// 检查是否是标准API响应格式 {code, data, msg} if (data && Array.isArray(data.records)) {
if ('code' in response) { // 更新总记录数
console.log('响应包含code字段:', response.code) totalItems.value = data.total || 0;
// 成功状态码处理 // 处理每条记录
if (response.code === 200 || response.code === '200' || response.code === 0) { data.records.forEach((item, index) => {
console.log('响应状态码为成功状态') const formattedItem = {
id: item.id,
healthInstitution: item.hospital,
date: item.date,
lisGroupName: item.groupName,
bloodCollectionTube: item.tube,
remark: item.remark || '',
editing: false
};
// 检查data字段 tableData.push(formattedItem);
if ('data' in response) { });
console.log('响应包含data字段数据类型:', typeof response.data) } else {
totalItems.value = 0;
// 格式1: {data: {rows: [], total: number}}
if (response.data && typeof response.data === 'object') {
// 处理双重嵌套格式 {data: {code, msg, data: []}}
if (response.data.data && (Array.isArray(response.data.data) || typeof response.data.data === 'object')) {
console.log('匹配到格式: 双重嵌套 data.data');
// 如果data.data是数组直接使用
if (Array.isArray(response.data.data)) {
items = response.data.data;
total = items.length;
console.log('双重嵌套格式1: data.data是数组数据量:', items.length);
}
// 如果data.data是对象检查是否有rows字段
else if (Array.isArray(response.data.data.rows)) {
items = response.data.data.rows;
total = response.data.data.total !== undefined ? response.data.data.total : items.length;
console.log('双重嵌套格式2: data.data包含rows数据量:', items.length, ',总数:', total);
}
}
// 标准格式检查
else if (Array.isArray(response.data.rows)) {
items = response.data.rows
total = response.data.total !== undefined ? response.data.total : items.length
console.log('匹配到格式1: response.data.rows数据量:', items.length, ',总数:', total)
}
// 格式2: {data: []}
else if (Array.isArray(response.data)) {
items = response.data
total = items.length
console.log('匹配到格式2: response.data直接是数组数据量:', items.length)
}
// 检查是否有其他可能的格式
else {
console.log('响应data不是预期格式详细数据:', response.data)
// 尝试将整个data对象作为单个条目处理有些API可能直接返回单个对象
if (response.data) {
items = [response.data]
total = 1
console.log('尝试将整个data对象作为单个条目处理')
}
}
} }
} else { } else {
console.log('响应不包含data字段') totalItems.value = 0;
} }
} else { } else {
// 非成功状态码 // 非成功状态码
const errorMsg = response.msg || response.message || `请求失败,状态码: ${response.code}` const errorMsg = response.msg || response.message || `请求失败,状态码: ${response.code}`
console.error('请求失败:', errorMsg)
showMessage(errorMsg, 'error') showMessage(errorMsg, 'error')
} totalItems.value = 0;
}
// 格式3: 响应本身是一个对象但没有code字段可能是单个记录
else if (!Array.isArray(response)) {
console.log('响应是对象但没有code字段尝试作为单个记录处理')
items = [response]
total = 1
}
}
// 格式4: 响应本身是数组
if (Array.isArray(response)) {
items = response
total = items.length
console.log('匹配到格式4: 响应本身是数组,数据量:', items.length)
}
// 记录最终解析到的数据量
console.log('最终解析到的数据量:', items.length, ',总数:', total)
} else {
console.error('接口返回空数据')
showMessage('获取LIS分组数据失败服务器返回空响应', 'error')
}
// 处理获取到的数据
if (items && items.length > 0) {
// 处理每条数据,确保字段正确映射
items.forEach((item, index) => {
// 确保item是对象类型
if (typeof item === 'object' && item !== null) {
console.log(`处理记录 ${index + 1} 原始数据:`, item);
// 创建格式化后的对象,确保字段存在
// 根据网络响应截图更正字段映射关系
const formattedItem = {
id: item.id || `temp_${Date.now()}_${index}`, // 确保有ID
// 更正字段映射hospital -> healthInstitution
healthInstitution: item.hospital || item.org || item.healthInstitution || '',
// 优先使用date字段
date: item.date || item.createTime || item.createDate || '',
// 更正字段映射groupName -> lisGroupName
lisGroupName: item.groupName || item.name || item.lisGroupName || '',
// 更正字段映射tube -> bloodCollectionTube
bloodCollectionTube: item.tube || item.property || item.bloodCollectionTube || item.bloodTube || '',
remark: item.remark || item.note || '',
editing: false,
// 保留原始对象的所有其他属性
...item
};
tableData.push(formattedItem);
console.log(`处理记录 ${index + 1} 格式化后数据:`, {
id: formattedItem.id,
healthInstitution: formattedItem.healthInstitution,
date: formattedItem.date,
lisGroupName: formattedItem.lisGroupName,
bloodCollectionTube: formattedItem.bloodCollectionTube,
remark: formattedItem.remark
});
} else {
console.warn(`记录 ${index + 1} 不是有效对象,跳过处理:`, item);
}
});
totalItems.value = total;
console.log('数据加载完成,表格显示', tableData.length, '条记录,总数:', totalItems.value);
} else {
console.log('未获取到有效数据,表格为空');
} }
} catch (error) { } catch (error) {
// 详细的错误信息记录 // 提取错误信息
console.error('加载LIS分组数据异常:', error)
// 提取更详细的错误信息
let errorMessage = '加载LIS分组数据失败请稍后重试' let errorMessage = '加载LIS分组数据失败请稍后重试'
if (error.response) { if (error.response) {
// 服务器响应了但状态码不是2xx // 服务器响应了但状态码不是2xx
console.error('HTTP错误状态:', error.response.status)
console.error('HTTP错误响应体:', error.response.data)
if (error.response.data) { if (error.response.data) {
errorMessage = error.response.data.msg || errorMessage = error.response.data.msg ||
error.response.data.message || error.response.data.message ||
@@ -355,36 +248,18 @@ export default {
} else { } else {
errorMessage = `服务器错误 (${error.response.status})` errorMessage = `服务器错误 (${error.response.status})`
} }
// 额外记录响应体详情便于调试
if (typeof error.response.data === 'object') {
console.error('响应体详细结构:', JSON.stringify(error.response.data, null, 2))
}
} else if (error.request) { } else if (error.request) {
// 请求已发出,但未收到响应 // 请求已发出,但未收到响应
console.error('请求已发送但未收到响应:', error.request)
errorMessage = '网络错误,服务器未响应,请检查网络连接' errorMessage = '网络错误,服务器未响应,请检查网络连接'
} else if (error.message) { } else if (error.message) {
// 请求配置出错 // 请求配置出错
console.error('请求配置错误:', error.message)
errorMessage = error.message errorMessage = error.message
} else {
console.error('未知错误类型:', typeof error)
} }
// 显示错误信息 // 显示错误信息
showMessage(errorMessage, 'error') showMessage(errorMessage, 'error')
// 确保表格状态正确
console.log('异常处理后表格状态:', {
tableDataLength: tableData.length,
totalItems: totalItems.value,
currentPage: currentPage.value
})
} finally { } finally {
loading.value = false loading.value = false
console.log('数据加载操作完成loading状态已更新为false')
console.log('最终表格数据量:', tableData.length, ',总数:', totalItems.value)
} }
} }
@@ -402,13 +277,6 @@ export default {
// 清除之前的错误信息 // 清除之前的错误信息
delete item.errors delete item.errors
// 验证卫生机构
if (!item.healthInstitution || item.healthInstitution.trim() === '') {
errors.healthInstitution = '卫生机构不能为空'
} else if (item.healthInstitution.length > 100) {
errors.healthInstitution = '卫生机构名称不能超过100个字符'
}
// 验证LIS分组名称 // 验证LIS分组名称
if (!item.lisGroupName || item.lisGroupName.trim() === '') { if (!item.lisGroupName || item.lisGroupName.trim() === '') {
errors.lisGroupName = 'LIS分组名称不能为空' errors.lisGroupName = 'LIS分组名称不能为空'
@@ -436,15 +304,11 @@ export default {
// 显示提示信息 // 显示提示信息
const showMessage = (message, type = 'success') => { const showMessage = (message, type = 'success') => {
// 在实际项目中,这里可以使用Element Plus或其他UI库的消息组件 // 使用Element Plus的消息组件
// 这里为了简单起见使用浏览器的alert但添加了类型标识
if (type === 'error') { if (type === 'error') {
console.error(message) ElMessage.error(message)
alert(`错误: ${message}`)
} else { } else {
console.log(message) ElMessage.success(message)
// 可以使用更友好的提示方式这里暂时注释掉alert以避免频繁弹窗
// alert(message)
} }
} }
@@ -504,17 +368,23 @@ export default {
const isNewRecord = item.isNew const isNewRecord = item.isNew
delete item.isNew delete item.isNew
// 添加保存成功样式,包括新增记录
item.success = true
// 0.5秒后移除成功样式
setTimeout(() => {
item.success = false
}, 500)
showMessage('保存成功') showMessage('保存成功')
// 如果是新增,重新加载数据确保数据一致 // 修改完成后重新请求后端本页数据确保数据准确
if (isNewRecord) { setTimeout(() => {
loadLisGroups() loadLisGroups()
} }, 500)
} else { } else {
showMessage(`保存失败: ${response.msg || '未知错误'}`, 'error') showMessage(`保存失败: ${response.msg || '未知错误'}`, 'error')
} }
} catch (error) { } catch (error) {
console.error('保存LIS分组数据失败:', error)
showMessage(`保存失败: ${error.message || '未知错误'}`, 'error') showMessage(`保存失败: ${error.message || '未知错误'}`, 'error')
} finally { } finally {
loading.value = false loading.value = false
@@ -543,13 +413,12 @@ export default {
item.editing = false item.editing = false
} }
} catch (error) { } catch (error) {
console.error('取消编辑失败:', error)
showMessage('取消编辑失败', 'error') showMessage('取消编辑失败', 'error')
} }
} }
// 添加新行 // 添加新行
const addRow = () => { const addRow = async () => {
// 检查是否已有正在编辑的行 // 检查是否已有正在编辑的行
const editingRow = tableData.find(item => item.editing) const editingRow = tableData.find(item => item.editing)
if (editingRow) { if (editingRow) {
@@ -558,9 +427,11 @@ export default {
} }
const newId = String(Math.max(...tableData.map(item => parseInt(item.id) || 0), 0) + 1) const newId = String(Math.max(...tableData.map(item => parseInt(item.id) || 0), 0) + 1)
// 从用户信息中获取当前卫生机构
const currentHospital = getCurrentHospital()
tableData.push({ tableData.push({
id: newId, id: '',
healthInstitution: '', // 默认 healthInstitution: currentHospital, // 默认当前操作工号的卫生机构
date: getCurrentDate(), // 默认当前系统时间 date: getCurrentDate(), // 默认当前系统时间
lisGroupName: '', lisGroupName: '',
bloodCollectionTube: '', bloodCollectionTube: '',
@@ -570,7 +441,14 @@ export default {
isNew: true // 标记为新记录 isNew: true // 标记为新记录
}) })
showMessage('请填写新记录信息') // 等待DOM更新后聚焦到第一个输入框
await nextTick()
const focusElement = document.querySelector('.focus-target')
if (focusElement) {
focusElement.focus()
// 清除focus-target类避免下次添加行时影响
focusElement.classList.remove('focus-target')
}
} }
// 删除行 // 删除行
@@ -582,10 +460,26 @@ export default {
return return
} }
if (confirm(`确定要删除"${item.lisGroupName}"这条记录吗?`)) {
try { try {
// 使用Element Plus的确认对话框
await ElMessageBox.confirm(
`确定要删除"${item.lisGroupName}"这条记录吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
loading.value = true loading.value = true
// 添加删除行标记,触发渐隐效果
item.deleting = true
// 等待300ms让渐隐效果完成
await new Promise(resolve => setTimeout(resolve, 300))
// 如果是新添加的记录,直接从数组中移除 // 如果是新添加的记录,直接从数组中移除
if (item.isNew) { if (item.isNew) {
tableData.splice(index, 1) tableData.splice(index, 1)
@@ -596,23 +490,37 @@ export default {
const response = await delLisGroup(item.id) const response = await delLisGroup(item.id)
if (response.code === 200) { if (response.code === 200) {
tableData.splice(index, 1)
showMessage('删除成功')
// 更新总数 // 更新总数
totalItems.value-- totalItems.value--
// 检查当前页码是否仍然有效
const newTotalPages = Math.ceil(totalItems.value / pageSize.value)
if (currentPage.value > newTotalPages && newTotalPages > 0) {
// 如果当前页码大于总页数且总页数大于0调整为最后一页
currentPage.value = newTotalPages
}
// 重新加载当前页数据,保持页码不变
loadLisGroups()
showMessage('删除成功')
} else { } else {
showMessage(`删除失败: ${response.msg || '未知错误'}`, 'error') showMessage(`删除失败: ${response.msg || '未知错误'}`, 'error')
// 如果删除失败,移除删除标记
item.deleting = false
} }
} }
} catch (error) { } catch (error) {
console.error('删除LIS分组数据失败:', error) if (error !== 'cancel') {
showMessage(`删除失败: ${error.message || '未知错误'}`, 'error') showMessage(`删除失败: ${error.message || '未知错误'}`, 'error')
// 如果删除失败,移除删除标记
if (tableData[index]) {
tableData[index].deleting = false
}
}
} finally { } finally {
loading.value = false loading.value = false
} }
} }
}
// 分页方法 // 分页方法
const prevPage = () => { const prevPage = () => {
@@ -680,7 +588,7 @@ export default {
align-items: center; align-items: center;
} }
.header h1 { .header h2 {
font-size: 18px; font-size: 18px;
font-weight: 500; font-weight: 500;
color: #333; color: #333;
@@ -718,6 +626,12 @@ export default {
background-color: #f0f8ff; background-color: #f0f8ff;
} }
/* 保存成功行样式 */
.success-row {
background-color: #f6ffed !important;
transition: background-color 0.3s ease;
}
.actions { .actions {
white-space: nowrap; white-space: nowrap;
} }
@@ -732,7 +646,7 @@ export default {
/* 分页样式 */ /* 分页样式 */
.pagination { .pagination {
display: flex; display: flex;
justify-content: flex-end; justify-content: center;
align-items: center; align-items: center;
padding: 16px 0; padding: 16px 0;
margin-top: 8px; margin-top: 8px;
@@ -741,6 +655,14 @@ export default {
.pagination span { .pagination span {
font-size: 14px; font-size: 14px;
margin: 0 16px; margin: 0 16px;
background-color: #1890FF;
color: white;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
} }
.pagination-btn { .pagination-btn {
@@ -957,14 +879,15 @@ input:disabled {
.data-table { .data-table {
font-size: 12px; font-size: 12px;
min-width: 600px; /* 确保表格在小屏幕上可以横向滚动 */ border-collapse: collapse;
min-width: 768px; /* 确保表格在小屏幕上可以横向滚动 */
} }
.data-table th, .data-table th,
.data-table td { .data-table td {
padding: 8px; padding: 8px;
text-align: center; text-align: center;
} }
.btn { .btn {
width: 20px; width: 20px;
@@ -984,7 +907,16 @@ input:disabled {
/* 平滑过渡效果 */ /* 平滑过渡效果 */
.btn, .btn,
.pagination-btn, .pagination-btn,
.editing-row { .editing-row,
tr {
transition: all 0.3s ease;
}
/* 删除行渐隐效果 */
.deleting-row {
opacity: 0;
height: 0;
overflow: hidden;
transition: all 0.3s ease; transition: all 0.3s ease;
} }

View File

@@ -1046,7 +1046,9 @@ function handlePrint() {
purchaseinventoryList: form.purchaseinventoryList, purchaseinventoryList: form.purchaseinventoryList,
}); });
console.log(result, '345678987654'); console.log(result, '345678987654');
const printElements = templateJson; const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
// printer: 'EPSON LQ-80KFII', // printer: 'EPSON LQ-80KFII',

View File

@@ -244,7 +244,9 @@ function handlePrint() {
purposeLocationName: printList[0].purposeLocationName, purposeLocationName: printList[0].purposeLocationName,
purchaseinventoryList: printList, purchaseinventoryList: printList,
}); });
const printElements = templateJson; const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
// printer: 'EPSON LQ-80KFII', // printer: 'EPSON LQ-80KFII',

View File

@@ -1436,8 +1436,11 @@ function handlePrintPreview() {
console.log("打印数据:", printData); console.log("打印数据:", printData);
// 创建打印模板实例 // 创建打印模板实例
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userUserStore.hospitalName)
);
const hiprintTemplate = new hiprint.PrintTemplate({ const hiprintTemplate = new hiprint.PrintTemplate({
template: templateJson, template: printElements,
}); });
// 打开预览窗口(推荐) // 打开预览窗口(推荐)
@@ -1515,8 +1518,11 @@ function handleDirectPrint() {
console.log("打印数据:", printData); console.log("打印数据:", printData);
// 创建打印模板实例 // 创建打印模板实例
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userUserStore.hospitalName)
);
const hiprintTemplate = new hiprint.PrintTemplate({ const hiprintTemplate = new hiprint.PrintTemplate({
template: templateJson, template: printElements,
}); });
// 直接打印到指定打印机 // 直接打印到指定打印机
@@ -1591,8 +1597,11 @@ function handlePrintWithSelectPrinter() {
console.log("打印数据:", printData); console.log("打印数据:", printData);
// 创建打印模板实例 // 创建打印模板实例
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userUserStore.hospitalName)
);
const hiprintTemplate = new hiprint.PrintTemplate({ const hiprintTemplate = new hiprint.PrintTemplate({
template: templateJson, template: printElements,
}); });
// 获取打印机列表并选择 // 获取打印机列表并选择

View File

@@ -243,7 +243,9 @@ function handlePrint() {
busNo: printList[0].busNo, busNo: printList[0].busNo,
detailsList: printList, detailsList: printList,
}); });
const printElements = templateJson; const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
title: '打印标题', title: '打印标题',

View File

@@ -696,7 +696,10 @@ async function printPrescription() {
console.log('药房result', result); console.log('药房result', result);
// 根据药品分类选择对应的打印模板 // 根据药品分类选择对应的打印模板
const printElements = tcmFlag.value === '1' ? chineseMedicineTemplateJson : templateJson; const template = tcmFlag.value === '1' ? chineseMedicineTemplateJson : templateJson;
const printElements = JSON.parse(
JSON.stringify(template).replace(/{{HOSPITAL_NAME}}/g, proxy.$store.useUserStore().hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({template: printElements}); // 定义模板 var hiprintTemplate = new hiprint.PrintTemplate({template: printElements}); // 定义模板
hiprintTemplate.print2(result, { hiprintTemplate.print2(result, {
height: 210, height: 210,

View File

@@ -1,112 +0,0 @@
<template>
<div class="app-container">
<h4 class="form-header h4">基本信息</h4>
<el-form :model="form" label-width="80px">
<el-row>
<el-col :span="8" :offset="2">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" disabled />
</el-form-item>
</el-col>
<el-col :span="8" :offset="2">
<el-form-item label="登录账号" prop="userName">
<el-input v-model="form.userName" disabled />
</el-form-item>
</el-col>
</el-row>
</el-form>
<h4 class="form-header h4">角色信息</h4>
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
<el-table-column label="序号" width="55" type="index" align="center">
<template #default="scope">
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
<el-table-column label="角色编号" align="center" prop="roleId" />
<el-table-column label="角色名称" align="center" prop="roleName" />
<el-table-column label="权限字符" align="center" prop="roleKey" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
<el-form label-width="100px">
<div style="text-align: center;margin-left:-120px;margin-top:30px;">
<el-button type="primary" @click="submitForm()">提交</el-button>
<el-button @click="close()">返回</el-button>
</div>
</el-form>
</div>
</template>
<script setup name="AuthRole">
import {getAuthRole, updateAuthRole} from "@/api/system/user copy";
const route = useRoute();
const { proxy } = getCurrentInstance();
const loading = ref(true);
const total = ref(0);
const pageNum = ref(1);
const pageSize = ref(10);
const roleIds = ref([]);
const roles = ref([]);
const form = ref({
nickName: undefined,
userName: undefined,
userId: undefined
});
/** 单击选中行数据 */
function clickRow(row) {
proxy.$refs["roleRef"].toggleRowSelection(row);
};
/** 多选框选中数据 */
function handleSelectionChange(selection) {
roleIds.value = selection.map(item => item.roleId);
};
/** 保存选中的数据编号 */
function getRowKey(row) {
return row.roleId;
};
/** 关闭按钮 */
function close() {
const obj = { path: "/system/user" };
proxy.$tab.closeOpenPage(obj);
};
/** 提交按钮 */
function submitForm() {
const userId = form.value.userId;
const rIds = roleIds.value.join(",");
updateAuthRole({ userId: userId, roleIds: rIds }).then(response => {
proxy.$modal.msgSuccess("授权成功");
close();
});
};
(() => {
const userId = route.params && route.params.userId;
if (userId) {
loading.value = true;
getAuthRole(userId).then(response => {
form.value = response.user;
roles.value = response.roles;
total.value = roles.value.length;
nextTick(() => {
roles.value.forEach(row => {
if (row.flag) {
proxy.$refs["roleRef"].toggleRowSelection(row);
}
});
});
loading.value = false;
});
}
})();
</script>

View File

@@ -1,621 +0,0 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--部门数据-->
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
prefix-icon="Search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container">
<el-tree
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="deptTreeRef"
node-key="id"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-col>
<!--用户数据-->
<el-col :span="20" :xs="24">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryParams.userName"
placeholder="请输入用户名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input
v-model="queryParams.phonenumber"
placeholder="请输入手机号码"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="用户状态"
clearable
style="width: 240px"
>
<el-option
v-for="dict in sys_normal_disable"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:user:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:user:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:user:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Upload"
@click="handleImport"
v-hasPermi="['system:user:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:user:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-value="0"
inactive-value="1"
@change="handleStatusChange(scope.row)"
></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
</el-tooltip>
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1">
<el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
</el-tooltip>
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1">
<el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
<!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
<el-form :model="form" :rules="rules" ref="userRef" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
:data="deptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择归属部门"
check-strictly
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option
v-for="dict in sys_user_sex"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_normal_disable"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:label="item.postName"
:value="item.postId"
:disabled="item.status == 1"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="角色">
<el-select v-model="form.roleIds" multiple placeholder="请选择">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
:disabled="item.status == 1"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="User">
import {getToken} from "@/utils/auth";
import {
addUser,
changeUserStatus,
delUser,
deptTreeSelect,
getUser,
listUser,
resetUserPwd,
updateUser
} from "@/api/system/user copy";
const router = useRouter();
const { proxy } = getCurrentInstance();
const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex");
const userList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const dateRange = ref([]);
const deptName = ref("");
const deptOptions = ref(undefined);
const initPassword = ref(undefined);
const postOptions = ref([]);
const roleOptions = ref([]);
/*** 用户导入参数 */
const upload = reactive({
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
});
// 列显隐信息
const columns = ref([
{ key: 0, label: `用户编号`, visible: true },
{ key: 1, label: `用户名称`, visible: true },
{ key: 2, label: `用户昵称`, visible: true },
{ key: 3, label: `部门`, visible: true },
{ key: 4, label: `手机号码`, visible: true },
{ key: 5, label: `状态`, visible: true },
{ key: 6, label: `创建时间`, visible: true }
]);
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
userName: undefined,
phonenumber: undefined,
status: undefined,
deptId: undefined
},
rules: {
userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }],
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watch(deptName, val => {
proxy.$refs["deptTreeRef"].filter(val);
});
/** 查询部门下拉树结构 */
function getDeptTree() {
deptTreeSelect().then(response => {
deptOptions.value = response.data;
});
};
/** 查询用户列表 */
function getList() {
loading.value = true;
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
loading.value = false;
userList.value = res.rows;
total.value = res.total;
});
};
/** 节点单击事件 */
function handleNodeClick(data) {
queryParams.value.deptId = data.id;
handleQuery();
};
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
queryParams.value.deptId = undefined;
proxy.$refs.deptTreeRef.setCurrentKey(null);
handleQuery();
};
/** 删除按钮操作 */
function handleDelete(row) {
const userIds = row.userId || ids.value;
proxy.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function () {
return delUser(userIds);
}).then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
}).catch(() => {});
};
/** 导出按钮操作 */
function handleExport() {
proxy.download("system/user/export", {
...queryParams.value,
},`user_${new Date().getTime()}.xlsx`);
};
/** 用户状态修改 */
function handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用";
proxy.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
return changeUserStatus(row.userId, row.status);
}).then(() => {
proxy.$modal.msgSuccess(text + "成功");
}).catch(function () {
row.status = row.status === "0" ? "1" : "0";
});
};
/** 更多操作 */
function handleCommand(command, row) {
switch (command) {
case "handleResetPwd":
handleResetPwd(row);
break;
case "handleAuthRole":
handleAuthRole(row);
break;
default:
break;
}
};
/** 跳转角色分配 */
function handleAuthRole(row) {
const userId = row.userId;
router.push("/system/user-auth/role/" + userId);
};
/** 重置密码按钮操作 */
function handleResetPwd(row) {
proxy.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return "不能包含非法字符:< > \" ' \\\ |"
}
},
}).then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
proxy.$modal.msgSuccess("修改成功,新密码是:" + value);
});
}).catch(() => {});
};
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.userId);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 导入按钮操作 */
function handleImport() {
upload.title = "用户导入";
upload.open = true;
};
/** 下载模板操作 */
function importTemplate() {
proxy.download("system/user/importTemplate", {
}, `user_template_${new Date().getTime()}.xlsx`);
};
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response, file, fileList) => {
upload.open = false;
upload.isUploading = false;
proxy.$refs["uploadRef"].handleRemove(file);
proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
getList();
};
/** 提交上传文件 */
function submitFileForm() {
proxy.$refs["uploadRef"].submit();
};
/** 重置操作表单 */
function reset() {
form.value = {
userId: undefined,
deptId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: "0",
remark: undefined,
postIds: [],
roleIds: []
};
proxy.resetForm("userRef");
};
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
};
/** 新增按钮操作 */
function handleAdd() {
reset();
getUser().then(response => {
postOptions.value = response.posts;
roleOptions.value = response.roles;
open.value = true;
title.value = "添加用户";
form.value.password = initPassword.value;
});
};
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const userId = row.userId || ids.value;
getUser(userId).then(response => {
form.value = response.data;
postOptions.value = response.posts;
roleOptions.value = response.roles;
form.value.postIds = response.postIds;
form.value.roleIds = response.roleIds;
open.value = true;
title.value = "修改用户";
form.password = "";
});
};
/** 提交按钮 */
function submitForm() {
proxy.$refs["userRef"].validate(valid => {
if (valid) {
if (form.value.userId != undefined) {
updateUser(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addUser(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
};
getDeptTree();
getList();
</script>

View File

@@ -1,87 +0,0 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<el-col :span="6" :xs="24">
<el-card class="box-card">
<template v-slot:header>
<div class="clearfix">
<span>个人信息</span>
</div>
</template>
<div>
<div class="text-center">
<userAvatar />
</div>
<ul class="list-group list-group-striped">
<li class="list-group-item">
<svg-icon icon-class="user" />用户名称
<div class="pull-right">{{ state.user.userName }}</div>
</li>
<li class="list-group-item">
<svg-icon icon-class="phone" />手机号码
<div class="pull-right">{{ state.user.phonenumber }}</div>
</li>
<li class="list-group-item">
<svg-icon icon-class="email" />用户邮箱
<div class="pull-right">{{ state.user.email }}</div>
</li>
<li class="list-group-item">
<svg-icon icon-class="tree" />所属部门
<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
</li>
<li class="list-group-item">
<svg-icon icon-class="peoples" />所属角色
<div class="pull-right">{{ state.roleGroup }}</div>
</li>
<li class="list-group-item">
<svg-icon icon-class="date" />创建日期
<div class="pull-right">{{ state.user.createTime }}</div>
</li>
</ul>
</div>
</el-card>
</el-col>
<el-col :span="18" :xs="24">
<el-card>
<template v-slot:header>
<div class="clearfix">
<span>基本资料</span>
</div>
</template>
<el-tabs v-model="activeTab">
<el-tab-pane label="基本资料" name="userinfo">
<userInfo :user="state.user" />
</el-tab-pane>
<el-tab-pane label="修改密码" name="resetPwd">
<resetPwd />
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="Profile">
import userAvatar from "./userAvatar";
import userInfo from "./userInfo";
import resetPwd from "./resetPwd";
import {getUserProfile} from "@/api/system/user copy";
const activeTab = ref("userinfo");
const state = reactive({
user: {},
roleGroup: {},
postGroup: {}
});
function getUser() {
getUserProfile().then(response => {
state.user = response.data;
state.roleGroup = response.roleGroup;
state.postGroup = response.postGroup;
});
};
getUser();
</script>

View File

@@ -1,65 +0,0 @@
<!--
* @Author: 徐靖博 xujb@ccjb.com.cn
* @Date: 2025-03-26 10:49:30
* @LastEditTime: 2025-03-26 13:57:02
* @LastEditors: 徐靖博 xujb@ccjb.com.cn
* @Description:
* @FilePath: \openhis-ui-vue3\src\views\system\user copy\profile\resetPwd.vue
-->
<template>
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>
<el-button type="danger" @click="close">关闭</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import {updateUserPwd} from "@/api/system/user copy";
const { proxy } = getCurrentInstance();
const user = reactive({
oldPassword: undefined,
newPassword: undefined,
confirmPassword: undefined
});
const equalToPassword = (rule, value, callback) => {
if (user.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
});
/** 提交按钮 */
function submit() {
proxy.$refs.pwdRef.validate(valid => {
if (valid) {
updateUserPwd(user.oldPassword, user.newPassword).then(response => {
proxy.$modal.msgSuccess("修改成功");
});
}
});
};
/** 关闭按钮 */
function close() {
proxy.$tab.closePage();
};
</script>

View File

@@ -1,171 +0,0 @@
<template>
<div class="user-info-head" @click="editCropper()">
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
:outputType="options.outputType"
@realTime="realTime"
v-if="visible"
/>
</el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="options.previews.url" :style="options.previews.img" />
</div>
</el-col>
</el-row>
<br />
<el-row>
<el-col :lg="2" :md="2">
<el-upload
action="#"
:http-request="requestUpload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<el-button>
选择
<el-icon class="el-icon--right"><Upload /></el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="uploadImg()"> </el-button>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script setup>
import "vue-cropper/dist/index.css";
import {VueCropper} from "vue-cropper";
import {uploadAvatar} from "@/api/system/user copy";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const open = ref(false);
const visible = ref(false);
const title = ref("修改头像");
//图片裁剪数据
const options = reactive({
img: userStore.avatar, // 裁剪图片的地址
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
outputType: "png", // 默认生成截图为PNG格式
filename: 'avatar', // 文件名称
previews: {} //预览数据
});
/** 编辑头像 */
function editCropper() {
open.value = true;
}
/** 打开弹出层结束时的回调 */
function modalOpened() {
visible.value = true;
}
/** 覆盖默认上传行为 */
function requestUpload() {}
/** 向左旋转 */
function rotateLeft() {
proxy.$refs.cropper.rotateLeft();
}
/** 向右旋转 */
function rotateRight() {
proxy.$refs.cropper.rotateRight();
}
/** 图片缩放 */
function changeScale(num) {
num = num || 1;
proxy.$refs.cropper.changeScale(num);
}
/** 上传预处理 */
function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
proxy.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。");
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
options.filename = file.name;
};
}
}
/** 上传图片 */
function uploadImg() {
proxy.$refs.cropper.getCropBlob(data => {
let formData = new FormData();
formData.append("avatarfile", data, options.filename);
uploadAvatar(formData).then(response => {
open.value = false;
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
userStore.avatar = options.img;
proxy.$modal.msgSuccess("修改成功");
visible.value = false;
});
});
}
/** 实时预览 */
function realTime(data) {
options.previews = data;
}
/** 关闭窗口 */
function closeDialog() {
options.img = userStore.avatar;
options.visible = false;
}
</script>
<style lang='scss' scoped>
.user-info-head {
position: relative;
display: inline-block;
height: 120px;
}
.user-info-head:hover:after {
content: "+";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #eee;
background: rgba(0, 0, 0, 0.5);
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
line-height: 110px;
border-radius: 50%;
}
</style>

View File

@@ -1,67 +0,0 @@
<template>
<el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" maxlength="30" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" maxlength="50" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.sex">
<el-radio label="0"></el-radio>
<el-radio label="1"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>
<el-button type="danger" @click="close">关闭</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import {updateUserProfile} from "@/api/system/user copy";
const props = defineProps({
user: {
type: Object
}
});
const { proxy } = getCurrentInstance();
const form = ref({});
const rules = ref({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
});
/** 提交按钮 */
function submit() {
proxy.$refs.userRef.validate(valid => {
if (valid) {
updateUserProfile(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
props.user.phonenumber = form.value.phonenumber;
props.user.email = form.value.email;
});
}
});
};
/** 关闭按钮 */
function close() {
proxy.$tab.closePage();
};
// 回显当前登录用户信息
watch(() => props.user, user => {
if (user) {
form.value = { nickName: user.nickName, phonenumber: user.phonenumber, email: user.email, sex: user.sex };
}
},{ immediate: true });
</script>

View File

@@ -31,13 +31,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<template v-for="(group, doctorName) in groupedPatients" :key="doctorName"> <template v-for="doctorName in paginatedDoctors" :key="doctorName">
<template v-if="groupedPatients[doctorName]">
<!-- 医生分组标题 --> <!-- 医生分组标题 -->
<tr class="doctor-header"> <tr class="doctor-header">
<td colspan="4">{{ doctorName }} 医生 (诊室: {{ getDoctorRoom(doctorName) }})</td> <td colspan="4">{{ doctorName }} 医生 (诊室: {{ getDoctorRoom(doctorName) }})</td>
</tr> </tr>
<!-- 患者列表 --> <!-- 患者列表 -->
<tr v-for="(patient, index) in group" :key="patient.id"> <tr v-for="(patient, index) in groupedPatients[doctorName]" :key="patient.id">
<td>{{ index + 1 }}</td> <td>{{ index + 1 }}</td>
<td>{{ formatPatientName(patient.name) }}</td> <td>{{ formatPatientName(patient.name) }}</td>
<td>{{ getDoctorRoom(doctorName) }}</td> <td>{{ getDoctorRoom(doctorName) }}</td>
@@ -46,6 +47,7 @@
</td> </td>
</tr> </tr>
</template> </template>
</template>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -126,9 +128,29 @@ const waitingCount = computed(() => {
return count return count
}) })
// 获取排序后的医生列表
const sortedDoctors = computed(() => {
return Object.keys(groupedPatients.value).sort()
})
// 按医生分组的分页逻辑
const paginatedDoctors = computed(() => {
const startIndex = (currentPage.value - 1) * 1 // 每页显示1个医生组
const endIndex = startIndex + 1
return sortedDoctors.value.slice(startIndex, endIndex)
})
// 获取当前页的患者
const currentPatients = computed(() => {
const result = {}
paginatedDoctors.value.forEach(doctor => {
result[doctor] = groupedPatients.value[doctor]
})
return result
})
const totalPages = computed(() => { const totalPages = computed(() => {
const totalPatients = patients.value.length return Math.ceil(sortedDoctors.value.length) || 1
return Math.ceil(totalPatients / patientsPerPage) || 1
}) })
// 方法 // 方法

View File

@@ -0,0 +1,378 @@
<template>
<div class="cardiology-queue">
<div class="app-container">
<el-card>
<template #header>
<div class="card-header">
<span class="title">心内科分诊排队管理</span>
<div class="header-actions">
<el-button type="primary" @click="refreshData" :loading="loading">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
</div>
</template>
<!-- 当前呼叫区 -->
<div class="current-call-section">
<div class="call-box">
<div class="call-text">
<span class="highlight">{{ currentCall?.number || '-' }}</span>
<span class="highlight">{{ currentCall?.name || '-' }}</span>
<span class="highlight">{{ currentCall?.room || '-' }}</span> 诊室就诊
</div>
</div>
</div>
<!-- 候诊队列 -->
<div class="queue-section">
<h3 class="section-title">候诊队列</h3>
<el-table
:data="queueList"
v-loading="loading"
stripe
border
style="width: 100%"
:default-sort="{ prop: 'displayOrder', order: 'ascending' }"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientName" label="患者姓名" width="120" align="center">
<template #default="scope">
{{ formatPatientName(scope.row.patientName) }}
</template>
</el-table-column>
<el-table-column prop="practitionerName" label="医生" width="120" align="center" />
<el-table-column prop="room" label="诊室" width="100" align="center" />
<el-table-column prop="displayOrder" label="流水号" width="120" align="center">
<template #default="scope">
{{ formatSerialNo(scope.row) }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="registerTime" label="挂号时间" width="180" align="center">
<template #default="scope">
{{ formatTime(scope.row.registerTime) }}
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
type="primary"
size="small"
@click="handleCall(scope.row)"
:disabled="scope.row.status === 'CALLED'"
>
叫号
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="queryParams.pageNo"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import { parseTime } from '@/utils/his'
import { getOutpatientRegistrationCurrent } from '@/views/charge/outpatientregistration/components/outpatientregistration'
// 响应式数据
const loading = ref(false)
const queueList = ref([])
const total = ref(0)
const currentCall = ref({
number: '-',
name: '-',
room: '-'
})
// 查询参数
const queryParams = reactive({
pageNo: 1,
pageSize: 20,
searchKey: '',
organizationId: null // 心内科科室ID可以从路由参数或配置中获取
})
// 计算流水号直接使用挂号记录表的主键IDencounterId
function formatSerialNo(row) {
if (!row) return '-'
// 直接使用主键ID作为流水号
if (row.encounterId != null && row.encounterId !== undefined) {
return String(row.encounterId)
} else if (row.id != null && row.id !== undefined) {
// 兼容其他可能的ID字段名
return String(row.id)
} else {
return '-'
}
}
// 格式化患者姓名(脱敏)
function formatPatientName(name) {
if (!name || typeof name !== 'string') return '-'
if (name.length <= 2) return name.charAt(0) + '*'
return name.charAt(0) + '*' + name.slice(-1)
}
// 格式化时间
function formatTime(time) {
if (!time) return '-'
return parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}')
}
// 获取状态类型
function getStatusType(status) {
const statusMap = {
'WAITING': 'info',
'CALLED': 'warning',
'IN_PROGRESS': 'success',
'COMPLETED': ''
}
return statusMap[status] || ''
}
// 获取状态文本
function getStatusText(status) {
const statusMap = {
'WAITING': '等待',
'CALLED': '已叫号',
'IN_PROGRESS': '就诊中',
'COMPLETED': '已完成'
}
return statusMap[status] || '未知'
}
// 获取队列数据
async function fetchQueueData() {
try {
loading.value = true
// 调用API获取当日就诊数据
const response = await getOutpatientRegistrationCurrent({
searchKey: queryParams.searchKey,
pageNo: queryParams.pageNo,
pageSize: queryParams.pageSize
})
if (response && response.code === 200) {
// 获取记录数据
const records = response.data?.records || response.data || []
// 过滤心内科的数据如果有organizationId
let filteredData = records
if (queryParams.organizationId) {
filteredData = records.filter(item => item.organizationId === queryParams.organizationId)
}
// 转换为队列数据格式
queueList.value = filteredData.map(item => ({
id: item.encounterId,
patientName: item.patientName,
practitionerName: item.practitionerName || '-',
organizationName: item.organizationName,
room: item.organizationName || '-', // 使用科室名称作为诊室
displayOrder: item.displayOrder,
registerTime: item.registerTime,
status: 'WAITING' // 默认状态,实际应该从后端获取
}))
total.value = response.data?.total || filteredData.length || 0
} else {
queueList.value = []
total.value = 0
if (response && response.msg) {
ElMessage.warning('获取数据失败: ' + response.msg)
}
}
} catch (error) {
console.error('获取队列数据失败:', error)
ElMessage.error('获取队列数据失败: ' + (error.message || '未知错误'))
queueList.value = []
total.value = 0
} finally {
loading.value = false
}
}
// 刷新数据
function refreshData() {
queryParams.pageNo = 1
fetchQueueData()
}
// 叫号
async function handleCall(row) {
try {
await ElMessageBox.confirm(
`确认叫号:${formatPatientName(row.patientName)}${formatSerialNo(row)}`,
'确认叫号',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
)
// TODO: 调用叫号API
// await callPatient(row.id)
// 更新当前呼叫信息
currentCall.value = {
number: row.displayOrder || '-',
name: formatPatientName(row.patientName),
room: row.room
}
// 更新状态
row.status = 'CALLED'
ElMessage.success('叫号成功')
// 刷新数据
await fetchQueueData()
} catch (error) {
if (error !== 'cancel') {
console.error('叫号失败:', error)
ElMessage.error('叫号失败: ' + (error.message || '未知错误'))
}
}
}
// 分页大小改变
function handleSizeChange(val) {
queryParams.pageSize = val
queryParams.pageNo = 1
fetchQueueData()
}
// 页码改变
function handlePageChange(val) {
queryParams.pageNo = val
fetchQueueData()
}
// 组件挂载时获取数据
onMounted(() => {
fetchQueueData()
// 定时刷新数据每30秒
const refreshInterval = setInterval(() => {
fetchQueueData()
}, 30000)
// 组件卸载时清理定时器
return () => {
clearInterval(refreshInterval)
}
})
</script>
<style lang="scss" scoped>
.cardiology-queue {
.app-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.header-actions {
display: flex;
gap: 10px;
}
}
.current-call-section {
margin: 20px 0;
text-align: center;
.call-box {
display: inline-block;
padding: 20px 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
animation: pulse 2s infinite;
.call-text {
font-size: 24px;
font-weight: bold;
color: white;
letter-spacing: 2px;
.highlight {
color: #ffd700;
font-size: 28px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
}
}
}
.queue-section {
margin-top: 20px;
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
padding-left: 10px;
border-left: 4px solid #667eea;
}
.pagination-wrapper {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
}
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.4);
}
70% {
box-shadow: 0 0 0 15px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
}
}
</style>

View File

@@ -0,0 +1,212 @@
-- 根据流水号查询病人信息
--
-- 流水号格式YYYYMMDD-XXX例如20260109-001
-- - 前面8位是日期YYYYMMDD
-- - 后面3位是序号XXX对应数据库的 display_order
--
-- 说明:
-- display_order 是按"科室+医生+当天"自增的,所以同一天不同科室/不同医生可能有相同的序号
-- 如果只知道序号(如 001建议配合科室、医生、日期范围一起查询
-- ========================================
-- 方案1知道完整流水号20260109-001
-- ========================================
-- 参数说明:
-- :serial_no = '20260109-001' -- 完整流水号
--
-- 解析逻辑:
-- 日期20260109 -> 2026-01-09
-- 序号001 -> 1
WITH parsed AS (
SELECT
-- 提取日期部分前8位并转换为日期格式
TO_DATE(SUBSTRING(:serial_no FROM 1 FOR 8), 'YYYYMMDD') AS target_date,
-- 提取序号部分最后3位并转换为整数
CAST(SUBSTRING(:serial_no FROM 10) AS INTEGER) AS target_order
)
SELECT
e.id AS ID,
e.bus_no AS ,
DATE(e.start_time) AS ,
e.start_time AS ,
e.organization_id AS ID,
e.registrar_id AS ID,
e.display_order AS ,
-- 计算完整流水号(用于验证)
TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0') AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS ,
p.gender AS ,
p.birth_date AS ,
p.phone AS ,
e.status_enum AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
CROSS JOIN parsed
WHERE e.delete_flag = '0'
AND DATE(e.start_time) = parsed.target_date
AND e.display_order = parsed.target_order
ORDER BY e.start_time DESC
LIMIT 10;
-- ========================================
-- 方案2只知道序号001配合其他条件查询
-- ========================================
-- 参数说明:
-- :order_no = '001' -- 序号(不带日期)
-- :start_date = '2026-01-01' -- 开始日期(可选,缩小范围)
-- :end_date = '2026-01-31' -- 结束日期(可选,缩小范围)
-- :organization_id = 123 -- 科室ID可选精确匹配
-- :registrar_id = 456 -- 医生ID可选精确匹配
-- :patient_name = '张三' -- 患者姓名(可选,模糊匹配)
SELECT
e.id AS ID,
e.bus_no AS ,
DATE(e.start_time) AS ,
e.start_time AS ,
TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0') AS ,
e.organization_id AS ID,
e.registrar_id AS ID,
e.display_order AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS ,
p.phone AS ,
e.status_enum AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
WHERE e.delete_flag = '0'
AND e.display_order = CAST(:order_no AS INTEGER) -- 序号匹配
-- 以下条件可选,去掉注释并根据需要填入值
-- AND DATE(e.start_time) >= CAST(:start_date AS DATE) -- 日期范围:开始
-- AND DATE(e.start_time) <= CAST(:end_date AS DATE) -- 日期范围:结束
-- AND e.organization_id = CAST(:organization_id AS BIGINT) -- 科室匹配
-- AND e.registrar_id = CAST(:registrar_id AS BIGINT) -- 医生匹配
-- AND p.name LIKE '%' || :patient_name || '%' -- 患者姓名模糊匹配
ORDER BY e.start_time DESC
LIMIT 50;
-- ========================================
-- 方案3按患者姓名 + 大概日期范围查询(最常用)
-- ========================================
-- 参数说明:
-- :patient_name = '张三' -- 患者姓名(模糊匹配)
-- :start_date = '2026-01-01' -- 开始日期
-- :end_date = '2026-01-31' -- 结束日期
SELECT
e.id AS ID,
e.bus_no AS ,
DATE(e.start_time) AS ,
e.start_time AS ,
TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0') AS ,
e.organization_id AS ID,
e.registrar_id AS ID,
e.display_order AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS ,
p.phone AS ,
e.status_enum AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
WHERE e.delete_flag = '0'
AND p.name LIKE '%' || :patient_name || '%'
AND DATE(e.start_time) >= CAST(:start_date AS DATE)
AND DATE(e.start_time) <= CAST(:end_date AS DATE)
ORDER BY e.start_time DESC;
-- ========================================
-- 方案4按就诊卡号查询最准确
-- ========================================
-- 参数说明:
-- :card_no = '12345678' -- 就诊卡号
SELECT
e.id AS ID,
e.bus_no AS ,
DATE(e.start_time) AS ,
e.start_time AS ,
TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0') AS ,
e.organization_id AS ID,
e.registrar_id AS ID,
e.display_order AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS ,
COALESCE(
(SELECT identifier_no
FROM adm_patient_identifier
WHERE patient_id = p.id
AND delete_flag = '0'
AND identifier_no IS NOT NULL
AND identifier_no != ''
ORDER BY create_time ASC
LIMIT 1),
p.bus_no,
''
) AS ,
p.phone AS ,
e.status_enum AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
WHERE e.delete_flag = '0'
AND (
-- 优先匹配 identifier_no
EXISTS (
SELECT 1
FROM adm_patient_identifier api
WHERE api.patient_id = p.id
AND api.delete_flag = '0'
AND api.identifier_no = :card_no
)
OR
-- 备用:匹配 patient.bus_no
p.bus_no = :card_no
)
ORDER BY e.start_time DESC
LIMIT 20;
-- ========================================
-- 方案5模糊查询流水号部分匹配
-- ========================================
-- 参数说明:
-- :serial_part = '20260109' -- 只输入日期部分
-- 或
-- :serial_part = '001' -- 只输入序号部分
-- 或
-- :serial_part = '20260109-00' -- 输入部分(前缀匹配)
SELECT
e.id AS ID,
e.bus_no AS ,
DATE(e.start_time) AS ,
e.start_time AS ,
TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0') AS ,
e.organization_id AS ID,
e.registrar_id AS ID,
e.display_order AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS ,
p.phone AS ,
e.status_enum AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
WHERE e.delete_flag = '0'
AND (
-- 匹配日期部分如输入20260109
TO_CHAR(e.start_time, 'YYYYMMDD') LIKE :serial_part || '%'
OR
-- 匹配序号部分如输入001
LPAD(e.display_order::text, 3, '0') LIKE '%' || :serial_part || '%'
OR
-- 匹配完整流水号前缀如输入20260109-00
(TO_CHAR(e.start_time, 'YYYYMMDD') || '-' || LPAD(e.display_order::text, 3, '0')) LIKE :serial_part || '%'
)
ORDER BY e.start_time DESC
LIMIT 50;

116
sql/query_encounter_id.sql Normal file
View File

@@ -0,0 +1,116 @@
-- 查询挂号记录表主键的SQL语句
-- 表名adm_encounter
-- 主键字段id (bigint雪花算法生成)
-- ========================================
-- 1. 查询所有挂号记录的主键(简单查询)
-- ========================================
SELECT id
FROM adm_encounter
WHERE delete_flag = '0'
ORDER BY id DESC;
-- ========================================
-- 2. 查询最近N条挂号记录的主键
-- ========================================
SELECT id
FROM adm_encounter
WHERE delete_flag = '0'
ORDER BY start_time DESC
LIMIT 10;
-- ========================================
-- 3. 查询指定日期范围内的挂号记录主键
-- ========================================
SELECT id
FROM adm_encounter
WHERE delete_flag = '0'
AND DATE(start_time) >= '2026-01-01'
AND DATE(start_time) <= '2026-01-31'
ORDER BY start_time DESC;
-- ========================================
-- 4. 查询指定科室的挂号记录主键
-- ========================================
SELECT id
FROM adm_encounter
WHERE delete_flag = '0'
AND organization_id = 123 -- 替换为实际的科室ID
ORDER BY start_time DESC;
-- ========================================
-- 5. 查询指定患者的挂号记录主键
-- ========================================
SELECT id
FROM adm_encounter
WHERE delete_flag = '0'
AND patient_id = 456 -- 替换为实际的患者ID
ORDER BY start_time DESC;
-- ========================================
-- 6. 查询主键及基本信息(常用)
-- ========================================
SELECT
id AS ID,
bus_no AS ,
patient_id AS ID,
organization_id AS ID,
registrar_id AS ID,
display_order AS ,
start_time AS ,
status_enum AS
FROM adm_encounter
WHERE delete_flag = '0'
ORDER BY start_time DESC
LIMIT 20;
-- ========================================
-- 7. 统计主键数量(按日期分组)
-- ========================================
SELECT
DATE(start_time) AS ,
COUNT(id) AS ,
MIN(id) AS ID,
MAX(id) AS ID
FROM adm_encounter
WHERE delete_flag = '0'
GROUP BY DATE(start_time)
ORDER BY DESC
LIMIT 30;
-- ========================================
-- 8. 查询主键的最大值、最小值、总数
-- ========================================
SELECT
COUNT(*) AS ,
MIN(id) AS ID,
MAX(id) AS ID,
MAX(id) - MIN(id) AS ID范围
FROM adm_encounter
WHERE delete_flag = '0';
-- ========================================
-- 9. 根据主键查询单条记录(精确查询)
-- ========================================
SELECT *
FROM adm_encounter
WHERE id = 1996923066055286785 -- 替换为实际的主键ID
AND delete_flag = '0';
-- ========================================
-- 10. 查询主键及关联的患者信息
-- ========================================
SELECT
e.id AS ID,
e.bus_no AS ,
e.display_order AS ,
e.start_time AS ,
p.id AS ID,
p.name AS ,
p.bus_no AS
FROM adm_encounter e
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
WHERE e.delete_flag = '0'
ORDER BY e.start_time DESC
LIMIT 20;

161
sql/sequence_explanation.md Normal file
View File

@@ -0,0 +1,161 @@
# PostgreSQL SEQUENCE序列详解
## 语句解析
```sql
DROP SEQUENCE IF EXISTS "public"."adm_encounter_id_seq";
CREATE SEQUENCE "public"."adm_encounter_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 99999999
START 200
CACHE 1;
```
## 逐行解释
### 1. `DROP SEQUENCE IF EXISTS "public"."adm_encounter_id_seq";`
- **作用**:如果序列已存在,先删除它
- **`IF EXISTS`**:如果序列不存在,不会报错,直接跳过
- **`"public"."adm_encounter_id_seq"`**:序列的完整名称
- `public`模式schema默认模式
- `adm_encounter_id_seq`:序列名称
### 2. `CREATE SEQUENCE "public"."adm_encounter_id_seq"`
- **作用**:创建一个新的序列
- **序列名称**`adm_encounter_id_seq`(用于 `adm_encounter` 表的主键自增)
### 3. `INCREMENT 1`
- **作用**:每次递增的步长
- **含义**:每次调用 `nextval()` 时,序列值增加 1
- **示例**:如果当前值是 200下次调用 `nextval()` 返回 201
### 4. `MINVALUE 1`
- **作用**:序列的最小值
- **含义**:序列值不能小于 1
- **注意**:如果序列达到最小值后继续递减,会报错(除非设置了 `CYCLE`
### 5. `MAXVALUE 99999999`
- **作用**:序列的最大值
- **含义**:序列值不能超过 999999998位数
- **注意**:如果序列达到最大值后继续递增,会报错(除非设置了 `CYCLE`
### 6. `START 200`
- **作用**:序列的起始值
- **含义**:序列从 200 开始
- **示例**:第一次调用 `nextval()` 返回 200第二次返回 201以此类推
### 7. `CACHE 1`
- **作用**:缓存大小
- **含义**:每次从数据库获取序列值时,预分配 1 个值到内存
- **说明**
- `CACHE 1`:每次只缓存 1 个值(最安全,但性能较低)
- `CACHE 20`:每次缓存 20 个值(性能更好,但可能跳号)
- **注意**:如果数据库重启,缓存中未使用的序列值会丢失,导致跳号
## 使用示例
### 1. 在表定义中使用(自动自增)
```sql
CREATE TABLE "adm_encounter" (
"id" int8 NOT NULL DEFAULT nextval('adm_encounter_id_seq'::regclass),
...
);
```
### 2. 手动获取下一个值
```sql
-- 获取下一个序列值
SELECT nextval('adm_encounter_id_seq');
-- 返回200第一次调用
SELECT nextval('adm_encounter_id_seq');
-- 返回201第二次调用
```
### 3. 查看当前值(不递增)
```sql
SELECT currval('adm_encounter_id_seq');
-- 返回:当前序列值(不会增加)
```
### 4. 重置序列
```sql
-- 将序列重置为指定值
SELECT setval('adm_encounter_id_seq', 200);
```
## 实际应用场景
### 场景1插入数据时自动生成ID
```sql
INSERT INTO adm_encounter (patient_id, organization_id, ...)
VALUES (123, 456, ...);
-- id 字段会自动使用序列的下一个值(如 200, 201, 202...
```
### 场景2手动指定ID不推荐
```sql
INSERT INTO adm_encounter (id, patient_id, ...)
VALUES (999, 123, ...);
-- 注意如果手动插入的ID与序列值冲突可能导致问题
```
## 注意事项
### 1. 序列与雪花算法的区别
- **序列**简单的数字递增1, 2, 3...
- **雪花算法**分布式ID生成算法1996923066055286785
- **当前项目**:虽然定义了序列,但实际使用的是雪花算法(`IdType.ASSIGN_ID`
### 2. 序列跳号的原因
- 事务回滚:如果事务回滚,序列值不会回退
- 缓存:`CACHE > 1` 时,数据库重启可能导致跳号
- 手动插入手动插入ID后序列不会自动调整
### 3. 序列的优缺点
**优点**
- 简单易用
- 性能好(预分配)
- 保证唯一性
**缺点**
- 可能跳号
- 不适合分布式环境
- 最大值有限制
## 相关查询语句
### 查看序列信息
```sql
-- 查看序列的详细信息
SELECT
sequence_name AS 序列名称,
last_value AS 当前值,
start_value AS 起始值,
increment_by AS 递增步长,
max_value AS 最大值,
min_value AS 最小值,
cache_size AS 缓存大小
FROM information_schema.sequences
WHERE sequence_name = 'adm_encounter_id_seq';
```
### 查看序列的下一个值(不实际使用)
```sql
SELECT last_value, is_called
FROM adm_encounter_id_seq;
```
### 修改序列
```sql
-- 修改序列的起始值
ALTER SEQUENCE adm_encounter_id_seq RESTART WITH 1000;
-- 修改序列的最大值
ALTER SEQUENCE adm_encounter_id_seq MAXVALUE 999999999;
-- 修改序列的缓存大小
ALTER SEQUENCE adm_encounter_id_seq CACHE 20;
```

View File

@@ -0,0 +1,32 @@
-- 检查并添加 adm_patient_identifier 表的 identifier_no 列(如果不存在)
-- 执行方式使用Navicat Premium或其他PostgreSQL客户端工具连接到数据库后执行
-- 检查列是否存在,如果不存在则添加
DO $$
BEGIN
-- 检查列是否存在
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'hisdev'
AND table_name = 'adm_patient_identifier'
AND column_name = 'identifier_no'
) THEN
-- 如果列不存在,则添加列
ALTER TABLE adm_patient_identifier
ADD COLUMN identifier_no VARCHAR(255);
-- 添加注释
COMMENT ON COLUMN adm_patient_identifier.identifier_no IS '标识号(就诊卡号)';
RAISE NOTICE '已添加 identifier_no 列';
ELSE
RAISE NOTICE 'identifier_no 列已存在,无需添加';
END IF;
END $$;
-- 插入迁移记录
INSERT INTO "__migrationshistory" ("version", "description")
VALUES ('202601090000 add_identifier_no_column', '1.0.0')
ON CONFLICT (version) DO NOTHING;

View File

@@ -0,0 +1,28 @@
-- 检查 adm_patient_identifier 表是否存在 identifier_no 列
-- 执行方式使用Navicat Premium或其他PostgreSQL客户端工具连接到数据库后执行
-- 检查列是否存在
SELECT
column_name,
data_type,
character_maximum_length,
is_nullable
FROM information_schema.columns
WHERE table_schema = 'hisdev'
AND table_name = 'adm_patient_identifier'
AND column_name = 'identifier_no';
-- 如果上面的查询返回空结果说明列不存在需要执行以下SQL添加列
-- ALTER TABLE adm_patient_identifier ADD COLUMN identifier_no VARCHAR(255);
-- COMMENT ON COLUMN adm_patient_identifier.identifier_no IS '标识号(就诊卡号)';
-- 检查表的所有列
SELECT
column_name,
data_type,
character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'hisdev'
AND table_name = 'adm_patient_identifier'
ORDER BY ordinal_position;

View File

@@ -0,0 +1,57 @@
-- adm_doctor_schedule definition
-- Drop table
-- DROP TABLE adm_doctor_schedule;
CREATE TABLE adm_doctor_schedule (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
weekday VARCHAR(50) NULL,
time_period VARCHAR(50) NULL,
doctor VARCHAR(100) NULL,
clinic VARCHAR(100) NULL,
start_time TIME NULL,
end_time TIME NULL,
limit_number INTEGER NULL,
call_sign_record VARCHAR(500) NOT NULL DEFAULT '',
register_item VARCHAR(200) NULL,
register_fee INTEGER NULL,
diagnosis_item VARCHAR(200) NULL,
diagnosis_fee INTEGER NULL,
is_online BOOLEAN NULL,
is_stopped BOOLEAN NULL,
stop_reason VARCHAR(500) NULL,
dept_id INTEGER NULL,
create_time TIMESTAMPTZ(6) NULL,
update_time TIMESTAMPTZ(6) NULL
);
COMMENT ON TABLE adm_doctor_schedule IS '医生排班表';
-- Column comments
COMMENT ON COLUMN adm_doctor_schedule.id IS '主键IDGENERATED ALWAYS';
COMMENT ON COLUMN adm_doctor_schedule.weekday IS '星期';
COMMENT ON COLUMN adm_doctor_schedule.time_period IS '时段(上午/下午)';
COMMENT ON COLUMN adm_doctor_schedule.doctor IS '医生姓名';
COMMENT ON COLUMN adm_doctor_schedule.clinic IS '诊室';
COMMENT ON COLUMN adm_doctor_schedule.start_time IS '开始时间';
COMMENT ON COLUMN adm_doctor_schedule.end_time IS '结束时间';
COMMENT ON COLUMN adm_doctor_schedule.limit_number IS '限号数量';
COMMENT ON COLUMN adm_doctor_schedule.call_sign_record IS '号源记录';
COMMENT ON COLUMN adm_doctor_schedule.register_item IS '挂号项目';
COMMENT ON COLUMN adm_doctor_schedule.register_fee IS '挂号费';
COMMENT ON COLUMN adm_doctor_schedule.diagnosis_item IS '诊查项目';
COMMENT ON COLUMN adm_doctor_schedule.diagnosis_fee IS '诊疗费';
COMMENT ON COLUMN adm_doctor_schedule.is_online IS '是否线上挂号';
COMMENT ON COLUMN adm_doctor_schedule.is_stopped IS '是否停诊';
COMMENT ON COLUMN adm_doctor_schedule.stop_reason IS '停诊原因';
COMMENT ON COLUMN adm_doctor_schedule.dept_id IS '关联科室ID';
COMMENT ON COLUMN adm_doctor_schedule.create_time IS '创建时间';
COMMENT ON COLUMN adm_doctor_schedule.update_time IS '更新时间';
-- 插入迁移记录
INSERT INTO __MigrationsHistory (MigrationId, ProductVersion)
VALUES ('202512251200 add_table adm_doctor_schedule', '1.0.0');