581 【住院医生站-临床医嘱-手术】手术申请单缺失多项核心业务字段与强拦截逻辑,导致医疗安全制度无法落地且阻断手术室排班闭环

This commit is contained in:
wangjian963
2026-06-02 13:22:09 +08:00
parent ac0d563274
commit cde58cf18f
8 changed files with 120 additions and 21 deletions

View File

@@ -48,6 +48,11 @@ public interface IOutpatientRegistrationAppService {
IPage<PractitionerMetadata> getPractitionerMetadataByLocationId(Long orgId, String searchKey, Integer pageNo, IPage<PractitionerMetadata> getPractitionerMetadataByLocationId(Long orgId, String searchKey, Integer pageNo,
Integer pageSize); Integer pageSize);
/**
* 查询全院医生(不限科室),按角色过滤
*/
IPage<PractitionerMetadata> getAllDoctors(String searchKey, Integer pageNo, Integer pageSize);
/** /**
* 根据机构id筛选服务项目 * 根据机构id筛选服务项目
* *

View File

@@ -243,6 +243,22 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
return practitionerMetadataPage; return practitionerMetadataPage;
} }
/**
* 查询全院医生(不限科室),按角色过滤
*/
@Override
public IPage<PractitionerMetadata> getAllDoctors(String searchKey, Integer pageNo, Integer pageSize) {
QueryWrapper<PractitionerMetadata> queryWrapper = HisQueryUtils.buildQueryWrapper(null, searchKey,
new HashSet<>(Arrays.asList("name", "py_str", "wb_str")), null);
IPage<PractitionerMetadata> page =
outpatientRegistrationAppMapper.getAllDoctorPage(new Page<>(pageNo, pageSize),
PractitionerRoles.DOCTOR.getCode(), queryWrapper);
page.getRecords().forEach(e -> {
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
});
return page;
}
/** /**
* 根据机构id筛选服务项目 * 根据机构id筛选服务项目
* *

View File

@@ -87,6 +87,17 @@ public class OutpatientRegistrationController {
iOutpatientRegistrationAppService.getPractitionerMetadataByLocationId(orgId, searchKey, pageNo, pageSize)); iOutpatientRegistrationAppService.getPractitionerMetadataByLocationId(orgId, searchKey, pageNo, pageSize));
} }
/**
* 查询全院医生(不限科室),用于手术申请等需跨科室选择医生的场景
*/
@GetMapping(value = "/all-doctors")
public R<?> getAllDoctors(
@RequestParam(value = "searchKey", defaultValue = "") String searchKey,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
return R.ok(iOutpatientRegistrationAppService.getAllDoctors(searchKey, pageNo, pageSize));
}
/** /**
* 根据机构id筛选服务项目 * 根据机构id筛选服务项目
*/ */

View File

@@ -24,6 +24,13 @@ public interface OutpatientRegistrationAppMapper {
@Param("orgId") Long orgId, @Param("RoleCode") String RoleCode, @Param("orgId") Long orgId, @Param("RoleCode") String RoleCode,
@Param(Constants.WRAPPER) QueryWrapper<PractitionerMetadata> queryWrapper); @Param(Constants.WRAPPER) QueryWrapper<PractitionerMetadata> queryWrapper);
/**
* 查询全院医生(不限科室),按角色过滤
*/
IPage<PractitionerMetadata> getAllDoctorPage(@Param("page") Page<PractitionerMetadata> page,
@Param("RoleCode") String RoleCode,
@Param(Constants.WRAPPER) QueryWrapper<PractitionerMetadata> queryWrapper);
/** /**
* 根据病人id和科室id查询当日挂号次数 * 根据病人id和科室id查询当日挂号次数
*/ */

View File

@@ -31,6 +31,34 @@
${ew.customSqlSegment} ${ew.customSqlSegment}
</select> </select>
<!-- 查询全院医生(不限科室),用于手术申请等需跨科室选择医生的场景 -->
<select id="getAllDoctorPage" resultType="com.openhis.web.chargemanage.dto.PractitionerMetadata">
SELECT T3.tenant_id,
T3.ID,
T3.NAME,
T3.gender_enum,
T3.py_str,
T3.wb_str,
T3.dr_profttl_code
FROM (
SELECT T1.tenant_id,
T1.ID,
T1.NAME,
T1.gender_enum,
T1.py_str,
T1.wb_str,
T1.dr_profttl_code
FROM adm_practitioner AS T1
WHERE T1.delete_flag = '0'
AND EXISTS(SELECT 1
FROM adm_practitioner_role AS T2
WHERE T2.practitioner_id = T1.ID
AND T2.delete_flag = '0'
AND T2.ROLE_code = #{RoleCode})
) AS T3
${ew.customSqlSegment}
</select>
<select id="getNumByPatientIdAndOrganizationId" resultType="Integer"> <select id="getNumByPatientIdAndOrganizationId" resultType="Integer">
SELECT COUNT SELECT COUNT
(1) (1)

View File

@@ -111,6 +111,17 @@ export function getSurgery(data) {
}); });
} }
/**
* 查询全院医生(不限科室),用于手术申请
*/
export function getAllDoctors(params) {
return request({
url: '/charge-manage/register/all-doctors',
method: 'get',
params: params,
});
}
/** /**
* 住院患者转科 * 住院患者转科
*/ */

View File

@@ -129,6 +129,8 @@ const showApplicationFormDialog = (name) => {
applicationFormNameRef?.value.getLocationInfo(); applicationFormNameRef?.value.getLocationInfo();
// 诊断目录列表 // 诊断目录列表
applicationFormNameRef?.value.getDiagnosisList(); applicationFormNameRef?.value.getDiagnosisList();
// 医生列表(手术申请单专用)
applicationFormNameRef?.value.loadDoctorOptions?.();
}, 150); }, 150);
} else { } else {
applicationFormName.value = components.value[name]; applicationFormName.value = components.value[name];
@@ -140,6 +142,8 @@ const showApplicationFormDialog = (name) => {
applicationFormNameRef?.value.getLocationInfo(); applicationFormNameRef?.value.getLocationInfo();
// 诊断目录列表 // 诊断目录列表
applicationFormNameRef?.value.getDiagnosisList(); applicationFormNameRef?.value.getDiagnosisList();
// 医生列表(手术申请单专用)
applicationFormNameRef?.value.loadDoctorOptions?.();
}); });
} }
}; };

View File

@@ -173,7 +173,7 @@
v-model="form.mainSurgeonId" v-model="form.mainSurgeonId"
filterable filterable
clearable clearable
placeholder="请选择主刀医生(支持搜索)" placeholder="请输入关键字搜索医生"
style="width: 100%" style="width: 100%"
> >
<el-option <el-option
@@ -196,7 +196,7 @@
v-model="form.assistant1Id" v-model="form.assistant1Id"
filterable filterable
clearable clearable
placeholder="请选择第一助手(支持搜索)" placeholder="请输入关键字搜索医生"
style="width: 100%" style="width: 100%"
> >
<el-option <el-option
@@ -219,7 +219,7 @@
v-model="form.assistant2Id" v-model="form.assistant2Id"
filterable filterable
clearable clearable
placeholder="请选择第二助手(支持搜索)" placeholder="请输入关键字搜索医生"
style="width: 100%" style="width: 100%"
> >
<el-option <el-option
@@ -331,21 +331,22 @@
</div> </div>
</template> </template>
<script setup name="Surgery"> <script setup name="Surgery">
import {computed, getCurrentInstance, onBeforeMount, onMounted, reactive, ref, watch} from 'vue'; import {computed, defineEmits, getCurrentInstance, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
import {patientInfo} from '../../../store/patient.js'; import {patientInfo} from '../../../store/patient.js';
import {getDepartmentList} from '@/api/public.js'; import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js'; import {getEncounterDiagnosis} from '../../api.js';
import {getSurgeryPage, saveSurgery} from './api'; import {getAllDoctors, getSurgeryPage, saveSurgery} from './api';
import {ElMessage} from 'element-plus'; import {ElMessage} from 'element-plus';
import {getDicts} from '@/api/system/dict/data'; import {getDicts} from '@/api/system/dict/data';
import {listUser} from '@/api/system/user';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const emits = defineEmits(['submitOk']);
const userStore = useUserStore(); const userStore = useUserStore();
// 模块级缓存:避免每次打开弹窗都重新请求手术项目列表 // 模块级缓存:避免每次打开弹窗都重新请求
let surgeryRecordsCache = null; // 原始 API 记录 let surgeryRecordsCache = null; // 原始 API 记录
let surgeryMappedCache = null; // 映射后的 el-transfer 数据 let surgeryMappedCache = null; // 映射后的 el-transfer 数据
let doctorCache = null; // 医生列表(含默认主刀医生 ID
const transferRef = ref(null); const transferRef = ref(null);
const dbTotal = ref(0); // 数据库中的手术项目总数 const dbTotal = ref(0); // 数据库中的手术项目总数
const checkedCount = computed(() => transferValue.value.length); const checkedCount = computed(() => transferValue.value.length);
@@ -476,7 +477,11 @@ const form = reactive({
primaryDiagnosisList: [], //主诊断目录 primaryDiagnosisList: [], //主诊断目录
otherDiagnosisList: [], //其他断目录 otherDiagnosisList: [], //其他断目录
}); });
const rules = reactive({}); const rules = reactive({
surgeryLevel: [{ required: true, message: '请选择手术等级', trigger: 'change' }],
anesthesiaType: [{ required: true, message: '请选择麻醉方式', trigger: 'change' }],
surgerySite: [{ required: true, message: '请选择手术部位', trigger: 'change' }],
});
const state = reactive({}); const state = reactive({});
// 字典选项 // 字典选项
const surgeryLevelOptions = ref([]); const surgeryLevelOptions = ref([]);
@@ -525,29 +530,32 @@ const loadDictOptions = async () => {
}; };
/** /**
* 加载医生选项列表 * 加载医生列表(全院 doctor 角色)+ 模块级缓存
* 后端 SQL 直接过滤 role_code='doctor',无需前端过滤,快且数据完整
*/ */
const loadDoctorOptions = async () => { const loadDoctorOptions = async () => {
if (doctorCache) {
doctorOptions.value = doctorCache.list;
if (doctorCache.defaultId) form.mainSurgeonId = doctorCache.defaultId;
return;
}
try { try {
const res = await listUser({ pageNo: 1, pageSize: 1000 }); const res = await getAllDoctors({ pageNo: 1, pageSize: 200 });
if (res.code === 200) { if (res.code === 200) {
const data = res.data?.records || res.rows || res.data || []; const data = res.data?.records || res.rows || res.data || [];
if (Array.isArray(data)) { if (Array.isArray(data)) {
doctorOptions.value = data.map(item => ({ doctorOptions.value = data.map(item => ({
id: item.practitionerId || item.id || item.userId, id: item.id,
name: item.nickName || item.name || item.userName, name: item.name,
})); }));
} }
} }
// 默认主刀医生设为当前登录用户 let defaultId = '';
if (userStore.nickName) { if (userStore.nickName) {
const currentUser = doctorOptions.value.find( const cur = doctorOptions.value.find(d => d.name === userStore.nickName);
doc => doc.name === userStore.nickName if (cur) { defaultId = cur.id; form.mainSurgeonId = defaultId; }
);
if (currentUser) {
form.mainSurgeonId = currentUser.id;
}
} }
doctorCache = { list: doctorOptions.value, defaultId };
} catch (e) { } catch (e) {
console.error('加载医生列表失败:', e); console.error('加载医生列表失败:', e);
} }
@@ -624,10 +632,19 @@ const submit = () => {
} }
}); });
}; };
/** 递归规范化树形科室 ID 为字符串,确保与 el-tree-select / findTreeItem 兼容 */
const normalizeOrgTreeIds = (nodes) => {
if (!Array.isArray(nodes)) return [];
return nodes.map((node) => ({
...node,
id: node.id != null ? String(node.id) : node.id,
children: node.children?.length ? normalizeOrgTreeIds(node.children) : undefined,
}));
};
/** 查询科室 */ /** 查询科室 */
const getLocationInfo = () => { const getLocationInfo = () => {
getDepartmentList().then((res) => { getDepartmentList().then((res) => {
orgOptions.value = res.data || []; orgOptions.value = normalizeOrgTreeIds(res.data || []);
}); });
}; };
// 获取诊断目录 // 获取诊断目录
@@ -664,7 +681,7 @@ function getDiagnosisList() {
} }
}); });
} }
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList }); defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList, loadDoctorOptions });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.surgery-container { .surgery-container {