Merge remote-tracking branch 'origin/关羽' into 关羽

This commit is contained in:
关羽
2026-05-13 15:11:55 +08:00
11 changed files with 188 additions and 85 deletions

View File

@@ -63,4 +63,20 @@ public interface IRequestFormManageAppService {
* @return 申请单
*/
IPage<RequestFormPageDto> getRequestFormPage(RequestFormDto requestFormDto);
/**
* 删除申请单(仅待签发状态可删除)
*
* @param requestFormId 申请单ID
* @return 结果
*/
R<?> deleteRequestForm(Long requestFormId);
/**
* 撤回申请单(已签发状态撤回至待签发)
*
* @param requestFormId 申请单ID
* @return 结果
*/
R<?> withdrawRequestForm(Long requestFormId);
}

View File

@@ -474,4 +474,68 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page);
}
/**
* 删除申请单(仅待签发状态可删除)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> deleteRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
if (!Integer.valueOf(0).equals(requestForm.getStatus())) {
return R.fail("仅待签发状态的申请单可删除");
}
// 删除申请单
iRequestFormService.removeById(requestFormId);
// 删除关联的诊疗项目及账单
String prescriptionNo = requestForm.getPrescriptionNo();
List<Long> serviceRequestIds = iServiceRequestService
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo))
.stream().map(ServiceRequest::getId).collect(Collectors.toList());
for (Long serviceRequestId : serviceRequestIds) {
iServiceRequestService.removeById(serviceRequestId);
iChargeItemService.deleteByServiceTableAndId(CommonConstants.TableName.WOR_SERVICE_REQUEST, serviceRequestId);
}
return R.ok(null, "删除成功");
}
/**
* 撤回申请单(已签发状态撤回至待签发)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> withdrawRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
if (!Integer.valueOf(1).equals(requestForm.getStatus())) {
return R.fail("仅已签发状态的申请单可撤回");
}
// 将申请单状态回滚至待签发
RequestForm updateForm = new RequestForm();
updateForm.setId(requestFormId);
updateForm.setStatus(0);
iRequestFormService.updateById(updateForm);
// 将关联的诊疗项目状态回滚至DRAFT
String prescriptionNo = requestForm.getPrescriptionNo();
List<ServiceRequest> serviceRequests = iServiceRequestService
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
for (ServiceRequest serviceRequest : serviceRequests) {
ServiceRequest updateService = new ServiceRequest();
updateService.setId(serviceRequest.getId());
updateService.setStatusEnum(RequestStatus.DRAFT.getValue());
iServiceRequestService.updateById(updateService);
}
return R.ok(null, "撤回成功");
}
}

View File

@@ -99,7 +99,7 @@ public class RequestFormManageController {
* @param startDate 开始日期可选格式yyyy-MM-dd
* @param endDate 结束日期可选格式yyyy-MM-dd
* @param status 单据状态(可选)
* @param keyword 关键字(可选,申请单号/检验项目模糊匹配)
* @param keyword 关键字(可选,申请单号/检验项目名称模糊匹配)
* @return 检验申请单
*/
@GetMapping(value = "/get-inspection")

View File

@@ -280,9 +280,17 @@
aa.balance_amount
) AS personal_account
ON personal_account.encounter_id = ae.id
LEFT JOIN med_medication_dispense mmd
LEFT JOIN (
SELECT med_req_id, status_enum
FROM (
SELECT med_req_id, status_enum,
ROW_NUMBER() OVER (PARTITION BY med_req_id ORDER BY id DESC) AS rn
FROM med_medication_dispense
WHERE delete_flag = '0'
) t
WHERE rn = 1
) mmd
ON mmd.med_req_id = T1.id
AND mmd.delete_flag = '0'
WHERE T1.delete_flag = '0'
AND T1.refund_medicine_id IS NULL
AND T1.generate_source_enum = #{doctorPrescription}

View File

@@ -59,4 +59,9 @@ public class RequestForm extends HisBaseEntity {
*/
private String typeCode;
/**
* 单据状态 0=待签发 1=已签发 2=已校对 3=待接收 4=已接收 5=已检查 6=已出报告 7=已作废
*/
private Integer status;
}

View File

@@ -15,7 +15,7 @@
DELETE
FROM adm_encounter_diagnosis
WHERE encounter_id = #{encounterId}
AND tcm_flag = 0
AND tcm_flag = 1
</delete>
<select id="getEncounterDiagnosisByEncounterConDefId"
resultType="com.openhis.administration.domain.EncounterDiagnosis">

View File

@@ -697,7 +697,6 @@ async function handleCategoryExpand(cat) {
code: m.code,
price: m.price || 0,
packageName: m.packageName || '',
packageId: m.packageId || null,
packagePrice: m.packagePrice || null,
serviceFee: m.serviceFee || null
}));
@@ -1009,16 +1008,11 @@ function handleRowClick(row) {
activeDetailTab.value = 'applyForm';
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => {
const resp = res.data || res;
// Bug #408修复: items 在 AjaxResult 顶层(res.items / resp.items),不在 ExamApply 对象内
// 防御性提取:优先取顶层 items兼容嵌套在 resp.data.items 的情况
let rawItems = res.items || resp.items;
if (!rawItems && resp.data && typeof resp.data === 'object') {
rawItems = resp.data.items;
}
rawItems = rawItems || [];
// 保存 items 在顶层响应中,避免后面 d.data 赋值后丢失
const rawItems = resp.items;
const d = resp.data || resp;
if (d) Object.assign(form, d);
if (Array.isArray(rawItems) && rawItems.length > 0) {
if (rawItems && Array.isArray(rawItems)) {
try {
// 为每个项目加载检查方法
const itemsWithMethods = await Promise.all(rawItems.map(async m => {
@@ -1049,18 +1043,12 @@ function handleRowClick(row) {
code: md.code,
price: m.itemFee || 0, // fallback 到已保存的价格
packageName: md.packageName || '',
packageId: md.packageId || null,
packagePrice: md.packagePrice || null, // Bug #384修复: 套餐价格
serviceFee: md.serviceFee || null
}));
// 如果有已保存的检查方法信息,尝试匹配
if (m.checkMethodId) {
item.selectedMethod = item.methods.find(md => md.id === m.checkMethodId) || null;
// 从已保存的方法中获取套餐信息
if (item.selectedMethod?.packageId) {
item.isPackage = true;
item.packageId = item.selectedMethod.packageId;
}
}
}
} catch (err) {
@@ -1121,13 +1109,6 @@ async function handleMethodSelect(checked, method, cat) {
const existingItem = selectedItems.value.find(s => s.id === targetItem.id);
if (existingItem) {
existingItem.selectedMethod = method;
// 从方法中获取套餐信息
if (method.packageId) {
existingItem.isPackage = true;
existingItem.packageId = method.packageId;
// 预加载套餐明细
loadPackageDetailsForItem(existingItem);
}
updateMethodDisplay();
return;
}
@@ -1142,7 +1123,7 @@ async function handleMethodSelect(checked, method, cat) {
}
}
const newItem = {
selectedItems.value.push({
id: targetItem.id, name: targetItem.name,
price: targetItem.price, quantity: 1,
serviceFee: targetItem.serviceFee || 0,
@@ -1154,16 +1135,9 @@ async function handleMethodSelect(checked, method, cat) {
methods: [method],
selectedMethod: method,
expanded: false,
// 从方法中获取套餐信息(优先级高于项目本身的 packageName
isPackage: !!method.packageId || !!targetItem.packageName,
packageId: method.packageId || targetItem.packageId || null
};
selectedItems.value.push(newItem);
// 如果是套餐,预加载套餐明细
if (newItem.isPackage && newItem.packageId) {
loadPackageDetailsForItem(newItem);
}
isPackage: !!targetItem.packageName,
packageId: targetItem.packageId || null
});
// 自动回填执行科室
if (selectedItems.value.length === 1 && cat?.performDeptName) {
@@ -1208,7 +1182,6 @@ async function handleItemSelect(checked, item, cat) {
code: m.code,
price: m.price || item.price, // fallback 到项目价格
packageName: m.packageName || '',
packageId: m.packageId || null,
packagePrice: m.packagePrice || null, // Bug #384修复: 套餐价格
serviceFee: m.serviceFee || null // Bug #384修复: 服务费
}));

View File

@@ -169,6 +169,7 @@
<script setup>
import {getCurrentInstance} from 'vue'; // 添加 nextTick 导入
import useUserStore from '@/store/modules/user';
import { formatDateStr } from '@/utils';
import {
delEncounterDiagnosis,
deleteDiagnosisBind,
@@ -286,10 +287,22 @@ function getList() {
if (obj.diagSrtNo == null) {
obj.diagSrtNo = '1';
}
// 补充缺失的元数据字段
if (!obj.diagnosisDoctor) {
obj.diagnosisDoctor = props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name;
}
if (!obj.diagnosisTime) {
obj.diagnosisTime = item.diagnosisTime || getCurrentDate();
}
if (!obj.classification) {
obj.classification = item.typeName === '中医诊断' ? '中医' : '西医';
}
if (obj.longTermFlag == null) {
obj.longTermFlag = 0;
}
return obj;
});
form.value.diagnosisList = datas;
// form.value.diagnosisList = res.data;
emits('diagnosisSave', false);
}
});
@@ -305,13 +318,16 @@ function getList() {
ybNo: item.ybNo,
medTypeCode: item.medTypeCode,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
diagnosisTime: item.diagnosisTime || getCurrentDate(),
diagSrtNo: item.diagSrtNo,
classification: '中医',
longTermFlag: item.longTermFlag || 0
});
});
// 将新数据添加到现有列表中
form.value.diagnosisList.push(...newList);
// 重新排序整个列表
form.value.diagnosisList.sort((a, b) => {
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
@@ -322,7 +338,7 @@ function getList() {
emits('diagnosisSave', false);
}
});
getTree();
}
@@ -339,11 +355,12 @@ function handleImport() {
if (!props.patientInfo || !props.patientInfo.encounterId) {
return;
}
if (props.patientInfo.contractName != '自费') {
// 获取患者慢性病信息
getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.data && res.data.length > 0) {
const currentDate = getCurrentDate();
res.data.forEach((item, index) => {
form.value.diagnosisList.push({
...item,
@@ -355,7 +372,10 @@ function handleImport() {
iptDiseTypeCode: 2,
diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN'),
diagnosisTime: currentDate,
onsetDate: currentDate,
classification: '西医',
longTermFlag: 0,
//添加 patientId
patientId: props.patientInfo.patientId
},
@@ -470,6 +490,7 @@ function handleAddDiagnosis() {
* 添加诊断项
*/
function addDiagnosisItem() {
const currentDate = getCurrentDate();
form.value.diagnosisList.push({
showPopover: false,
name: undefined,
@@ -479,9 +500,10 @@ function addDiagnosisItem() {
iptDiseTypeCode: 2,
diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN'),
// 新增这一行:为每个诊断项添加 patientId
diagnosisTime: currentDate,
classification: '西医',
onsetDate: currentDate,
longTermFlag: 0,
patientId: props.patientInfo.patientId
});
@@ -558,16 +580,7 @@ function handleMaindise(value, index) {
/**
* 保存诊断
*/
/**
* 保存诊断
*/
/**
* 保存诊断
*/
/**
* 保存诊断
*/
function handleSaveDiagnosis() {
async function handleSaveDiagnosis() {
for (let index = 0; index < (form.value.diagnosisList || []).length; index++) {
const item = form.value.diagnosisList[index];
if (!item.diagSrtNo) {
@@ -578,7 +591,7 @@ function handleSaveDiagnosis() {
break;
}
}
proxy.$refs.formRef.validate((valid) => {
proxy.$refs.formRef.validate(async (valid) => {
if (valid) {
if (form.value.diagnosisList.length === 0) {
proxy.$modal.msgWarning('诊断不能为空');
@@ -591,43 +604,51 @@ function handleSaveDiagnosis() {
// 设置保存标志避免触发watch监听器
isSaving.value = true;
// 步骤1深拷贝并按 diagSrtNo 排序
const sortedList = [...form.value.diagnosisList].sort((a, b) => {
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
return aNo - bNo;
});
try {
// 步骤1深拷贝并按 diagSrtNo 排序
const sortedList = [...form.value.diagnosisList].sort((a, b) => {
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
return aNo - bNo;
});
// 步骤2重新分配连续的序号从1开始
sortedList.forEach((item, index) => {
item.diagSrtNo = index + 1; // 这里是关键!把“诊断排序”改成新顺序
});
// 步骤2转换日期格式为后端期望的格式
const diagnosisChildList = sortedList.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
}));
// 步骤3提交排序后的数据
const res = await saveDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: diagnosisChildList,
});
// 步骤3提交排序后的数据
saveDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: sortedList,
}).then((res) => {
if (res.code === 200) {
// 步骤4更新本地数据,使用全新对象防止响应式问题
form.value.diagnosisList = sortedList.map(item => ({ ...item }));
// 步骤4从服务器刷新数据,确保获取最新的诊断信息(避免重复记录)
await getList();
getTree();
emits('diagnosisSave', false);
proxy.$modal.msgSuccess('诊断已保存');
// 食源性疾病逻辑
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
if (res2.code === 20 && res2.data) {
if (res2.code === 200 && res2.data) {
window.open(res2.data, '_blank');
}
});
}
}).finally(() => {
} catch (error) {
console.error('保存诊断失败:', error);
const errorMsg = error?.response?.data?.msg || error?.message || '保存诊断失败,请稍后重试';
proxy.$modal.msgError(errorMsg);
} finally {
setTimeout(() => {
isSaving.value = false;
}, 100);
});
}
}
});
}
@@ -687,6 +708,7 @@ function handleNodeClick(data) {
proxy.$modal.msgWarning('该诊断项已存在');
return;
}
const currentDate = getCurrentDate();
form.value.diagnosisList.push({
ybNo: data.ybNo,
name: data.name,
@@ -694,9 +716,12 @@ function handleNodeClick(data) {
medTypeCode: undefined,
diagSrtNo: form.value.diagnosisList.length + 1,
definitionId: data.definitionId,
classification: '西医',
onsetDate: currentDate,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN'),
// 添加 patientId
diagnosisTime: currentDate,
longTermFlag: 0,
selectedDiseases: data.ybNo ? [data.ybNo] : [],
patientId: props.patientInfo.patientId
});
if (form.value.diagnosisList.length == 1) {

View File

@@ -164,7 +164,7 @@ onMounted(() => {
* type(1watch监听类型 2:点击保存类型)
* selectProjectIds(选中项目的id数组)
* */
const projectWithDepartment = (selectProjectIds, type) => {
const projectWithDepartment = (selectProjectIds) => {
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
let isRelease = true;
// 选中项目的数组

View File

@@ -483,7 +483,7 @@ const submit = () => {
encounterId: patientInfo.value.encounterId,
organizationId: patientInfo.value.inHospitalOrgId,
requestFormId: '',
name: applicationListAllFilter.map(item => item.adviceName).join('、'),
name: '检查申请单',
descJson: JSON.stringify(submitForm),
categoryEnum: '2',
}).then((res) => {

View File

@@ -631,6 +631,12 @@ function getListInfo(addNewRow) {
handleAddPrescription();
}
});
}).catch((err) => {
console.error('处方列表加载失败:', err);
// 🔧 Bug #405 修复列表加载失败时确保所有行被锁定isEdit=false
// 防止行永久处于可编辑状态
prescriptionList.value.forEach(item => { item.isEdit = false; });
loadingInstance.close();
});
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
contractList.value = res.data;
@@ -1443,6 +1449,12 @@ function handleSaveBatch() {
})
.catch((error) => {
isSaving.value = false;
// 🔧 Bug #405 修复:保存失败时也锁定行,防止行永久处于可编辑状态
filterPrescriptionList.value.forEach(item => {
if (item.statusEnum == 1 && !item.requestId) {
item.isEdit = false;
}
});
proxy.$modal.msgError(error?.msg || '保存失败,请重试');
});
}