Compare commits

..

1 Commits

Author SHA1 Message Date
荀彧
da2e8b5d92 Fix Bug #463: [目录管理-诊疗目录] 新增/编辑弹窗中"诊疗子项"检索功能失效,无法搜到已维护的项目
根因:ActivityDefinitionManageMapper.xml 中 getDiseaseTreatmentPage 查询使用 INNER JOIN
关联 adm_charge_item_definition 价格表,导致 55 个没有价格记录的诊疗项目被完全排除
在搜索结果之外。改为 LEFT JOIN 后,即使项目暂无价格记录也能被搜索到。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 20:15:34 +08:00
11 changed files with 123 additions and 67 deletions

10
.husky/pre-commit Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env sh
# ============================================================
# Husky Pre-commit Hook - HIS项目
# 配置: 关羽 | 日期: 2026-04-24
# 功能: 提交前检查(已禁用)
# ============================================================
# 🔧 已禁用所有检查,直接允许提交
echo "⏭️ [Pre-commit] 检查已禁用,允许提交"
exit 0

View File

@@ -728,8 +728,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
/**
* 处理耗材请求
* 🔧 BugFix #443: 签发时跳过 handDevice避免重复创建 DeviceDispense 并覆盖关键字段(如 performLocation
* 签发时只需更新状态(下方 sign-advice 批量更新逻辑已处理)
*/
this.handDevice(deviceList, curDate, adviceOpType);
if (AdviceOpType.SAVE_ADVICE.getCode().equals(adviceOpType)) {
this.handDevice(deviceList, curDate, adviceOpType);
}
// 签发时,把草稿状态的账单更新为待收费
if (AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType) && !adviceSaveList.isEmpty()) {

View File

@@ -246,7 +246,8 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
EncounterDiagnosis encounterDiagnosis;
for (SaveDiagnosisChildParam saveDiagnosisChildParam : diagnosisChildList) {
encounterDiagnosis = new EncounterDiagnosis();
encounterDiagnosis.setId(saveDiagnosisChildParam.getEncounterDiagnosisId());
// 注意:不设置 encounterDiagnosisId,因为上面已经删除了所有记录
// 如果设置旧的 IDsaveOrUpdate 会尝试 UPDATE 不存在的记录导致失败或重复插入
encounterDiagnosis.setEncounterId(encounterId);
encounterDiagnosis.setConditionId(saveDiagnosisChildParam.getConditionId());
encounterDiagnosis.setMaindiseFlag(saveDiagnosisChildParam.getMaindiseFlag());

View File

@@ -245,7 +245,7 @@ public class InpatientAdviceDto {
/**
* 药品/服务类型
*/
private String categoryCode;
private Integer categoryCode;
/**
* 执行科室
*/

View File

@@ -91,13 +91,15 @@
os.surgery_nature AS surgeryType,
cs.incision_level AS incisionLevel,
fc.contract_name AS feeType,
os.fee_type AS feeType,
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo
FROM op_schedule os
LEFT JOIN adm_patient ap ON os.patient_id = ap.id
INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no AND cs.delete_flag = '0'
LEFT JOIN adm_organization o ON cs.org_id = o.id
LEFT JOIN doc_request_form drf ON drf.prescription_no=cs.surgery_no
LEFT JOIN adm_encounter ae ON ae.id = os.visit_id AND ae.delete_flag = '0'
LEFT JOIN adm_account aa ON aa.encounter_id = ae.id AND aa.delete_flag = '0'
LEFT JOIN fin_contract fc ON fc.bus_no = aa.contract_no AND fc.delete_flag = '0'
LEFT JOIN (
SELECT patient_id, identifier_no
FROM (

View File

@@ -42,8 +42,8 @@
T5.package_name,
T6.name as sub_item_name
FROM wor_activity_definition T1
/* 只JOIN必要的价格表使用INNER JOIN避免笛卡尔积 */
INNER JOIN adm_charge_item_definition T2
/* 价格表使用LEFT JOIN避免因缺少价格记录导致搜索不到项目 */
LEFT JOIN adm_charge_item_definition T2
ON T1.id = T2.instance_id
AND T2.instance_table = 'wor_activity_definition'
/* 检验类型关联 */

View File

@@ -305,28 +305,28 @@
T1.occurrence_end_time AS end_time,
T1.requester_id AS requester_id,
T1.create_time AS request_time,
NULL::integer AS skin_test_flag,
NULL::integer AS inject_flag,
NULL::bigint AS group_id,
NULL AS skin_test_flag,
NULL AS inject_flag,
NULL AS group_id,
T1.performer_check_id,
T2."name" AS advice_name,
T2.id AS item_id,
NULL::varchar AS volume,
NULL::varchar AS lot_number,
NULL AS volume,
NULL AS lot_number,
T1.quantity AS quantity,
T1.unit_code AS unit_code,
T1.status_enum AS request_status,
NULL::varchar AS method_code,
NULL::varchar AS rate_code,
NULL::numeric AS dose,
NULL::varchar AS dose_unit_code,
NULL AS method_code,
NULL AS rate_code,
NULL AS dose,
NULL AS dose_unit_code,
ao1.id AS position_id,
ao1."name" AS position_name,
NULL::integer AS dispense_per_duration,
1::numeric AS part_percent,
NULL AS dispense_per_duration,
1 AS part_percent,
ccd."name" AS condition_definition_name,
T1.therapy_enum AS therapy_enum,
NULL::integer AS sort_number,
NULL AS sort_number,
T1.quantity AS execute_num,
af.day_times,
ae.bus_no,
@@ -341,7 +341,7 @@
personal_account.balance_amount,
personal_account.id AS account_id,
T2.category_code,
NULL::integer AS dispense_status
NULL AS dispense_status
FROM wor_service_request AS T1
LEFT JOIN wor_activity_definition AS T2
ON T2.id = T1.activity_id

View File

@@ -45,18 +45,6 @@
<if test="endDate != null and endDate != ''">
AND drf.create_time &lt;= (#{endDate}::date + INTERVAL '1 day' - INTERVAL '1 second')
</if>
<if test="status != null and status != ''">
AND CASE
WHEN MIN(wsr.status_enum) = 1 THEN 0
WHEN MIN(wsr.status_enum) = 2 THEN 1
WHEN MIN(wsr.status_enum) = 3 AND MAX(CASE WHEN wsr.performer_check_id IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 2
WHEN MIN(wsr.status_enum) = 3 THEN 4
WHEN MIN(wsr.status_enum) = 4 THEN 3
WHEN MIN(wsr.status_enum) = 5 OR MIN(wsr.status_enum) = 6 OR MIN(wsr.status_enum) = 7 THEN 7
WHEN MIN(wsr.status_enum) = 8 THEN 6
ELSE NULL
END = #{status}::integer
</if>
<if test="keyword != null and keyword != ''">
AND (drf.prescription_no ILIKE '%' || #{keyword} || '%'
OR EXISTS (
@@ -72,6 +60,19 @@
</if>
GROUP BY drf.id, drf.encounter_id, drf.prescription_no, drf.name, drf.desc_json,
drf.requester_id, drf.create_time, ap.name
<if test="status != null and status != ''">
HAVING CASE MIN(wsr.status_enum)
WHEN 1 THEN 0
WHEN 2 THEN 1
WHEN 3 THEN 4
WHEN 4 THEN 4
WHEN 5 THEN 5
WHEN 6 THEN 5
WHEN 7 THEN 5
WHEN 8 THEN 6
ELSE NULL
END = #{status}::integer
</if>
</select>
<select id="getRequestFormDetail" resultType="com.openhis.web.regdoctorstation.dto.RequestFormDetailQueryDto">

View File

@@ -1362,8 +1362,9 @@ async function handleMethodSelect(checked, method, cat) {
existingItem.isPackage = true;
existingItem.packageId = method.packageId;
existingItem.packageName = method.packageName || existingItem.packageName; // #428修复: 确保 packageName 同步
existingItem.expanded = true; // #428修复: 有套餐时默认展开,展示套餐明细
// 预加载套餐明细
loadPackageDetailsForItem(existingItem);
await loadPackageDetailsForItem(existingItem);
}
updateMethodDisplay();
return;
@@ -1399,9 +1400,10 @@ async function handleMethodSelect(checked, method, cat) {
};
selectedItems.value.push(newItem);
// 如果是套餐,预加载套餐明细
// 如果是套餐,预加载套餐明细并默认展开
if (newItem.isPackage && newItem.packageId) {
loadPackageDetailsForItem(newItem);
newItem.expanded = true;
await loadPackageDetailsForItem(newItem);
}
// 自动回填执行科室
@@ -1523,7 +1525,10 @@ async function handleItemSelect(checked, item, cat) {
// Bug #384修复 + #426修复: 展开/收起项目卡片
async function toggleItemExpand(item) {
item.expanded = !item.expanded;
if (item.expanded && (item.isPackage || item.packageName) && (!item.packageDetails || item.packageDetails.length === 0) && !item.packageDetailsLoading) {
const carrier = getPackageCarrier(item);
const hasDetails = Array.isArray(item.packageDetailsDisplay) && item.packageDetailsDisplay.length > 0
|| Array.isArray(carrier?.packageDetails) && carrier.packageDetails.length > 0;
if (item.expanded && (item.isPackage || item.packageName) && !hasDetails && !item.packageDetailsLoading) {
await loadPackageDetailsForItem(item);
}
if (item.expanded && shouldShowPackageBody(item)) {
@@ -1577,7 +1582,7 @@ async function loadMethodPackageDetails(item, method) {
const packageId = packages[0].id;
// 查询套餐明细
const detailRes = await request({
url: `/system/package/${packageId}/details`,
url: `/system/check-type/package/${packageId}/details`,
method: 'get'
});
if (detailRes.code === 200 && detailRes.data) {

View File

@@ -289,43 +289,62 @@ function getList() {
return obj;
});
form.value.diagnosisList = datas;
// form.value.diagnosisList = res.data;
// 去重:按 conditionId 去重,防止后端重复插入导致重复记录
deduplicateDiagnosisList();
emits('diagnosisSave', false);
}
});
getTcmDiagnosis({ encounterId: props.patientInfo.encounterId }).then((res) => {
console.log('getTcmDiagnosis=======>', JSON.stringify(res.data.illness));
console.log('getTcmDiagnosis=======>', JSON.stringify(res.data?.illness));
if (res.code == 200) {
if (res.data.illness.length > 0) {
diagnosisNetDatas.value = res.data.illness;
res.data.illness.forEach((item, index) => {
newList.push({
name: item.name + '-' + (res.data.symptom[index]?.name || ''),
ybNo: item.ybNo,
medTypeCode: item.medTypeCode,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
});
if (res.code == 200 && res.data?.illness?.length > 0) {
diagnosisNetDatas.value = res.data.illness;
res.data.illness.forEach((item, index) => {
newList.push({
name: item.name + '-' + (res.data.symptom?.[index]?.name || ''),
ybNo: item.ybNo,
medTypeCode: item.medTypeCode,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
});
// 将新数据添加到现有列表中
form.value.diagnosisList.push(...newList);
// 重新排序整个列表
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;
});
}
emits('diagnosisSave', false);
});
// 将新数据添加到现有列表中
form.value.diagnosisList.push(...newList);
// 重新排序整个列表
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;
});
// TCM 数据添加后也去重
deduplicateDiagnosisList();
}
emits('diagnosisSave', false);
});
getTree();
}
/**
* 诊断列表去重:按 ybNo + name 组合去重,保留第一条记录
* 防止后端 saveOrUpdate 在删除后误 INSERT 导致重复
*/
function deduplicateDiagnosisList() {
const seen = new Set();
const dedupedList = [];
for (const item of form.value.diagnosisList) {
// 使用 ybNo 和 name 组合作为唯一标识(中医诊断没有 ybNo用 name 去重)
const key = item.ybNo ? `${item.ybNo}` : `name_${item.name}`;
if (!seen.has(key)) {
seen.add(key);
dedupedList.push(item);
}
}
form.value.diagnosisList = dedupedList;
}
init();
function init() {
diagnosisInit().then((res) => {
@@ -603,6 +622,18 @@ function handleSaveDiagnosis() {
return aNo - bNo;
});
// 步骤1.5:确保每条诊断都有诊断医生和诊断时间(元数据补全)
const doctorName = props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name;
const now = new Date().toLocaleString('zh-CN');
sortedList.forEach((item) => {
if (!item.diagnosisDoctor) {
item.diagnosisDoctor = doctorName;
}
if (!item.diagnosisTime) {
item.diagnosisTime = now;
}
});
// 步骤2重新分配连续的序号从1开始
sortedList.forEach((item, index) => {
item.diagSrtNo = index + 1; // 这里是关键!把”诊断排序”改成新顺序

View File

@@ -1131,15 +1131,15 @@ function handleLocationClick(item, row, index) {
.then((res) => {
const list = res.data || [];
const d = pickBestOrgQuantityRow(list);
const strictOk = d && Number(d.orgQuantity ?? 0) > 0;
if (strictOk) {
// 严格批号查询有库存orgQuantity > 0
if (d && Number(d.orgQuantity ?? 0) > 0) {
applyFromDto(d, false);
if (Number(r.totalQuantity) <= 0) {
proxy.$message.warning('仓库数量为0无法调用');
}
persistStore();
return;
}
// 严格查询无库存或数量为0 → 回退到非严格查询(查同仓库其他批号)
if (lotTrimmed) {
return runGet(false).then((res2) => {
const list2 = res2.data || [];
@@ -1157,6 +1157,8 @@ function handleLocationClick(item, row, index) {
persistStore();
});
}
// 没有指定批号,直接提示
r.totalQuantity = 0;
r.price = 0;
proxy.$message.warning('仓库数量为0无法调用');