Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -641,11 +641,13 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// 就诊id
|
// 就诊id
|
||||||
Long encounterId = adviceSaveList.get(0).getEncounterId();
|
Long encounterId = adviceSaveList.get(0).getEncounterId();
|
||||||
iChargeItemService.update(new LambdaUpdateWrapper<ChargeItem>()
|
|
||||||
.set(ChargeItem::getStatusEnum, ChargeItemStatus.PLANNED.getValue())
|
// 使用安全的更新方法,避免并发冲突
|
||||||
.eq(ChargeItem::getEncounterId, encounterId)
|
iChargeItemService.updateChargeStatusByConditionSafe(
|
||||||
.eq(ChargeItem::getStatusEnum, ChargeItemStatus.DRAFT.getValue())
|
encounterId,
|
||||||
.in(ChargeItem::getServiceId, requestIds));
|
ChargeItemStatus.DRAFT.getValue(),
|
||||||
|
ChargeItemStatus.PLANNED.getValue(),
|
||||||
|
requestIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据变更后清理相关缓存
|
// 数据变更后清理相关缓存
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.openhis.web.doctorstation.controller;
|
package com.openhis.web.doctorstation.controller;
|
||||||
|
|
||||||
|
import com.core.common.annotation.RepeatSubmit;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.openhis.common.enums.AdviceOpType;
|
import com.openhis.common.enums.AdviceOpType;
|
||||||
import com.openhis.common.enums.Whether;
|
import com.openhis.common.enums.Whether;
|
||||||
@@ -85,6 +86,7 @@ public class DoctorStationAdviceController {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping(value = "/sign-advice")
|
@PostMapping(value = "/sign-advice")
|
||||||
|
@RepeatSubmit(interval = 5000, message = "请勿重复签发医嘱,请稍候再试")
|
||||||
public R<?> signAdvice(@RequestBody AdviceSaveParam adviceSaveParam) {
|
public R<?> signAdvice(@RequestBody AdviceSaveParam adviceSaveParam) {
|
||||||
return iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, AdviceOpType.SIGN_ADVICE.getCode());
|
return iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, AdviceOpType.SIGN_ADVICE.getCode());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ public class RequestBaseDto {
|
|||||||
@JsonSerialize(using = ToStringSerializer.class)
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
private Long chargeItemId;
|
private Long chargeItemId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 医嘱定义对应表名
|
||||||
|
*/
|
||||||
|
private String adviceTableName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 医嘱名称
|
* 医嘱名称
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ public class MedicalDeviceDispenseAppServiceImpl implements IMedicalDeviceDispen
|
|||||||
}
|
}
|
||||||
List<ChargeItem> chargeItemList = chargeItemService.listByIds(chargeItemIds);
|
List<ChargeItem> chargeItemList = chargeItemService.listByIds(chargeItemIds);
|
||||||
if (chargeItemList == null || chargeItemList.isEmpty()) {
|
if (chargeItemList == null || chargeItemList.isEmpty()) {
|
||||||
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
|
return R.fail("未查询到耗材收费项目信息");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取发申请id列表
|
// 获取发申请id列表
|
||||||
|
|||||||
@@ -58,12 +58,32 @@ public interface IChargeItemService extends IService<ChargeItem> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新收费状态
|
* 更新收费状态
|
||||||
*
|
*
|
||||||
* @param chargeItemIdList 收费项目id集合
|
* @param chargeItemIdList 收费项目id集合
|
||||||
* @param value 状态值
|
* @param value 状态值
|
||||||
*/
|
*/
|
||||||
void updatePaymentStatus(List<Long> chargeItemIdList, Integer value);
|
void updatePaymentStatus(List<Long> chargeItemIdList, Integer value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全更新收费项目状态(按条件更新,避免并发冲突)
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @param fromStatusEnum 原状态
|
||||||
|
* @param toStatusEnum 目标状态
|
||||||
|
* @param serviceIds 服务ID列表
|
||||||
|
*/
|
||||||
|
void updateChargeStatusByCondition(Long encounterId, Integer fromStatusEnum, Integer toStatusEnum, List<Long> serviceIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全批量更新收费项目状态(逐个更新,避免并发冲突)
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @param fromStatusEnum 原状态
|
||||||
|
* @param toStatusEnum 目标状态
|
||||||
|
* @param serviceIds 服务ID列表
|
||||||
|
*/
|
||||||
|
void updateChargeStatusByConditionSafe(Long encounterId, Integer fromStatusEnum, Integer toStatusEnum, List<Long> serviceIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据表名和id删除费用项
|
* 根据表名和id删除费用项
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ import com.openhis.administration.service.IChargeItemService;
|
|||||||
import com.openhis.common.enums.*;
|
import com.openhis.common.enums.*;
|
||||||
import com.openhis.common.utils.EnumUtils;
|
import com.openhis.common.utils.EnumUtils;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +40,7 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class ChargeItemServiceImpl extends ServiceImpl<ChargeItemMapper, ChargeItem> implements IChargeItemService {
|
public class ChargeItemServiceImpl extends ServiceImpl<ChargeItemMapper, ChargeItem> implements IChargeItemService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -130,10 +133,93 @@ public class ChargeItemServiceImpl extends ServiceImpl<ChargeItemMapper, ChargeI
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updatePaymentStatus(List<Long> chargeItemIdList, Integer value) {
|
public void updatePaymentStatus(List<Long> chargeItemIdList, Integer value) {
|
||||||
baseMapper.update(
|
if (chargeItemIdList == null || chargeItemIdList.isEmpty()) {
|
||||||
new ChargeItem().setStatusEnum(value).setPerformerId(SecurityUtils.getLoginUser().getPractitionerId()),
|
return;
|
||||||
new LambdaUpdateWrapper<ChargeItem>().in(ChargeItem::getId, chargeItemIdList).eq(ChargeItem::getDeleteFlag,
|
}
|
||||||
DelFlag.NO.getCode()));
|
|
||||||
|
// 逐个更新,避免并发冲突
|
||||||
|
for (Long chargeItemId : chargeItemIdList) {
|
||||||
|
int retryCount = 0;
|
||||||
|
final int maxRetries = 3;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
// 先查询当前状态,确保更新操作的安全性
|
||||||
|
ChargeItem currentChargeItem = baseMapper.selectById(chargeItemId);
|
||||||
|
|
||||||
|
if (currentChargeItem == null) {
|
||||||
|
log.warn("收费项目不存在,ID: {}", chargeItemId);
|
||||||
|
break; // 项目不存在,跳出重试循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前状态是否已经是目标状态,避免不必要的更新
|
||||||
|
if (currentChargeItem.getStatusEnum().equals(value)) {
|
||||||
|
log.debug("收费项目已是目标状态,跳过更新,ID: {}, 状态: {}", chargeItemId, value);
|
||||||
|
break; // 已是目标状态,跳出重试循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用精确的条件更新,包括当前状态,防止并发更新冲突
|
||||||
|
int updatedRows = baseMapper.update(
|
||||||
|
new ChargeItem()
|
||||||
|
.setStatusEnum(value)
|
||||||
|
.setPerformerId(SecurityUtils.getLoginUser().getPractitionerId()),
|
||||||
|
new LambdaUpdateWrapper<ChargeItem>()
|
||||||
|
.eq(ChargeItem::getId, chargeItemId)
|
||||||
|
.eq(ChargeItem::getStatusEnum, currentChargeItem.getStatusEnum()) // 使用当前状态作为条件
|
||||||
|
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode()));
|
||||||
|
|
||||||
|
// 如果成功更新了行数,跳出重试循环
|
||||||
|
if (updatedRows > 0) {
|
||||||
|
log.debug("收费项目状态更新成功,ID: {}, 状态: {}", chargeItemId, value);
|
||||||
|
break; // 成功更新,跳出重试循环
|
||||||
|
} else {
|
||||||
|
log.warn("收费项目更新失败,可能已被其他事务更改,ID: {}, 目标状态: {},重试次数: {}",
|
||||||
|
chargeItemId, value, retryCount + 1);
|
||||||
|
|
||||||
|
// 增加重试计数
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
// 如果达到最大重试次数,检查最终状态
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
ChargeItem finalChargeItem = baseMapper.selectById(chargeItemId);
|
||||||
|
if (finalChargeItem != null && finalChargeItem.getStatusEnum().equals(value)) {
|
||||||
|
log.debug("收费项目最终已是目标状态,ID: {}", chargeItemId);
|
||||||
|
break; // 最终状态符合预期,跳出重试循环
|
||||||
|
} else {
|
||||||
|
log.warn("收费项目状态更新失败,达到最大重试次数,ID: {}, 期望状态: {}, 当前状态: {}",
|
||||||
|
chargeItemId, value, finalChargeItem != null ? finalChargeItem.getStatusEnum() : "NULL");
|
||||||
|
break; // 达到最大重试次数,跳出重试循环
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 短暂延迟后重试,避免过于频繁的重试
|
||||||
|
try {
|
||||||
|
Thread.sleep(10); // 10毫秒延迟
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break; // 中断线程,跳出重试循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新收费项目状态失败,ID: {}, 状态: {},重试次数: {}", chargeItemId, value, retryCount + 1, e);
|
||||||
|
|
||||||
|
retryCount++;
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
log.error("更新收费项目状态失败,达到最大重试次数,ID: {}, 状态: {}", chargeItemId, value, e);
|
||||||
|
// 不抛出异常,以避免整个事务回滚
|
||||||
|
break; // 达到最大重试次数,跳出重试循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 短暂延迟后重试
|
||||||
|
try {
|
||||||
|
Thread.sleep(10);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break; // 中断线程,跳出重试循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,4 +410,130 @@ public class ChargeItemServiceImpl extends ServiceImpl<ChargeItemMapper, ChargeI
|
|||||||
});
|
});
|
||||||
return costDetails;
|
return costDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateChargeStatusByCondition(Long encounterId, Integer fromStatusEnum, Integer toStatusEnum, List<Long> serviceIds) {
|
||||||
|
if (serviceIds == null || serviceIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用逐个更新的方式,避免批量更新的并发问题
|
||||||
|
for (Long serviceId : serviceIds) {
|
||||||
|
try {
|
||||||
|
int updatedRows = baseMapper.update(
|
||||||
|
new ChargeItem().setStatusEnum(toStatusEnum),
|
||||||
|
new LambdaUpdateWrapper<ChargeItem>()
|
||||||
|
.eq(ChargeItem::getEncounterId, encounterId)
|
||||||
|
.eq(ChargeItem::getStatusEnum, fromStatusEnum) // 确保原状态匹配
|
||||||
|
.eq(ChargeItem::getServiceId, serviceId)
|
||||||
|
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果没有更新任何行,可能是因为状态已经被其他事务更改
|
||||||
|
if (updatedRows == 0) {
|
||||||
|
log.warn("收费项目状态更新失败或无变化,encounterId: {}, serviceId: {}, 原状态: {}, 目标状态: {}",
|
||||||
|
encounterId, serviceId, fromStatusEnum, toStatusEnum);
|
||||||
|
|
||||||
|
// 查询当前状态以供调试
|
||||||
|
List<ChargeItem> currentChargeItems = baseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ChargeItem>()
|
||||||
|
.eq(ChargeItem::getEncounterId, encounterId)
|
||||||
|
.eq(ChargeItem::getServiceId, serviceId)
|
||||||
|
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (ChargeItem item : currentChargeItems) {
|
||||||
|
log.info("收费项目当前状态,ID: {}, 状态: {}", item.getId(), item.getStatusEnum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新收费项目状态失败,encounterId: {}, serviceId: {}, 原状态: {}, 目标状态: {}",
|
||||||
|
encounterId, serviceId, fromStatusEnum, toStatusEnum, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateChargeStatusByConditionSafe(Long encounterId, Integer fromStatusEnum, Integer toStatusEnum, List<Long> serviceIds) {
|
||||||
|
if (serviceIds == null || serviceIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用逐个更新的方式,避免批量更新的并发问题
|
||||||
|
for (Long serviceId : serviceIds) {
|
||||||
|
int retryCount = 0;
|
||||||
|
final int maxRetries = 3;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
// 先查询当前符合条件的收费项目
|
||||||
|
List<ChargeItem> chargeItems = baseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ChargeItem>()
|
||||||
|
.eq(ChargeItem::getEncounterId, encounterId)
|
||||||
|
.eq(ChargeItem::getStatusEnum, fromStatusEnum)
|
||||||
|
.eq(ChargeItem::getServiceId, serviceId)
|
||||||
|
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chargeItems.isEmpty()) {
|
||||||
|
log.debug("未找到符合条件的收费项目,encounterId: {}, serviceId: {}, 原状态: {}",
|
||||||
|
encounterId, serviceId, fromStatusEnum);
|
||||||
|
break; // 没有符合条件的项目,跳出重试循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对每个符合条件的收费项目进行更新
|
||||||
|
for (ChargeItem chargeItem : chargeItems) {
|
||||||
|
int updatedRows = baseMapper.update(
|
||||||
|
new ChargeItem().setStatusEnum(toStatusEnum),
|
||||||
|
new LambdaUpdateWrapper<ChargeItem>()
|
||||||
|
.eq(ChargeItem::getId, chargeItem.getId())
|
||||||
|
.eq(ChargeItem::getStatusEnum, fromStatusEnum) // 确保原状态匹配
|
||||||
|
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedRows > 0) {
|
||||||
|
log.debug("收费项目状态更新成功,ID: {}, 从状态: {} 到状态: {}",
|
||||||
|
chargeItem.getId(), fromStatusEnum, toStatusEnum);
|
||||||
|
} else {
|
||||||
|
log.warn("收费项目状态更新失败,ID: {}, 从状态: {} 到状态: {}",
|
||||||
|
chargeItem.getId(), fromStatusEnum, toStatusEnum);
|
||||||
|
|
||||||
|
// 检查项目当前状态
|
||||||
|
ChargeItem currentChargeItem = baseMapper.selectById(chargeItem.getId());
|
||||||
|
if (currentChargeItem != null) {
|
||||||
|
log.debug("收费项目当前状态,ID: {}, 状态: {}", currentChargeItem.getId(), currentChargeItem.getStatusEnum());
|
||||||
|
|
||||||
|
// 如果已经是目标状态,则认为更新成功
|
||||||
|
if (currentChargeItem.getStatusEnum().equals(toStatusEnum)) {
|
||||||
|
log.debug("收费项目已是目标状态,ID: {}", currentChargeItem.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功更新,跳出重试循环
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
retryCount++;
|
||||||
|
log.warn("收费项目状态更新失败,encounterId: {}, serviceId: {}, 原状态: {}, 目标状态: {},重试次数: {}",
|
||||||
|
encounterId, serviceId, fromStatusEnum, toStatusEnum, retryCount, e);
|
||||||
|
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
log.error("收费项目状态更新失败,达到最大重试次数,encounterId: {}, serviceId: {}, 原状态: {}, 目标状态: {}",
|
||||||
|
encounterId, serviceId, fromStatusEnum, toStatusEnum, e);
|
||||||
|
// 不抛出异常,以避免影响整体流程
|
||||||
|
} else {
|
||||||
|
// 短暂延迟后重试
|
||||||
|
try {
|
||||||
|
Thread.sleep(10);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break; // 中断线程,跳出重试循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ export function getList(queryParams) {
|
|||||||
/**
|
/**
|
||||||
* 患者处方列表
|
* 患者处方列表
|
||||||
*/
|
*/
|
||||||
export function getChargeList(encounterId) {
|
export function getChargeList(encounterId, config = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: '/charge-manage/charge/patient-prescription?encounterId=' + encounterId,
|
url: '/charge-manage/charge/patient-prescription?encounterId=' + encounterId,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
...config
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,11 +112,12 @@ export function unprecharge(data) {
|
|||||||
/**
|
/**
|
||||||
* 发耗材
|
* 发耗材
|
||||||
*/
|
*/
|
||||||
export function dispenseMedicalConsumables(data) {
|
export function dispenseMedicalConsumables(data, config = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: '/pharmacy-manage/device-dispense/consumables-dispense',
|
url: '/pharmacy-manage/device-dispense/consumables-dispense',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data,
|
||||||
|
...config
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,11 +125,12 @@ export function dispenseMedicalConsumables(data) {
|
|||||||
/**
|
/**
|
||||||
* 补打小票
|
* 补打小票
|
||||||
*/
|
*/
|
||||||
export function getChargeInfo(param) {
|
export function getChargeInfo(param, config = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: '/payment/bill/getDetail',
|
url: '/payment/bill/getDetail',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: param
|
params: param,
|
||||||
|
...config
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -476,9 +476,12 @@ async function printReceipt(param) {
|
|||||||
return amount > 0 ? amount + ' 元' : amount;
|
return amount > 0 ? amount + ' 元' : amount;
|
||||||
})(), // 个人现金支付金额(现金)
|
})(), // 个人现金支付金额(现金)
|
||||||
SELF_CASH_VX_VALUE: (() => {
|
SELF_CASH_VX_VALUE: (() => {
|
||||||
const amount = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
|
// 微信+银联+支付宝的合计金额
|
||||||
return amount > 0 ? amount + ' 元' : amount;
|
const vxValue = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
|
||||||
})(), // 个人现金支付金额(微信)
|
const unionValue = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
|
||||||
|
const aliValue = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
|
||||||
|
return (Number(vxValue) + Number(unionValue) + Number(aliValue)).toFixed(2) + ' 元';
|
||||||
|
})(),
|
||||||
SELF_CASH_ALI_VALUE: (() => {
|
SELF_CASH_ALI_VALUE: (() => {
|
||||||
const amount = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
|
const amount = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
|
||||||
return amount > 0 ? amount + ' 元' : amount;
|
return amount > 0 ? amount + ' 元' : amount;
|
||||||
@@ -537,14 +540,6 @@ async function printReceipt(param) {
|
|||||||
const amount = param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0;
|
const amount = param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0;
|
||||||
return amount > 0 ? amount + ' 元' : amount;
|
return amount > 0 ? amount + ' 元' : amount;
|
||||||
})(), // 保健预支基金
|
})(), // 保健预支基金
|
||||||
//微信刷卡支付
|
|
||||||
SELF_CASH_VX_VALUE: (() => {
|
|
||||||
// const cashValue = param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0;
|
|
||||||
const vxValue = param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0;
|
|
||||||
const unionValue = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
|
|
||||||
const aliValue = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
|
|
||||||
return (Number(vxValue) + Number(unionValue) + Number(aliValue)).toFixed(2) + ' 元';
|
|
||||||
})(),
|
|
||||||
|
|
||||||
Mr_QR_Code: param.regNo,
|
Mr_QR_Code: param.regNo,
|
||||||
regNo: param.regNo || '',
|
regNo: param.regNo || '',
|
||||||
@@ -659,16 +654,24 @@ async function submit() {
|
|||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
getChargeInfo({ paymentId: props.paymentId }).then((res) => {
|
getChargeInfo({ paymentId: props.paymentId }, { skipErrorMsg: true })
|
||||||
// 传递完整的选中数据信息到打印方法
|
.then((res) => {
|
||||||
printReceipt({ ...res.data, chargedItems: props.chargedItems });
|
// 传递完整的选中数据信息到打印方法
|
||||||
});
|
printReceipt({ ...res.data, chargedItems: props.chargedItems });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 打印失败不影响收费成功流程
|
||||||
|
});
|
||||||
formData.selfPay = [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }];
|
formData.selfPay = [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }];
|
||||||
emit('close', 'success', res.msg);
|
emit('close', 'success', res.msg);
|
||||||
emit('refresh'); // 发送刷新事件给父组件
|
emit('refresh'); // 发送刷新事件给父组件
|
||||||
// 长春市朝阳区中医院自动发耗材
|
// 长春市朝阳区中医院自动发耗材(静默执行,不阻塞主流程)
|
||||||
if (userStore.fixmedinsCode == 'H22010200672' && props.consumablesIdList.length > 0) {
|
if (userStore.fixmedinsCode == 'H22010200672' && props.consumablesIdList.length > 0) {
|
||||||
dispenseMedicalConsumables(props.consumablesIdList);
|
dispenseMedicalConsumables(props.consumablesIdList, { skipErrorMsg: true })
|
||||||
|
.then(() => {})
|
||||||
|
.catch(() => {
|
||||||
|
// 发耗材失败不影响收费成功流程
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError(res.msg || '收费失败');
|
proxy.$modal.msgError(res.msg || '收费失败');
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ function handleClose(value, msg) {
|
|||||||
if (value == 'success') {
|
if (value == 'success') {
|
||||||
proxy.$modal.msgSuccess(msg);
|
proxy.$modal.msgSuccess(msg);
|
||||||
chargeLoading.value = true;
|
chargeLoading.value = true;
|
||||||
getChargeList(patientInfo.value.encounterId).then((res) => {
|
getChargeList(patientInfo.value.encounterId, { skipErrorMsg: true }).then((res) => {
|
||||||
chargeList.value = res.data;
|
chargeList.value = res.data;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
chargeLoading.value = false;
|
chargeLoading.value = false;
|
||||||
|
|||||||
@@ -262,24 +262,19 @@ function fetchFromApi(searchKey) {
|
|||||||
adviceBaseList.value = res.data.records.map((item) => {
|
adviceBaseList.value = res.data.records.map((item) => {
|
||||||
console.log('[Debug] 耗材项:', item.name, 'price:', item.price, 'retailPrice:', item.retailPrice);
|
console.log('[Debug] 耗材项:', item.name, 'price:', item.price, 'retailPrice:', item.retailPrice);
|
||||||
return {
|
return {
|
||||||
|
...item,
|
||||||
|
// 🔧 Bug Fix: 强制覆盖后端返回的字段,确保数据正确
|
||||||
adviceName: item.name || item.busNo,
|
adviceName: item.name || item.busNo,
|
||||||
adviceType: 4,
|
adviceType: 4, // 强制设置为前端耗材类型
|
||||||
|
adviceTableName: 'adm_device_definition',
|
||||||
unitCode: item.unitCode || '',
|
unitCode: item.unitCode || '',
|
||||||
unitCode_dictText: item.unitCode_dictText || '',
|
unitCode_dictText: item.unitCode_dictText || '',
|
||||||
minUnitCode: item.minUnitCode || item.unitCode || '',
|
minUnitCode: item.minUnitCode || item.unitCode || '',
|
||||||
minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
|
minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
|
||||||
volume: item.size || item.totalVolume || '',
|
volume: item.size || item.totalVolume || '',
|
||||||
partPercent: item.partPercent || 1,
|
partPercent: item.partPercent || 1,
|
||||||
// 🔧 Bug #220 修复:正确处理耗材价格,支持price或retailPrice字段
|
|
||||||
// 零售价可能是0,所以不能用简单的布尔判断,需要明确检查null/undefined
|
|
||||||
priceList: (item.price !== undefined && item.price !== null)
|
|
||||||
? [{ price: item.price }]
|
|
||||||
: ((item.retailPrice !== undefined && item.retailPrice !== null)
|
|
||||||
? [{ price: item.retailPrice }]
|
|
||||||
: []),
|
|
||||||
inventoryList: [],
|
inventoryList: [],
|
||||||
adviceDefinitionId: item.id,
|
adviceDefinitionId: item.id,
|
||||||
adviceTableName: 'adm_device_definition',
|
|
||||||
chargeItemDefinitionId: item.id,
|
chargeItemDefinitionId: item.id,
|
||||||
positionId: item.locationId,
|
positionId: item.locationId,
|
||||||
positionName: item.locationId_dictText || '',
|
positionName: item.locationId_dictText || '',
|
||||||
@@ -293,17 +288,7 @@ function fetchFromApi(searchKey) {
|
|||||||
categoryCode: item.categoryCode || '',
|
categoryCode: item.categoryCode || '',
|
||||||
deviceId: item.id,
|
deviceId: item.id,
|
||||||
deviceName: item.name,
|
deviceName: item.name,
|
||||||
// 🔧 Bug Fix: ...item 展开放在前面,然后用前端字段覆盖
|
// 🔧 Bug #220 修复:正确处理耗材价格,支持price或retailPrice字段
|
||||||
...item,
|
|
||||||
// 确保前端覆盖后端可能冲突的字段
|
|
||||||
adviceName: item.name || item.busNo,
|
|
||||||
adviceType: 4, // 强制设置为前端耗材类型
|
|
||||||
unitCode: item.unitCode || '',
|
|
||||||
unitCode_dictText: item.unitCode_dictText || '',
|
|
||||||
minUnitCode: item.minUnitCode || item.unitCode || '',
|
|
||||||
minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
|
|
||||||
volume: item.size || item.totalVolume || '',
|
|
||||||
partPercent: item.partPercent || 1,
|
|
||||||
// 价格字段优先使用retailPrice
|
// 价格字段优先使用retailPrice
|
||||||
priceList: (item.retailPrice !== undefined && item.retailPrice !== null)
|
priceList: (item.retailPrice !== undefined && item.retailPrice !== null)
|
||||||
? [{ price: item.retailPrice }]
|
? [{ price: item.retailPrice }]
|
||||||
|
|||||||
@@ -1588,6 +1588,11 @@ function getListInfo(addNewRow) {
|
|||||||
console.log('BugFix#219: 过滤掉已作废的会诊医嘱, requestId=', item.requestId);
|
console.log('BugFix#219: 过滤掉已作废的会诊医嘱, requestId=', item.requestId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 🔧 Bug Fix: 过滤掉项目名称为空的无效医嘱
|
||||||
|
if (!item.adviceName || item.adviceName.trim() === '') {
|
||||||
|
console.log('BugFix: 过滤掉空白医嘱, requestId=', item.requestId, 'adviceType=', item.adviceType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2336,14 +2341,10 @@ function handleSave(prescriptionId) {
|
|||||||
|
|
||||||
// 签发核心逻辑
|
// 签发核心逻辑
|
||||||
function executeSaveLogic() {
|
function executeSaveLogic() {
|
||||||
// 🔧 Bug Fix: 获取当前选中的费用性质,如果是'ZIFEI'或0则转为null,让后端查询默认账户
|
// 🔧 Bug Fix: 获取当前选中的费用性质,保持字符串类型避免大整数精度丢失
|
||||||
let finalAccountId = accountId.value;
|
let finalAccountId = accountId.value;
|
||||||
if (finalAccountId === 'ZIFEI' || finalAccountId === 0) {
|
if (finalAccountId === 'ZIFEI' || finalAccountId === 0) {
|
||||||
finalAccountId = null;
|
finalAccountId = null;
|
||||||
} else if (finalAccountId && !isNaN(Number(finalAccountId))) {
|
|
||||||
finalAccountId = Number(finalAccountId);
|
|
||||||
} else {
|
|
||||||
finalAccountId = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔧 Bug Fix: 校验患者信息完整性
|
// 🔧 Bug Fix: 校验患者信息完整性
|
||||||
@@ -2881,11 +2882,10 @@ function handleSaveBatch(prescriptionId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🔧 Bug Fix: 在保存时才转换 accountId
|
// 🔧 Bug Fix: 在保存时才转换 accountId
|
||||||
|
// 保持为字符串类型,避免 JavaScript 大整数精度丢失问题
|
||||||
let finalAccountId = accountId.value;
|
let finalAccountId = accountId.value;
|
||||||
if (finalAccountId === 'ZIFEI') {
|
if (finalAccountId === 'ZIFEI' || finalAccountId === 0) {
|
||||||
finalAccountId = null;
|
finalAccountId = null;
|
||||||
} else if (finalAccountId && !isNaN(Number(finalAccountId))) {
|
|
||||||
finalAccountId = Number(finalAccountId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新到处方对象
|
// 更新到处方对象
|
||||||
@@ -2967,12 +2967,10 @@ function handleSaveBatch(prescriptionId) {
|
|||||||
};
|
};
|
||||||
const contentJson = JSON.stringify(itemToSave);
|
const contentJson = JSON.stringify(itemToSave);
|
||||||
|
|
||||||
// 🔧 Bug Fix: 处理accountId,如果是'ZIFEI'或0则转为null,让后端查询默认账户
|
// 🔧 Bug Fix: 处理accountId,保持字符串类型避免大整数精度丢失
|
||||||
let itemAccountId = finalAccountId;
|
let itemAccountId = finalAccountId;
|
||||||
if (itemAccountId === 'ZIFEI' || itemAccountId === 0) {
|
if (itemAccountId === 'ZIFEI' || itemAccountId === 0) {
|
||||||
itemAccountId = null;
|
itemAccountId = null;
|
||||||
} else if (itemAccountId && !isNaN(Number(itemAccountId))) {
|
|
||||||
itemAccountId = Number(itemAccountId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔧 Bug Fix: 确保库存匹配成功的关键字段
|
// 🔧 Bug Fix: 确保库存匹配成功的关键字段
|
||||||
@@ -3146,6 +3144,12 @@ function syncGroupFields(row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setValue(row) {
|
function setValue(row) {
|
||||||
|
// 🔧 Bug Fix: 强制设置耗材类型,确保 adviceType 为 4
|
||||||
|
// 如果 adviceTableName 是 adm_device_definition,强制设为耗材类型
|
||||||
|
if (row.adviceTableName === 'adm_device_definition') {
|
||||||
|
row.adviceType = 4;
|
||||||
|
}
|
||||||
|
|
||||||
unitCodeList.value = [];
|
unitCodeList.value = [];
|
||||||
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
||||||
unitCodeList.value.push({
|
unitCodeList.value.push({
|
||||||
|
|||||||
@@ -375,7 +375,9 @@ async function submit() {
|
|||||||
emit('refresh'); // 发送刷新事件给父组件
|
emit('refresh'); // 发送刷新事件给父组件
|
||||||
// 长春市朝阳区中医院自动发耗材
|
// 长春市朝阳区中医院自动发耗材
|
||||||
if (userStore.fixmedinsCode == 'H22010200672' && props.consumablesIdList.length > 0) {
|
if (userStore.fixmedinsCode == 'H22010200672' && props.consumablesIdList.length > 0) {
|
||||||
dispenseMedicalConsumables(props.consumablesIdList);
|
dispenseMedicalConsumables(props.consumablesIdList).catch(err => {
|
||||||
|
console.warn('自动发耗材失败:', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -595,16 +595,120 @@
|
|||||||
{{ $index + 1 }}
|
{{ $index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="项目名称/规格">
|
<el-table-column label="项目名称/规格" min-width="200">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<template v-if="editingRowId === $index">
|
<template v-if="editingRowId === $index">
|
||||||
<el-input
|
<div class="project-selector-cell">
|
||||||
v-model="row.name"
|
<el-input
|
||||||
placeholder="请输入或选择项目名称"
|
v-model="row.name"
|
||||||
size="small"
|
placeholder="点击选择项目"
|
||||||
:ref="(el) => setItemNameRef(el, $index)"
|
size="small"
|
||||||
@blur="validateItemName(row, $index)"
|
readonly
|
||||||
/>
|
@click="openProjectSelector(row)"
|
||||||
|
style="width: 100%; cursor: pointer;"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<!-- 自定义 Popover 弹窗 -->
|
||||||
|
<el-popover
|
||||||
|
placement="bottom-start"
|
||||||
|
width="700"
|
||||||
|
trigger="manual"
|
||||||
|
v-model:visible="showProjectPopover"
|
||||||
|
popper-class="diagnosis-project-popover"
|
||||||
|
>
|
||||||
|
<div class="popover-container" v-loading="tableLoading">
|
||||||
|
<!-- 左侧:分类导航 -->
|
||||||
|
<div class="category-sidebar">
|
||||||
|
<div
|
||||||
|
v-for="cat in categoryList"
|
||||||
|
:key="cat.value"
|
||||||
|
class="category-item"
|
||||||
|
:class="{ active: currentCategoryCode === cat.value }"
|
||||||
|
@click="fetchProjectsByCategory(cat.value, true)"
|
||||||
|
>
|
||||||
|
{{ cat.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:数据列表 -->
|
||||||
|
<div class="project-list-area">
|
||||||
|
<el-input
|
||||||
|
v-model="popoverSearchKey"
|
||||||
|
placeholder="搜索项目名称..."
|
||||||
|
size="small"
|
||||||
|
clearable
|
||||||
|
@input="handlePopoverSearch"
|
||||||
|
style="margin-bottom: 8px;"
|
||||||
|
>
|
||||||
|
<template #prefix><el-icon><Search /></el-icon></template>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="projectTableData"
|
||||||
|
:key="tableKey"
|
||||||
|
height="300"
|
||||||
|
highlight-current-row
|
||||||
|
@row-click="handleSelectProject"
|
||||||
|
style="width: 100%"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<el-table-column prop="name" label="项目名称" show-overflow-tooltip min-width="150" />
|
||||||
|
<el-table-column prop="retailPrice" label="单价" width="80" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.retailPrice">¥{{ row.retailPrice }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="permittedUnitCode_dictText" label="单位" width="60" />
|
||||||
|
<el-table-column prop="categoryName" label="类别" width="80" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div v-if="projectTableData.length === 0 && !tableLoading" class="empty-tip">
|
||||||
|
暂无相关项目
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页条(在列表下方) -->
|
||||||
|
<div @mousedown.stop @click.stop style="margin-top: 8px; display: flex; align-items: center; gap: 6px; justify-content: flex-end;">
|
||||||
|
<!-- 每页条数:独立 el-select,placement=top 向上弹出,teleported=false 保持在 popover DOM 内 -->
|
||||||
|
<el-select
|
||||||
|
v-if="projectTableData.length > 0 || popoverTotal > 0"
|
||||||
|
v-model="popoverPageSize"
|
||||||
|
size="small"
|
||||||
|
style="width: 110px; flex-shrink: 0;"
|
||||||
|
placement="top"
|
||||||
|
:teleported="false"
|
||||||
|
@change="handlePopoverSizeChange"
|
||||||
|
>
|
||||||
|
<el-option :value="10" label="10条/页" />
|
||||||
|
<el-option :value="20" label="20条/页" />
|
||||||
|
<el-option :value="50" label="50条/页" />
|
||||||
|
</el-select>
|
||||||
|
<el-pagination
|
||||||
|
v-if="projectTableData.length > 0 || popoverTotal > 0"
|
||||||
|
v-model:current-page="popoverPageNo"
|
||||||
|
:page-size="popoverPageSize"
|
||||||
|
:total="popoverTotal || projectTableData.length"
|
||||||
|
layout="prev, pager, next, jumper"
|
||||||
|
small
|
||||||
|
background
|
||||||
|
:teleported="false"
|
||||||
|
style="justify-content: flex-end;"
|
||||||
|
@current-change="handlePopoverPageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #reference>
|
||||||
|
<div style="display: none;"></div>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<!-- 关闭由 document mousedown 监听处理 -->
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ row.name || '-' }}
|
{{ row.name || '-' }}
|
||||||
@@ -766,7 +870,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import {computed, getCurrentInstance, nextTick, onActivated, onMounted, ref, watch} from 'vue';
|
import {computed, getCurrentInstance, nextTick, onActivated, onMounted, onUnmounted, ref, watch} from 'vue';
|
||||||
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
|
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
|
||||||
import {Check, Edit, Plus, Delete, Refresh, RefreshRight, Search, Download, Close} from '@element-plus/icons-vue';
|
import {Check, Edit, Plus, Delete, Refresh, RefreshRight, Search, Download, Close} from '@element-plus/icons-vue';
|
||||||
import {onBeforeRouteUpdate, useRoute, useRouter} from 'vue-router';
|
import {onBeforeRouteUpdate, useRoute, useRouter} from 'vue-router';
|
||||||
@@ -780,7 +884,8 @@ import {
|
|||||||
getDiagnosisTreatmentList,
|
getDiagnosisTreatmentList,
|
||||||
addDiagnosisTreatment,
|
addDiagnosisTreatment,
|
||||||
editDiagnosisTreatment,
|
editDiagnosisTreatment,
|
||||||
stopDiseaseTreatment
|
stopDiseaseTreatment,
|
||||||
|
getDiseaseTreatmentInit
|
||||||
} from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
|
} from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
|
||||||
import {listLisGroup} from '@/api/system/checkType';
|
import {listLisGroup} from '@/api/system/checkType';
|
||||||
import {
|
import {
|
||||||
@@ -902,15 +1007,10 @@ function isChildTypeRow(row) {
|
|||||||
return !!p.subRaw;
|
return !!p.subRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检验类型表:保持“当前顺序”展示,不在前端根据序号实时重新排序,
|
// 检验类型表:保持"当前顺序"展示,不在前端根据序号实时重新排序
|
||||||
// 这样在点击“修改”或编辑序号时,行号不会在编辑过程中发生变化。
|
|
||||||
const sortedTypeRows = computed(() => {
|
|
||||||
return [...tableData.value];
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagedTypeRows = computed(() => {
|
const pagedTypeRows = computed(() => {
|
||||||
const start = (typeCurrentPage.value - 1) * typePageSize.value;
|
const start = (typeCurrentPage.value - 1) * typePageSize.value;
|
||||||
return sortedTypeRows.value.slice(start, start + typePageSize.value);
|
return tableData.value.slice(start, start + typePageSize.value);
|
||||||
});
|
});
|
||||||
function typeGoPage(p) {
|
function typeGoPage(p) {
|
||||||
if (p < 1 || p > typeTotalPages.value) return;
|
if (p < 1 || p > typeTotalPages.value) return;
|
||||||
@@ -1446,81 +1546,6 @@ const remarks = ref('');
|
|||||||
// 检验套餐明细项目 - 从后端API获取
|
// 检验套餐明细项目 - 从后端API获取
|
||||||
const packageItems = ref([]);
|
const packageItems = ref([]);
|
||||||
|
|
||||||
// 从后端API获取检验项目数据并转换为套餐明细格式
|
|
||||||
const loadPackageItemsFromAPI = () => {
|
|
||||||
queryDiagnosisItems('', (results) => {
|
|
||||||
// 将API返回的检验项目转换为套餐明细格式
|
|
||||||
const formattedItems = results.map(item => ({
|
|
||||||
name: item.name,
|
|
||||||
dosage: '项/人',
|
|
||||||
route: '项/人',
|
|
||||||
frequency: '',
|
|
||||||
days: '',
|
|
||||||
quantity: 1,
|
|
||||||
unit: item.unit || '项',
|
|
||||||
unitPrice: parseFloat(item.retailPrice || 0.00),
|
|
||||||
amount: parseFloat(item.retailPrice || 0.00),
|
|
||||||
serviceFee: 0.00,
|
|
||||||
totalAmount: parseFloat(item.retailPrice || 0.00),
|
|
||||||
origin: ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 只保留前几个项目作为示例
|
|
||||||
packageItems.value = formattedItems.slice(0, 10);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 查询诊疗目录中的检验项目
|
|
||||||
const queryDiagnosisItems = (queryString, cb) => {
|
|
||||||
// 调用诊疗目录API,查询检验类别的项目
|
|
||||||
const params = {
|
|
||||||
searchKey: queryString || '',
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 100 // 增加分页大小,显示更多项目
|
|
||||||
};
|
|
||||||
|
|
||||||
getDiagnosisTreatmentList(params).then(response => {
|
|
||||||
// 处理不同的数据结构
|
|
||||||
let data;
|
|
||||||
if (response.data && response.data.records) {
|
|
||||||
data = response.data.records;
|
|
||||||
} else if (response.data && Array.isArray(response.data)) {
|
|
||||||
data = response.data;
|
|
||||||
} else if (response.data && response.data.rows) {
|
|
||||||
data = response.data.rows;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
return cb([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤出目录类别为检验的项目
|
|
||||||
// 支持多种可能的字段名
|
|
||||||
const inspectionItems = data.filter(item => {
|
|
||||||
return item.categoryCode_dictText === '检验' ||
|
|
||||||
item.categoryName === '检验' ||
|
|
||||||
item.category === '检验';
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理每个检验项目,确保有正确的字段映射
|
|
||||||
const results = inspectionItems.map(item => {
|
|
||||||
// 确保每个项目都有必要的字段
|
|
||||||
return {
|
|
||||||
value: item.name || item.itemName || item.drugName || '',
|
|
||||||
label: `${item.name || item.itemName || item.drugName || ''} - ${item.unit || item.usageUnit || '项'} - ¥${item.retailPrice || item.price || item.unitPrice || 0.00}`,
|
|
||||||
name: item.name || item.itemName || item.drugName || '',
|
|
||||||
unit: item.unit || item.usageUnit || '',
|
|
||||||
retailPrice: item.retailPrice || item.price || item.unitPrice || 0.00,
|
|
||||||
...item
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
cb(results);
|
|
||||||
}).catch(error => {
|
|
||||||
ElMessage.error('查询诊疗目录失败,请稍后重试');
|
|
||||||
cb([]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let addingItem = false;
|
let addingItem = false;
|
||||||
const addPackageItem = () => {
|
const addPackageItem = () => {
|
||||||
if (addingItem) return;
|
if (addingItem) return;
|
||||||
@@ -1730,13 +1755,7 @@ const calculateAmounts = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemNameRefs = ref([]); // 存储每行项目名称输入框的 DOM 引用
|
const itemNameRefs = ref([]);
|
||||||
|
|
||||||
const setItemNameRef = (el, index) => {
|
|
||||||
if (el) {
|
|
||||||
itemNameRefs.value[index] = el;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 检验类型相关方法
|
// 检验类型相关方法
|
||||||
@@ -2294,24 +2313,6 @@ const handleSave = () => {
|
|||||||
savePackageData(basicInfo, detailData);
|
savePackageData(basicInfo, detailData);
|
||||||
};
|
};
|
||||||
|
|
||||||
//定义表单数据
|
|
||||||
const form = ref({
|
|
||||||
tenantId: undefined, // 卫生机构 ID
|
|
||||||
packageType: '检验套餐', // 套餐类别
|
|
||||||
packageLevel: '', // 套餐级别
|
|
||||||
packageName: '', // 套餐名称
|
|
||||||
amount: 0.00, // 套餐金额
|
|
||||||
discount: '', // 折扣 %
|
|
||||||
creator: '超级管理员', // 制单人
|
|
||||||
remark: '', // 备注
|
|
||||||
isDisabled: false, // 是否停用 (false=启用)
|
|
||||||
showPackageName: true, // 显示套餐名
|
|
||||||
generateServiceFee: true, // 生成服务费
|
|
||||||
enablePackagePrice: true, // 套餐价格启用
|
|
||||||
serviceFee: 0.00, // 服务费
|
|
||||||
lisGroup: '', // lis分组
|
|
||||||
bloodVolume: '' // 血量
|
|
||||||
});
|
|
||||||
const fetchTenantList = async () => {
|
const fetchTenantList = async () => {
|
||||||
if (loadingTenant.value) return;
|
if (loadingTenant.value) return;
|
||||||
loadingTenant.value = true;
|
loadingTenant.value = true;
|
||||||
@@ -2354,22 +2355,12 @@ const fetchTenantList = async () => {
|
|||||||
const zhonglianHospital = activeTenants.find(item => item.id === 1);
|
const zhonglianHospital = activeTenants.find(item => item.id === 1);
|
||||||
|
|
||||||
if (zhonglianHospital) {
|
if (zhonglianHospital) {
|
||||||
form.value.tenantId = 1;
|
selectedTenantId.value = 1;
|
||||||
} else {
|
} else {
|
||||||
// 增加 userInfoStore 的存在性检查
|
selectedTenantId.value = activeTenants[0].id;
|
||||||
// 防止 store 未初始化导致报错
|
|
||||||
const userStore = userInfoStore; // 获取 store 引用
|
|
||||||
|
|
||||||
if (userStore && userStore.tenantId) {
|
|
||||||
const currentTenant = activeTenants.find(item => item.id === userStore.tenantId);
|
|
||||||
form.value.tenantId = currentTenant ? currentTenant.id : activeTenants[0].id;
|
|
||||||
} else {
|
|
||||||
// 如果没有用户信息或 store 未就绪,直接选第一个
|
|
||||||
form.value.tenantId = activeTenants[0].id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
form.value.tenantId = undefined;
|
selectedTenantId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2380,7 +2371,274 @@ const fetchTenantList = async () => {
|
|||||||
loadingTenant.value = false;
|
loadingTenant.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const showProjectPopover = ref(false);
|
||||||
|
const categoryList = ref([]);
|
||||||
|
const currentCategoryCode = ref('');
|
||||||
|
const projectTableData = ref([]);
|
||||||
|
const tableLoading = ref(false);
|
||||||
|
const popoverSearchKey = ref('');
|
||||||
|
const currentEditingRow = ref(null);
|
||||||
|
const isSelectingProject = ref(false);
|
||||||
|
const tableKey = ref(0);
|
||||||
|
// 弹窗内项目列表分页
|
||||||
|
const popoverPageNo = ref(1);
|
||||||
|
const popoverPageSize = ref(10);
|
||||||
|
const popoverTotal = ref(0);
|
||||||
|
/**
|
||||||
|
* 点击输入框时:记录当前行并打开弹窗
|
||||||
|
*/
|
||||||
|
const openProjectSelector = (row) => {
|
||||||
|
if (isSelectingProject.value) return;
|
||||||
|
|
||||||
|
currentEditingRow.value = row;
|
||||||
|
showProjectPopover.value = true;
|
||||||
|
|
||||||
|
if (categoryList.value.length === 0) {
|
||||||
|
initDiagnosisCategories();
|
||||||
|
} else {
|
||||||
|
fetchProjectsByCategory(currentCategoryCode.value, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听 popover 开关,动态注册/注销 document 级监听
|
||||||
|
watch(showProjectPopover, (val) => {
|
||||||
|
if (val) {
|
||||||
|
// nextTick 后注册,避免打开时的点击事件立即触发关闭
|
||||||
|
nextTick(() => {
|
||||||
|
document.addEventListener('mousedown', _captureMousedown, true);
|
||||||
|
document.addEventListener('mousedown', onDocumentMousedown);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('mousedown', _captureMousedown, true);
|
||||||
|
document.removeEventListener('mousedown', onDocumentMousedown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSelectProject = (selectedItem) => {
|
||||||
|
if (!currentEditingRow.value) {
|
||||||
|
ElMessage.warning('未检测到编辑行,请重新点击输入框');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = currentEditingRow.value;
|
||||||
|
|
||||||
|
// --- 数据回填逻辑 ---
|
||||||
|
row.name = selectedItem.name;
|
||||||
|
row.spec = selectedItem.spec || '';
|
||||||
|
row.unitPrice = parseFloat(selectedItem.retailPrice || 0);
|
||||||
|
row.unit = selectedItem.permittedUnitCode_dictText || selectedItem.unit || '次';
|
||||||
|
if (!row.quantity) row.quantity = 1;
|
||||||
|
row.amount = row.unitPrice * row.quantity;
|
||||||
|
row.totalAmount = row.amount + (row.serviceFee || 0);
|
||||||
|
|
||||||
|
// 1. 开启“防抖”锁,阻止 openProjectSelector 执行
|
||||||
|
isSelectingProject.value = true;
|
||||||
|
|
||||||
|
// 2. 关闭弹窗
|
||||||
|
showProjectPopover.value = false;
|
||||||
|
|
||||||
|
// 3. 延迟一小段时间后解锁并清空当前行
|
||||||
|
// 100ms 足够让浏览器的 click/focus 事件处理完毕
|
||||||
|
setTimeout(() => {
|
||||||
|
currentEditingRow.value = null;
|
||||||
|
isSelectingProject.value = false;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
|
||||||
|
ElMessage.success(`已选择:${row.name}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const initDiagnosisCategories = async () => {
|
||||||
|
if (categoryList.value.length > 0) return;
|
||||||
|
try {
|
||||||
|
const res = await getDiseaseTreatmentInit();
|
||||||
|
if (res.code === 200 && res.data?.diagnosisCategoryOptions) {
|
||||||
|
categoryList.value = res.data.diagnosisCategoryOptions.map(item => ({
|
||||||
|
value: item.value,
|
||||||
|
label: item.info
|
||||||
|
}));
|
||||||
|
if (categoryList.value.length > 0) {
|
||||||
|
currentCategoryCode.value = categoryList.value[0].value;
|
||||||
|
fetchProjectsByCategory(currentCategoryCode.value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('加载分类失败,请稍后重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类代码加载项目列表(支持分页)
|
||||||
|
*/
|
||||||
|
// 分页数据缓存:key = `${categoryCode}_${pageNo}_${pageSize}_${searchKey}`
|
||||||
|
const _pageCache = new Map();
|
||||||
|
|
||||||
|
const _cacheKey = (code, pageNo, pageSize, searchKey) =>
|
||||||
|
`${code}_${pageNo}_${pageSize}_${searchKey || ''}`;
|
||||||
|
|
||||||
|
/** 静默预加载单页,结果存入缓存 */
|
||||||
|
const _prefetchPage = (code, pageNo, pageSize, searchKey) => {
|
||||||
|
const key = _cacheKey(code, pageNo, pageSize, searchKey);
|
||||||
|
if (_pageCache.has(key)) return;
|
||||||
|
const params = { pageNo, pageSize, statusEnum: 2, categoryCode: code, searchKey: searchKey || undefined };
|
||||||
|
getDiagnosisTreatmentList(params).then(response => {
|
||||||
|
if (response.code === 200) _pageCache.set(key, response.data);
|
||||||
|
}).catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 静默并行预加载多页:当前页前后各2页 + 后4页,覆盖常用跳转范围 */
|
||||||
|
const _prefetchPages = (code, currentPage, pageSize, searchKey, totalPages) => {
|
||||||
|
const pagesToFetch = new Set();
|
||||||
|
for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages || 999, currentPage + 4); i++) {
|
||||||
|
pagesToFetch.add(i);
|
||||||
|
}
|
||||||
|
pagesToFetch.forEach(pageNo => _prefetchPage(code, pageNo, pageSize, searchKey));
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 第1页加载完成后,并行预取所有页(上限20页),覆盖任意跳转 */
|
||||||
|
const _prefetchAllPages = (code, pageSize, searchKey, total) => {
|
||||||
|
const totalPages = Math.min(Math.ceil(total / pageSize), 80);
|
||||||
|
for (let i = 2; i <= totalPages; i++) {
|
||||||
|
_prefetchPage(code, i, pageSize, searchKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProjectsByCategory = async (code, resetPage = false) => {
|
||||||
|
currentCategoryCode.value = code;
|
||||||
|
if (resetPage) {
|
||||||
|
popoverPageNo.value = 1;
|
||||||
|
// 切换分类/搜索时清空缓存
|
||||||
|
_pageCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNo = popoverPageNo.value;
|
||||||
|
const pageSize = popoverPageSize.value;
|
||||||
|
const searchKey = popoverSearchKey.value || '';
|
||||||
|
const key = _cacheKey(code, pageNo, pageSize, searchKey);
|
||||||
|
|
||||||
|
if (_pageCache.has(key)) {
|
||||||
|
// 命中缓存,直接渲染,无需 loading
|
||||||
|
const cached = _pageCache.get(key);
|
||||||
|
if (cached && cached.records) {
|
||||||
|
projectTableData.value = cached.records;
|
||||||
|
popoverTotal.value = cached.total != null ? cached.total : cached.records.length;
|
||||||
|
} else if (Array.isArray(cached)) {
|
||||||
|
projectTableData.value = cached;
|
||||||
|
popoverTotal.value = cached.length;
|
||||||
|
}
|
||||||
|
// 预加载周边页
|
||||||
|
_prefetchPages(code, pageNo, pageSize, searchKey, Math.ceil(popoverTotal.value / pageSize));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableLoading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
pageNo,
|
||||||
|
pageSize,
|
||||||
|
statusEnum: 2,
|
||||||
|
categoryCode: code,
|
||||||
|
searchKey: searchKey || undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await getDiagnosisTreatmentList(params);
|
||||||
|
|
||||||
|
if (response.code === 200) {
|
||||||
|
let list = [];
|
||||||
|
if (response.data && response.data.records) {
|
||||||
|
list = response.data.records;
|
||||||
|
popoverTotal.value = (response.data.total != null ? response.data.total : list.length);
|
||||||
|
} else if (Array.isArray(response.data)) {
|
||||||
|
list = response.data;
|
||||||
|
popoverTotal.value = list.length;
|
||||||
|
}
|
||||||
|
projectTableData.value = list;
|
||||||
|
// 写入缓存
|
||||||
|
_pageCache.set(key, response.data);
|
||||||
|
// 预加载周边页
|
||||||
|
const totalPages = Math.ceil(popoverTotal.value / pageSize);
|
||||||
|
_prefetchPages(code, pageNo, pageSize, searchKey, totalPages);
|
||||||
|
} else {
|
||||||
|
projectTableData.value = [];
|
||||||
|
popoverTotal.value = 0;
|
||||||
|
ElMessage.warning(response.msg || '暂无数据');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('查询失败');
|
||||||
|
projectTableData.value = [];
|
||||||
|
popoverTotal.value = 0;
|
||||||
|
} finally {
|
||||||
|
tableLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗内搜索触发(重置页码)
|
||||||
|
*/
|
||||||
|
const handlePopoverSearch = () => {
|
||||||
|
if (currentCategoryCode.value) {
|
||||||
|
fetchProjectsByCategory(currentCategoryCode.value, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 捕获阶段记录真实点击目标(在 DOM 变化前)
|
||||||
|
let _capturedTarget = null;
|
||||||
|
const _captureMousedown = (e) => { _capturedTarget = e.target; };
|
||||||
|
|
||||||
|
// 分页操作时暂时屏蔽 document mousedown 关闭逻辑
|
||||||
|
let _ignoreMaskOnce = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗内分页页码变化
|
||||||
|
*/
|
||||||
|
const handlePopoverPageChange = (page) => {
|
||||||
|
_ignoreMaskOnce = true;
|
||||||
|
popoverPageNo.value = page;
|
||||||
|
fetchProjectsByCategory(currentCategoryCode.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗内每页条数变化
|
||||||
|
*/
|
||||||
|
const handlePopoverSizeChange = (size) => {
|
||||||
|
_ignoreMaskOnce = true;
|
||||||
|
popoverPageSize.value = size;
|
||||||
|
popoverPageNo.value = 1;
|
||||||
|
fetchProjectsByCategory(currentCategoryCode.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document 级 mousedown 监听:点击 popover 面板及所有浮层之外时关闭
|
||||||
|
* 使用捕获阶段记录的真实目标,避免 DOM 销毁后 contains 检测失效
|
||||||
|
*/
|
||||||
|
const onDocumentMousedown = (e) => {
|
||||||
|
if (!showProjectPopover.value) return;
|
||||||
|
|
||||||
|
// 分页操作触发时,忽略本次关闭
|
||||||
|
if (_ignoreMaskOnce) {
|
||||||
|
_ignoreMaskOnce = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用捕获阶段记录的目标(DOM 变化前的真实元素)
|
||||||
|
const target = _capturedTarget || e.target;
|
||||||
|
|
||||||
|
// 判断点击目标是否在 popover 面板内
|
||||||
|
const popoverEl = document.querySelector('.diagnosis-project-popover');
|
||||||
|
if (popoverEl && popoverEl.contains(target)) return;
|
||||||
|
|
||||||
|
// 判断点击目标是否在任意浮层内(el-select-dropdown / el-popper 等)
|
||||||
|
const floatEls = document.querySelectorAll('.el-select-dropdown, .el-popper, .el-picker-panel');
|
||||||
|
for (const el of floatEls) {
|
||||||
|
if (el.contains(target)) return;
|
||||||
|
}
|
||||||
|
// 用坐标兜底:浮层可能已销毁,检查元素是否曾属于浮层类名
|
||||||
|
if (target && target.closest && target.closest('.el-select-dropdown, .el-popper, .el-picker-panel')) return;
|
||||||
|
|
||||||
|
showProjectPopover.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// 保存套餐数据到数据库
|
// 保存套餐数据到数据库
|
||||||
@@ -2601,8 +2859,6 @@ const handlePackageManagement = () => {
|
|||||||
|
|
||||||
const refreshPage = () => {
|
const refreshPage = () => {
|
||||||
getInspectionTypeList();
|
getInspectionTypeList();
|
||||||
// 刷新时也重新加载套餐项目
|
|
||||||
// loadPackageItemsFromAPI();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2693,7 +2949,7 @@ onMounted(async () => {
|
|||||||
// 3. 等待 Vue 完成下一轮的 DOM 更新
|
// 3. 等待 Vue 完成下一轮的 DOM 更新
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
// 4. 【核心逻辑】强制默认选中“中联医院” (ID = 1)
|
// 4. 【核心逻辑】强制默认选中"中联医院" (ID = 1)
|
||||||
// 检查下拉列表中是否存在 value 为 1 的选项
|
// 检查下拉列表中是否存在 value 为 1 的选项
|
||||||
const hasZhonglian = tenantOptions.value.some(item => item.value === 1);
|
const hasZhonglian = tenantOptions.value.some(item => item.value === 1);
|
||||||
|
|
||||||
@@ -2714,15 +2970,18 @@ onMounted(async () => {
|
|||||||
selectedTenantId.value = tenantOptions.value[0].value;
|
selectedTenantId.value = tenantOptions.value[0].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('mousedown', _captureMousedown, true);
|
||||||
|
document.removeEventListener('mousedown', onDocumentMousedown);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Element UI 表格编辑行样式 */
|
/* 编辑模式行样式 */
|
||||||
.el-table .editing-row {
|
.el-table .editing-row {
|
||||||
background-color: #fdf6ec !important;
|
background-color: #fdf6ec !important;
|
||||||
}
|
}
|
||||||
@@ -2985,8 +3244,83 @@ onMounted(async () => {
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 编辑模式行样式 */
|
/* 诊疗项目选择器单元格 */
|
||||||
.el-table .editing-row {
|
.project-selector-cell {
|
||||||
background-color: #f0f7ff;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
/* 全屏遮罩,确保点击弹窗外任意位置关闭 */
|
||||||
|
.popover-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 999; /* 低于 popover 的 z-index */
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Popover 内部容器布局 */
|
||||||
|
.popover-container {
|
||||||
|
display: flex;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧分类栏 */
|
||||||
|
.category-sidebar {
|
||||||
|
width: 120px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-right: 1px solid #e4e7ed;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
padding: 12px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item:hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item.active {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
color: #409EFF;
|
||||||
|
border-left-color: #409EFF;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧列表区域 */
|
||||||
|
.project-list-area {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全局样式:调整 popover 的 padding 和 z-index */
|
||||||
|
.diagnosis-project-popover {
|
||||||
|
padding: 0 !important;
|
||||||
|
z-index: 2000 !important;
|
||||||
|
}</style>
|
||||||
Reference in New Issue
Block a user