Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	healthlink-his-ui/src/views/charge/cliniccharge/index.vue
#	healthlink-his-ui/src/views/inpatientDoctor/home/components/diagnosis/chineseMedicineDialog.vue
#	healthlink-his-ui/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue
#	healthlink-his-ui/src/views/pharmacymanagement/westernmedicine/index.vue
This commit is contained in:
2026-06-25 11:16:15 +08:00
43 changed files with 1406 additions and 169504 deletions

26
.gitignore vendored
View File

@@ -18,12 +18,7 @@
/.playwright-mcp/page-2026-05-19T03-20-04-342Z.yml
/.playwright-mcp/page-2026-05-19T03-21-08-820Z.yml
/.playwright-mcp/page-2026-05-19T03-21-43-735Z.yml
/.idea/compiler.xml
/.idea/encodings.xml
/.idea/jarRepositories.xml
/.idea/misc.xml
/.idea/vcs.xml
/.idea/workspace.xml
/.idea/
/node_modules/.bin/husky
/node_modules/.bin/husky.cmd
/node_modules/.bin/husky.ps1
@@ -416,21 +411,4 @@
/node_modules/proxy-from-env/package.json
/node_modules/proxy-from-env/README.md
/node_modules/.package-lock.json
/.idea/shelf/在进行更新之前于_2026_6_5_16_37_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_07_53_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_07_58_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_03_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_07_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_17_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/_2026_6_5_16_37____.xml
/.idea/shelf/_2026_6_6_07_53____.xml
/.idea/shelf/_2026_6_6_07_58____.xml
/.idea/shelf/_2026_6_6_09_03____.xml
/.idea/shelf/_2026_6_6_09_07____.xml
/.idea/shelf/_2026_6_6_09_17____.xml
/.idea/shelf/在进行更新之前于_2026_6_5_16_37_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_07_53_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_07_58_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_03_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_07_取消提交了更改_[更改]/shelved.patch
/.idea/shelf/在进行更新之前于_2026_6_6_09_17_取消提交了更改_[更改]/shelved.patch
/.idea/

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal" created-in="IU-253.29346.138">
<data-source name="postgresql@192.168.110.252" uuid="6f44e2a0-c865-4e9f-83bf-d35db0680dc5">
<database-info product="PostgreSQL" version="17.6" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.6" exact-driver-version="42.7">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
<secret-storage>master_key</secret-storage>
<user-name>postgresql</user-name>
<schema-mapping>
<introspection-scope>
<node kind="database" qname="@">
<node kind="schema" qname="@" />
</node>
</introspection-scope>
</schema-mapping>
</data-source>
<data-source name="postgresql@47.116.196.11" uuid="6fe4fd90-1701-4834-8548-f5c97301fd70">
<database-info product="PostgreSQL" version="17.6" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.6" exact-driver-version="42.7">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
<secret-storage>master_key</secret-storage>
<user-name>postgresql</user-name>
<schema-mapping>
<introspection-scope>
<node kind="database" qname="@">
<node kind="schema" qname="@" />
</node>
</introspection-scope>
</schema-mapping>
</data-source>
</component>
</project>

29
.idea/dataSources.xml generated
View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="postgresql@192.168.110.252" uuid="6f44e2a0-c865-4e9f-83bf-d35db0680dc5">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=healthlink_his&amp;characterEncoding=UTF-8&amp;client_encoding=UTF-8</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="postgresql@47.116.196.11" uuid="6fe4fd90-1701-4834-8548-f5c97301fd70">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=healthlink_his&amp;characterEncoding=UTF-8&amp;client_encoding=UTF-8</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
#n:healthlink_his
!<md> [905128, 0, null, null, -2147483648, -2147483648]

View File

@@ -1,2 +0,0 @@
#n:information_schema
!<md> [null, 0, null, null, -2147483648, -2147483648]

View File

@@ -1,2 +0,0 @@
#n:pg_catalog
!<md> [null, 0, null, null, -2147483648, -2147483648]

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
#n:healthlink_his
!<md> [786700, 0, null, null, -2147483648, -2147483648]

View File

@@ -1,2 +0,0 @@
#n:information_schema
!<md> [null, 0, null, null, -2147483648, -2147483648]

View File

@@ -1,2 +0,0 @@
#n:pg_catalog
!<md> [null, 0, null, null, -2147483648, -2147483648]

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="db-tree-configuration">
<option name="data" value="----------------------------------------&#10;1:0:6f44e2a0-c865-4e9f-83bf-d35db0680dc5&#10;2:0:6fe4fd90-1701-4834-8548-f5c97301fd70&#10;" />
</component>
</project>

View File

@@ -1,4 +0,0 @@
<changelist name="在进行更新之前于_2026_6_17_11_43_取消提交了更改_[更改]" date="1781667802685" recycled="true" deleted="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_17_11_43_取消提交了更改_[更改]/shelved.patch" />
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/17 11:43 取消提交了更改 [更改]" />
</changelist>

File diff suppressed because one or more lines are too long

View File

@@ -979,11 +979,29 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
"SELECT start_time,end_time FROM adm_encounter WHERE id = ? AND patient_id = ? AND status_enum = ? AND class_enum = ?";
Object[] params = {encounterId, patientId, EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue(),
EncounterClass.IMP.getValue()};
Map<String, Object> result = jdbcTemplate.queryForMap(sql, params);
HashMap<String, Date> map = new HashMap<>();
map.put("hospDate", (Timestamp)result.get("start_time"));
map.put("outTime", (Timestamp)result.get("end_time"));
return map;
try {
Map<String, Object> result = jdbcTemplate.queryForMap(sql, params);
HashMap<String, Date> map = new HashMap<>();
map.put("hospDate", (Timestamp)result.get("start_time"));
map.put("outTime", (Timestamp)result.get("end_time"));
return map;
} catch (org.springframework.dao.EmptyResultDataAccessException e) {
try {
String fallbackSql = "SELECT start_time,end_time FROM adm_encounter WHERE id = ? AND patient_id = ? AND class_enum = ?";
Object[] fallbackParams = {encounterId, patientId, EncounterClass.IMP.getValue()};
Map<String, Object> result = jdbcTemplate.queryForMap(fallbackSql, fallbackParams);
HashMap<String, Date> map = new HashMap<>();
map.put("hospDate", (Timestamp)result.get("start_time"));
map.put("outTime", (Timestamp)result.get("end_time"));
return map;
} catch (Exception ex) {
log.warn("Querying adm_encounter failed: ", ex);
HashMap<String, Date> map = new HashMap<>();
map.put("hospDate", new Date());
map.put("outTime", null);
return map;
}
}
}
/**
@@ -996,8 +1014,15 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
String sql =
"SELECT ael.start_time FROM adm_encounter ae INNER JOIN adm_encounter_location ael ON ae.ID=ael.encounter_id AND ael.form_enum=? AND ael.status_enum=? AND ael.delete_flag='0' AND ael.tenant_id=1 LEFT JOIN adm_location al ON ael.location_id=al.ID AND al.delete_flag='0' AND al.tenant_id=1 WHERE ae.ID=? AND ae.delete_flag='0' AND ae.tenant_id=1";
Object[] params = {LocationForm.BED.getValue(), EncounterActivityStatus.ACTIVE.getValue(), encounterId};
Timestamp timestamp = jdbcTemplate.queryForObject(sql, params, Timestamp.class);
return Date.from(timestamp.toInstant());
try {
List<Timestamp> list = jdbcTemplate.queryForList(sql, Timestamp.class, params);
if (list != null && !list.isEmpty() && list.get(0) != null) {
return Date.from(list.get(0).toInstant());
}
} catch (Exception e) {
log.warn("Querying location admission date failed: ", e);
}
return new Date();
}
/**

View File

@@ -115,4 +115,13 @@ public interface IATDManageAppService {
*
*/
R<?> getPendingMedication(Long encounterId);
/**
* 退床 (取消分床)
*
* @param encounterId 住院患者id
* @return 结果
*/
R<?> cancelBedAssignment(Long encounterId);
}

View File

@@ -59,6 +59,14 @@ public interface IAdviceProcessAppService {
*/
R<?> adviceReject(List<PerformInfoDto> performInfoList);
/**
* 撤销医嘱校对
*
* @param performInfoList 医嘱信息集合
* @return 操作结果
*/
R<?> adviceCancelVerify(List<PerformInfoDto> performInfoList);
/**
* 医嘱执行
*

View File

@@ -13,6 +13,8 @@ import com.core.common.utils.DateUtils;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.healthlink.his.administration.domain.Encounter;
import com.healthlink.his.administration.domain.ChargeItem;
import com.healthlink.his.administration.service.IChargeItemService;
import com.healthlink.his.administration.domain.EncounterLocation;
import com.healthlink.his.administration.domain.EncounterParticipant;
import com.healthlink.his.administration.domain.Practitioner;
@@ -115,6 +117,9 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
@Resource
private ApplicationEventPublisher eventPublisher;
@Resource
private IChargeItemService chargeItemService;
/**
* 入出转管理页面初始化
*
@@ -1002,4 +1007,85 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
docStatisticsAppService.saveOrUpdateAdmissionSigns(list);
}
}
/**
* 退床 (取消分床)
*
* @param encounterId 住院患者id
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> cancelBedAssignment(Long encounterId) {
if (encounterId == null) {
return R.fail("退床失败,请选择有效的就诊记录");
}
Encounter encounter = encounterService.getById(encounterId);
if (encounter == null) {
return R.fail("未找到该住院就诊记录");
}
// 仅已入院状态允许退床
if (!EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue().equals(encounter.getStatusEnum())) {
return R.fail("该患者未在科,无法办理退床");
}
// 校验是否产生了医嘱或计费
// 1. 检查药品医嘱
long medCount = medicationRequestService.count(
new LambdaQueryWrapper<MedicationRequest>()
.eq(MedicationRequest::getEncounterId, encounterId)
.eq(MedicationRequest::getDeleteFlag, DelFlag.NO.getCode()));
if (medCount > 0) {
return R.fail("患者已产生医嘱或计费,无法直接退床");
}
// 2. 检查诊疗医嘱
long svcCount = serviceRequestService.count(
new LambdaQueryWrapper<ServiceRequest>()
.eq(ServiceRequest::getEncounterId, encounterId)
.eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode()));
if (svcCount > 0) {
return R.fail("患者已产生医嘱或计费,无法直接退床");
}
// 3. 检查耗材医嘱
long devCount = deviceRequestService.count(
new LambdaQueryWrapper<DeviceRequest>()
.eq(DeviceRequest::getEncounterId, encounterId)
.eq(DeviceRequest::getDeleteFlag, DelFlag.NO.getCode()));
if (devCount > 0) {
return R.fail("患者已产生医嘱或计费,无法直接退床");
}
// 4. 检查计费记录
long chargeCount = chargeItemService.count(
new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getEncounterId, encounterId));
if (chargeCount > 0) {
return R.fail("患者已产生医嘱或计费,无法直接退床");
}
// 更新原病床状态为 空闲 (LocationStatus.IDLE)
List<EncounterLocation> bedLocations = encounterLocationService.getEncounterLocationList(encounterId,
LocationForm.BED, EncounterActivityStatus.ACTIVE);
if (bedLocations != null && !bedLocations.isEmpty()) {
for (EncounterLocation bedLoc : bedLocations) {
locationService.updateStatusById(bedLoc.getLocationId(), LocationStatus.IDLE.getValue());
}
}
// 更新病床和病房就诊位置状态为已完成 (EncounterActivityStatus.COMPLETED)
// isTransfer 为 false不更新病区 WARD以保留患者的病区归属从而能继续在入科列表中显示并重新分床
encounterLocationService.updateEncounterLocationStatus(encounterId, false);
// 更新医疗参与者(住院医生、责任护士等)状态为已完成
encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
// 回滚住院状态为 待入科 (EncounterZyStatus.REGISTERED)
encounter.setStatusEnum(EncounterZyStatus.REGISTERED.getValue());
encounterService.saveOrUpdateEncounter(encounter);
return R.ok("退床成功");
}
}

View File

@@ -601,6 +601,102 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
return R.ok(null, "退回成功");
}
/**
* 撤销医嘱校对
*
* @param performInfoList 医嘱信息集合
* @return 操作结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> adviceCancelVerify(List<PerformInfoDto> performInfoList) {
if (performInfoList == null || performInfoList.isEmpty()) {
return R.fail("请先选择医嘱信息");
}
// 分别创建列表来存储不同类型的请求
List<PerformInfoDto> serviceRequestList = new ArrayList<>();
List<PerformInfoDto> medRequestList = new ArrayList<>();
List<PerformInfoDto> deviceRequestList = new ArrayList<>();
for (PerformInfoDto item : performInfoList) {
if (CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(item.getRequestTable())) {
serviceRequestList.add(item);
} else if (CommonConstants.TableName.MED_MEDICATION_REQUEST.equals(item.getRequestTable())) {
medRequestList.add(item);
} else if (CommonConstants.TableName.WOR_DEVICE_REQUEST.equals(item.getRequestTable())) {
deviceRequestList.add(item);
}
}
List<Long> allRequestIds = performInfoList.stream().map(PerformInfoDto::getRequestId).toList();
// 校验①:校验医嘱是否已执行。若医嘱状态已经是“已执行”,则不允许撤销校对。
List<Procedure> allProcedures = procedureService.list(
new LambdaQueryWrapper<Procedure>()
.in(Procedure::getRequestId, allRequestIds)
.eq(Procedure::getDeleteFlag, "0"));
Set<Long> executedIds = allProcedures.stream()
.filter(p -> EventStatus.COMPLETED.getValue().equals(p.getStatusEnum()))
.map(Procedure::getId)
.collect(Collectors.toSet());
Set<Long> cancelledRefundIds = allProcedures.stream()
.filter(p -> EventStatus.CANCEL.getValue().equals(p.getStatusEnum()) && p.getRefundId() != null)
.map(Procedure::getRefundId)
.collect(Collectors.toSet());
executedIds.removeAll(cancelledRefundIds);
if (!executedIds.isEmpty()) {
return R.fail("该医嘱已执行,无法撤销校对,请先去医嘱执行模块取消执行");
}
// 校验②:校验该医嘱是否已记账扣费。若已扣费,则不允许撤销校对。
List<ChargeItem> chargeItems = chargeItemService.getChargeItemInfoByReqId(allRequestIds);
boolean isBilled = chargeItems.stream().anyMatch(ci -> ChargeItemStatus.BILLED.getValue().equals(ci.getStatusEnum()));
if (isBilled) {
return R.fail("该医嘱已记账收费,若需撤销请先进行退费/计账回滚");
}
// 校验③:若为药品医嘱,校验药房是否已发药(配药)。若已发药,则不允许撤销校对。
if (!medRequestList.isEmpty()) {
List<Long> medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList();
List<MedicationDispense> dispenseList = medicationDispenseService.list(
new LambdaQueryWrapper<MedicationDispense>()
.in(MedicationDispense::getMedReqId, medReqIds)
.in(MedicationDispense::getStatusEnum, Arrays.asList(
DispenseStatus.COMPLETED.getValue(),
DispenseStatus.PREPARED.getValue(),
DispenseStatus.PART_COMPLETED.getValue()
)));
if (!dispenseList.isEmpty()) {
return R.fail("药房已发药,请先进行退药申请");
}
}
// 满足所有校验,执行撤销校对(回退至“未校对”,即 ACTIVE 状态)
if (!serviceRequestList.isEmpty()) {
serviceRequestService.update(new LambdaUpdateWrapper<ServiceRequest>()
.in(ServiceRequest::getId, serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList())
.set(ServiceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue())
.set(ServiceRequest::getPerformerCheckId, null)
.set(ServiceRequest::getCheckTime, null));
}
if (!medRequestList.isEmpty()) {
medicationRequestService.update(new LambdaUpdateWrapper<MedicationRequest>()
.in(MedicationRequest::getId, medRequestList.stream().map(PerformInfoDto::getRequestId).toList())
.set(MedicationRequest::getStatusEnum, RequestStatus.ACTIVE.getValue())
.set(MedicationRequest::getPerformerCheckId, null)
.set(MedicationRequest::getCheckTime, null));
}
if (!deviceRequestList.isEmpty()) {
deviceRequestService.update(new LambdaUpdateWrapper<DeviceRequest>()
.in(DeviceRequest::getId, deviceRequestList.stream().map(PerformInfoDto::getRequestId).toList())
.set(DeviceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue())
.set(DeviceRequest::getPerformerCheckId, null)
.set(DeviceRequest::getCheckTime, null));
}
return R.ok(null, "撤销校对成功");
}
/**
* 医嘱执行
*

View File

@@ -166,4 +166,16 @@ public class ATDManageController {
public R<?> getPendingMedication(Long encounterId) {
return atdManageAppService.getPendingMedication(encounterId);
}
/**
* 退床 (取消分床)
*
* @param encounterId 住院患者id
* @return 结果
*/
@PutMapping(value = "/cancel-bed-assignment")
public R<?> cancelBedAssignment(Long encounterId) {
return atdManageAppService.cancelBedAssignment(encounterId);
}
}

View File

@@ -87,6 +87,17 @@ public class AdviceProcessController {
return adviceProcessAppService.adviceReject(performInfoList);
}
/**
* 撤销医嘱校对
*
* @param performInfoList 医嘱信息集合
* @return 操作结果
*/
@PutMapping(value = "/advice-cancel-verify")
public R<?> adviceCancelVerify(@RequestBody List<PerformInfoDto> performInfoList) {
return adviceProcessAppService.adviceCancelVerify(performInfoList);
}
/**
* 医嘱执行
*

View File

@@ -0,0 +1,9 @@
-- Initialize update_time for all TCM syndromes to a default past timestamp so they don't sort before active ones in nulls-first ORDER BY
UPDATE cli_condition_definition
SET update_time = '2000-01-01 00:00:00'
WHERE source_enum = 6;
-- Set update_time for the 8 core TCM syndromes so that they sort at the top of the list
UPDATE cli_condition_definition
SET update_time = CURRENT_TIMESTAMP
WHERE source_enum = 6 AND name IN ('阴证', '阳证', '寒证', '热证', '虚证', '实证', '闭证', '脱证');

View File

@@ -124,7 +124,7 @@
ii.reception_time,
ii.start_time,
ii.status_enum
ORDER BY ii.reception_time,
ORDER BY ii.reception_time DESC,
ii.start_time DESC
</select>
<select id="selectMedicineDispenseOrderPage" resultMap="medicineDispenseOrderMap">

View File

@@ -50,6 +50,7 @@
>
重置
</el-button>
<slot name="extra-buttons" />
<el-button
v-if="needCollapse"
link

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div class="app-container">
<el-row :gutter="20">
<!--药品目录-->
@@ -207,6 +207,7 @@
:data="medicationList"
width="90%"
@checkbox-change="handleSelectionChange"
@checkbox-all="handleSelectionChange"
>
<vxe-column
type="checkbox"
@@ -648,10 +649,10 @@ function submitFileForm() {
}
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
function handleSelectionChange({ records }) {
ids.value = records.map((item) => item.id);
single.value = records.length != 1;
multiple.value = !records.length;
}
/** 打开新增弹窗 */

View File

@@ -1,195 +1,239 @@
<template>
<div
<el-container
v-loading="readCardLoading"
style="display: flex; justify-content: space-between"
class="app-container"
:element-loading-text="loadingText"
>
<el-card style="width: 30%">
<template #header>
<span style="vertical-align: middle">{{ $t('billing.patientList') }}</span>
</template>
<div style="width: 100%">
<el-input
v-model="queryParams.searchKey"
:placeholder="$t('billing.searchPlaceholder')"
clearable
style="width: 48%; margin-bottom: 10px; margin-right: 10px"
@keyup.enter="getPatientList"
>
<template #append>
<el-button
icon="Search"
@click="getPatientList"
/>
</template>
</el-input>
<el-select
v-model="queryParams.statusEnum"
style="width: 48%; margin-bottom: 10px; margin-right: 10px"
:placeholder="$t('billing.chargeStatus')"
@change="getPatientList"
>
<el-option
v-for="item in chargeStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<div style="width: 100%">
<el-date-picker
v-model="receptionTime"
type="daterange"
range-separator="~"
:start-placeholder="$t('billing.startTime')"
:end-placeholder="$t('billing.endTime')"
placement="bottom"
value-format="YYYY-MM-DD"
style="width: 84%; margin-bottom: 10px; margin-right: 10px"
@change="getPatientList"
/>
<el-button
type="primary"
style="margin-bottom: 10px"
@click="getPatientList"
>
{{ $t('billing.search') }}
</el-button>
</div>
<el-aside width="30%" style="min-width: 280px">
<el-card>
<template #header>
<el-row justify="space-between" align="middle">
<span style="font-weight: 600">患者列表</span>
<span style="font-size: 12px; color: #909399">
{{ patientList.length }}
</span>
</el-row>
</template>
<!-- 检索区域 -->
<el-form>
<el-row style="margin-bottom: 8px">
<el-col :span="24">
<el-input
v-model="queryParams.searchKey"
placeholder="患者名 / 病历号"
clearable
@keyup.enter="getPatientList"
>
<template #append>
<el-button icon="Search" @click="getPatientList" />
</template>
</el-input>
</el-col>
</el-row>
<el-row :gutter="8" style="margin-bottom: 8px">
<el-col :span="10">
<el-select
v-model="queryParams.statusEnum"
style="width: 100%"
placeholder="收费状态"
clearable
@change="getPatientList"
>
<el-option
v-for="item in chargeStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-col>
<el-col :span="14">
<el-date-picker
v-model="receptionTime"
type="daterange"
range-separator="~"
start-placeholder="开始"
end-placeholder="结束"
placement="bottom"
value-format="YYYY-MM-DD"
style="width: 100%"
@change="getPatientList"
/>
</el-col>
</el-row>
<el-row :gutter="8" style="margin-bottom: 8px">
<el-col :span="12">
<el-button type="primary" style="width: 100%" @click="getPatientList">
<el-icon><Search /></el-icon>
搜索
</el-button>
</el-col>
<el-col :span="12">
<el-button style="width: 100%" @click="resetQuery">
<el-icon><Refresh /></el-icon>
重置
</el-button>
</el-col>
</el-row>
</el-form>
<!-- 患者列表表格 -->
<vxe-table
ref="patientListRef"
height="620"
:height="tableHeight"
:data="patientList"
:row-config="{ keyField: 'encounterId' }"
:row-config="{ keyField: 'encounterId', isCurrent: true }"
:radio-config="{ trigger: 'row' }"
highlight-current-row
@cell-click="clickRow"
>
<vxe-column
:title="$t('billing.medicalRecordNo')"
title="病历号"
align="center"
field="encounterBusNo"
width="100"
/>
<vxe-column
:title="$t('billing.name')"
title="姓名"
align="center"
field="patientName"
width="70"
/>
<!-- <vxe-column title="时间" align="center" field="receptionTime" width="160">
<template #default="scope">
{{ formatDate(scope.row.receptionTime) }}
</template>
</vxe-column> -->
<vxe-column
:title="$t('billing.chargeStatus')"
title="就诊时间"
align="center"
field="receptionTime"
width="140"
>
<template #default="scope">
{{ formatDateStr(scope.row.receptionTime, 'MM-DD HH:mm') }}
</template>
</vxe-column>
<vxe-column
title="收费状态"
align="center"
field="statusEnum_enumText"
/>
min-width="80"
>
<template #default="scope">
<el-tag
:type="scope.row.statusEnum === 1 ? '' : scope.row.statusEnum === 5 ? 'success' : scope.row.statusEnum === 8 ? 'danger' : 'warning'"
size="small"
disable-transitions
>
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</vxe-column>
</vxe-table>
</div>
</el-card>
<div style="width: 69%">
<el-card style="margin-bottom: 20px">
</el-card>
</el-aside>
<el-main style="margin-left: 16px">
<el-card style="margin-bottom: 16px">
<template #header>
<span style="vertical-align: middle">{{ $t('billing.basicInfo') }}</span>
<span style="font-weight: 600">基本信息</span>
</template>
<el-descriptions :column="5">
<el-descriptions-item :label="$t('billing.name') + ':'">
{{ patientInfo.patientName }}
<el-descriptions :column="5" border size="small">
<el-descriptions-item label="姓名" min-width="80">
{{ patientInfo.patientName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('billing.gender') + ':'">
{{ patientInfo.genderEnum_enumText }}
<el-descriptions-item label="性别" min-width="60">
{{ patientInfo.genderEnum_enumText || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('billing.age') + ':'">
{{ patientInfo.age }}
<el-descriptions-item label="年龄" min-width="50">
{{ patientInfo.age || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('billing.department') + ':'">
{{ patientInfo.organizationName }}
<el-descriptions-item label="科室" min-width="100">
{{ patientInfo.organizationName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('billing.visitTime') + ':'">
{{ formatDateStr(patientInfo.receptionTime, 'YYYY-MM-DD HH:mm:ss') }}
<el-descriptions-item label="就诊时间" min-width="140">
{{ formatDateStr(patientInfo.receptionTime, 'YYYY-MM-DD HH:mm:ss') || '-' }}
</el-descriptions-item>
<!-- <el-descriptions-item label="身份证号:">{{ patientInfo.idCard }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="手机号">{{ patientInfo.name }}</el-descriptions-item>
<el-descriptions-item label="出生日期">{{ patientInfo.name }}</el-descriptions-item> -->
</el-descriptions>
</el-card>
<el-card style="min-width: 1100px">
<el-card>
<template #header>
<span style="vertical-align: middle">{{ $t('billing.chargeItems') }}</span>
<el-row justify="space-between" align="middle">
<span style="font-weight: 600">收费项目</span>
<span style="font-size: 15px; color: #e6a23c">
合计金额
<strong style="font-size: 18px">
¥{{ totalAmounts ? totalAmounts.toFixed(2) : '0.00' }}
</strong>
</span>
</el-row>
</template>
<div style="margin-bottom: 10px">
<!-- 操作按钮区 -->
<el-row align="middle" style="margin-bottom: 12px; flex-wrap: wrap">
<el-button
type="primary"
:disabled="buttonDisabled"
@click="confirmCharge()"
>
{{ $t('billing.confirmCharge') }}
确认收费
</el-button>
<el-divider direction="vertical" />
<el-button-group>
<el-button
type="primary"
plain
@click="handleReadCard('01')"
>
电子凭证
</el-button>
<el-button
type="primary"
plain
:disabled="true"
@click="handleReadCard('02')"
>
身份证
</el-button>
<el-button
type="primary"
plain
@click="handleReadCard('03')"
>
医保卡
</el-button>
</el-button-group>
<el-divider direction="vertical" />
<el-button
type="primary"
type="warning"
plain
style="width: 65px"
@click="handleReadCard('01')"
>
{{ $t('billing.electronicCertificate') }}
</el-button>
<el-button
type="primary"
plain
style="width: 65px"
:disabled="true"
@click="handleReadCard('02')"
>
{{ $t('billing.idCard') }}
</el-button>
<el-button
type="primary"
plain
style="width: 65px"
@click="handleReadCard('03')"
>
{{ $t('billing.medicalInsuranceCard') }}
</el-button>
<el-button
type="primary"
style="margin-left: 20px"
:disabled="buttonDisabled"
@click="payToSelt()"
>
{{ $t('billing.insuranceToSelf') }}
医保转自费
</el-button>
<el-button
type="primary"
style="margin-left: 20px"
type="success"
plain
:disabled="buttonDisabled"
@click="patToMedicalInsurance()"
>
{{ $t('billing.selfToInsurance') }}
自费转医保
</el-button>
<el-button
type="primary"
style="margin-left: 20px"
type="warning"
plain
:disabled="buttonDisabled"
@click="studentPayTosStudentSelf()"
>
{{ $t('billing.studentInsuranceToSelf') }}
学生医保转学生自费
</el-button>
<el-button
type="primary"
style="margin-left: 20px"
type="success"
plain
:disabled="buttonDisabled"
@click="studentSelfToStudentPay()"
>
{{ $t('billing.studentSelfToInsurance') }}
学生自费转学生医保
</el-button>
</div>
<div style="text-align: right; padding-right: 20px; margin-bottom: 10px;">
<span style="font-weight: bold; font-size: 14px;">{{ $t('billing.totalAmount') }}{{ totalAmounts ? totalAmounts.toFixed(2) : 0 }}{{ $t('billing.yuan') }}</span>
</div>
</el-row>
<vxe-table
ref="chargeListRef"
v-loading="chargeLoading"
height="530"
:height="tableHeight"
:data="chargeList"
:span-method="objectSpanMethod"
border
@@ -201,40 +245,40 @@
width="55"
/>
<vxe-column
:title="$t('billing.docNo')"
title="单据号"
align="center"
field="busNo"
width="180"
/>
<vxe-column
:title="$t('billing.chargeItems')"
title="收费项目"
align="center"
field="itemName"
width="200"
/>
<vxe-column
:title="$t('billing.quantity')"
title="数量"
align="center"
field="quantityValue"
width="80"
/>
<vxe-column
:title="$t('billing.medicalType')"
title="医疗类型"
align="center"
field="medTypeCode_dictText"
/>
<vxe-column
:title="$t('billing.insuranceCode')"
title="医保编码"
align="center"
field="ybNo"
/>
<vxe-column
:title="$t('billing.feeNature')"
title="费用性质"
align="center"
field="contractName"
/>
<vxe-column
:title="$t('billing.chargeStatus')"
title="收费状态"
align="center"
field="statusEnum_enumText"
width="150"
@@ -270,22 +314,22 @@
</template>
</vxe-column>
<vxe-column
:title="$t('billing.amount')"
title="金额"
align="right"
field="totalPrice"
header-align="center"
>
<template #default="scope">
{{ scope.row.totalPrice.toFixed(2) + ' ' + $t('billing.yuan') || '0.00' + ' ' + $t('billing.yuan') }}
{{ scope.row.totalPrice.toFixed(2) + ' 元' || '0.00' + ' 元' }}
</template>
</vxe-column>
<vxe-column
:title="$t('billing.cashier')"
title="收款人"
align="center"
field="entererId_dictText"
/>
<vxe-column
:title="$t('billing.operation')"
title="操作"
align="center"
fixed="right"
header-align="center"
@@ -298,13 +342,13 @@
type="primary"
@click="printCharge(scope.row)"
>
{{ $t('billing.print') }}
打印
</el-button>
</template>
</vxe-column>
</vxe-table>
</el-card>
</div>
</el-main>
<ChargeDialog
ref="chargeDialogRef"
@@ -324,7 +368,7 @@
@close="handleClose"
@refresh="getPatientList"
/>
</div>
</el-container>
</template>
<script setup name="ClinicCharge">
@@ -340,13 +384,12 @@ import {
precharge,
} from './components/api';
import {invokeYbPlugin5000, invokeYbPlugin5001} from '@/api/public';
import { Search, Refresh } from '@element-plus/icons-vue';
import ChargeDialog from './components/chargeDialog.vue';
import {formatDateStr} from '@/utils';
import useUserStore from '@/store/modules/user';
import Decimal from 'decimal.js';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
@@ -370,13 +413,7 @@ const openDialog = ref(false);
const totalAmount = ref(0);
const chargeListRef = ref();
const details = ref({});
const chargeStatusOptionsRaw = ref([]);
const chargeStatusOptions = computed(() => {
return (chargeStatusOptionsRaw.value || []).map(item => ({
value: item.value,
label: item.label ? t('billing.' + item.label, item.label) : item.label,
}));
});
const chargeStatusOptions = ref([]);
const receptionTime = ref([
formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),
@@ -386,6 +423,21 @@ const buttonDisabled = computed(() => {
});
const chargedItems = ref([]);
// 动态计算患者列表表格高度(减去检索区域大概高度)
const tableHeight = ref(window.innerHeight - 420);
function onWindowResize() {
tableHeight.value = window.innerHeight - 420;
}
onMounted(() => {
window.addEventListener('resize', onWindowResize);
});
onUnmounted(() => {
window.removeEventListener('resize', onWindowResize);
});
watch(
() => selectedRows.value,
(newVlaue) => {
@@ -441,9 +493,20 @@ function getPatientList() {
});
}
/** 重置查询条件 */
function resetQuery() {
queryParams.value.searchKey = undefined;
queryParams.value.statusEnum = 1;
receptionTime.value = [
formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),
];
getPatientList();
}
function initOption() {
init().then((res) => {
chargeStatusOptionsRaw.value = res.data.chargeItemStatusOptions;
chargeStatusOptions.value = res.data.chargeItemStatusOptions;
});
}
@@ -493,9 +556,9 @@ function handleClose(value, msg) {
const consumablesIdList = ref([]);
// 确认收费
function confirmCharge() {
let selectRows = chargeListRef.value.getSelectionRows();
let selectRows = chargeListRef.value.getCheckboxRecords();
if (selectRows.length == 0) {
proxy.$modal.msgWarning(t('billing.selectChargeItem'));
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
@@ -531,7 +594,7 @@ function confirmCharge() {
}) || [];
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || t('billing.preSettleFail'));
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
// console.log(patientInfo)
@@ -574,7 +637,7 @@ async function handleReadCard(value) {
})
.then((res) => {
readCardLoading.value = true;
loadingText.value = t('billing.reading');
loadingText.value = '正在读取...';
jsonResult = res.data;
})
.catch(() => {
@@ -586,24 +649,24 @@ async function handleReadCard(value) {
cardInfo = JSON.parse(JSON.stringify(jsonResult));
let message = JSON.parse(cardInfo.data);
userMessage = {
certType: '02',
certNo: message.data.idNo,
psnCertType: '02',
certType: '02', // 证件类型
certNo: message.data.idNo, // 身份证号
psnCertType: '02', // 居民身份证
};
userCardInfo = {
certType: '01',
certNo: message.data.idNo,
psnCertType: '01',
busiCardInfo: message.data.ecToken,
certType: '01', // 证件类型
certNo: message.data.idNo, // 身份证号
psnCertType: '01', // 居民身份证
busiCardInfo: message.data.ecToken, // 令牌
};
BusiCardInfo.value = message.data.ecToken;
console.log(BusiCardInfo.value);
break;
case '02':
break;
case '03':
case '03': // 社保卡
readCardLoading.value = true;
loadingText.value = t('billing.reading');
loadingText.value = '正在读取...';
await invokeYbPlugin5001(
JSON.stringify({
FunctionId: 1,
@@ -657,9 +720,9 @@ async function handleReadCard(value) {
}
readCardLoading.value = false;
if (userMessage.certNo) {
let selectRows = chargeListRef.value.getSelectionRows();
let selectRows = chargeListRef.value.getCheckboxRecords();
if (selectRows.length == 0) {
proxy.$modal.msgWarning(t('billing.selectChargeItem'));
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
@@ -695,7 +758,7 @@ async function handleReadCard(value) {
});
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || t('billing.preSettleFail'));
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
}
@@ -712,7 +775,7 @@ async function handleReadCard(value) {
function payToSelt() {
changeToSelfPay(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(t('billing.opSuccess'));
proxy.$modal.msgSuccess('操作成功');
}
});
}
@@ -723,7 +786,7 @@ function payToSelt() {
function patToMedicalInsurance() {
changeToMedicalInsurance(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(t('billing.opSuccess'));
proxy.$modal.msgSuccess('操作成功');
}
});
}
@@ -734,7 +797,7 @@ function patToMedicalInsurance() {
function studentPayTosStudentSelf() {
changeStudentPayTosStudentSelf(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(t('billing.opSuccess'));
proxy.$modal.msgSuccess('操作成功');
}
});
}
@@ -745,7 +808,7 @@ function studentPayTosStudentSelf() {
function studentSelfToStudentPay() {
changeStudentSelfToStudentPay(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(t('billing.opSuccess'));
proxy.$modal.msgSuccess('操作成功');
}
});
}
@@ -846,4 +909,14 @@ function printCharge(row) {
:deep(.vxe-table--body) tr:hover td.no-hover-column {
background-color: inherit !important;
}
/* 覆盖 el-main 默认 padding */
:deep(.el-main) {
padding: 0;
}
/* 覆盖 el-aside 默认 overflow */
:deep(.el-aside) {
overflow: visible;
}
</style>

View File

@@ -138,6 +138,8 @@ import {
getTcmDiagnosis,
} from '@/views/doctorstation/components/api';
import { DIAG_TYPE } from '@/utils/medicalConstants';
import { formatDateStr } from '@/utils';
import useUserStore from '@/store/modules/user';
const props = defineProps({
openAddDiagnosisDialog: {
@@ -168,6 +170,7 @@ const timestamp = ref('');
const selectedDisease = ref(false);
const { proxy } = getCurrentInstance();
const emit = defineEmits(['close']);
const userStore = useUserStore();
// 请求序列号,防止异步回调竞态导致数据重复
let openSeq = 0;
@@ -321,7 +324,10 @@ function handleClickRow({ row }) {
syndromeGroupNo: timestamp.value,
verificationStatusEnum: 4,
medTypeCode: DIAG_TYPE.TCM_MAIN_DISEASE, // 诊断类型:中医主病诊断 (值2)
isExisting: false // 标记为新增
isExisting: false, // 标记为新增
onsetDate: getCurrentDate(), // 发病日期
diagnosisTime: getCurrentDate(), // 诊断日期
diagnosisDoctor: getDoctorName(), // 诊断医生
});
tcmDiagonsisList.value.push({
conditionName: row.name,
@@ -351,7 +357,10 @@ function clickSyndromeRow({ row }) {
ybNo: row.ybNo,
syndromeGroupNo: timestamp.value,
medTypeCode: DIAG_TYPE.TCM_MAIN_SYNDROME, // 诊断类型:中医主证诊断 (值3)
isExisting: false // 标记为新增
isExisting: false, // 标记为新增
onsetDate: getCurrentDate(), // 发病日期
diagnosisTime: getCurrentDate(), // 诊断日期
diagnosisDoctor: getDoctorName(), // 诊断医生
});
tcmDiagonsisList.value[tcmDiagonsisList.value.length - 1].syndromeName = row.name;
syndromeSelected.value = true;
@@ -378,13 +387,20 @@ function save() {
return;
}
// 格式化日期为后端期望的 yyyy/M/d HH:mm:ss 格式
const formattedList = newDiagnosisList.map(item => ({
...item,
onsetDate: item.onsetDate ? formatDateStr(item.onsetDate, 'YYYY/M/D HH:mm:ss') : null,
diagnosisTime: item.diagnosisTime ? formatDateStr(item.diagnosisTime, 'YYYY/M/D HH:mm:ss') : null,
}));
if (props.updateZy.length > 0) {
// 修改模式
console.log('[addDiagnosisDialog] 调用 updateTcmDiagnosis');
updateTcmDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: newDiagnosisList,
diagnosisChildList: formattedList,
}).then((res) => {
console.log('[addDiagnosisDialog] updateTcmDiagnosis 响应, code=', res.code);
if (res.code == 200) {
@@ -398,7 +414,7 @@ function save() {
saveTcmDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: newDiagnosisList,
diagnosisChildList: formattedList,
}).then((res) => {
console.log('[addDiagnosisDialog] saveTcmDiagnosis 响应, code=', res.code);
if (res.code == 200) {
@@ -431,6 +447,24 @@ function close() {
console.log('[addDiagnosisDialog] close() 触发 (弹窗@close事件)');
emit('close');
}
function getCurrentDate() {
const date = new Date();
const year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
month = month < 10 ? "0" + month : month;
day = day < 10 ? "0" + day : day;
return `${year}-${month}-${day}`;
}
function getDoctorName() {
return props.patientInfo?.practitionerName
|| props.patientInfo?.doctorName
|| props.patientInfo?.physicianName
|| userStore?.name
|| '';
}
</script>
<style scoped>

View File

@@ -568,6 +568,8 @@ async function getList() {
typeName: '中医诊断',
classification: '中医', // 中医诊断默认分类
onsetDate: item.onsetDate,
diagnosisTime: item.diagnosisTime, // 诊断日期
diagnosisDoctor: item.diagnosisDoctor, // 诊断医生
updateId: item.encounterDiagnosisId + '-' + (symptom?.encounterDiagnosisId || ''),
illnessDefinitionId : item.definitionId,
symptomDefinitionId : symptom?.definitionId || '',

View File

@@ -1,107 +1,62 @@
<template>
<div class="app-container">
<div class="left">
<div class="form">
<el-form
v-show="showSearch"
ref="queryRef"
:model="queryParams"
:inline="true"
label-position="right"
<div class="med-detail-container">
<el-card style="width: 38%">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center">
<span>患者列表</span>
<span style="font-size: 12px; color: #909399; font-weight: 400"> {{ total }} </span>
</div>
</template>
<div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px">
<el-input
v-model="queryParams.searchKey"
style="width: 180px"
placeholder="姓名/证件号"
clearable
@keyup.enter="handleQuery"
>
<el-form-item
label="患者信息"
prop="condition"
>
<el-input
v-model="queryParams.condition"
placeholder="请输入姓名/证件号"
clearable
style="width: 160px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item
label="发药状态"
prop="departmentId"
style="margin-left: 10px"
>
<el-select
v-model="queryParams.statusEnum"
placeholder="请选择发药状态"
clearable
style="width: 160px"
@change="handleQuery"
>
<el-option
v-for="item in dispenseStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="入院日期">
<el-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 250px"
value-format="YYYY-MM-DD"
@change="handleQuery"
/>
</el-form-item>
<el-form-item style="margin-left: 15px">
<el-button
type="primary"
@click="handleQuery"
>
搜索
</el-button>
<el-button @click="resetQuery">
重置
</el-button>
</el-form-item>
</el-form>
<template #append>
<el-button icon="Search" @click="handleQuery" />
</template>
</el-input>
<el-select
v-model="queryParams.statusEnum"
placeholder="发药状态"
style="width: 140px"
clearable
@change="handleQuery"
>
<el-option
v-for="item in dispenseStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始"
end-placeholder="结束"
style="width: 240px"
value-format="YYYY-MM-DD"
@change="handleQuery"
/>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</div>
<vxe-table
ref="patientListRef"
:row-config="{ isCurrent: true }"
:data="patientList"
border
style="width: 100%; flex: 1; min-height: 0"
auto-resize
@cell-click="handleCurrentChange"
>
<vxe-column
field="encounterNo"
title="住院号"
width="150"
align="center"
/>
<vxe-column
field="patientName"
title="姓名"
width="130"
align="center"
/>
<vxe-column
field="genderEnum_enumText"
title="性别"
width="80"
align="center"
/>
<vxe-column
field="age"
title="年龄"
width="80"
align="center"
/>
<!-- <vxe-column field="receptionTime" title="就诊日期" align="center">
<template #default="scope">
{{ scope.row.receptionTime ? formatDate(scope.row.receptionTime) : '-' }}
</template>
</vxe-column> -->
<vxe-column field="encounterNo" title="住院号" min-width="90" align="center" show-overflow="title" />
<vxe-column field="patientName" title="姓名" min-width="60" align="center" />
<vxe-column field="genderEnum_enumText" title="性别" min-width="50" align="center" />
<vxe-column field="age" title="年龄" min-width="50" align="center" />
</vxe-table>
<pagination
v-show="total > 0"
@@ -110,18 +65,23 @@
:total="total"
@pagination="getList"
/>
</div>
</el-card>
<div class="right">
<div class="top">
<span style="color: #606266; font-size: 14px; font-weight: 700; margin-right: 15px">
调配药师
</span>
<el-select
v-model="preparerDoctor"
placeholder="调配药师"
style="width: 160px"
>
<el-card style="width: 61%">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center">
<span>药品明细</span>
<span style="font-size: 15px; color: #e6a23c">
总金额
<strong style="font-size: 18px">
¥{{ medicineTotalPrice ? medicineTotalPrice.toFixed(2) : '0.00' }}
</strong>
</span>
</div>
</template>
<div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px">
<span style="color: #606266; font-size: 14px; font-weight: 700">调配药师</span>
<el-select v-model="preparerDoctor" placeholder="调配药师" style="width: 140px">
<el-option
v-for="item in preparerDoctorOptions"
:key="item.value"
@@ -129,20 +89,12 @@
:value="item.value"
/>
</el-select>
<span style="color: #606266; font-size: 14px; font-weight: 700; margin-right: 15px">
药品分类
</span>
<span style="color: #606266; font-size: 14px; font-weight: 700; margin-left: 16px">药品分类</span>
<el-select
v-model="tcmFlag"
placeholder="药品分类"
style="width: 160px"
@change="
() => {
if (currentRow.encounterId) {
getMedicineList(currentRow.encounterId);
}
}
"
style="width: 140px"
@change="() => { if (currentRow.encounterId) { getMedicineList(currentRow.encounterId) } }"
>
<el-option
v-for="item in medCategoryCode"
@@ -154,187 +106,58 @@
<el-button
:disabled="medicineInfoList && medicineInfoList.length == 0"
type="primary"
style="margin-left: 30px"
style="margin-left: 16px"
@click="handleBatch()"
>
批量发药
</el-button>
<!-- <el-button type="primary" @click="handleScan()" style="margin-left: 30px"> 扫码 </el-button> -->
<el-button
type="primary"
style="margin-left: 30px"
@click="printPrescription()"
>
<el-button type="primary" style="margin-left: 8px" @click="printPrescription()">
处方打印
</el-button>
<div style="position: absolute; top: 30px; right: 25px">
总金额{{ medicineTotalPrice ? medicineTotalPrice.toFixed(2) : '0.00' }}
</div>
</div>
<vxe-table
ref="tableRef"
v-loading="loading"
:data="medicineInfoList"
border
style="width: 100%; flex: 1; min-height: 0; margin-top: 10px"
auto-resize
:span-method="spanMethod"
@select="handleSelectionChange"
@cell-dblclick="handleCellDbClick"
>
<vxe-column
type="checkbox"
width="55"
align="center"
fixed="left"
/>
<vxe-column
field="prescriptionNo"
title="处方号"
width="120"
align="center"
/>
<vxe-column
field="itemName"
title="项目名称"
width="160"
align="center"
/>
<vxe-column
field="statusEnum_enumText"
title="发药状态"
width="100"
align="center"
>
<vxe-column type="checkbox" width="55" align="center" fixed="left" />
<vxe-column field="prescriptionNo" title="处方号" min-width="110" align="center" />
<vxe-column field="itemName" title="项目名称" min-width="140" align="center" show-overflow="title" />
<vxe-column field="statusEnum_enumText" title="发药状态" min-width="90" align="center">
<template #default="scope">
<el-tag :type="tagType(scope.row.statusEnum)">
<el-tag :type="tagType(scope.row.statusEnum)" size="small" effect="light">
{{ formatDrugStatusText(scope.row) }}
</el-tag>
</template>
</vxe-column>
<vxe-column
field="quantity"
title="发药数量"
width="130"
align="center"
>
<vxe-column field="quantity" title="发药数量" min-width="110" align="center">
<template #default="scope">
<span> {{ scope.row.quantity }}{{ scope.row.unitCode_dictText }} </span>
<span>{{ scope.row.quantity }}{{ scope.row.unitCode_dictText }}</span>
</template>
</vxe-column>
<!-- <vxe-column field="flag" title="组合" width="60" align="center" /> -->
<!-- <vxe-column field="quantity" title="发药数量" width="100" align="center" /> -->
<vxe-column
field="totalVolume"
title="规格"
width="100"
align="center"
/>
<!-- <vxe-column field="unitCode_dictText" title="单位" width="100" align="center" /> -->
<!-- <vxe-column
field="doseUnitCode_dictText"
title="单次剂量"
width="80"
align="center"
v-if="tcmFlag == '0'"
>
<template #default="scope">
{{ scope.row.dose }}{{ scope.row.doseUnitCode_dictText }}
</template>
</vxe-column> -->
<!-- <vxe-column
field="traceNo"
title="追溯码"
width="180"
align="center"
v-if="tcmFlag == '0'"
>
<template #default="scope">
<el-tooltip
:content="formatContent(scope.row.traceNo)"
placement="top"
popper-class="custom-tooltip"
>
<el-input
:ref="'traceNoRef' + scope.rowIndex"
@input="handleTraceNoInput(scope.row, scope.rowIndex)"
v-model="scope.row.traceNo"
placeholder="请输入追溯码"
/>
</el-tooltip>
</template>
</vxe-column> -->
<vxe-column
field="lotNumber"
title="批次号"
width="160"
align="center"
>
<vxe-column field="totalVolume" title="规格" min-width="100" align="center" />
<vxe-column field="lotNumber" title="批次号" min-width="140" align="center" show-overflow="title">
<template #default="scope">
<span>{{ scope.row.lotNumber }}</span>
</template>
</vxe-column>
<vxe-column
field="totalPrice"
title="金额"
width="100"
:formatter="formatPrice"
align="right"
header-align="center"
/>
<vxe-column
field="locationName"
title="发药药房"
width="90"
align="center"
/>
<vxe-column
field="manufacturerText"
title="生产厂家"
width="200"
align="center"
/>
<vxe-column
field="doctorName"
title="开单医生"
width="100"
align="center"
/>
<vxe-column
field="conditionName"
title="诊断"
width="120"
align="center"
/>
<!-- <vxe-column field="dose" title="剂量" width="100" align="center" /> -->
<vxe-column
field="rateCode"
title="频次"
width="100"
align="center"
/>
<vxe-column
field="methodCode_dictText"
title="用法"
width="100"
align="center"
/>
<vxe-column
field="dispensePerDuration"
title="天数"
width="80"
align="center"
/>
<vxe-column
title="操作"
align="center"
width="160"
fixed="right"
class-name="small-padding fixed-width"
>
<vxe-column field="totalPrice" title="金额" min-width="90" :formatter="formatPrice" align="right" header-align="center" />
<vxe-column field="locationName" title="发药药房" min-width="80" align="center" />
<vxe-column field="manufacturerText" title="生产厂家" min-width="150" align="center" show-overflow="title" />
<vxe-column field="doctorName" title="开单医生" min-width="80" align="center" />
<vxe-column field="conditionName" title="诊断" min-width="100" align="center" show-overflow="title" />
<vxe-column field="rateCode" title="频次" min-width="70" align="center" />
<vxe-column field="methodCode_dictText" title="用法" min-width="70" align="center" />
<vxe-column field="dispensePerDuration" title="天数" min-width="60" align="center" />
<vxe-column title="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-button
link
type="primary"
link type="primary"
:disabled="!(scope.row.statusEnum == 2 || scope.row.statusEnum == 14)"
icon="SuccessFilled"
@click="handleBatch(scope.row)"
@@ -342,8 +165,7 @@
发药
</el-button>
<el-button
link
type="primary"
link type="primary"
:disabled="scope.row.statusEnum != 2"
icon="CircleClose"
@click="backMedicine(scope.row)"
@@ -353,17 +175,10 @@
</template>
</vxe-column>
</vxe-table>
</div>
<el-dialog
v-model="showDialog"
title="选择作废原因"
width="30%"
>
<!-- 下拉选择框 -->
<el-select
v-model="notPerformedReasonEnum"
placeholder="请选择作废原因"
>
</el-card>
<el-dialog v-model="showDialog" title="选择作废原因" width="30%">
<el-select v-model="notPerformedReasonEnum" placeholder="请选择作废原因">
<el-option
v-for="item in backReason"
:key="item.value"
@@ -371,14 +186,9 @@
:value="item.value"
/>
</el-select>
<!-- 弹窗底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button
type="primary"
@click="handleConfirm"
> </el-button>
<el-button type="primary" @click="handleConfirm"> </el-button>
<el-button @click="handleCancel"> </el-button>
</span>
</template>
@@ -474,7 +284,7 @@ const data = reactive({
queryParams: {
pageNo: 1,
pageSize: 10,
condition: null,
searchKey: null,
departmentId: null,
statusEnum: 18,
classEnum: 1,
@@ -486,6 +296,7 @@ const { queryParams } = toRefs(data);
onMounted(() => {
getList();
});
function handleScan() {
openTraceNoDialog.value = true;
}
@@ -1188,59 +999,26 @@ function handleCancel() {
</script>
<style lang="scss" scoped>
.app-container {
padding: 20px;
.med-detail-container {
height: 100%;
display: flex;
height: calc(100vh - 84px);
justify-content: space-between;
overflow: hidden;
}
.left {
width: 25%;
min-width: 0;
flex-shrink: 0;
display: flex;
flex-direction: column;
overflow: hidden;
.form {
width: 100%;
display: flex;
justify-content: space-between;
flex-shrink: 0;
flex-wrap: wrap;
gap: 8px;
}
}
.right {
margin-left: 12px;
flex: 1;
min-width: 0;
overflow: auto;
}
:deep(.vxe-table tbody tr:hover > td) {
background-color: inherit !important;
}
.el-form--inline .el-form-item {
margin-right: 0px;
}
:deep(.el-textarea .el-textarea__inner) {
resize: none !important;
}
/* 添加图标悬停样式 */
.editable-icon {
transition: color 0.3s ease;
}
.editable-icon:hover {
color: #3B82F6; /* Element Plus 主题色,可根据需要调整 */
color: #3B82F6;
cursor: pointer;
}
/* 批次号容器样式 */
.lot-number-container {
display: flex;
justify-content: space-between;
@@ -1248,7 +1026,6 @@ function handleCancel() {
width: 100%;
}
/* 确认图标样式 */
.confirm-icon {
transition: color 0.3s ease;
margin-left: 5px;
@@ -1259,18 +1036,12 @@ function handleCancel() {
cursor: pointer;
}
/* 其他已有样式保持不变 */
:deep(.vxe-table tbody tr:hover > td) {
background-color: inherit !important;
}
.el-form--inline .el-form-item {
margin-right: 0px;
}
:deep(.el-textarea .el-textarea__inner) {
resize: none !important;
}
/* 表头不换行 */
:deep(.vxe-header--column .vxe-cell) {
white-space: nowrap;
}
</style>

View File

@@ -229,7 +229,7 @@ function handleClickRow(row) {
selectedDisease.value = true;
syndromeSelected.value = false;
timestamp.value = Date.now();
getTcmSyndrome().then((res) => {
getTcmSyndrome({ pageSize: 3000 }).then((res) => {
syndromeList.value = res.data.records;
});
tcmDiagonsisSaveList.value.push({

View File

@@ -1,7 +1,7 @@
<template>
<el-dialog
:model-value="visible"
:title="$t('inpatientDoctor.diagnosis.chineseMedicineDiagnosis')"
title="中医诊断"
:width="width"
:z-index="20"
teleported
@@ -17,12 +17,12 @@
label-width="100px"
>
<el-form-item
:label="$t('inpatientDoctor.diagnosis.chineseMedicineDiagnosis')"
label="中医诊断"
prop="conditionCode"
>
<el-select
v-model="formData.conditionCode"
:placeholder="$t('inpatientDoctor.diagnosis.selectChineseMedicineDiagnosis')"
placeholder="请选择中医诊断"
filterable
clearable
style="width: 100%"
@@ -37,23 +37,21 @@
</el-select>
</el-form-item>
<el-form-item
:label="$t('inpatientDoctor.diagnosis.chineseMedicineSyndrome')"
label="中医证候"
prop="syndromeCode"
>
<el-select
<el-cascader
ref="cascaderRef"
v-model="formData.syndromeCode"
:placeholder="$t('inpatientDoctor.diagnosis.selectChineseMedicineSyndrome')"
:options="getGroupedSyndromeOptions(syndromeOptions)"
:props="{ emitPath: false, checkStrictly: true }"
:show-all-levels="false"
placeholder="请选择中医证候"
filterable
clearable
style="width: 100%"
>
<el-option
v-for="item in syndromeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
@change="handleSyndromeCodeChange"
/>
</el-form-item>
</el-form>
<template #footer>
@@ -62,29 +60,28 @@
class="margin-left-auto"
@click="cancelAct"
>
{{ $t('common.cancel') }}
取消
</el-button>
<el-button
size="fixed"
type="primary"
@click="handleSubmit"
>
{{ $t('common.save') }}
保存
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import { getTcmCondition, getTcmSyndrome, saveTcmDiagnosis } from '../api'
const { proxy } = getCurrentInstance()
const { t } = useI18n()
const conditionOptions = ref([])
const syndromeOptions = ref([])
const cascaderRef = ref(null)
const formData = ref({
conditionCode: '',
@@ -92,8 +89,8 @@ const formData = ref({
})
const rules = reactive({
conditionCode: [{ required: true, message: t('inpatientDoctor.diagnosis.selectChineseMedicineDiagnosis'), trigger: ['blur', 'change'] }],
syndromeCode: [{ required: true, message: t('inpatientDoctor.diagnosis.selectChineseMedicineSyndrome'), trigger: ['blur', 'change'] }],
conditionCode: [{ required: true, message: '请选择中医诊断', trigger: ['blur', 'change'] }],
syndromeCode: [{ required: true, message: '请选择中医证候', trigger: ['blur', 'change'] }],
})
const props = defineProps({
@@ -131,7 +128,7 @@ function loadConditionOptions() {
}
function loadSyndromeOptions(conditionCode) {
const params = conditionCode ? { conditionCode } : {}
const params = conditionCode ? { conditionCode, pageSize: 3000 } : { pageSize: 3000 }
getTcmSyndrome(params).then((res) => {
if (res.data && res.data.records) {
syndromeOptions.value = res.data.records.map((item) => ({
@@ -161,14 +158,14 @@ const handleSubmit = async () => {
}]
saveTcmDiagnosis(submitData).then((res) => {
if (res.code === 200) {
ElMessage.success(t('inpatientDoctor.diagnosis.chineseMedicineSaveSuccess'))
ElMessage.success('中医诊断保存成功')
emit('ok-act')
cancelAct()
} else {
ElMessage.error(res.msg || t('inpatientDoctor.diagnosis.saveFailed'))
ElMessage.error(res.msg || '保存失败')
}
}).catch(() => {
ElMessage.error(t('inpatientDoctor.diagnosis.saveFailedRetry'))
ElMessage.error('保存失败,请重试')
})
}
})
@@ -179,6 +176,154 @@ const openAct = () => {
loadConditionOptions()
loadSyndromeOptions()
}
function getGroupedSyndromeOptions(options) {
if (!options || !options.length) return []
const seen = new Set()
const uniqueOptions = []
options.forEach(item => {
if (!seen.has(item.label)) {
seen.add(item.label)
uniqueOptions.push(item)
}
})
const nameToItems = {}
uniqueOptions.forEach(item => {
const name = item.label
if (!nameToItems[name]) {
nameToItems[name] = []
}
nameToItems[name].push(item)
})
// Level 3 Children of 阴证 (寒证, 虚证)
const childrenOfYin = []
;['寒证', '虚证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYin.push(...nameToItems[name])
}
})
// Level 3 Children of 阳证 (热证, 实证)
const childrenOfYang = []
;['热证', '实证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYang.push(...nameToItems[name])
}
})
// Level 2 under 八纲总纲 (阴证, 阳证)
const level2OfBagang = []
if (nameToItems['阴证']) {
nameToItems['阴证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYin.length ? childrenOfYin : undefined
})
})
} else if (childrenOfYin.length > 0) {
level2OfBagang.push({
value: 'virtual-yin',
label: '阴证',
children: childrenOfYin
})
}
if (nameToItems['阳证']) {
nameToItems['阳证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYang.length ? childrenOfYang : undefined
})
})
} else if (childrenOfYang.length > 0) {
level2OfBagang.push({
value: 'virtual-yang',
label: '阳证',
children: childrenOfYang
})
}
// Level 2 under 危重急症 (闭证, 脱证)
const level2OfWeizhong = []
;['闭证', '脱证'].forEach(name => {
if (nameToItems[name]) {
level2OfWeizhong.push(...nameToItems[name])
}
})
// Level 2 under 其他证候 (all other syndromes)
const level2OfOther = []
const specialNames = new Set(['阴证', '阳证', '寒证', '热证', '虚证', '实证', '闭证', '脱证'])
options.forEach(item => {
if (!specialNames.has(item.label)) {
level2OfOther.push(item)
}
})
const finalTree = []
if (level2OfBagang.length > 0) {
finalTree.push({
value: 'root-bagang',
label: '八纲总纲',
children: level2OfBagang
})
}
if (level2OfWeizhong.length > 0) {
finalTree.push({
value: 'root-weizhong',
label: '危重急症',
children: level2OfWeizhong
})
}
if (level2OfOther.length > 0) {
finalTree.push({
value: 'root-other',
label: '其他证候',
children: level2OfOther
})
}
return finalTree
}
function handleSyndromeCodeChange(val) {
console.log('handleSyndromeCodeChange called with:', val)
if (val === 'root-bagang' || val === 'root-weizhong' || val === 'root-other' || (typeof val === 'string' && val.startsWith('virtual-'))) {
formData.value.syndromeCode = ''
} else if (val) {
if (cascaderRef.value) {
console.log('Cascader Ref component instance:', cascaderRef.value)
if (typeof cascaderRef.value.togglePopperVisible === 'function') {
cascaderRef.value.togglePopperVisible(false)
}
if (typeof cascaderRef.value.toggleDropDownVisible === 'function') {
cascaderRef.value.toggleDropDownVisible(false)
}
cascaderRef.value.popperVisible = false
if (typeof cascaderRef.value.blur === 'function') {
cascaderRef.value.blur()
}
}
if (document.activeElement && typeof document.activeElement.blur === 'function') {
console.log('Blurring active element:', document.activeElement)
document.activeElement.blur()
}
setTimeout(() => {
document.body.click()
}, 50)
}
}
const closedAct = () => {
emit('update:visible', false)
}

View File

@@ -5,9 +5,9 @@
:span="4"
:xs="24"
>
<el-input
v-model="diagnosis"
:placeholder="$t('inpatientDoctor.diagnosis.diagnosisName')"
<el-input
v-model="diagnosis"
placeholder="诊断名称"
clearable
style="width: 100%; margin-bottom: 10px"
@keyup.enter="queryDiagnosisUse"
@@ -53,7 +53,7 @@
"
width="200"
:hide-after="10"
:title="$t('inpatientDoctor.diagnosis.confirmDeleteCommonDiagnosis')"
title="确认删除此常用诊断吗"
placement="top-start"
@confirm="deleteChild(data)"
>
@@ -86,14 +86,14 @@
:disabled="hasUnsavedDiagnosis"
@click="handleAddDiagnosis()"
>
{{ $t('inpatientDoctor.diagnosis.addDiagnosis') }}
新增诊断
</el-button>
<el-button
type="primary"
plain
@click="handleSaveDiagnosis()"
>
{{ $t('inpatientDoctor.diagnosis.saveDiagnosis') }}
保存诊断
</el-button>
<!-- <el-button type="primary" plain @click="handleAddTcmDiagonsis()"> 中医诊断 </el-button> -->
<el-button
@@ -102,7 +102,7 @@
:disabled="hasUnsavedDiagnosis"
@click="handleImport()"
>
{{ $t('inpatientDoctor.diagnosis.importChronicDisease') }}
导入慢性病诊断
</el-button>
</div>
@@ -110,7 +110,7 @@
v-if="hasUnsavedDiagnosis"
class="unsaved-diagnosis-tip"
>
<el-icon><WarningFilled /></el-icon> {{ $t('inpatientDoctor.diagnosis.unsavedDiagnosisTip') }}
<el-icon><WarningFilled /></el-icon> 当前有未保存的诊断,请先保存后再新增
</div>
<el-form
ref="formRef"
@@ -123,7 +123,7 @@
height="650"
>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.seqNo')"
title="序号"
width="50"
>
<template #default="scope">
@@ -131,7 +131,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisSort')"
title="诊断排序"
align="center"
field="diagSrtNo"
width="120"
@@ -151,7 +151,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisSystem')"
title="诊断体系"
align="center"
field="diagnosisSystem"
width="120"
@@ -167,11 +167,11 @@
@change="handleDiagnosisSystemChange(scope.row)"
>
<el-option
:label="$t('inpatientDoctor.diagnosis.western')"
label="西医"
value="西医"
/>
<el-option
:label="$t('inpatientDoctor.diagnosis.chinese')"
label="中医"
value="中医"
/>
</el-select>
@@ -179,7 +179,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisCategory')"
title="诊断类别"
align="center"
field="medTypeCode"
width="160"
@@ -205,7 +205,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisName')"
title="诊断名称"
align="center"
field="name"
min-width="180"
@@ -231,7 +231,7 @@
<template #reference>
<el-input
v-model="scope.row.name"
:placeholder="$t('inpatientDoctor.diagnosis.selectDiagnosis')"
placeholder="请选择诊断"
@input="handleChange"
@focus="handleFocus(scope.row, scope.rowIndex)"
@blur="handleBlur(scope.row)"
@@ -243,7 +243,7 @@
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisDoctor')"
title="诊断医生"
align="center"
field="diagnosisDoctor"
width="120"
@@ -260,30 +260,27 @@
width="180"
>
<template #header>
<span>{{ $t('inpatientDoctor.diagnosis.tcmSyndrome') }} <span style="color: #EF4444;">*</span></span>
<span>中医证候 <span style="color: #EF4444;">*</span></span>
</template>
<template #default="scope">
<template v-if="scope.row.diagnosisSystem === '中医'">
<el-form-item
:prop="`diagnosisList.${scope.rowIndex}.tcmSyndromeCode`"
:rules="scope.row.diagnosisSystem === '中医' ? [{ required: true, message: t('inpatientDoctor.diagnosis.selectTcmSyndrome'), trigger: 'change' }] : []"
:rules="scope.row.diagnosisSystem === '中医' ? [{ required: true, message: '请选择中医证候', trigger: 'change' }] : []"
>
<el-select
<el-cascader
:ref="el => setCascaderRef(el, scope.rowIndex)"
v-model="scope.row.tcmSyndromeCode"
:placeholder="$t('inpatientDoctor.diagnosis.selectTcmSyndrome')"
:options="getGroupedSyndromeOptions(scope.row.syndromeOptions)"
:props="{ emitPath: false, checkStrictly: true }"
:show-all-levels="false"
placeholder="请选择中医证候"
filterable
clearable
style="width: 100%"
@focus="loadSyndromeOptions(scope.row)"
@change="(val) => handleSyndromeSelect(val, scope.row)"
>
<el-option
v-for="item in (scope.row.syndromeOptions || [])"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
@change="(val) => handleSyndromeSelect(val, scope.row, scope.rowIndex)"
/>
</el-form-item>
</template>
<el-form-item v-else>
@@ -292,7 +289,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisTime')"
title="诊断时间"
align="center"
field="diagnosisTime"
width="180"
@@ -304,7 +301,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisCode')"
title="诊断代码"
align="center"
field="ybNo"
width="160"
@@ -316,7 +313,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('inpatientDoctor.diagnosis.diagnosisType')"
title="诊断类型"
align="center"
field="maindiseFlag"
width="170"
@@ -350,7 +347,7 @@
</template>
</vxe-column>
<vxe-column
:title="$t('common.operation')"
title="操作"
align="center"
width="130"
>
@@ -361,7 +358,7 @@
type="primary"
@click="handleDeleteDiagnosis(scope.row, scope.rowIndex)"
>
{{ $t('common.delete') }}
删除
</el-button>
</el-form-item>
</template>
@@ -385,8 +382,7 @@
</template>
<script setup>
import {getCurrentInstance, ref, computed, watch} from 'vue';
import { useI18n } from 'vue-i18n';
import {getCurrentInstance, ref, computed, watch} from 'vue'; // 添加 nextTick 导入
import useUserStore from '@/store/modules/user';
import {
delEncounterDiagnosis,
@@ -420,6 +416,12 @@ const rowIndex = ref();
const diagnosis = ref();
const orgOrUser = ref();
const syndromeOptions = ref([]);
const cascaderRefs = ref({});
const setCascaderRef = (el, index) => {
if (el) {
cascaderRefs.value[index] = el;
}
};
const form = ref({
diagnosisList: [],
});
@@ -440,14 +442,13 @@ const props = defineProps({
const emits = defineEmits(['diagnosisSave']);
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const { t } = useI18n();
// 获取诊断类型字典(住院诊断类别)
const { inpatient_diag_category } = proxy.useDict('inpatient_diag_category');
const rules = computed(() => ({
name: [{ required: true, message: t('inpatientDoctor.diagnosis.selectDiagnosis'), trigger: 'change' }],
medTypeCode: [{ required: true, message: t('inpatientDoctor.diagnosis.selectDiagnosisType'), trigger: 'change' }],
diagSrtNo: [{ required: true, message: t('inpatientDoctor.diagnosis.enterDiagnosisSortNo'), trigger: 'change' }],
}));
const rules = ref({
name: [{ required: true, message: '请选择诊断', trigger: 'change' }],
medTypeCode: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
diagSrtNo: [{ required: true, message: '请输入诊断序号', trigger: 'change' }],
});
const diagnosisNetDatas = ref([]);
const tcmDiagnosisListForEdit = ref([]);
@@ -637,7 +638,7 @@ function handleImport() {
// 检查是否已填写病历
if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst'));
proxy.$modal.msgWarning('请先填写病历');
return;
}
@@ -646,7 +647,7 @@ function handleImport() {
(item) => !item.conditionId && !item.encounterDiagnosisId
);
if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis'));
proxy.$modal.msgWarning('请保存当前诊断');
return;
}
@@ -690,7 +691,7 @@ function addChild(data) {
function deleteChild(data) {
deleteDiagnosisBind(data.id).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(t('inpatientDoctor.diagnosis.deleteSuccess'));
proxy.$modal.msgSuccess('删除成功');
getTree();
}
});
@@ -751,7 +752,7 @@ function handleAddDiagnosis() {
// 检查是否已填写病历(必须先于其他检查)
if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst'));
proxy.$modal.msgWarning('请先填写病历');
return;
}
@@ -760,7 +761,7 @@ function handleAddDiagnosis() {
(item) => !item.conditionId && !item.encounterDiagnosisId
);
if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis'));
proxy.$modal.msgWarning('请保存当前诊断');
return;
}
@@ -780,7 +781,7 @@ function handleAddDiagnosis() {
);
if (!valid || hasUnsavedNow) {
if (hasUnsavedNow) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis'));
proxy.$modal.msgWarning('请保存当前诊断');
}
return;
}
@@ -834,8 +835,11 @@ function handleDiagnosisSystemChange(row) {
// 加载中医证候选项(按诊断名称关联过滤)
function loadSyndromeOptions(row) {
if (row.syndromeOptions && row.syndromeOptions.length) {
return;
}
const conditionCode = row.ybNo;
const params = conditionCode ? { conditionCode } : {};
const params = conditionCode ? { conditionCode, pageSize: 3000 } : { pageSize: 3000 };
getTcmSyndrome(params).then((res) => {
if (res.data && res.data.records) {
row.syndromeOptions = res.data.records.map((item) => ({
@@ -850,11 +854,43 @@ function loadSyndromeOptions(row) {
}
// 中医证候选中赋值
function handleSyndromeSelect(val, row) {
function handleSyndromeSelect(val, row, rowIndex) {
console.log('handleSyndromeSelect called with:', val, 'rowIndex:', rowIndex);
if (val) {
if (val === 'root-bagang' || val === 'root-weizhong' || val === 'root-other' || (typeof val === 'string' && val.startsWith('virtual-'))) {
row.tcmSyndromeCode = '';
row.tcmSyndromeName = '';
row.syndromeDefinitionId = '';
return;
}
const selected = (row.syndromeOptions || []).find((item) => item.value === val);
row.tcmSyndromeName = selected ? selected.label : '';
row.syndromeDefinitionId = selected ? selected.id : '';
// Close the cascader dropdown programmatically
if (rowIndex !== undefined && cascaderRefs.value[rowIndex]) {
const refEl = cascaderRefs.value[rowIndex];
console.log('Cascader Ref component instance:', refEl);
if (refEl) {
if (typeof refEl.togglePopperVisible === 'function') {
refEl.togglePopperVisible(false);
}
if (typeof refEl.toggleDropDownVisible === 'function') {
refEl.toggleDropDownVisible(false);
}
refEl.popperVisible = false;
if (typeof refEl.blur === 'function') {
refEl.blur();
}
}
}
if (document.activeElement && typeof document.activeElement.blur === 'function') {
console.log('Blurring active element:', document.activeElement);
document.activeElement.blur();
}
setTimeout(() => {
document.body.click();
}, 50);
} else {
row.tcmSyndromeName = '';
row.syndromeDefinitionId = '';
@@ -915,7 +951,7 @@ function handleMaindise(value, index) {
});
if (flag > 1) {
form.value.diagnosisList[index].maindiseFlag = 0;
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.onlyOneMainDiagnosis'));
proxy.$modal.msgWarning('只能有一条主诊断');
}
}
}
@@ -941,20 +977,20 @@ function handleSaveDiagnosis() {
for (let index = 0; index < (form.value.diagnosisList || []).length; index++) {
const item = form.value.diagnosisList[index];
if (!item.diagSrtNo) {
ElMessage({
type: 'error',
message: t('inpatientDoctor.diagnosis.enterDiagnosisSortNo'),
});
ElMessage({
type: 'error',
message: '请录入诊断序号',
});
break;
}
}
proxy.$refs.formRef.validate((valid) => {
if (valid) {
if (form.value.diagnosisList.length === 0) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.diagnosisCannotBeEmpty'));
proxy.$modal.msgWarning('诊断不能为空');
return;
} else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.atLeastOneMainDiagnosis'));
proxy.$modal.msgWarning('至少添加一条主诊断');
return;
}
@@ -962,11 +998,11 @@ function handleSaveDiagnosis() {
for (let i = 0; i < form.value.diagnosisList.length; i++) {
const item = form.value.diagnosisList[i];
if (!item.name) {
ElMessage.warning(t('inpatientDoctor.diagnosis.diagnosisNameRequired', { row: i + 1 }));
ElMessage.warning(`第${i + 1}行诊断名称不能为空`);
return;
}
if (item.diagnosisSystem === '中医' && !item.tcmSyndromeCode) {
ElMessage.error(t('inpatientDoctor.diagnosis.tcmDiagnosisIncomplete'));
ElMessage.error('中医诊断不完整,请录入对应的证候!');
return;
}
}
@@ -1077,7 +1113,7 @@ function handleSaveDiagnosis() {
// 所有保存完成后刷新
emits('diagnosisSave', false);
proxy.$modal.msgSuccess(t('inpatientDoctor.diagnosis.diagnosisSaved'));
proxy.$modal.msgSuccess('诊断已保存');
getList();
// 食源性疾病逻辑
@@ -1088,7 +1124,7 @@ function handleSaveDiagnosis() {
});
} catch (e) {
console.error('保存诊断失败', e);
proxy.$modal.msgError(t('inpatientDoctor.diagnosis.saveDiagnosisFailed'));
proxy.$modal.msgError('保存诊断失败');
} finally {
setTimeout(() => {
isSaving.value = false;
@@ -1103,7 +1139,7 @@ function handleSaveDiagnosis() {
*/
function closeDiagnosisDialog(str) {
if (str === 'success') {
proxy.$modal.msgSuccess(t('inpatientDoctor.diagnosis.operationSuccess'));
proxy.$modal.msgSuccess('操作成功');
}
openAddDiagnosisDialog.value = false;
@@ -1122,9 +1158,14 @@ function handleChange(value) {
*/
function handleSelsectDiagnosis(row) {
console.log(row);
form.value.diagnosisList[rowIndex.value].ybNo = row.ybNo;
form.value.diagnosisList[rowIndex.value].name = row.name;
form.value.diagnosisList[rowIndex.value].definitionId = row.id;
const targetRow = form.value.diagnosisList[rowIndex.value];
targetRow.ybNo = row.ybNo;
targetRow.name = row.name;
targetRow.definitionId = row.id;
targetRow.syndromeOptions = [];
targetRow.tcmSyndromeCode = '';
targetRow.tcmSyndromeName = '';
targetRow.syndromeDefinitionId = '';
}
/**获取焦点时 打开列表 */
function handleFocus(row, index) {
@@ -1146,7 +1187,7 @@ function handleNodeClick(data) {
// 检查是否已填写病历
if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst'));
proxy.$modal.msgWarning('请先填写病历');
return;
}
@@ -1155,7 +1196,7 @@ function handleNodeClick(data) {
(item) => !item.conditionId && !item.encounterDiagnosisId
);
if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis'));
proxy.$modal.msgWarning('请保存当前诊断');
return;
}
@@ -1163,7 +1204,7 @@ function handleNodeClick(data) {
(diagnosis) => diagnosis.ybNo === data.ybNo || diagnosis.name === data.name
);
if (isDuplicate) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.diagnosisAlreadyExists'));
proxy.$modal.msgWarning('该诊断项已存在');
return;
}
form.value.diagnosisList.push({
@@ -1191,6 +1232,126 @@ function handleNodeClick(data) {
}
}
function getGroupedSyndromeOptions(options) {
if (!options || !options.length) return [];
const seen = new Set();
const uniqueOptions = [];
options.forEach(item => {
if (!seen.has(item.label)) {
seen.add(item.label);
uniqueOptions.push(item);
}
});
const nameToItems = {};
uniqueOptions.forEach(item => {
const name = item.label;
if (!nameToItems[name]) {
nameToItems[name] = [];
}
nameToItems[name].push(item);
});
// Level 3 Children of 阴证 (寒证, 虚证)
const childrenOfYin = [];
['寒证', '虚证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYin.push(...nameToItems[name]);
}
});
// Level 3 Children of 阳证 (热证, 实证)
const childrenOfYang = [];
['热证', '实证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYang.push(...nameToItems[name]);
}
});
// Level 2 under 八纲总纲 (阴证, 阳证)
const level2OfBagang = [];
if (nameToItems['阴证']) {
nameToItems['阴证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYin.length ? childrenOfYin : undefined
});
});
} else if (childrenOfYin.length > 0) {
level2OfBagang.push({
value: 'virtual-yin',
label: '阴证',
children: childrenOfYin
});
}
if (nameToItems['阳证']) {
nameToItems['阳证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYang.length ? childrenOfYang : undefined
});
});
} else if (childrenOfYang.length > 0) {
level2OfBagang.push({
value: 'virtual-yang',
label: '阳证',
children: childrenOfYang
});
}
// Level 2 under 危重急症 (闭证, 脱证)
const level2OfWeizhong = [];
['闭证', '脱证'].forEach(name => {
if (nameToItems[name]) {
level2OfWeizhong.push(...nameToItems[name]);
}
});
// Level 2 under 其他证候 (all other syndromes)
const level2OfOther = [];
const specialNames = new Set(['阴证', '阳证', '寒证', '热证', '虚证', '实证', '闭证', '脱证']);
options.forEach(item => {
if (!specialNames.has(item.label)) {
level2OfOther.push(item);
}
});
const finalTree = [];
if (level2OfBagang.length > 0) {
finalTree.push({
value: 'root-bagang',
label: '八纲总纲',
children: level2OfBagang
});
}
if (level2OfWeizhong.length > 0) {
finalTree.push({
value: 'root-weizhong',
label: '危重急症',
children: level2OfWeizhong
});
}
if (level2OfOther.length > 0) {
finalTree.push({
value: 'root-other',
label: '其他证候',
children: level2OfOther
});
}
return finalTree;
}
defineExpose({ getList, getDetail, handleSaveDiagnosis });
</script>

View File

@@ -446,7 +446,7 @@ function handleTcmSyndromeClick(row, index) {
function handleSyndromeSearch() {}
function loadSyndromeList() {
getTcmSyndrome().then((res) => {
getTcmSyndrome({ pageSize: 3000 }).then((res) => {
if (res.data && res.data.records) {
syndromeList.value = res.data.records
}

View File

@@ -146,6 +146,18 @@ export function terminalCleaning(encounterId) {
});
}
//退床 (取消分床)
export function cancelBedAssignment(encounterId) {
return request({
url: '/nurse-station/atd-manage/cancel-bed-assignment',
method: 'put',
params: {
encounterId: encounterId,
},
});
}
/**
* 获取病区列表(与病区管理页面相同的接口)
*/

View File

@@ -4,6 +4,7 @@
<PendingPatientList
:list="patientList"
:active-id="activePatientId"
id-key="encounterId"
@item-click="handleCardClick"
@item-dblclick="handleCardDblClick"
@dragstart="handleDragStart"
@@ -39,6 +40,15 @@
/>
</el-select>
</template>
<template #extra-buttons>
<el-button
type="danger"
plain
@click="handleCancelBedAssignment"
>
退床
</el-button>
</template>
</Filter>
</div>
<el-scrollbar class="right-scrollbar">
@@ -109,7 +119,7 @@ import Filter from '@/components/TableLayout/Filter.vue';
import {computed, onBeforeMount, onMounted, reactive, ref} from 'vue';
import TransferInDialog from './transferInDialog.vue';
import SignEntryDialog from './signEntryDialog.vue';
import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard} from './api';
import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard, cancelBedAssignment} from './api';
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
import PendingPatientList from '@/components/PendingPatientList/index.vue';
@@ -161,7 +171,7 @@ const selectHoouseLoding = ref(true);
const bedStatusFilter = ref('');
const activePatientId = computed(() => {
const active = patientList.value?.find?.((it) => it?.active);
return active?.id || '';
return active?.encounterId || '';
});
const filterItems = computed(() => [
@@ -389,24 +399,11 @@ const handleTransferInOk = async () => {
await getList();
};
// 单击患者卡片事件 - 直接触发入科选床界面
// 单击患者卡片事件 - 仅高亮选中该患者
function handleCardClick(item: any, index: number) {
if (item.encounterStatus == 2) {
// 显示提示信息,指导用户如何分配床位
ElMessage({
message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位',
type: 'warning',
grouping: true,
showClose: true,
});
} else {
pendingInfo.value = {
...item,
entranceType: 1,
};
transferInDialogVisible.value = true;
}
patientList.value.forEach((p) => {
p.active = p.encounterId === item.encounterId;
});
}
// 双击患者卡片事件 - 保持原有逻辑
@@ -429,6 +426,50 @@ function handleCardDblClick(item: any) {
}
}
// 退床操作 (取消分床)
const handleCancelBedAssignment = async () => {
const activePatient = patientList.value?.find?.((it) => it?.active);
if (!activePatient) {
ElMessage.warning('请先从左侧患者列表点击选中一名需要退床的已入院患者');
return;
}
if (activePatient.encounterStatus != 5) {
ElMessage.warning('选中的患者未在科,无法办理退床');
return;
}
try {
await ElMessageBox.confirm(
`确定要对患者【${activePatient.patientName || ''}】办理退床操作吗?退床后,该患者将回滚为待入科状态。`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
);
const loadingInstance = ElLoading.service({ fullscreen: true, text: '正在办理退床...' });
try {
const res = await cancelBedAssignment(activePatient.encounterId);
if (res.code === 200) {
ElMessage.success(res.msg || '退床成功');
// 重新加载列表数据
await getList();
} else {
ElMessage.error(res.msg || '退床失败');
}
} catch (err) {
console.error('退床失败:', err);
} finally {
loadingInstance.close();
}
} catch (cancel) {
// 用户取消了操作
}
};
// 拖拽开始事件
function handleDragStart(event: DragEvent, item: any) {
if (event.dataTransfer) {

View File

@@ -75,4 +75,15 @@ export function adviceNoExecute(data) {
method: 'put',
data: data
})
}
/**
* 撤销医嘱校对
*/
export function adviceCancelVerify(data) {
return request({
url: '/nurse-station/advice-process/advice-cancel-verify',
method: 'put',
data: data
})
}

View File

@@ -67,19 +67,13 @@
退回
</el-button>
</template>
<!-- 已校对tab显示执行/不执行 -->
<!-- 已校对tab显示撤销校对 -->
<template v-else-if="activeTab === 'verified'">
<el-button
type="success"
@click="handleExecute"
type="danger"
@click="handleCancelVerify"
>
执行
</el-button>
<el-button
type="warning"
@click="handleVoid"
>
不执行
撤销校对
</el-button>
</template>
</div>
@@ -412,7 +406,7 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from 'vue';
import {adviceVerify, cancel, adviceExecute, adviceNoExecute, getPrescriptionList} from './api';
import {adviceVerify, cancel, adviceExecute, adviceNoExecute, getPrescriptionList, adviceCancelVerify} from './api';
import {patientInfoList} from '../../components/store/patient.js';
import {formatDateStr} from '@/utils/index';
import {RequestStatus} from '@/utils/medicalConstants';
@@ -829,6 +823,23 @@ function handleVoid() {
});
}
/**
* 撤销校对
*/
function handleCancelVerify() {
let list = getSelectRows();
if (list.length === 0) {
proxy.$message.warning('请先选择医嘱信息');
return;
}
adviceCancelVerify(list).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg);
handleGetPrescription();
}
});
}
defineExpose({
handleGetPrescription,
});

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div style="display: flex">
<el-button
type="primary"
@@ -797,6 +797,8 @@ const onSearch = (value) => {
}
.sheet {
flex: 1;
overflow: auto;
padding-bottom: 40px;
/* background: red; */
}
}

View File

@@ -1,6 +1,9 @@
<template>
<div class="app-container">
<div class="left">
<el-card class="left" shadow="hover">
<template #header>
<span style="font-weight: 600">患者列表</span>
</template>
<div class="form">
<el-form
v-show="showSearch"
@@ -10,25 +13,25 @@
label-position="right"
>
<el-form-item
:label="$t('pharmacy.patientInfo')"
label="患者信息"
prop="searchKey"
>
<el-input
v-model="queryParams.searchKey"
:placeholder="$t('pharmacy.placeholderNameOrId')"
placeholder="请输入姓名/证件号"
clearable
style="width: 160px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item
:label="$t('pharmacy.dispenseStatus')"
label="发药状态"
prop="departmentId"
style="margin-left: 10px"
>
<el-select
v-model="queryParams.statusEnum"
:placeholder="$t('pharmacy.placeholderSelectDispenseStatus')"
placeholder="请选择发药状态"
clearable
style="width: 160px"
@change="handleQuery"
@@ -41,152 +44,103 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('pharmacy.visitDate')">
<el-form-item label="就诊日期">
<el-date-picker
v-model="dateRange"
type="daterange"
:start-placeholder="$t('pharmacy.startDate')"
:end-placeholder="$t('pharmacy.endDate')"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 250px"
value-format="YYYY-MM-DD"
@change="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="科室" prop="departmentId">
<el-select
v-model="queryParams.departmentId"
placeholder="请选择科室"
clearable
style="width: 160px"
@change="handleQuery"
>
<el-option
v-for="item in departmentList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> -->
<el-form-item style="margin-left: 15px">
<el-button
type="primary"
@click="handleQuery"
>
{{ $t('common.search') }}
搜索
</el-button>
<el-button @click="resetQuery">
{{ $t('common.reset') }}
重置
</el-button>
</el-form-item>
</el-form>
</div>
<vxe-table
:row-config="{ isCurrent: true }"
:data="patientList"
border
style="width: 100%; flex: 1; min-height: 0"
@cell-click="handleCurrentChange"
>
<vxe-column
field="patientName"
:title="$t('pharmacy.name')"
width="130"
align="center"
/>
<vxe-column
field="genderEnum_enumText"
:title="$t('pharmacy.gender')"
width="80"
align="center"
/>
<vxe-column
field="age"
:title="$t('pharmacy.age')"
width="80"
align="center"
/>
<vxe-column
field="phone"
:title="$t('pharmacy.phone')"
width="80"
align="center"
/>
<vxe-column
field="receptionTime"
:title="$t('pharmacy.visitDateCol')"
align="center"
<div class="table-wrapper">
<vxe-table
:row-config="{ isCurrent: true, keyField: 'encounterId' }"
:data="patientList"
border
highlight-current-row
:auto-resize="true"
@cell-click="handleCurrentChange"
>
<template #default="scope">
{{ scope.row.receptionTime ? formatDate(scope.row.receptionTime) : "-" }}
</template>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</div>
<div class="right">
<!-- <div class="select_wrapper_div">
<p style="margin-right: 60px; font-size: 19px">患者基本信息</p> -->
<!-- <el-button type="success" plain @click="print" icon="Printer" style="margin-left: 30px;">打印</el-button> -->
<!-- </div> -->
<div class="top">
<!-- <el-descriptions :column="4" title="患者基本信息">
<el-descriptions-item label="姓名:">{{ personInfo.patientName }}</el-descriptions-item>
<el-descriptions-item label="性别:">
{{ personInfo.genderEnum_enumText }}
</el-descriptions-item>
<el-descriptions-item label="年龄:">{{ personInfo.age }}</el-descriptions-item>
<el-descriptions-item label="合同类型:">
{{ personInfo.categoryEnum_enumText }}
</el-descriptions-item>
<el-descriptions-item label="就诊科室:">
{{ personInfo.organizationName }}
</el-descriptions-item>
<el-descriptions-item label="就诊日期:">
{{ personInfo.encounterDate }}
</el-descriptions-item>
<el-descriptions-item label="证件号:">{{ personInfo.idCard }}</el-descriptions-item>
<el-descriptions-item label="总金额:">
{{ personInfo.totalPrice ? personInfo.totalPrice.toFixed(2) : '0.00' }}
</el-descriptions-item>
</el-descriptions> -->
<!-- <el-row>
<el-col :span="4">姓名{{ personInfo.patientName }}</el-col>
<el-col :span="3">性别{{ personInfo.genderEnum_enumText }}</el-col>
<el-col :span="3">年龄{{ personInfo.age }}</el-col>
<el-col :span="5">合同类型{{ personInfo.categoryEnum_enumText }}</el-col> </el-row
><br />
<el-row>
<el-col :span="5">就诊科室{{ personInfo.organizationName }}</el-col>
<el-col :span="5">就诊日期{{ personInfo.encounterDate }}</el-col>
<el-col :span="7">证件号{{ personInfo.idCard }}</el-col>
</el-row
><br />
<el-row>
<el-col :span="4"
>总金额{{
personInfo.totalPrice ? personInfo.totalPrice.toFixed(2) : '0.00'
}}</el-col
<vxe-column
field="patientName"
title="姓名"
min-width="55"
align="center"
/>
<vxe-column
field="genderEnum_enumText"
title="性别"
width="56"
align="center"
/>
<vxe-column
field="age"
title="年龄"
width="56"
align="center"
/>
<vxe-column
field="phone"
title="电话"
min-width="55"
align="center"
/>
<vxe-column
field="receptionTime"
title="就诊日期"
min-width="100"
align="center"
>
</el-row> -->
<template #default="scope">
{{ scope.row.receptionTime ? formatDate(scope.row.receptionTime) : "-" }}
</template>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</div>
</el-card>
<el-card class="right" shadow="hover">
<template #header>
<el-row justify="space-between" align="middle">
<span style="font-weight: 600">发药明细</span>
<span style="font-size: 14px; color: #e6a23c">
总金额<strong>¥{{ medicineTotalPrice ? medicineTotalPrice.toFixed(2) : '0.00' }}</strong>
</span>
</el-row>
</template>
<div class="top">
<span
style="color: #606266; font-size: 14px; font-weight: 700; margin-right: 15px"
>
{{ $t('pharmacy.preparer') }}
调配药师
</span>
<el-select
v-model="preparerDoctor"
:placeholder="$t('pharmacy.placeholderPreparer')"
placeholder="调配药师"
style="width: 160px"
>
<el-option
@@ -199,11 +153,11 @@
<span
style="color: #606266; font-size: 14px; font-weight: 700; margin-right: 15px"
>
{{ $t('pharmacy.drugCategory') }}
药品分类
</span>
<el-select
v-model="tcmFlag"
:placeholder="$t('pharmacy.placeholderDrugCategory')"
placeholder="药品分类"
style="width: 160px"
@change="
() => {
@@ -223,11 +177,11 @@
<span
style="color: #606266; font-size: 14px; font-weight: 700; margin-right: 15px"
>
{{ $t('pharmacy.project') }}
项目
</span>
<el-select
v-model="projectTypeCode"
:placeholder="$t('pharmacy.placeholderProject')"
placeholder="项目"
style="width: 160px"
:clearable="false"
@change="
@@ -240,15 +194,15 @@
"
>
<el-option
:label="$t('pharmacy.all')"
label="全部"
value="1"
/>
<el-option
:label="$t('pharmacy.drug')"
label="药品"
value="2"
/>
<el-option
:label="$t('pharmacy.consumable')"
label="耗材"
value="3"
/>
</el-select>
@@ -258,32 +212,29 @@
style="margin-left: 30px"
@click="handleBatch()"
>
{{ $t('pharmacy.batchDispense') }}
批量发药
</el-button>
<el-button
type="primary"
style="margin-left: 30px"
@click="handleScan()"
>
{{ $t('pharmacy.scan') }}
扫码
</el-button>
<el-button
type="primary"
style="margin-left: 30px"
@click="printPrescription()"
>
{{ $t('pharmacy.prescriptionPrint') }}
处方打印
</el-button>
<div style="position: absolute; top: 30px; right: 25px">
{{ $t('pharmacy.totalAmount') }}{{ medicineTotalPrice ? medicineTotalPrice.toFixed(2) : "0.00" }}{{ $t('pharmacy.yuan') }}
</div>
</div>
<vxe-table
ref="tableRef"
v-loading="loading"
:data="medicineInfoList"
border
style="width: 100%; flex: 1; min-height: 0; margin-top: 10px"
:auto-resize="true"
:span-method="spanMethod"
:cell-class-name="cellClassName"
@select="handleSelectionChange"
@@ -297,37 +248,37 @@
/>
<vxe-column
field="prescriptionNo"
:title="$t('pharmacy.prescriptionNo')"
title="处方号"
width="120"
align="center"
/>
<vxe-column
field="conditionName"
:title="$t('pharmacy.diagnosis')"
title="诊断"
width="120"
align="center"
/>
<vxe-column
field="dispenseEnum_enumText"
:title="$t('pharmacy.prescriptionType')"
title="处方类型"
width="120"
align="center"
/>
<vxe-column
field="itemName"
:title="$t('pharmacy.itemName')"
title="项目名称"
width="160"
align="center"
/>
<vxe-column
field="merchandiseName"
:title="$t('pharmacy.merchandiseName')"
title="商品名称"
width="160"
align="center"
/>
<vxe-column
field="quantity"
:title="$t('pharmacy.dispenseQty')"
title="发药数量"
width="130"
align="center"
>
@@ -367,7 +318,7 @@
</vxe-column>
<vxe-column
field="lotNumber"
:title="$t('pharmacy.lotNumber')"
title="批次号"
width="160"
align="center"
>
@@ -405,7 +356,7 @@
</vxe-column>
<vxe-column
field="statusEnum_enumText"
:title="$t('pharmacy.dispenseStatusCol')"
title="发药状态"
width="100"
align="center"
>
@@ -417,7 +368,7 @@
</vxe-column>
<vxe-column
field="dispensePerDuration"
:title="$t('pharmacy.days')"
title="天数"
width="80"
align="center"
/>
@@ -425,7 +376,7 @@
<!-- <vxe-column field="quantity" title="发药数量" width="100" align="center" /> -->
<vxe-column
field="totalVolume"
:title="$t('pharmacy.spec')"
title="规格"
width="100"
align="center"
/>
@@ -444,7 +395,7 @@
<vxe-column
v-if="tcmFlag == '0'"
field="traceNo"
:title="$t('pharmacy.traceNo')"
title="追溯码"
width="180"
align="center"
>
@@ -457,7 +408,7 @@
<el-input
:ref="'traceNoRef' + scope.rowIndex"
v-model="scope.row.traceNo"
:placeholder="$t('pharmacy.placeholderTraceNo')"
placeholder="请输入追溯码"
@input="handleTraceNoInput(scope.row, scope.rowIndex)"
/>
</el-tooltip>
@@ -465,7 +416,7 @@
</vxe-column>
<vxe-column
field="totalPrice"
:title="$t('pharmacy.amount')"
title="金额"
width="100"
:formatter="formatPrice"
align="right"
@@ -473,37 +424,37 @@
/>
<vxe-column
field="locationName"
:title="$t('pharmacy.dispensePharmacy')"
title="发药药房"
width="90"
align="center"
/>
<vxe-column
field="manufacturerText"
:title="$t('pharmacy.manufacturer')"
title="生产厂家"
width="200"
align="center"
/>
<vxe-column
field="doctorName"
:title="$t('pharmacy.orderingDoctor')"
title="开单医生"
width="100"
align="center"
/>
<!-- <vxe-column field="dose" title="剂量" width="100" align="center" /> -->
<vxe-column
field="rateCode"
:title="$t('pharmacy.frequency')"
title="频次"
width="100"
align="center"
/>
<vxe-column
field="methodCode_dictText"
:title="$t('pharmacy.usage')"
title="用法"
width="100"
align="center"
/>
<vxe-column
:title="$t('pharmacy.operation')"
title="操作"
align="center"
width="160"
fixed="right"
@@ -517,7 +468,7 @@
icon="SuccessFilled"
@click="handleBatch(scope.row)"
>
{{ $t('pharmacy.dispense') }}
发药
</el-button>
<el-button
link
@@ -526,7 +477,7 @@
icon="CircleClose"
@click="backMedicine(scope.row)"
>
{{ $t('pharmacy.void') }}
作废
</el-button>
</template>
</vxe-column>
@@ -549,16 +500,17 @@
header-align="center"
/> -->
</vxe-table>
</div>
</el-card>
<el-dialog
v-model="showDialog"
:title="$t('pharmacy.selectVoidReason')"
title="选择作废原因"
width="30%"
>
<!-- 下拉选择框 -->
<el-select
v-model="notPerformedReasonEnum"
:placeholder="$t('pharmacy.placeholderSelectVoidReason')"
placeholder="请选择作废原因"
>
<el-option
v-for="item in backReason"
@@ -574,8 +526,8 @@
<el-button
type="primary"
@click="handleConfirm"
>{{ $t('pharmacy.confirm') }}</el-button>
<el-button @click="handleCancel">{{ $t('pharmacy.cancel') }}</el-button>
> </el-button>
<el-button @click="handleCancel"> </el-button>
</span>
</template>
</el-dialog>
@@ -589,8 +541,7 @@
</template>
<script setup name="westernmedicine">
import {onMounted, ref, computed} from 'vue';
import {useI18n} from 'vue-i18n';
import {onMounted, ref} from 'vue';
import {ElMessage} from 'element-plus';
import {
deviceDispense,
@@ -617,7 +568,6 @@ import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const { t } = useI18n();
const {proxy} = getCurrentInstance();
const showSearch = ref(true);
const total = ref(0);
@@ -657,13 +607,13 @@ const ypName = ref('');
const medicineTotalPrice = ref(0);
const projectTypeCode = ref("2");
const selectedRow = ref(null);
const medCategoryCode = computed(() => [
const medCategoryCode = ref([
{
label: t('pharmacy.westernChinese'),
label: "西药中成药",
value: "0",
},
{
label: t('pharmacy.chinese'),
label: "中药",
value: "1",
},
]);
@@ -699,7 +649,7 @@ function submit(value) {
itemTraceNo(list).then((res) => {
if (res.code === 200) {
if (!res.data) {
proxy.$modal.msgWarning(t('pharmacy.msgNoMatchTraceNo'));
proxy.$modal.msgWarning("未在库存中匹配到追溯码,请在发药列表中单独扫描");
openTraceNoDialog.value = false;
proxy.$refs["traceNoRef0"].focus();
return;
@@ -709,7 +659,7 @@ function submit(value) {
res.data[item.medicineId] &&
res.data[item.medicineId].split(",") > item.quantity
) {
proxy.$modal.msgWarning(t('pharmacy.msgOperationFailed'));
proxy.$modal.msgWarning("操作失败");
return;
}
medicineInfoList.value[index].traceNo = res.data[item.medicineId];
@@ -724,12 +674,16 @@ function getList() {
queryParams.value.receptionTimeETime = dateRange.value[1] + ' 23:59:59';
if (projectTypeCode.value == 2) {
listPatient(queryParams.value).then((response) => {
patientList.value = response.data.records;
patientList.value = (response.data.records || []).sort((a, b) => {
return new Date(b.receptionTime) - new Date(a.receptionTime);
});
total.value = response.data.total;
});
} else if (projectTypeCode.value == 3) {
devicePatientList(queryParams.value).then((response) => {
patientList.value = response.data.records;
patientList.value = (response.data.records || []).sort((a, b) => {
return new Date(b.receptionTime) - new Date(a.receptionTime);
});
total.value = response.data.total;
});
} else {
@@ -744,6 +698,10 @@ function getList() {
(item, index, self) =>
index === self.findIndex((record) => record.encounterId === item.encounterId)
);
// 按就诊时间倒序
uniqueRecords.sort((a, b) => {
return new Date(b.receptionTime) - new Date(a.receptionTime);
});
patientList.value = uniqueRecords;
// 合并总数量(如果需要精确数量,可能需要后端支持)
total.value = uniqueRecords.length;
@@ -762,13 +720,13 @@ function getList() {
async function printPrescription() {
// 空值检查 - 防止 tableRef 为空时报错
if (!tableRef.value) {
proxy.$modal.msgWarning(t('pharmacy.printWarningNoTable'));
proxy.$modal.msgWarning("表格组件未初始化,请刷新页面重试");
return;
}
const selectedRows = tableRef.value.getSelectionRows();
if (!selectedRows || selectedRows.length === 0) {
proxy.$modal.msgWarning(t('pharmacy.printWarningNoSelect'));
proxy.$modal.msgWarning("未选择要打印的项目,请重新选择,打印失败");
return;
}
@@ -779,7 +737,7 @@ async function printPrescription() {
const res = await advicePrint({requestIds: requestIds, isPrescription: "1"});
if (!res || !res.data || !res.data.adviceItemList) {
proxy.$modal.msgWarning(t('pharmacy.printWarningNoData'));
proxy.$modal.msgWarning("获取打印数据失败,请稍后重试");
return;
}
@@ -885,7 +843,7 @@ async function printPrescription() {
});
} catch (error) {
console.error('处方打印失败:', error);
proxy.$modal.msgError(t('pharmacy.printFailed') + ': ' + (error.message || t('common.error')));
proxy.$modal.msgError('处方打印失败: ' + (error.message || '未知错误'));
}
}
@@ -920,14 +878,14 @@ function formatLotNumberLabel(row, item) {
if (row.unitCode == item.maxUnitCode) {
return (
item.inventoryLotNumber +
' ' + t('pharmacy.remaining') + '' +
' 剩余' +
formatInventory(
item.inventoryQuantity,
item.partPercent,
item.maxUnitCode_dictText,
item.inventoryUnitCode_dictText
) +
' ' + t('pharmacy.expDate') + '' +
' 有效期至' +
proxy.formatDateStr(item.expirationDate, 'YYYY-MM-DD')
);
} else {
@@ -937,7 +895,7 @@ function formatLotNumberLabel(row, item) {
item.inventoryQuantity +
' ' +
item.inventoryUnitCode_dictText +
' ' + t('pharmacy.expDate') + '' +
' 有效期至' +
proxy.formatDateStr(item.expirationDate, 'YYYY-MM-DD')
);
}
@@ -1054,9 +1012,9 @@ function handleCellDbClick(row, column, cellValue, event) {
function formatPrice(row, column, cellValue) {
if (cellValue === null || cellValue === undefined) {
return "0.00 " + t('pharmacy.yuan');
return "0.00 元"; // 如果值为空返回0.00
}
return cellValue.toFixed(2) + " " + t('pharmacy.yuan');
return cellValue.toFixed(2) + " "; // 保留两位小数
}
function tagType(statusEnum) {
@@ -1071,11 +1029,14 @@ function tagType(statusEnum) {
}
}
function handleCurrentChange(row) {
function handleCurrentChange(params) {
// vxe-table v4 cell-click 事件传递 { row, column, ... } 包装对象
const row = params.row || params;
loading.value = true;
// 切换患者时先清空旧数据,避免显示上一个患者的内容
medicineInfoList.value = [];
medicineTotalPrice.value = 0;
currentRow.value = row; // 更新当前选中行的数据
currentRow.value.statusEnum = undefined;
currentRow.value.dispenseStatus = queryParams.value.statusEnum;
getAdjustPriceSwitchState().then((res) => {
adjustPriceSwitchState.value = res.data;
if (adjustPriceSwitchState.value) {
@@ -1083,6 +1044,8 @@ function handleCurrentChange(row) {
.then((res) => {
if (res.code == 200) {
getMedicineList(row.encounterId);
} else {
loading.value = false;
}
})
.catch(() => {
@@ -1091,11 +1054,14 @@ function handleCurrentChange(row) {
} else {
getMedicineList(row.encounterId);
}
}).catch(() => {
loading.value = false;
});
}
function getMedicineList(encounterId) {
// 根据projectTypeCode的值决定调用哪些接口
const requestedEncounterId = encounterId; // 记录本次请求的encounterId防止竞态
if (projectTypeCode.value == 1) {
// 同时调用两个接口并将数据合并显示
Promise.all([
@@ -1128,11 +1094,16 @@ function getMedicineList(encounterId) {
medicineInfoList.value = [...westernData, ...reportData];
// 处理合并后的数据
processMedicineListData();
loading.value = false;
// 竞态保护:仅当当前选中患者未变化时才更新数据
if (currentRow.value && currentRow.value.encounterId === requestedEncounterId) {
processMedicineListData();
}
})
.catch((error) => {
proxy.$modal.msgError(t('pharmacy.msgDispenseFailed'));
.catch(() => {
proxy.$modal.msgError('获取发药明细失败');
})
.finally(() => {
loading.value = false;
});
} else if (projectTypeCode.value == 2) {
// 只调用listWesternmedicine接口
@@ -1148,8 +1119,13 @@ function getMedicineList(encounterId) {
? response.data.records
: [response.data.records];
// 处理数据
processMedicineListData();
// 🔧 竞态保护:仅当当前选中患者未变化时才更新数据
if (currentRow.value && currentRow.value.encounterId === requestedEncounterId) {
processMedicineListData();
}
}).catch(() => {
proxy.$modal.msgError('获取发药明细失败');
}).finally(() => {
loading.value = false;
});
} else if (projectTypeCode.value == 3) {
@@ -1163,8 +1139,13 @@ function getMedicineList(encounterId) {
? response.data.records
: [response.data.records];
// 处理数据
processMedicineListData();
// 🔧 竞态保护
if (currentRow.value && currentRow.value.encounterId === requestedEncounterId) {
processMedicineListData();
}
}).catch(() => {
proxy.$modal.msgError('获取耗材明细失败');
}).finally(() => {
loading.value = false;
});
}
@@ -1230,7 +1211,7 @@ function submitMedicine(saveList) {
promises.push(
deviceDispense(deviceList).then((res) => {
if (res.code != 200) {
throw new Error(t('pharmacy.msgDispenseFailed'));
throw new Error("耗材发药失败");
}
return res;
})
@@ -1249,7 +1230,7 @@ function submitMedicine(saveList) {
promises.push(
prepareMedicion(preparationList).then((res) => {
if (res.code != 200) {
throw new Error(t('pharmacy.msgDispenseFailed'));
throw new Error('药品发药失败');
}
return res;
})
@@ -1259,7 +1240,7 @@ function submitMedicine(saveList) {
promises.push(
updateMedicion(preparedList).then((res) => {
if (res.code != 200) {
throw new Error(t('pharmacy.msgDispenseFailed'));
throw new Error("药品发药失败");
}
return res;
})
@@ -1275,12 +1256,12 @@ function submitMedicine(saveList) {
}
})
.then((response) => {
proxy.$modal.msgSuccess(t('pharmacy.msgDispenseSuccess'));
proxy.$modal.msgSuccess("发药成功");
// 重新获取数据
getMedicineList(currentRow.value.encounterId);
})
.catch((error) => {
proxy.$modal.msgError(t('pharmacy.msgDispenseFailed') + ': ' + error.message);
proxy.$modal.msgError('发药失败: ' + error.message);
});
} else if (projectTypeCode.value == 2) {
let preparationList = saveList.filter((item) => {
@@ -1298,7 +1279,7 @@ function submitMedicine(saveList) {
prepareMedicion(preparationList).then((res) => {
if (res.code == 200) {
updateMedicion(preparationList).then((response) => {
proxy.$modal.msgSuccess(t('pharmacy.msgDispenseSuccess'));
proxy.$modal.msgSuccess('发药成功');
getMedicineList(currentRow.value.encounterId);
});
}
@@ -1322,7 +1303,7 @@ function validInventory(row, item) {
quantity = quantity * item.partPercent;
}
if (quantity > item.inventoryQuantity) {
proxy.$modal.msgError(t('pharmacy.msgInsufficientStock'));
proxy.$modal.msgError('当前批次库存不足');
return;
}
}
@@ -1338,10 +1319,10 @@ function handleQuantity(row) {
});
if (totalQuantity > row.requestQuantity) {
couldSave.value = false;
proxy.$modal.msgError(t('pharmacy.msgQtyExceedsTotal'));
proxy.$modal.msgError('发药数量不能大于总数量');
} else if (totalQuantity < row.requestQuantity) {
couldSave.value = false;
proxy.$modal.msgError(t('pharmacy.msgQtyBelowTotal'));
proxy.$modal.msgError('发药数量不能小于总数量');
} else {
couldSave.value = true;
}
@@ -1430,7 +1411,7 @@ function handleBatch(row) {
};
});
} else {
proxy.$modal.msgWarning(t('pharmacy.msgSelectDispenseItem'));
proxy.$modal.msgWarning('未选择要发药的项目,请重新选择,发药失败');
return;
}
}
@@ -1458,7 +1439,7 @@ async function handleConfirm() {
return;
}
const loading = ElLoading.service({text: t('pharmacy.voiding')});
const loading = ElLoading.service({text: "作废中..."});
try {
const params = buildParams();
@@ -1474,10 +1455,10 @@ async function handleConfirm() {
// 分离验证逻辑
function validate() {
if (!notPerformedReasonEnum.value) {
return {success: false, message: t('pharmacy.msgSelectVoidReason')};
return {success: false, message: "请选择作废原因"};
}
if (!selectedRow.value) {
return {success: false, message: t('pharmacy.msgNoDataSelected')};
return {success: false, message: "未选择数据"};
}
return {success: true};
}
@@ -1488,32 +1469,64 @@ function validate() {
padding: 20px;
display: flex;
height: calc(100vh - 84px);
min-width: 1024px;
overflow: hidden;
}
.left {
width: 25%;
min-width: 0;
width: 35%;
min-width: 420px;
flex-shrink: 0;
display: flex;
flex-direction: column;
overflow: hidden;
overflow: auto;
:deep(.el-card__body) {
flex: 1;
display: flex;
flex-direction: column;
}
.form {
width: 100%;
display: flex;
justify-content: space-between;
flex-shrink: 0;
flex-wrap: wrap;
gap: 8px;
}
.table-wrapper {
display: flex;
flex-direction: column;
}
}
.right {
margin-left: 12px;
flex: 1;
min-width: 0;
overflow: auto;
display: flex;
flex-direction: column;
overflow: hidden;
:deep(.el-card__body) {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
.top {
flex-shrink: 0;
margin-bottom: 10px;
padding: 5px 0;
position: relative;
}
.vxe-table {
flex: 1;
min-height: 0;
}
}
/* 表格文字颜色改为纯黑色 */
@@ -1604,4 +1617,8 @@ function validate() {
:deep(.vxe-table--border th.gutter:last-of-type) {
border-color: #dddde0;
}
:deep(.vxe-header--column .vxe-cell) {
white-space: nowrap;
}
</style>