1. 在ActivityDefinition实体类及相关DTO中添加inspectionTypeId字段

2. 新增检验类型分页查询接口及前端API调用
3. 优化检验申请模块的前后端交互逻辑
4.完成修改78 增加门诊医生开立检验申请单立检验申请单的检验项目写死的问题
5.对检验目录设置的查询,更新和保存进行修改完善。
6.对检验项目设置的页面使用vue3+elementui进行修改。
This commit is contained in:
wangjian963
2026-03-12 18:58:54 +08:00
parent bc12cc1b08
commit 5f134945ab
13 changed files with 917 additions and 1365 deletions

View File

@@ -949,7 +949,23 @@ export function deleteInspectionApplication(applyNo) {
}
/**
* 获取检验类型列表(分类)
* 分页获取检验类型列表(分类)
* @param {Object} queryParams - 查询参数
* @param {number} queryParams.pageNo - 页码
* @param {number} queryParams.pageSize - 每页数量
* @param {string} queryParams.searchKey - 搜索关键词
*/
export function getInspectionTypePage(queryParams) {
return request({
url: '/system/inspection-type/page',
method: 'get',
params: queryParams,
});
}
/**
* 获取检验类型列表(不分页,兼容旧接口)
* @deprecated 建议使用 getInspectionTypePage 分页接口
*/
export function getInspectionTypeList() {
return request({
@@ -965,6 +981,7 @@ export function getInspectionTypeList() {
* @param {number} queryParams.pageNo - 页码
* @param {number} queryParams.pageSize - 每页数量
* @param {string} queryParams.categoryCode - 目录类别编码(检验)
* @param {string} queryParams.inspectionTypeId - 检验类型ID列表多个用逗号分隔
*/
export function getInspectionItemList(queryParams) {
return request({

View File

@@ -490,7 +490,7 @@
</template>
<script setup>
import {onMounted, reactive, ref, watch, nextTick} from 'vue'
import {onMounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import { DocumentChecked, Plus, Document, Printer, Delete, Check } from '@element-plus/icons-vue'
import {
@@ -502,9 +502,20 @@ import {
} from '../api'
import useUserStore from '@/store/modules/user.js'
// 迁移到 hiprint
import { simplePrint, PRINT_TEMPLATE, previewPrint } from '@/utils/printUtils.js'
import { previewPrint } from '@/utils/printUtils.js'
import {storeToRefs} from 'pinia'
// 获取当前组件实例和字典
const { proxy } = getCurrentInstance()
const { activity_category_code } = proxy.useDict('activity_category_code')
// 获取"检验"分类的字典值(与检验项目设置维护保持一致)
const inspectionCategoryCode = computed(() => {
const dictList = activity_category_code.value
const inspectionItem = dictList?.find(item => item.label === '检验')
return inspectionItem?.value || '22' // 默认使用数据库中检验的 category_code 值
})
// Props
const props = defineProps({
patientInfo: {
@@ -520,7 +531,6 @@ const props = defineProps({
const emit = defineEmits(['save'])
// 响应式数据
const showForm = ref(false)
const loading = ref(false)
const total = ref(0)
const leftActiveTab = ref('application')
@@ -532,11 +542,8 @@ const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userS
// 修改 initData 函数
async function initData() {
console.log('【检验】开始初始化数据当前patientInfo:', props.patientInfo)
// 先初始化患者信息(如果有)
if (props.patientInfo && props.patientInfo.encounterId) {
console.log('【检验】初始化患者信息')
queryParams.encounterId = props.patientInfo.encounterId
formData.visitNo = props.patientInfo.busNo || ''
formData.patientId = props.patientInfo.patientId || ''
@@ -550,20 +557,10 @@ async function initData() {
formData.applyOrganizationId = props.patientInfo.orgId || ''
formData.encounterId = props.patientInfo.encounterId
console.log('【检验】患者信息初始化完成formData:', JSON.stringify({
patientName: formData.patientName,
medicalrecordNumber: formData.medicalrecordNumber,
applyDepartment: formData.applyDepartment,
applyDocName: formData.applyDocName
}, null, 2))
// 生成申请单号
generateApplicationNo().then((newApplyNo) => {
formData.applyNo = newApplyNo;
console.log('【检验】申请单号生成:', newApplyNo)
});
} else {
console.log('【检验】没有有效的patientInfo跳过患者信息初始化')
formData.applyNo = newApplyNo
})
}
}
@@ -614,18 +611,6 @@ const formData = reactive({
encounterId: ''
})
// 表单验证规则
const formRules = {
natureofCost: [{ required: true, message: '请选择费用性质', trigger: 'change' }],
applyTime: [{ required: true, message: '请选择申请日期', trigger: 'change' }],
executeDepartment: [{ required: true, message: '请选择执行科室', trigger: 'change' }],
clinicDesc: [{ required: true, message: '请输入诊断描述', trigger: 'blur' }],
clinicDiag: [{ required: true, message: '请输入临床诊断', trigger: 'blur' }],
medicalHistorySummary: [{ required: true, message: '请输入病史摘要', trigger: 'blur' }],
purposeofInspection: [{ required: true, message: '请输入检验目的', trigger: 'blur' }],
labApplyItemList: [{ required: true, message: '请至少选择一个检验项目', trigger: 'change' }]
}
// 表单引用
const formRef = ref()
@@ -664,39 +649,30 @@ const inspectionLoading = ref(false)
async function loadInspectionData() {
// 如果已经加载过数据,直接返回(避免重复请求)
if (inspectionCategories.value.length > 0) {
console.log('【检验】数据已缓存,跳过重复加载')
return
}
inspectionLoading.value = true
const startTime = Date.now() // 性能监控开始时间
try {
console.log('【检验】开始并行请求数据')
// 并行请求:同时获取检验类型列表和检验项目列表
const [typeRes, itemRes] = await Promise.all([
// 添加错误处理和重试机制
getInspectionTypeList().catch(error => {
console.error('【检验】获取检验类型失败:', error)
return { data: [] } // 返回空数据作为降级方案
console.error('获取检验类型失败:', error)
return { data: [] }
}),
getInspectionItemList({
pageNo: 1,
pageSize: 200, // 进一步优化:减少数据量,按需加载
searchKey: '', // 添加搜索关键词参数
categoryCode: 'inspection' // 明确指定检验类别
getInspectionItemList({
pageNo: 1,
pageSize: 500,
searchKey: '',
categoryCode: inspectionCategoryCode.value
}).catch(error => {
console.error('【检验】获取检验项目失败:', error)
return { data: { records: [] } } // 返回空数据作为降级方案
console.error('获取检验项目失败:', error)
return { data: { records: [] } }
})
])
const endTime = Date.now()
console.log(`【检验】API请求完成耗时: ${endTime - startTime}ms`)
const typeList = typeRes.data || []
console.log('【检验】获取到检验类型数量:', typeList.length)
// 解析检验项目数据
let allItems = []
@@ -708,40 +684,39 @@ async function loadInspectionData() {
allItems = itemRes
}
console.log('【检验】获取到检验项目数量:', allItems.length)
// 按分类组织数据
// 按分类组织数据(与检验项目设置维护保持一致的字段映射)
const categories = typeList
.filter(type => type.validFlag === 1 || type.validFlag === undefined)
.map((type, index) => {
const categoryItems = allItems
.filter(item => {
// 更宽松的过滤条件:如果没有明确的检验类别标识,也认为是检验项目
const isInspection = !item.categoryCode_dictText ||
!item.categoryName ||
!item.categoryCode ||
item.categoryCode_dictText === '检验' ||
item.categoryName === '检验' ||
item.categoryCode === 'inspection'
const matchType = item.typeName === type.name ||
item.inspectionTypeName === type.name ||
item.bigClassName === type.name ||
item.typeCode === type.code
return isInspection && (matchType || typeList.length === 1)
// 转换为字符串进行比较,避免类型不匹配问题
const itemTypeId = String(item.inspectionTypeId || '')
const typeId = String(type.id || '')
const itemTypeName = String(item.inspectionTypeId_dictText || item.typeName || '').trim()
const typeName = String(type.name || '').trim()
// 按检验类型ID匹配优先使用 inspectionTypeId
const matchById = itemTypeId && typeId && itemTypeId === typeId
const matchByName = itemTypeName && typeName && itemTypeName === typeName
const matchByCode = String(item.typeCode || '') === String(type.code || '')
return matchById || matchByName || matchByCode
})
.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substr(2, 9),
itemId: item.id || item.activityId || Math.random().toString(36).substring(2, 11),
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
itemAmount: item.retailPrice || item.price || 0,
sampleType: item.sampleType || '血液',
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
itemQty: 1,
serviceFee: 0,
type: type.name,
type: type.name || item.inspectionTypeId_dictText || '检验',
isSelfPay: false,
activityId: item.activityId,
code: item.code || item.activityCode
code: item.busNo || item.code || item.activityCode,
inspectionTypeId: item.inspectionTypeId || null
}))
return {
@@ -755,41 +730,50 @@ async function loadInspectionData() {
// 过滤掉没有项目的分类
const validCategories = categories.filter(cat => cat.items.length > 0)
// 如果没有有效分类,但有项目数据,创建一个默认分类
// 如果没有有效分类,但有项目数据,按项目自带的检验类型文本分组
if (validCategories.length === 0 && allItems.length > 0) {
const defaultItems = allItems
.slice(0, 50) // 优化:限制显示数量
.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substr(2, 9),
// 按检验类型文本分组
const typeMap = new Map()
allItems.forEach(item => {
const typeName = item.inspectionTypeId_dictText || '其他检验'
if (!typeMap.has(typeName)) {
typeMap.set(typeName, [])
}
typeMap.get(typeName).push(item)
})
// 创建分类
typeMap.forEach((items, typeName) => {
const mappedItems = items.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substring(2, 11),
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
itemAmount: item.retailPrice || item.price || 0,
sampleType: item.sampleType || '血液',
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
itemQty: 1,
serviceFee: 0,
type: '检验',
type: typeName,
isSelfPay: false,
activityId: item.activityId,
code: item.code || item.activityCode
code: item.busNo || item.code || item.activityCode,
inspectionTypeId: item.inspectionTypeId || null
}))
if (defaultItems.length > 0) {
validCategories.push({
key: 'default',
label: '检验项目',
expanded: true,
items: defaultItems
key: `type_${typeName}`,
label: typeName,
expanded: validCategories.length === 0,
items: mappedItems
})
}
})
}
if (validCategories.length > 0) {
inspectionCategories.value = validCategories
activeCategory.value = validCategories[0].key
console.log('【检验】数据加载成功,分类数量:', validCategories.length)
} else {
console.warn('【检验】未获取到有效的检验项目数据,使用备用数据')
console.warn('未获取到有效的检验项目数据,使用备用数据')
// 直接使用备用数据,不抛出错误
inspectionCategories.value = [
{
@@ -864,14 +848,11 @@ const getFilteredItems = (categoryKey) => {
function getInspectionList() {
// 如果没有encounterId,不调用接口
if (!queryParams.encounterId) {
// console.warn('【检验】encounterId为空,不调用接口')
return
}
loading.value = true
// 调用分页API传递分页参数和 encounterId
// console.log('【检验】调用API,encounterId:', queryParams.encounterId, 'pageNo:', queryParams.pageNo, 'pageSize:', queryParams.pageSize)
getApplyList({
encounterId: queryParams.encounterId,
pageNo: queryParams.pageNo,
@@ -885,7 +866,7 @@ function getInspectionList() {
// 处理数据:将同一个申请单的多个明细合并成一条记录
inspectionList.value = mergeInspectionApplyRecords(res.data.records)
total.value = res.data.total || 0
}
}
// 如果返回的是普通数组
else if (Array.isArray(res.data)) {
// 处理数据:将同一个申请单的多个明细合并成一条记录
@@ -901,14 +882,12 @@ function getInspectionList() {
inspectionList.value = []
total.value = 0
}
// console.log('【检验】获取数据成功,数量:', inspectionList.value.length, '总数:', total.value)
} else {
inspectionList.value = []
total.value = 0
ElMessage.error(res.message || '获取检验申请单列表失败')
}
}).catch((error) => {
// console.error('获取检验申请单列表异常:', error)
inspectionList.value = []
total.value = 0
ElMessage.error('获取检验申请单列表异常: ' + (error.message || ''))
@@ -959,7 +938,6 @@ function formatAmount(amount) {
// 新增申请单
async function handleNewApplication() {
// console.log('点击新增按钮')
resetForm()
// 生成新的申请单号
formData.applyNo = await generateApplicationNo()
@@ -977,15 +955,11 @@ const checkApplicationNoExists = async (applyNo) => {
// 后端返回格式:如果存在返回{applyNo: "..."}不存在返回null
return response.code === 200 && response.data && response.data.applyNo;
} catch (error) {
// 隐藏具体的错误信息,仅记录在控制台
console.debug('检查申请单号时发生错误:', error.message);
// 如果API调用失败假设单号不存在以避免阻塞
return false;
}
};
// 生成申请单号
let counter = 0;
const generateApplicationNo = async () => {
let applyNo;
let isUnique = false;
@@ -1028,8 +1002,6 @@ const generateApplicationNo = async () => {
}
if (!isUnique) {
console.warn(`经过${maxAttempts}次尝试仍未生成唯一申请单号,使用备用方案`);
// 如果多次尝试后仍无法生成唯一单号使用UUID的一部分作为备用方案
const timestamp = Date.now().toString();
const randomPart = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
const fallbackNo = 'F' + timestamp.slice(-17) + randomPart; // 使用F开头表示备用方案
@@ -1080,12 +1052,6 @@ async function resetForm() {
formRef.value?.clearValidate()
}
// 返回列表
function handleBack() {
showForm.value = false
getInspectionList()
}
// 保存
function handleSave() {
// 重置验证错误状态
@@ -1153,8 +1119,6 @@ function handleSave() {
// 先检查申请单号是否已存在
console.log('查询申请单号:', formData.applyNo)
// 如果正在生成新申请单号,则跳过重复检查,直接保存
if (isGeneratingNewApplyNo.value) {
// 正在使用新生成的单号,直接保存
isGeneratingNewApplyNo.value = false; // 重置标志
@@ -1217,10 +1181,8 @@ function handleSave() {
})
}
// 执行保存操作
const executeSave = (saveData) => {
saveInspectionApplication(saveData).then((res) => {
console.log('保存检验申请单结果:', res.code)
if (res.code === 200) {
ElMessage.success('保存成功')
resetForm()
@@ -1253,22 +1215,8 @@ const executeSave = (saveData) => {
}
// 表单保存
function handleFormSave() {
formRef.value?.validate((valid) => {
if (valid) {
formData.labApplyItemList = selectedInspectionItems.value.map(item => item.itemId)
// console.log('保存检验申请单表单数据:', formData)
ElMessage.success('保存成功')
showForm.value = false
getInspectionList()
}
})
}
// 查看详情
function handleView(row) {
// console.log('点击查看按钮,数据:', row)
// 加载表单数据
Object.assign(formData, row)
@@ -1307,8 +1255,7 @@ function switchCategory(category) {
// 处理搜索
function handleSearch() {
// 搜索逻辑已在getFilteredItems中实现,这里可以添加额外的搜索逻辑
// console.log('搜索关键词:', searchKeyword.value)
// 搜索逻辑已在getFilteredItems中实现
}
// 处理项目项点击(排除勾选框点击)
@@ -1497,13 +1444,9 @@ watch(() => props.activeTab, async (newVal) => {
// 监听patientInfo变化,确保encounterId及时更新并重新加载数据
watch(() => props.patientInfo, async (newVal) => {
console.log('【检验】patientInfo变化:', newVal)
console.log('【检验】接收到的完整patientInfo:', JSON.stringify(newVal, null, 2))
if (newVal && newVal.encounterId) {
const oldEncounterId = queryParams.encounterId
queryParams.encounterId = newVal.encounterId
console.log('【检验】更新encounterId:', queryParams.encounterId)
// 初始化数据
await initData();
@@ -1530,9 +1473,7 @@ watch(() => selectedInspectionItems.value, (newVal) => {
// 组件挂载时预加载检验项目数据不依赖patientInfo
onMounted(async () => {
console.log('【检验】组件挂载,开始预加载检验项目数据')
await loadInspectionData()
console.log('【检验】检验项目数据预加载完成')
})
// 暴露方法
@@ -1604,6 +1545,9 @@ defineExpose({
height: 650px;
overflow-y: auto;
padding: 20px;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin: 10px;
}
/* 选择区域 */
@@ -1631,6 +1575,7 @@ defineExpose({
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.pagination-container {
@@ -1959,6 +1904,8 @@ defineExpose({
border: 1px solid #ebeef5;
border-radius: 4px;
background: #fafafa;
max-height: 280px;
overflow-y: auto;
}
.category-tree-item {
@@ -2049,10 +1996,6 @@ defineExpose({
flex-direction: column;
}
.selected-header {
flex-shrink: 0;
}
.selected-tree {
flex: 1;
}
@@ -2181,39 +2124,8 @@ defineExpose({
}
}
/* 优化表单样式 */
.application-form {
padding: 20px;
height: 700px;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin: 10px;
}
/* 优化已选项目区样式 */
.selected-items-area .selected-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.selected-items-area .selected-tree {
max-height: 270px;
overflow-y: auto;
}
/* 优化搜索框样式 */
.inspection-selector .el-input {
margin-bottom: 15px;
}
/* 优化分类树样式 */
.category-tree {
max-height: 280px;
overflow-y: auto;
}
</style>

File diff suppressed because it is too large Load Diff