Merge branch 'develop' of http://192.168.110.253:3000/wangyizhe/his into develop

This commit is contained in:
guanyu
2026-04-14 17:12:08 +08:00
12 changed files with 94 additions and 543 deletions

1
GIT_TEST_CHENLIN.md Normal file
View File

@@ -0,0 +1 @@
陈琳Git提交测试 - 2026-04-14 16:57:08

1
TEST.md Normal file
View File

@@ -0,0 +1 @@
# 张飞测试记录

1
ZHAOYUN_TEST.md Normal file
View File

@@ -0,0 +1 @@
# 赵云测试提交

View File

@@ -154,6 +154,8 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setDoctorId(raw.getDoctorId());
dto.setDepartmentId(raw.getDepartmentId());
dto.setRealPatientId(raw.getPatientId());
dto.setOrderId(raw.getOrderId());
dto.setOrderNo(raw.getOrderNo());
// 性别处理:直接使用患者表中的 genderEnum
Integer genderEnum = raw.getGenderEnum();

View File

@@ -115,4 +115,15 @@ public class TicketDto {
* 身份证号
*/
private String idCard;
/**
* 预约订单ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long orderId;
/**
* 预约订单号
*/
private String orderNo;
}

View File

@@ -146,4 +146,11 @@ public class CurrentDayEncounterDto {
*/
private Integer displayOrder;
/**
* 是否来自预约签到
* true: 预约签到
* false: 正常挂号
*/
private Boolean isFromAppointment;
}

View File

@@ -72,6 +72,12 @@ public class EncounterFormData {
@JsonSerialize(using = ToStringSerializer.class)
private Long organizationId;
/**
* 预约订单ID用于预约签到时关联预约订单
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long orderId;
/**
* 设置默认值
*/

View File

@@ -67,7 +67,8 @@
T9.payment_id AS paymentId,
T9.picture_url AS pictureUrl,
T9.birth_date AS birthDate,
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo,
COALESCE(T9.order_id IS NOT NULL, false) AS isFromAppointment
from (
SELECT T1.tenant_id AS tenant_id,
T1.id AS encounter_id,
@@ -93,7 +94,8 @@
ai.picture_url AS picture_url,
T8.birth_date AS birth_date,
T8.bus_no AS patient_bus_no,
T18.identifier_no AS identifier_no
T18.identifier_no AS identifier_no,
T1.order_id AS order_id
FROM adm_encounter AS T1
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'

View File

@@ -151,4 +151,9 @@ public class Encounter extends HisBaseEntity {
*/
@TableField("missed_time")
private Date missedTime;
/**
* 预约订单ID
*/
private Long orderId;
}

View File

@@ -445,7 +445,13 @@
prop="healthcareName"
:show-overflow-tooltip="true"
width="200"
/>
>
<template #default="scope">
<span>
{{ (scope.row.healthcareName || '').replace('挂号', '') }}{{ scope.row.isFromAppointment ? '预约' : '挂号' }}
</span>
</template>
</el-table-column>
<!-- <el-table-column
label="专家账号"
align="center"
@@ -1776,6 +1782,7 @@ async function confirmCheckIn() {
priorityEnum: 3, // 默认优先级
serviceTypeId: service.id,
organizationId: patient.departmentId,
orderId: patient.orderId, // 预约订单ID预约签到时需要传递
},
encounterLocationFormData: {
locationId: null,

View File

@@ -650,6 +650,7 @@ import {
getOrgTree,
getInspectionPackageDetails
} from '../api'
import { getLabActivityDefinitionPage } from '@/api/lab/labActivityDefinition'
import useUserStore from '@/store/modules/user.js'
// 迁移到 hiprint
import { previewPrint } from '@/utils/printUtils.js'
@@ -1075,17 +1076,18 @@ const loadCategoryItems = async (categoryKey, loadMore = false) => {
searchKey: searchKeyword.value || ''
}
// 如果有类型ID添加筛选条件从检验项目维护页面获取数据
// 如果有类型 ID添加筛选条件
if (category.typeId) {
params.inspectionTypeId = category.typeId
}
const res = await getInspectionItemList(params)
const res = await getLabActivityDefinitionPage(params)
// 解析数据
let records = []
let total = 0
if (res.data && res.data.records) {
if (res.code === 200 && res.data && res.data.records) {
records = res.data.records
total = res.data.total || 0
} else if (res.data && Array.isArray(res.data)) {
@@ -1096,34 +1098,34 @@ const loadCategoryItems = async (categoryKey, loadMore = false) => {
total = records.length
}
// 映射数据格式
// 映射数据格式(从检验项目维护页面的数据结构映射)
const mappedItems = records.map(item => {
// BugFix: 套餐项目使用套餐金额,普通项目使用零售价
// 套餐项目处理:套餐项目使用套餐金额,普通项目使用零售价
// BugFix#404: 增加对空字符串的判断避免空字符串被误认为有效套餐ID
const isPackage = item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null'
const itemPrice = isPackage
? (item.packageAmount || item.retailPrice || item.price || 0)
: (item.retailPrice || item.price || 0)
? (parseFloat(item.packageAmount || 0) || parseFloat(item.retailPrice || 0) || parseFloat(item.price || 0))
: (parseFloat(item.retailPrice || 0) || parseFloat(item.price || 0))
return {
itemId: item.id || item.activityId || Math.random().toString(36).substring(2, 11),
itemName: item.name || item.itemName || '',
itemId: item.id || Math.random().toString(36).substring(2, 11),
itemName: item.name || '',
itemPrice: itemPrice,
itemAmount: itemPrice,
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
sampleType: item.specimenCode || '血液',
unit: item.permittedUnitCode_dictText || item.unit || '',
itemQty: 1,
serviceFee: item.serviceFee || 0,
serviceFee: 0,
type: category.label,
isSelfPay: false,
activityId: item.activityId,
code: item.busNo || item.code || item.activityCode,
inspectionTypeId: item.inspectionTypeId || category.typeId, // 如果项目没有inspectionTypeId使用分类的typeId
code: item.busNo || '',
inspectionTypeId: item.inspectionTypeId ? Number(item.inspectionTypeId) : null,
activityId: item.id, // 保存 activityId 用于保存申请单时使用
// 套餐相关字段
isPackage: isPackage,
feePackageId: item.feePackageId,
packageName: item.packageName,
children: isPackage ? [] : undefined, // 套餐项目预留children字段
children: isPackage ? [] : undefined,
hasChildren: isPackage
}
})
@@ -1142,6 +1144,7 @@ const loadCategoryItems = async (categoryKey, loadMore = false) => {
category.loaded = true
} catch (error) {
console.error('加载检验项目失败:', error)
// 加载失败时设置空数据
if (!loadMore) {
category.items = []
@@ -1225,25 +1228,26 @@ const querySearchInspectionItems = async (queryString, cb) => {
searchKey: queryString
}
const res = await getInspectionItemList(params)
const res = await getLabActivityDefinitionPage(params)
let suggestions = []
if (res.data && res.data.records) {
// 映射数据格式,与 loadInspectionItemsByType 保持一致
if (res.code === 200 && res.data && res.data.records) {
// 映射数据格式,与 loadCategoryItems 保持一致
suggestions = res.data.records.map(item => ({
itemId: item.id || item.activityId,
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
code: item.busNo || item.code || item.activityCode,
activityId: item.activityId,
inspectionTypeId: item.inspectionTypeId || null
itemId: item.id,
itemName: item.name || '',
itemPrice: parseFloat(item.packageAmount || 0),
sampleType: item.specimenCode || '血液',
unit: item.permittedUnitCode_dictText || item.unit || '',
code: item.busNo || '',
activityId: item.id,
inspectionTypeId: item.inspectionTypeId ? Number(item.inspectionTypeId) : null
}))
}
cb(suggestions)
} catch (error) {
console.error('搜索检验项目失败:', error)
cb([])
}
}
@@ -1656,62 +1660,17 @@ const isItemSelected = (item) => {
}
// 切换检验项目选择
// 切换检验项目选择状态(支持异步获取检验类型)
// BugFix#CodeReview: 移除竞态条件风险,使用加载标志替代 setTimeout sleep
const toggleInspectionItem = async (item) => {
const toggleInspectionItem = (item) => {
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
if (index > -1) {
// 取消选择项目
selectedInspectionItems.value.splice(index, 1)
// 检查剩余项目:如果为空则清空执行科室,否则保持不变
if (selectedInspectionItems.value.length === 0) {
formData.executeDepartment = ''
}
} else {
// 选择新项目
// 创建新对象,包含 itemName 属性(等于 name 属性)
const newItem = {
...item,
itemName: item.itemName,
// BugFix: 套餐项目需要初始化展开属性,否则点击无法展开
expanded: false,
childrenLoaded: false,
loading: false,
children: item.isPackage ? [] : undefined
}
selectedInspectionItems.value.push(newItem)
// Bug #329: 根据检验项目所属类型自动设置执行科室默认值
const inspectionTypeId = item.inspectionTypeId
if (inspectionTypeId) {
// BugFix#CodeReview: 使用加载标志等待,替代 setTimeout sleep
// 确保执行科室列表已加载完成最多等待5秒
if (!isExecuteDepartmentLoaded.value) {
const maxWaitTime = 5000
const startTime = Date.now()
while (!isExecuteDepartmentLoaded.value && (Date.now() - startTime) < maxWaitTime) {
await new Promise(resolve => setTimeout(resolve, 100))
}
if (!isExecuteDepartmentLoaded.value) {
console.warn('执行科室列表加载超时,跳过自动设置')
return
}
}
// 异步获取执行科室(支持动态获取缺失的检验类型)
const defaultDeptCode = await getDefaultPerformDeptCode(inspectionTypeId)
if (defaultDeptCode) {
formData.executeDepartment = defaultDeptCode
} else {
// BugFix#CodeReview: 匹配失败时提示用户手动选择
console.warn('未找到检验类型对应的执行科室typeId:', inspectionTypeId)
ElMessage.warning('未能自动匹配执行科室,请手动选择')
}
} else {
console.warn('检验项目没有 inspectionTypeId')
}
itemName: item.itemName
};
selectedInspectionItems.value.push(newItem);
}
}
@@ -1720,61 +1679,12 @@ const removeInspectionItem = (item) => {
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
if (index > -1) {
selectedInspectionItems.value.splice(index, 1)
// 检查剩余项目:如果为空则清空执行科室,否则保持不变
if (selectedInspectionItems.value.length === 0) {
formData.executeDepartment = ''
}
}
}
// 清空所有选择
const clearAllSelected = () => {
selectedInspectionItems.value = []
formData.executeDepartment = ''
}
// BugFix#326: 展开/收起套餐明细
const togglePackageExpand = async (item) => {
if (!item.isPackage) return
item.expanded = !item.expanded
// 如果展开且未加载明细,则加载
// BugFix#404: 增加对有效套餐ID的校验避免请求404
const feePackageIdStr = String(item.feePackageId || '').trim()
const validPackageId = feePackageIdStr && feePackageIdStr !== '' && feePackageIdStr !== 'null' && feePackageIdStr !== 'undefined'
console.log('togglePackageExpand debug:', {
feePackageId: item.feePackageId,
feePackageIdStr,
validPackageId,
isPackage: item.isPackage,
expanded: item.expanded,
childrenLoaded: item.childrenLoaded
})
if (item.expanded && !item.childrenLoaded && validPackageId) {
item.loading = true
try {
console.log('正在请求套餐明细, packageId:', feePackageIdStr)
const res = await getInspectionPackageDetails(feePackageIdStr)
console.log('套餐明细响应:', res)
if (res.code === 200 && res.data) {
item.children = res.data.map(detail => ({
detailId: detail.detailId,
itemName: detail.itemName,
unit: detail.unit,
unitPrice: detail.unitPrice,
quantity: detail.quantity
}))
item.childrenLoaded = true
}
} catch (e) {
console.error('加载套餐明细失败:', e)
item.children = []
} finally {
item.loading = false
}
}
}
// 分页大小改变
@@ -1793,75 +1703,6 @@ const handleSelectionChange = (selection) => {
selectedRows.value = selection
}
// Bug #326: 处理展开/收起
const handleExpandChange = (row, expandedRows) => {
// 更新展开状态
if (expandedRows.includes(row)) {
if (!expandedRowKeys.value.includes(row.applicationId)) {
expandedRowKeys.value.push(row.applicationId)
}
} else {
const index = expandedRowKeys.value.indexOf(row.applicationId)
if (index > -1) {
expandedRowKeys.value.splice(index, 1)
}
}
}
// 判断是否展开
const isExpanded = (applicationId) => {
return expandedRowKeys.value.includes(applicationId)
}
// 切换展开状态
const toggleExpand = (row) => {
if (isExpanded(row.applicationId)) {
// 收起
expandedRowKeys.value = expandedRowKeys.value.filter(id => id !== row.applicationId)
} else {
// 展开
expandedRowKeys.value.push(row.applicationId)
// 如果没有加载过明细,加载明细数据
if (!row.childrenLoaded) {
loadInspectionItemDetails(row)
}
}
}
// 加载检验项目明细(套餐子项)
const loadInspectionItemDetails = async (row) => {
try {
const res = await getInspectionApplyDetail(row.applyNo)
if (res.code === 200 && res.data) {
const detail = res.data
if (detail.labApplyItemList && detail.labApplyItemList.length > 0) {
// 将明细数据转换为 children
row.children = detail.labApplyItemList.map(item => ({
itemId: item.itemId || item.id,
itemName: item.itemName || item.name,
sampleType: item.sampleType || '未知',
unit: item.unit || '',
itemPrice: item.itemPrice || item.price || 0,
itemQty: item.itemQty || 1,
itemAmount: item.itemAmount || item.price || 0
}))
row.childrenLoaded = true
// 标记是否有子项
row.hasChildren = row.children.length > 0
} else {
row.children = []
row.childrenLoaded = true
row.hasChildren = false
}
}
} catch (error) {
console.error('加载检验项目明细失败:', error)
row.children = []
row.childrenLoaded = true
row.hasChildren = false
}
}
const handlePrint = (row) => {
// 切换到申请单TAB
leftActiveTab.value = 'application'
@@ -1949,6 +1790,7 @@ const loadApplicationToForm = async (row) => {
// 根据申请单号获取完整详情
try {
const res = await getInspectionApplyDetail(row.applyNo)
console.log('申请单详情API返回:', res) // 临时调试日志
if (res.code === 200 && res.data) {
const detail = res.data
// 加载完整的表单数据
@@ -1995,14 +1837,6 @@ const loadApplicationToForm = async (row) => {
itemName: item.itemName || item.name || '',
itemPrice: item.itemPrice || item.price || 0,
itemAmount: item.itemAmount || item.price || 0,
// BugFix: 套餐项目需要初始化展开属性,否则点击无法展开
// BugFix#404: 增加对空字符串的判断避免空字符串被误认为有效套餐ID
isPackage: item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null',
feePackageId: item.feePackageId,
expanded: false,
childrenLoaded: false,
loading: false,
children: (item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null') ? [] : undefined
}))
} else if (detail.inspectionItem || detail.itemName) {
// 如果只有项目名称,尝试从本地分类中查找匹配项
@@ -2010,14 +1844,7 @@ const loadApplicationToForm = async (row) => {
inspectionCategories.value.forEach(category => {
category.items.forEach(item => {
if (itemNames.includes(item.itemName)) {
selectedInspectionItems.value.push({
...item,
// BugFix: 套餐项目需要初始化展开属性
expanded: false,
childrenLoaded: false,
loading: false,
children: item.isPackage ? [] : undefined
})
selectedInspectionItems.value.push({ ...item })
}
})
})
@@ -2078,17 +1905,15 @@ watch(() => selectedInspectionItems.value, (newVal) => {
}
}, { deep: true })
// 监听执行科室选项加载完成,不自动设置默认值
// Bug #329: 等待选择检验项目时根据检验类型自动设置
watch(() => executeDepartmentOptions.value, (newVal) => {
// 不再自动设置第一个值为默认值
// 监听执行科室字典数据,设置默认值为第一个选项
watch(() => inspection_lab_dept.value, (newVal) => {
if (newVal && newVal.length > 0 && !formData.executeDepartment) {
formData.executeDepartment = newVal[0].value
}
}, { immediate: true })
// 组件挂载时预加载检验项目数据不依赖patientInfo
onMounted(async () => {
// Bug #329: 先加载执行科室列表,确保选择检验项目时能正确映射
await loadExecuteDepartmentList()
// 再加载检验类型分类
await loadInspectionData()
})
@@ -2120,18 +1945,6 @@ defineExpose({
padding: 0;
}
/* Bug #326: 展开内容样式 */
.expand-content {
padding: 10px;
}
.expand-empty {
padding: 10px;
text-align: center;
color: #999;
font-size: 12px;
}
/* Bug#334: 顶部操作按钮区 - 优化垂直空间利用率 */
.top-action-bar {
display: flex;
@@ -2229,26 +2042,11 @@ defineExpose({
display: flex;
flex-direction: column;
gap: 8px;
height: calc(100vh - 200px);
min-height: 600px;
}
.inspection-selector,
.selected-items-area {
flex: 1;
min-height: 300px;
display: flex;
flex-direction: column;
}
/* 检验项目选择区 - 固定高度 */
.inspection-selector {
max-height: 350px;
}
/* 已选项目区 - 固定高度 */
.selected-items-area {
min-height: 300px;
height: auto;
}
.card-title {
@@ -2256,144 +2054,6 @@ defineExpose({
color: var(--el-text-color-primary);
}
/* 搜索输入框样式 */
.search-input {
margin-bottom: 8px;
}
/* 分类树样式 */
.category-tree {
flex: 1;
overflow-y: auto;
}
.category-tree-item {
margin-bottom: 4px;
}
.category-tree-header {
padding: 8px 12px;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s;
}
.category-tree-header:hover {
background-color: #f5f7fa;
}
.category-tree-header.active {
background-color: #e6f1ff;
color: #409EFF;
}
.category-tree-icon {
font-size: 12px;
color: #909399;
}
.category-count {
font-size: 12px;
color: #909399;
margin-left: auto;
}
.category-tree-children {
padding-left: 24px;
margin-top: 4px;
}
.inspection-tree-item {
padding: 6px 12px;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s;
}
.inspection-tree-item:hover {
background-color: #f5f7fa;
}
.inspection-tree-item.selected {
background-color: #e6f1ff;
color: #409EFF;
}
.item-itemName {
flex: 1;
font-size: 13px;
}
.item-price {
font-size: 12px;
color: #67c23a;
font-weight: 500;
}
/* 已选项目列表样式 */
.selected-tree {
flex: 1;
overflow-y: auto;
}
.selected-items-list {
margin-top: 8px;
}
.selected-list-item {
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s;
}
.selected-list-item:hover {
background-color: #f5f7fa;
}
.selected-item-content {
gap: 8px;
}
/* 加载状态样式 */
.loading-placeholder,
.load-more,
.no-more {
padding: 8px 12px;
text-align: center;
font-size: 12px;
color: #909399;
}
.load-info {
margin-left: 8px;
font-size: 12px;
color: #909399;
}
/* 展开内容样式 */
.expand-content {
padding: 12px 16px;
background-color: #f5f7fa;
border-radius: 4px;
margin: 8px;
}
.expand-empty {
padding: 20px;
text-align: center;
color: #909399;
font-size: 13px;
background-color: #f5f7fa;
border-radius: 4px;
margin: 8px;
}
.search-input {
margin-bottom: 8px;
}
@@ -2550,65 +2210,6 @@ defineExpose({
font-weight: bold;
}
/* BugFix#326: 套餐明细展开样式 */
.selected-item-wrapper {
margin-bottom: 4px;
}
.expand-icon {
cursor: pointer;
margin-right: 4px;
transition: transform 0.3s;
}
.expand-icon:hover {
color: #409EFF;
}
.package-details {
margin-left: 24px;
padding: 8px 12px;
background: #f5f7fa;
border-radius: 4px;
margin-top: 4px;
}
.package-details.loading {
display: flex;
align-items: center;
gap: 8px;
color: #909399;
}
.package-detail-item {
display: flex;
justify-content: space-between;
padding: 4px 0;
font-size: 12px;
color: #606266;
border-bottom: 1px dashed #e4e7ed;
}
.package-detail-item:last-child {
border-bottom: none;
}
.detail-name {
flex: 1;
}
.detail-unit {
width: 50px;
text-align: center;
color: #909399;
}
.detail-price {
width: 70px;
text-align: right;
color: #e6a23c;
}
.inspection-details {
margin-top: 30px;
padding: 20px;
@@ -2846,100 +2447,6 @@ defineExpose({
border-top: 1px solid #ebeef5;
}
/* BugFix#326: 已选择区域树形结构样式(仿照项目选择区) */
.selected-tree-item {
border-bottom: 1px solid #ebeef5;
}
.selected-tree-item:last-child {
border-bottom: none;
}
.selected-tree-header {
display: flex;
align-items: center;
padding: 10px 12px;
cursor: default;
transition: all 0.3s ease;
background: #fff;
border-radius: 4px;
margin: 2px;
}
.selected-tree-header:hover {
background-color: #f5f5f5;
}
.selected-tree-item.is-package .selected-tree-header {
cursor: pointer;
}
.selected-tree-item.is-package .selected-tree-header:hover {
background-color: #e6f7ff;
}
.selected-tree-item.is-package .selected-tree-header.expanded {
background-color: #e6f7ff;
font-weight: bold;
}
.selected-tree-icon {
margin-right: 8px;
font-size: 12px;
width: 12px;
text-align: center;
color: #51A3F3;
}
.selected-tree-children {
background: #fafafa;
border-top: 1px solid #ebeef5;
padding: 8px 0;
}
.selected-tree-detail {
display: flex;
align-items: center;
padding: 6px 12px 6px 32px;
font-size: 12px;
color: #606266;
}
.selected-tree-detail:hover {
background-color: #f0f0f0;
}
.selected-tree-detail .detail-name {
flex: 1;
min-width: 100px;
}
.selected-tree-detail .detail-unit {
width: 50px;
text-align: center;
color: #909399;
}
.selected-tree-detail .detail-qty {
width: 40px;
text-align: center;
color: #909399;
}
.selected-tree-detail .detail-price {
width: 60px;
text-align: right;
color: #e6a23c;
font-weight: 500;
}
.no-detail {
padding: 12px 32px;
color: #909399;
font-size: 12px;
text-align: center;
}
/* 加载中占位样式 */
.loading-placeholder {
display: flex;

1
test-liubei-gitea.md Normal file
View File

@@ -0,0 +1 @@
# Liebei Test Commit 2026-04-14