refactor(检验申请): 重构检验申请单生成逻辑,由后端统一处理
- 移除前端生成申请单号的逻辑,改为后端在保存时自动生成 - 申请日期由后端统一处理,前端实时显示当前时间 - 优化金额计算逻辑,确保后端重新计算防止篡改 - 增加废号处理机制,记录生成但保存失败的申请单号 - 简化前端代码,移除不必要的检查逻辑
This commit is contained in:
@@ -19,11 +19,4 @@ public interface IDoctorStationInspectionLabApplyService {
|
||||
* @return 删除结果
|
||||
*/
|
||||
R<?> deleteInspectionLabApply(String applyNo);
|
||||
|
||||
/**
|
||||
* 生成检验申请单号
|
||||
* 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增)
|
||||
* @return 申请单号
|
||||
*/
|
||||
String generateApplyNo();
|
||||
}
|
||||
|
||||
@@ -94,8 +94,39 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
* 保存检验申请单信息逻辑
|
||||
* 保存检验申请单信息同时根据检验申请单检验项目数据保存检验申请单明细信息
|
||||
*/
|
||||
log.debug("保存检验申请单信息:{}", doctorStationLabApplyDto);
|
||||
log.debug("保存申请单明细信息:{}",doctorStationLabApplyDto.getLabApplyItemList());
|
||||
|
||||
// 申请单号为空或"待生成"时,由后端生成新单号
|
||||
String applyNo = doctorStationLabApplyDto.getApplyNo();
|
||||
boolean isNewApplyNo = false;
|
||||
if (applyNo == null || applyNo.trim().isEmpty() || "待生成".equals(applyNo) || "自动生成".equals(applyNo)) {
|
||||
applyNo = generateApplyNo();
|
||||
isNewApplyNo = true;
|
||||
}
|
||||
// 将生成的单号设置回 DTO
|
||||
doctorStationLabApplyDto.setApplyNo(applyNo);
|
||||
|
||||
try {
|
||||
// 执行保存逻辑
|
||||
doSaveInspectionLabApply(doctorStationLabApplyDto, applyNo);
|
||||
} catch (Exception e) {
|
||||
// 记录废号日志(申请单号已生成但保存失败)
|
||||
if (isNewApplyNo) {
|
||||
log.error("申请单号 {} 因保存失败成为废号,原因:{}", applyNo, e.getMessage());
|
||||
}
|
||||
throw e; // 重新抛出异常,让事务回滚
|
||||
}
|
||||
|
||||
// 返回生成的申请单号
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("applyNo", applyNo);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行保存检验申请单的实际逻辑
|
||||
*/
|
||||
private void doSaveInspectionLabApply(DoctorStationLabApplyDto doctorStationLabApplyDto, String applyNo) {
|
||||
|
||||
//获取当前登陆用户 ID
|
||||
String userId = String.valueOf(SecurityUtils.getLoginUser().getUserId());
|
||||
InspectionLabApply inspectionLabApply = new InspectionLabApply();
|
||||
@@ -108,18 +139,30 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
inspectionLabApply.setOperatorId(userId);
|
||||
inspectionLabApply.setCreateTime(new Date());
|
||||
inspectionLabApply.setDeleteFlag(DelFlag.NO.getCode());
|
||||
// 申请日期使用服务器当前系统时间
|
||||
inspectionLabApply.setApplyTime(new Date());
|
||||
|
||||
log.debug("保存检验申请单信息:{}", inspectionLabApply);
|
||||
inspectionLabApplyService.saveOrUpdate(inspectionLabApply);
|
||||
|
||||
// 金额校验和重算:后端重新计算金额,防止前端篡改
|
||||
java.math.BigDecimal totalAmount = java.math.BigDecimal.ZERO;
|
||||
int index = 0;
|
||||
|
||||
//遍历 doctorStationLabApplyDto.getLabApplyItemList()
|
||||
int index = 0;
|
||||
for (DoctorStationLabApplyItemDto doctorStationLabApplyItemDto : doctorStationLabApplyDto.getLabApplyItemList()) {
|
||||
//将 dto 数据复制到 InspectionLabApplyItem 对象中
|
||||
InspectionLabApplyItem inspectionLabApplyItem = new InspectionLabApplyItem();
|
||||
BeanUtils.copyProperties(doctorStationLabApplyItemDto, inspectionLabApplyItem);
|
||||
|
||||
// 后端重新计算金额:金额 = 单价 × 数量
|
||||
java.math.BigDecimal itemPrice = doctorStationLabApplyItemDto.getItemPrice();
|
||||
java.math.BigDecimal itemQty = doctorStationLabApplyItemDto.getItemQty();
|
||||
if (itemPrice != null && itemQty != null) {
|
||||
java.math.BigDecimal calculatedAmount = itemPrice.multiply(itemQty).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
inspectionLabApplyItem.setItemAmount(calculatedAmount);
|
||||
totalAmount = totalAmount.add(calculatedAmount);
|
||||
}
|
||||
|
||||
//设置从表申请单明细的申请单号
|
||||
inspectionLabApplyItem.setApplyNo(doctorStationLabApplyDto.getApplyNo());
|
||||
//执行科室代码,取值于检验申请单明细(前端传递的字典值)
|
||||
@@ -131,7 +174,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
index++;
|
||||
|
||||
inspectionLabApplyItem.setDeleteFlag(DelFlag.NO.getCode());
|
||||
log.debug("保存申请单明细信息:{}", inspectionLabApplyItem);
|
||||
inspectionLabApplyItemService.saveOrUpdate(inspectionLabApplyItem);
|
||||
|
||||
//创建条码对象
|
||||
@@ -147,8 +189,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
barCode.setCreateTime(new Date());
|
||||
barCode.setDeleteFlag(DelFlag.NO.getCode());
|
||||
|
||||
|
||||
log.debug("插入条码数据前,barCode:{}",barCode);
|
||||
inspectionLabBarCodeService.saveOrUpdate(barCode);
|
||||
}
|
||||
|
||||
@@ -195,15 +235,12 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
);
|
||||
if (organization != null) {
|
||||
positionId = organization.getId();
|
||||
} else {
|
||||
log.warn("未找到执行科室代码对应的科室:{}", performDeptCode);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有指定执行科室,使用当前医生所在的科室作为默认执行科室
|
||||
if (positionId == null) {
|
||||
positionId = SecurityUtils.getDeptId();
|
||||
log.debug("检验项目未指定执行科室,使用当前科室:{}", positionId);
|
||||
}
|
||||
|
||||
// 4. 创建医嘱保存对象
|
||||
@@ -282,12 +319,7 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
adviceSaveParam.setAdviceSaveList(adviceSaveList);
|
||||
|
||||
// 调用门诊医嘱保存接口,创建关联的医嘱记录
|
||||
try {
|
||||
iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, "1"); // "1"表示保存操作
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("创建关联医嘱记录失败", e);
|
||||
}
|
||||
return R.ok();
|
||||
iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, "1"); // "1"表示保存操作
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,8 +591,7 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
* 支持并发安全:使用 Redis 原子递增保证唯一性
|
||||
* @return 申请单号
|
||||
*/
|
||||
@Override
|
||||
public String generateApplyNo() {
|
||||
private String generateApplyNo() {
|
||||
// 获取当前日期
|
||||
LocalDate today = LocalDate.now();
|
||||
String dateStr = today.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
@@ -574,8 +605,8 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
// 使用 Redis 原子递增获取流水号(并发安全)
|
||||
long sequence = redisCache.incr(redisKey, 1);
|
||||
|
||||
// 设置 Redis key 过期时间为 2 天,避免数据积累
|
||||
redisCache.expire(redisKey, 2 * 24 * 60 * 60);
|
||||
// 设置 Redis key 过期时间(每天的 key 按日期独立,隔天不再使用,25小时确保跨午夜场景安全)
|
||||
redisCache.expire(redisKey, 25 * 60 * 60);
|
||||
|
||||
// 格式化流水号为5位,不足前补0
|
||||
String sequenceStr = String.format("%05d", sequence);
|
||||
@@ -583,7 +614,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
// 生成完整的申请单号
|
||||
String applyNo = prefix + sequenceStr;
|
||||
|
||||
log.debug("生成检验申请单号:{}", applyNo);
|
||||
return applyNo;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,18 +64,4 @@ public class DoctorStationInspectionLabApplyController {
|
||||
log.debug("删除检验申请单:{}", applyNo);
|
||||
return R.ok(iDoctorStationInspectionLabApplyService.deleteInspectionLabApply(applyNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成检验申请单号
|
||||
* 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增)
|
||||
* @return 申请单号
|
||||
*/
|
||||
@GetMapping(value = "/generate-apply-no")
|
||||
public R<?> generateApplyNo(){
|
||||
log.debug("生成检验申请单号");
|
||||
String applyNo = iDoctorStationInspectionLabApplyService.generateApplyNo();
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("applyNo", applyNo);
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,18 +1003,6 @@ export function deleteInspectionApplication(applyNo) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成检验申请单号
|
||||
* 规则:LS + YYYYMMDD + 5位流水号(每日从1开始递增)
|
||||
* @returns {Promise} { code: 200, data: { applyNo: string } }
|
||||
*/
|
||||
export function generateInspectionApplyNo() {
|
||||
return request({
|
||||
url: '/doctor-station/inspection/generate-apply-no',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取检验类型列表(分类)
|
||||
* @param {Object} queryParams - 查询参数
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
<el-tab-pane label="申请单" name="application">
|
||||
<el-form class="application-form" :model="formData" label-width="auto">
|
||||
<el-form-item label="申请单号" style="margin-bottom: 1px">
|
||||
<el-input v-model="formData.applyNo" readonly size="small" />
|
||||
<el-input v-model="formData.applyNo" disabled size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 患者信息行 -->
|
||||
@@ -141,14 +141,10 @@
|
||||
<!--申请日期-->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="申请日期" required>
|
||||
<el-date-picker
|
||||
<el-input
|
||||
v-model="formData.applyTime"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
readonly
|
||||
size="small"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -539,17 +535,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
|
||||
import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading } from '@element-plus/icons-vue'
|
||||
import {
|
||||
checkInspectionApplicationNo,
|
||||
deleteInspectionApplication, getApplyList,
|
||||
saveInspectionApplication,
|
||||
getInspectionTypeList,
|
||||
getInspectionItemList,
|
||||
getEncounterDiagnosis,
|
||||
generateInspectionApplyNo,
|
||||
getInspectionApplyDetail
|
||||
} from '../api'
|
||||
import useUserStore from '@/store/modules/user.js'
|
||||
@@ -588,14 +582,16 @@ const loading = ref(false)
|
||||
const saving = ref(false) // 保存状态
|
||||
const total = ref(0)
|
||||
const leftActiveTab = ref('application')
|
||||
const isGeneratingNewApplyNo = ref(false) // 标志:是否正在生成新申请单号
|
||||
|
||||
// 申请日期实时更新定时器
|
||||
let applyTimeTimer = null
|
||||
|
||||
// 用户信息store
|
||||
const userStore = useUserStore()
|
||||
const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userStore)
|
||||
|
||||
// 修改 initData 函数
|
||||
async function initData() {
|
||||
const initData = async () => {
|
||||
// 先初始化患者信息(如果有)
|
||||
if (props.patientInfo && props.patientInfo.encounterId) {
|
||||
queryParams.encounterId = props.patientInfo.encounterId
|
||||
@@ -606,15 +602,16 @@ async function initData() {
|
||||
formData.applyDepartment = props.patientInfo.organizationName || ''
|
||||
formData.applyDocName = userNickName.value || userName.value || ''
|
||||
formData.applyDocCode = userId.value || ''
|
||||
//此处样本数据暂时固定为血液,后续根据实际情况调整
|
||||
formData.specimenName = '血液'
|
||||
formData.applyDeptCode = props.patientInfo.organizationName || ''
|
||||
formData.applyOrganizationId = props.patientInfo.orgId || ''
|
||||
formData.encounterId = props.patientInfo.encounterId
|
||||
|
||||
// 生成申请单号
|
||||
generateApplicationNo().then((newApplyNo) => {
|
||||
formData.applyNo = newApplyNo
|
||||
})
|
||||
// 申请单号在保存时由后端生成,此处显示"自动生成"
|
||||
formData.applyNo = '自动生成'
|
||||
// 申请日期实时更新(启动定时器)
|
||||
startApplyTimeTimer()
|
||||
|
||||
// 获取主诊断信息
|
||||
try {
|
||||
@@ -657,7 +654,7 @@ const formData = reactive({
|
||||
patientName: '',
|
||||
medicalrecordNumber: '',
|
||||
natureofCost: 'self',
|
||||
applyTime: new Date(),
|
||||
applyTime: '', // 初始值设为空字符串,在新增时设置为当前时间
|
||||
applyDepartment: '',
|
||||
applyDocName: '',
|
||||
executeDepartment: '',
|
||||
@@ -728,7 +725,7 @@ const PAGE_SIZE = 50
|
||||
const SEARCH_DEBOUNCE_TIME = 300
|
||||
|
||||
// 加载检验类型分类列表(只加载分类,项目懒加载)
|
||||
async function loadInspectionData() {
|
||||
const loadInspectionData = async () => {
|
||||
// 如果已经加载过分类,直接返回
|
||||
if (inspectionCategories.value.length > 0) {
|
||||
return
|
||||
@@ -776,7 +773,7 @@ async function loadInspectionData() {
|
||||
}
|
||||
|
||||
// 懒加载分类项目(分页)
|
||||
async function loadCategoryItems(categoryKey, loadMore = false) {
|
||||
const loadCategoryItems = async (categoryKey, loadMore = false) => {
|
||||
const category = inspectionCategories.value.find(c => c.key === categoryKey)
|
||||
if (!category) return
|
||||
|
||||
@@ -860,7 +857,7 @@ async function loadCategoryItems(categoryKey, loadMore = false) {
|
||||
}
|
||||
|
||||
// 加载更多项目
|
||||
function loadMoreItems(categoryKey) {
|
||||
const loadMoreItems = (categoryKey) => {
|
||||
const category = inspectionCategories.value.find(c => c.key === categoryKey)
|
||||
if (!category || !category.hasMore || category.loading) return
|
||||
|
||||
@@ -869,7 +866,7 @@ function loadMoreItems(categoryKey) {
|
||||
}
|
||||
|
||||
// 处理滚动事件(无限滚动)
|
||||
function handleScroll({ scrollTop, scrollHeight, clientHeight }) {
|
||||
const handleScroll = ({ scrollTop, scrollHeight, clientHeight }) => {
|
||||
// 距离底部 50px 时触发加载更多
|
||||
if (scrollHeight - scrollTop - clientHeight < 50) {
|
||||
const expandedCategory = inspectionCategories.value.find(c => c.expanded)
|
||||
@@ -916,7 +913,7 @@ const getFilteredItems = (categoryKey) => {
|
||||
}
|
||||
|
||||
// 搜索建议查询(自动完成)
|
||||
async function querySearchInspectionItems(queryString, cb) {
|
||||
const querySearchInspectionItems = async (queryString, cb) => {
|
||||
if (!queryString) {
|
||||
cb([])
|
||||
return
|
||||
@@ -954,7 +951,7 @@ async function querySearchInspectionItems(queryString, cb) {
|
||||
}
|
||||
|
||||
// 搜索选择处理
|
||||
function handleSearchSelect(item) {
|
||||
const handleSearchSelect = (item) => {
|
||||
// 直接添加到已选列表
|
||||
if (!isItemSelected(item)) {
|
||||
selectedInspectionItems.value.push({
|
||||
@@ -967,12 +964,12 @@ function handleSearchSelect(item) {
|
||||
}
|
||||
|
||||
// 搜索框清空处理
|
||||
function handleSearchClear() {
|
||||
const handleSearchClear = () => {
|
||||
searchKeyword.value = ''
|
||||
}
|
||||
|
||||
// 获取检验申请单列表
|
||||
function getInspectionList() {
|
||||
const getInspectionList = () => {
|
||||
// 如果没有encounterId,不调用接口
|
||||
if (!queryParams.encounterId) {
|
||||
return
|
||||
@@ -1024,7 +1021,7 @@ function getInspectionList() {
|
||||
}
|
||||
|
||||
// 合并检验申请单记录:将同一个申请单的多个明细合并成一条记录
|
||||
function mergeInspectionApplyRecords(records) {
|
||||
const mergeInspectionApplyRecords = (records) => {
|
||||
if (!records || records.length === 0) {
|
||||
return []
|
||||
}
|
||||
@@ -1052,7 +1049,7 @@ function mergeInspectionApplyRecords(records) {
|
||||
}
|
||||
|
||||
// 格式化金额:确保显示两位小数
|
||||
function formatAmount(amount) {
|
||||
const formatAmount = (amount) => {
|
||||
if (amount === null || amount === undefined || amount === '') {
|
||||
return '0.00'
|
||||
}
|
||||
@@ -1063,53 +1060,60 @@ function formatAmount(amount) {
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
// 格式化日期时间为字符串 YYYY-MM-DD HH:mm:ss
|
||||
const formatDateTime = (date) => {
|
||||
if (!date) return ''
|
||||
const d = new Date(date)
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hours = String(d.getHours()).padStart(2, '0')
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
// 启动申请日期实时更新定时器
|
||||
const startApplyTimeTimer = () => {
|
||||
// 先清除已存在的定时器
|
||||
stopApplyTimeTimer()
|
||||
// 立即更新一次
|
||||
formData.applyTime = formatDateTime(new Date())
|
||||
// 每秒更新
|
||||
applyTimeTimer = setInterval(() => {
|
||||
formData.applyTime = formatDateTime(new Date())
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 停止申请日期实时更新定时器
|
||||
const stopApplyTimeTimer = () => {
|
||||
if (applyTimeTimer) {
|
||||
clearInterval(applyTimeTimer)
|
||||
applyTimeTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// 新增申请单
|
||||
async function handleNewApplication() {
|
||||
const handleNewApplication = async () => {
|
||||
resetForm()
|
||||
// 生成新的申请单号
|
||||
formData.applyNo = await generateApplicationNo()
|
||||
// 申请单号在保存时由后端生成,此处显示"待生成"
|
||||
formData.applyNo = '自动生成'
|
||||
// 申请日期实时更新(启动定时器)
|
||||
startApplyTimeTimer()
|
||||
// 确保申请医生是当前登录医生
|
||||
formData.applyDocName = userNickName.value || userName.value || ''
|
||||
leftActiveTab.value = 'application'
|
||||
}
|
||||
|
||||
// 查询数据库中是否已存在指定申请单号
|
||||
const checkApplicationNoExists = async (applyNo) => {
|
||||
try {
|
||||
// 这里调用API检查申请单号是否已存在
|
||||
// 注意:你需要根据实际API接口调整此调用
|
||||
const response = await checkInspectionApplicationNo(applyNo);
|
||||
// 后端返回格式:如果存在返回{applyNo: "..."},不存在返回null
|
||||
return response.code === 200 && response.data && response.data.applyNo;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 调用后端接口生成申请单号(LS + YYYYMMDD + 5位流水号)
|
||||
// 后端使用 Redis 原子递增保证并发安全
|
||||
const generateApplicationNo = async () => {
|
||||
try {
|
||||
const res = await generateInspectionApplyNo();
|
||||
if (res.code === 200 && res.data && res.data.applyNo) {
|
||||
return res.data.applyNo;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
async function resetForm() {
|
||||
const resetForm = async () => {
|
||||
Object.assign(formData, {
|
||||
applicationId: null,
|
||||
applyOrganizationId: props.patientInfo.orgId || '',
|
||||
patientName: props.patientInfo.patientName || '',
|
||||
medicalrecordNumber: props.patientInfo.identifierNo || '',
|
||||
natureofCost: 'self',
|
||||
applyTime: new Date(),
|
||||
applyTime: '', // 申请日期由定时器实时更新
|
||||
applyDepartment: props.patientInfo.organizationName || '',
|
||||
applyDeptCode: props.patientInfo.organizationName,
|
||||
applyDocCode: userId.value || '',
|
||||
@@ -1164,9 +1168,10 @@ async function resetForm() {
|
||||
}
|
||||
|
||||
// 保存
|
||||
function handleSave() {
|
||||
// 如果正在保存,直接返回
|
||||
const handleSave = () => {
|
||||
// P1:防重复提交 - 立即设置标志
|
||||
if (saving.value) return
|
||||
saving.value = true
|
||||
|
||||
// 重置验证错误状态
|
||||
Object.keys(validationErrors).forEach(key => {
|
||||
@@ -1175,6 +1180,20 @@ function handleSave() {
|
||||
|
||||
let hasErrors = false
|
||||
|
||||
// P0:检查患者信息是否已加载
|
||||
if (!formData.patientName?.trim() || !formData.medicalrecordNumber?.trim()) {
|
||||
ElMessage.error('患者信息未加载,请稍后重试')
|
||||
saving.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// P0:检查就诊信息是否有效
|
||||
if (!formData.encounterId) {
|
||||
ElMessage.error('就诊信息无效,请重新选择患者')
|
||||
saving.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// 检查必填字段,执行科室
|
||||
if (!formData.executeDepartment) {
|
||||
validationErrors.executeDepartment = true
|
||||
@@ -1205,16 +1224,14 @@ function handleSave() {
|
||||
validationErrors.labApplyItemList = true
|
||||
hasErrors = true
|
||||
ElMessage.error('请至少选择一项检验项目')
|
||||
saving.value = false
|
||||
return
|
||||
}
|
||||
// 检查必填字段,申请日期
|
||||
if(!formData.applyTime || (typeof formData.applyTime === 'string' && !formData.applyTime?.trim())) {
|
||||
validationErrors.applyTime = true
|
||||
hasErrors = true
|
||||
}
|
||||
// 申请日期由后端在保存时自动生成,无需前端校验
|
||||
|
||||
if (hasErrors) {
|
||||
ElMessage.error('请填写所有必填字段')
|
||||
saving.value = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1229,87 +1246,36 @@ function handleSave() {
|
||||
...formData,
|
||||
labApplyItemList,
|
||||
physicalExamination: formData.physicalExam, // 字段名映射:前端 physicalExam -> 后端 physicalExamination
|
||||
inspectionItemsText: selectedInspectionItems.value.map(item => item.itemName).join('+'),
|
||||
amount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount, 0),
|
||||
serviceFee: selectedInspectionItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0),
|
||||
totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount + (item.serviceFee || 0), 0)
|
||||
inspectionItemsText: selectedInspectionItems.value.map(item => item.itemName).join('+')
|
||||
// 金额由后端计算,前端不传递
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 先检查申请单号是否已存在
|
||||
if (isGeneratingNewApplyNo.value) {
|
||||
// 正在使用新生成的单号,直接保存
|
||||
isGeneratingNewApplyNo.value = false; // 重置标志
|
||||
const saveData = prepareSaveData();
|
||||
executeSave(saveData);
|
||||
return;
|
||||
}
|
||||
|
||||
checkInspectionApplicationNo(formData.applyNo).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
// res.data有值表示申请单存在
|
||||
// 注意:res.data可能是{applyNo: "..."}或包含其他字段
|
||||
const exists = res.data && (res.data.applyNo || res.data.exists);
|
||||
if (exists) {
|
||||
// 申请单号已存在,提示用户
|
||||
ElMessageBox.confirm(
|
||||
'当前申请单号已重复,将重新为您生成新的申请单号',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
// 设置标志,表示正在生成新申请单号
|
||||
isGeneratingNewApplyNo.value = true;
|
||||
// 重新生成申请单号
|
||||
generateApplicationNo().then((newApplyNo) => {
|
||||
if (newApplyNo && newApplyNo.trim() !== '') {
|
||||
formData.applyNo = newApplyNo;
|
||||
ElMessage.success('新申请单号已生成,请点击保存按钮继续,请核对数据');
|
||||
// 不自动保存,等待用户再次点击保存
|
||||
} else {
|
||||
isGeneratingNewApplyNo.value = false;
|
||||
}
|
||||
});
|
||||
}).catch((error) => {
|
||||
// 用户点击取消或其他原因导致的拒绝
|
||||
if (error !== 'cancel' && error !== 'close') {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 申请单号不存在,继续保存操作
|
||||
const saveData = prepareSaveData();
|
||||
executeSave(saveData);
|
||||
}
|
||||
} else {
|
||||
// 其他情况继续保存
|
||||
const saveData = prepareSaveData();
|
||||
executeSave(saveData);
|
||||
}
|
||||
}).catch(() => {
|
||||
// 如果检查过程出错,仍然继续保存操作,以免影响正常使用
|
||||
const saveData = prepareSaveData();
|
||||
executeSave(saveData);
|
||||
})
|
||||
// 申请单号由后端在保存时生成,直接保存
|
||||
const saveData = prepareSaveData();
|
||||
executeSave(saveData);
|
||||
|
||||
}
|
||||
const executeSave = (saveData) => {
|
||||
saving.value = true
|
||||
saveInspectionApplication(saveData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
// 停止申请日期实时更新
|
||||
stopApplyTimeTimer()
|
||||
// 从后端返回获取生成的申请单号
|
||||
if (res.data && res.data.applyNo) {
|
||||
ElMessage.info(`申请单号:${res.data.applyNo}`)
|
||||
// 显示后端返回的实际申请时间
|
||||
if (res.data.applyTime) {
|
||||
formData.applyTime = res.data.applyTime
|
||||
}
|
||||
}
|
||||
emit('save', res.data) // 通知父组件保存成功
|
||||
resetForm()
|
||||
// 生成新的申请单号
|
||||
generateApplicationNo().then((newApplyNo) => {
|
||||
if (newApplyNo && newApplyNo.trim() !== '') {
|
||||
formData.applyNo = newApplyNo;
|
||||
} else {
|
||||
}
|
||||
});
|
||||
// 设置下一个新单为"待生成"
|
||||
formData.applyNo = '自动生成'
|
||||
// 启动新的申请日期实时更新
|
||||
startApplyTimeTimer()
|
||||
leftActiveTab.value = 'application'
|
||||
// 刷新列表
|
||||
getInspectionList()
|
||||
@@ -1331,7 +1297,9 @@ const executeSave = (saveData) => {
|
||||
|
||||
|
||||
// 查看详情
|
||||
function handleView(row) {
|
||||
const handleView = (row) => {
|
||||
// 停止申请日期实时更新(查看已保存的申请单)
|
||||
stopApplyTimeTimer()
|
||||
// 加载表单数据
|
||||
Object.assign(formData, row)
|
||||
|
||||
@@ -1350,7 +1318,7 @@ function handleView(row) {
|
||||
}
|
||||
|
||||
// 切换分类(修改为懒加载)
|
||||
function switchCategory(category) {
|
||||
const switchCategory = (category) => {
|
||||
if (activeCategory.value === category) {
|
||||
// 如果点击的是当前激活的分类,则收起
|
||||
activeCategory.value = ''
|
||||
@@ -1365,7 +1333,7 @@ function switchCategory(category) {
|
||||
inspectionCategories.value.forEach(cat => {
|
||||
cat.expanded = cat.key === category
|
||||
})
|
||||
|
||||
|
||||
// 懒加载该分类的项目
|
||||
const targetCategory = inspectionCategories.value.find(c => c.key === category)
|
||||
if (targetCategory && !targetCategory.loaded) {
|
||||
@@ -1375,17 +1343,17 @@ function switchCategory(category) {
|
||||
}
|
||||
|
||||
// 处理项目项点击(排除勾选框点击)
|
||||
function handleItemClick(item) {
|
||||
const handleItemClick = (item) => {
|
||||
toggleInspectionItem(item)
|
||||
}
|
||||
|
||||
// 判断项目是否已选择
|
||||
function isItemSelected(item) {
|
||||
const isItemSelected = (item) => {
|
||||
return selectedInspectionItems.value.some(selected => selected.itemId === item.itemId)
|
||||
}
|
||||
|
||||
// 切换检验项目选择
|
||||
function toggleInspectionItem(item) {
|
||||
const toggleInspectionItem = (item) => {
|
||||
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
|
||||
if (index > -1) {
|
||||
selectedInspectionItems.value.splice(index, 1)
|
||||
@@ -1400,7 +1368,7 @@ function toggleInspectionItem(item) {
|
||||
}
|
||||
|
||||
// 移除检验项目
|
||||
function removeInspectionItem(item) {
|
||||
const removeInspectionItem = (item) => {
|
||||
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
|
||||
if (index > -1) {
|
||||
selectedInspectionItems.value.splice(index, 1)
|
||||
@@ -1408,27 +1376,27 @@ function removeInspectionItem(item) {
|
||||
}
|
||||
|
||||
// 清空所有选择
|
||||
function clearAllSelected() {
|
||||
const clearAllSelected = () => {
|
||||
selectedInspectionItems.value = []
|
||||
}
|
||||
|
||||
// 分页大小改变
|
||||
function handleSizeChange(size) {
|
||||
const handleSizeChange = (size) => {
|
||||
queryParams.pageSize = size
|
||||
getInspectionList()
|
||||
}
|
||||
|
||||
function handleCurrentChange(page) {
|
||||
const handleCurrentChange = (page) => {
|
||||
queryParams.pageNo = page
|
||||
getInspectionList()
|
||||
}
|
||||
|
||||
// 选择框变化
|
||||
function handleSelectionChange(selection) {
|
||||
const handleSelectionChange = (selection) => {
|
||||
selectedRows.value = selection
|
||||
}
|
||||
|
||||
function handlePrint(row) {
|
||||
const handlePrint = (row) => {
|
||||
// 切换到申请单TAB
|
||||
leftActiveTab.value = 'application'
|
||||
|
||||
@@ -1449,7 +1417,7 @@ function handlePrint(row) {
|
||||
}
|
||||
|
||||
// 删除申请单
|
||||
function handleDelete(row) {
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除申请单 "${row.applyNo}" 吗?此操作将同时删除对应的医嘱。`,
|
||||
'删除确认',
|
||||
@@ -1480,7 +1448,7 @@ function handleDelete(row) {
|
||||
}
|
||||
|
||||
// 单元格点击 - 点击表格行时加载申请单详情
|
||||
function handleCellClick(row, column) {
|
||||
const handleCellClick = (row, column) => {
|
||||
// 点击表格行时,将该申请单的数据加载到表单中
|
||||
// 使用 applyNo 判断是否有效
|
||||
if (row && row.applyNo) {
|
||||
@@ -1489,7 +1457,7 @@ function handleCellClick(row, column) {
|
||||
}
|
||||
|
||||
// 行点击事件处理
|
||||
function handleRowClick(currentRow, oldRow) {
|
||||
const handleRowClick = (currentRow, oldRow) => {
|
||||
// 点击表格行时,将该申请单的数据加载到表单中
|
||||
// 使用 applyNo 判断是否有效
|
||||
if (currentRow && currentRow.applyNo) {
|
||||
@@ -1498,7 +1466,9 @@ function handleRowClick(currentRow, oldRow) {
|
||||
}
|
||||
|
||||
// 提取公共方法加载申请单到表单
|
||||
async function loadApplicationToForm(row) {
|
||||
const loadApplicationToForm = async (row) => {
|
||||
// 停止申请日期实时更新(加载已保存的申请单)
|
||||
stopApplyTimeTimer()
|
||||
// 切换到申请单 TAB
|
||||
leftActiveTab.value = 'application'
|
||||
|
||||
@@ -1640,6 +1610,11 @@ onMounted(async () => {
|
||||
await loadInspectionData()
|
||||
})
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
onUnmounted(() => {
|
||||
stopApplyTimeTimer()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
getList: getInspectionList
|
||||
|
||||
Reference in New Issue
Block a user