300,301,302预约挂号展示问题
This commit is contained in:
@@ -142,6 +142,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
|
||||
// 基础字段映射
|
||||
dto.setSlot_id(raw.getSlotId());
|
||||
dto.setSeqNo(raw.getSeqNo());
|
||||
dto.setBusNo(String.valueOf(raw.getSlotId()));
|
||||
dto.setDoctor(raw.getDoctor());
|
||||
dto.setDepartment(raw.getDepartmentName()); // 注意:以前这里传成了ID,导致前端出Bug,现在修复成了真正的科室名
|
||||
@@ -319,6 +320,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
// --- 基础字段处理 ---
|
||||
// 注意:这里已经变成了极其舒服的 .getSlotId() 方法调用,告别魔鬼字符串!
|
||||
dto.setSlot_id(raw.getSlotId());
|
||||
dto.setSeqNo(raw.getSeqNo());
|
||||
dto.setBusNo(String.valueOf(raw.getSlotId())); // 暂时借用真实槽位ID做唯一流水号
|
||||
dto.setDoctor(raw.getDoctor());
|
||||
dto.setDepartment(raw.getDepartmentName());
|
||||
|
||||
@@ -23,6 +23,11 @@ public class TicketDto {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long slot_id;
|
||||
|
||||
/**
|
||||
* 号源序号(对应 adm_schedule_slot.seq_no)
|
||||
*/
|
||||
private Integer seqNo;
|
||||
|
||||
/**
|
||||
* 号源编码
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.time.LocalTime;
|
||||
public class TicketSlotDTO {
|
||||
// 基础信息
|
||||
private Long slotId;
|
||||
private Integer seqNo;
|
||||
private Long scheduleId;
|
||||
private String doctor;
|
||||
private Long doctorId;
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||
SELECT
|
||||
s.id AS slotId,
|
||||
s.seq_no AS seqNo,
|
||||
p.schedule_id AS scheduleId,
|
||||
p.doctor_name AS doctor,
|
||||
p.dept_id AS departmentId,
|
||||
@@ -91,6 +92,7 @@
|
||||
<select id="selectTicketSlotById" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||
SELECT
|
||||
s.id AS slotId,
|
||||
s.seq_no AS seqNo,
|
||||
p.schedule_id AS scheduleId,
|
||||
p.doctor_name AS doctor,
|
||||
p.doctor_id AS doctorId,
|
||||
@@ -205,6 +207,7 @@
|
||||
<select id="selectTicketSlotsPage" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||
SELECT
|
||||
s.id AS slotId,
|
||||
s.seq_no AS seqNo,
|
||||
p.schedule_id AS scheduleId,
|
||||
p.doctor_name AS doctor,
|
||||
p.doctor_id AS doctorId,
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<div class="ticket-grid" v-if="filteredTickets.length > 0">
|
||||
<div v-for="(item, index) in filteredTickets" :key="item.slot_id" class="ticket-card" @dblclick="handleDoubleClick(item)" @contextmenu.prevent="handleRightClick($event, item)">
|
||||
<!-- 序号放在最右侧 -->
|
||||
<div class="ticket-index">{{ index + 1 }}</div>
|
||||
<div class="ticket-index">{{ item.seqNo != null ? item.seqNo : index + 1 }}</div>
|
||||
<!-- 1.时间 -->
|
||||
<div class="ticket-id-time">{{ item.dateTime }}</div>
|
||||
<!-- 2. 状态标签 -->
|
||||
@@ -126,7 +126,7 @@
|
||||
<div class="ticket-type">{{ item.ticketType === 'general' ? '普通' : '专家' }}</div>
|
||||
<!-- 7. 已预约患者信息 -->
|
||||
<div v-if="(item.status === '已预约' || item.status === '已取号') && item.patientName" class="ticket-patient">
|
||||
{{ item.patientName }}({{ item.patientId }})
|
||||
{{ item.patientName }}({{ item.patientId }},{{ getGenderText(item.gender || item.patientGender) }})
|
||||
</div>
|
||||
<div class="ticket-actions">
|
||||
<button class="action-button book-button" @click="openPatientSelectModal(item.slot_id)" :disabled="item.status !== '未预约'" :class="{ 'disabled': item.status !== '未预约' }">
|
||||
@@ -195,6 +195,7 @@
|
||||
>
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ patient.name }}</td>
|
||||
<td>{{ patient.identifierNo || patient.medicalCard || patient.id }}</td>
|
||||
<td>{{ patient.identifierNo }}</td>
|
||||
<td>{{ getGenderText(patient.genderEnum_enumText || patient.genderEnum || patient.gender || patient.sex) }}</td>
|
||||
<td>{{ patient.idCard }}</td>
|
||||
@@ -366,14 +367,23 @@ export default {
|
||||
this.searchPatients();
|
||||
},
|
||||
getPatientUniqueId(patient, index = null) {
|
||||
return patient.idCard || patient.medicalCard || patient.id || (index !== null ? `temp_${index}` : null);
|
||||
return patient.id || patient.patientId || patient.identifierNo || patient.medicalCard || patient.idCard ||
|
||||
(index !== null ? `temp_${index}` : null);
|
||||
},
|
||||
matchPatientKeyword(patient, keyword) {
|
||||
const normalizedKeyword = String(keyword || '').trim().toLowerCase();
|
||||
if (!normalizedKeyword) {
|
||||
return true;
|
||||
}
|
||||
const fields = [patient.name, patient.id, patient.medicalCard, patient.idCard, patient.phone];
|
||||
const fields = [
|
||||
patient.name,
|
||||
patient.id,
|
||||
patient.patientId,
|
||||
patient.identifierNo,
|
||||
patient.medicalCard,
|
||||
patient.idCard,
|
||||
patient.phone
|
||||
];
|
||||
return fields.some(field => String(field || '').toLowerCase().includes(normalizedKeyword));
|
||||
},
|
||||
searchPatients() {
|
||||
@@ -396,6 +406,8 @@ export default {
|
||||
const rowKey = this.getPatientUniqueId(patient, index);
|
||||
return {
|
||||
...patient,
|
||||
// 统一口径:预约场景的就诊卡号使用患者标识表中的 identifierNo
|
||||
medicalCard: patient.identifierNo || patient.medicalCard || '',
|
||||
_rowKey: rowKey
|
||||
};
|
||||
});
|
||||
@@ -496,6 +508,7 @@ export default {
|
||||
this.tickets[ticketIndex].patientName = null;
|
||||
this.tickets[ticketIndex].patientId = null;
|
||||
this.tickets[ticketIndex].patientGender = null;
|
||||
this.tickets[ticketIndex].gender = null;
|
||||
this.tickets[ticketIndex].medicalCard = null;
|
||||
this.tickets[ticketIndex].phone = null;
|
||||
}
|
||||
@@ -556,12 +569,22 @@ export default {
|
||||
|
||||
try {
|
||||
const userStore = useUserStore();
|
||||
const patientPrimaryId = this.selectedPatient.id || this.selectedPatient.patientId;
|
||||
const medicalCard = this.selectedPatient.identifierNo || this.selectedPatient.medicalCard;
|
||||
if (!patientPrimaryId) {
|
||||
ElMessage.error('患者ID缺失,无法预约,请重新选择患者');
|
||||
return;
|
||||
}
|
||||
if (!medicalCard) {
|
||||
ElMessage.error('就诊卡号缺失,无法预约,请先维护患者就诊卡号');
|
||||
return;
|
||||
}
|
||||
const appointmentData = {
|
||||
ticketId: this.currentTicket.slot_id,
|
||||
slotId: this.currentTicket.slot_id,
|
||||
patientId: this.selectedPatient.id || this.selectedPatient.idCard || this.selectedPatient.medicalCard,
|
||||
patientId: patientPrimaryId,
|
||||
patientName: this.selectedPatient.name,
|
||||
medicalCard: this.selectedPatient.medicalCard || this.selectedPatient.id,
|
||||
medicalCard,
|
||||
phone: this.selectedPatient.phone,
|
||||
gender: this.getGenderValueForBackend(this.selectedPatient),
|
||||
fee: Number(this.currentTicket.fee) || 0,
|
||||
@@ -580,8 +603,9 @@ export default {
|
||||
if (ticketIndex !== -1) {
|
||||
this.tickets[ticketIndex].status = '已预约';
|
||||
this.tickets[ticketIndex].patientName = this.selectedPatient.name;
|
||||
this.tickets[ticketIndex].patientId = this.selectedPatient.medicalCard || this.selectedPatient.id;
|
||||
this.tickets[ticketIndex].patientId = medicalCard;
|
||||
this.tickets[ticketIndex].patientGender = this.selectedPatient.genderEnum_enumText || this.selectedPatient.genderEnum || this.selectedPatient.gender || this.selectedPatient.sex;
|
||||
this.tickets[ticketIndex].gender = this.getGenderText(this.tickets[ticketIndex].patientGender);
|
||||
}
|
||||
|
||||
this.closePatientSelectModal();
|
||||
@@ -603,9 +627,9 @@ export default {
|
||||
},
|
||||
// 获取性别文本
|
||||
getGenderText(genderValue) {
|
||||
// 如果值为null或undefined,返回'-'
|
||||
// 如果值为null或undefined,返回"未知"
|
||||
if (genderValue === null || genderValue === undefined) {
|
||||
return '-';
|
||||
return '未知';
|
||||
}
|
||||
|
||||
// 将值转换为字符串进行比较
|
||||
@@ -625,8 +649,8 @@ export default {
|
||||
return '女';
|
||||
}
|
||||
|
||||
// 如果都不是,返回原值或'-'
|
||||
return '-';
|
||||
// 如果都不是,返回"未知"
|
||||
return '未知';
|
||||
},
|
||||
// 获取用于后端的性别值
|
||||
getGenderValueForBackend(patient) {
|
||||
|
||||
@@ -164,7 +164,14 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="检查方法">
|
||||
<el-input v-model="form.inspectionMethod" readonly />
|
||||
<el-select v-model="form.inspectionMethod" placeholder="请选择" clearable filterable style="width: 100%;">
|
||||
<el-option
|
||||
v-for="method in availableMethods"
|
||||
:key="method.id"
|
||||
:label="method.name"
|
||||
:value="method.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -308,6 +315,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Printer, Delete } from '@element-plus/icons-vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import request from '@/utils/request';
|
||||
import { listCheckMethod } from '@/api/system/checkType';
|
||||
|
||||
const props = defineProps({
|
||||
patientInfo: { type: Object, default: () => ({}) },
|
||||
@@ -373,10 +381,69 @@ const categoryList = ref([]); // 原始分类+项目数据
|
||||
const dictSearchKey = ref('');
|
||||
const activeNames = ref([]); // 当前展开的折叠项
|
||||
|
||||
const allMethods = ref([]);
|
||||
|
||||
// 加载所有检查方法
|
||||
async function loadAllMethods() {
|
||||
try {
|
||||
const res = await listCheckMethod(); // 使用已导入的或者直接利用 request 请求
|
||||
let methods = [];
|
||||
if (res && res.data) {
|
||||
if (Array.isArray(res.data)) {
|
||||
methods = res.data;
|
||||
} else if (res.data.records) {
|
||||
methods = res.data.records;
|
||||
} else if (res.data.data && Array.isArray(res.data.data)) {
|
||||
methods = res.data.data;
|
||||
}
|
||||
} else if (Array.isArray(res)) {
|
||||
methods = res;
|
||||
} else if (res && res.rows) {
|
||||
methods = res.rows;
|
||||
}
|
||||
allMethods.value = methods;
|
||||
} catch (err) {
|
||||
console.error('加载检查方法失败', err);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadAllMethods();
|
||||
await loadCategoryList();
|
||||
});
|
||||
|
||||
// 动态可用的检查方法(根据已选部位所属的检查类型进行过滤)
|
||||
const normalizeTypeValue = value => String(value ?? '').trim().toLowerCase();
|
||||
|
||||
const availableMethods = computed(() => {
|
||||
// 获取当前已选部位的检查类型(可取第一个选中的部位的 checkType)
|
||||
const currentType = form.examTypeCode || (selectedItems.value.length > 0 ? selectedItems.value[0].checkType : '');
|
||||
const normalizedCurrentType = normalizeTypeValue(currentType);
|
||||
if (normalizedCurrentType) {
|
||||
// 兼容脏数据:method 的类型可能落在 checkType/type/typeCode/code/typeName/categoryName 中
|
||||
const filtered = allMethods.value.filter(m => {
|
||||
const typeCandidates = [
|
||||
m.checkType,
|
||||
m.type,
|
||||
m.typeCode,
|
||||
m.code,
|
||||
m.typeName,
|
||||
m.categoryName
|
||||
].map(normalizeTypeValue).filter(Boolean);
|
||||
return typeCandidates.includes(normalizedCurrentType);
|
||||
});
|
||||
return filtered.length > 0 ? filtered : allMethods.value;
|
||||
}
|
||||
return allMethods.value;
|
||||
});
|
||||
|
||||
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
|
||||
watch(availableMethods, (newMethods) => {
|
||||
if (form.inspectionMethod && !newMethods.find(m => m.name === form.inspectionMethod)) {
|
||||
form.inspectionMethod = '';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载检查类型(分类)和检查项目(部位/项目),按类型分组展示
|
||||
*/
|
||||
@@ -390,25 +457,45 @@ async function loadCategoryList() {
|
||||
params: { pageNo: 1, pageSize: 500 } // 取全量分类数据
|
||||
});
|
||||
let types = [];
|
||||
if (typeRes.data?.records) types = typeRes.data.records;
|
||||
else if (Array.isArray(typeRes.data)) types = typeRes.data;
|
||||
else if (Array.isArray(typeRes.rows)) types = typeRes.rows;
|
||||
if (typeRes && typeRes.data) {
|
||||
if (Array.isArray(typeRes.data)) {
|
||||
types = typeRes.data;
|
||||
} else if (typeRes.data.records) {
|
||||
types = typeRes.data.records;
|
||||
} else if (typeRes.data.data && Array.isArray(typeRes.data.data)) {
|
||||
types = typeRes.data.data;
|
||||
}
|
||||
} else if (Array.isArray(typeRes)) {
|
||||
types = typeRes;
|
||||
} else if (typeRes && typeRes.rows) {
|
||||
types = typeRes.rows;
|
||||
}
|
||||
|
||||
// 2. 加载检查项目(检查部位项目)
|
||||
const partRes = await request({ url: '/check/part/list', method: 'get' });
|
||||
let parts = [];
|
||||
if (Array.isArray(partRes)) parts = partRes;
|
||||
else if (Array.isArray(partRes.data?.data)) parts = partRes.data.data; // 双层嵌套:{ data: { data: [...] } }
|
||||
else if (Array.isArray(partRes.data)) parts = partRes.data;
|
||||
else if (Array.isArray(partRes.rows)) parts = partRes.rows;
|
||||
else if (partRes.data?.records) parts = partRes.data.records;
|
||||
if (partRes && partRes.data) {
|
||||
if (Array.isArray(partRes.data)) {
|
||||
parts = partRes.data;
|
||||
} else if (partRes.data.records) {
|
||||
parts = partRes.data.records;
|
||||
} else if (partRes.data.data && Array.isArray(partRes.data.data)) {
|
||||
parts = partRes.data.data;
|
||||
}
|
||||
} else if (Array.isArray(partRes)) {
|
||||
parts = partRes;
|
||||
} else if (partRes && partRes.rows) {
|
||||
parts = partRes.rows;
|
||||
}
|
||||
|
||||
// 3. 按 checkType 归类
|
||||
const dict = [];
|
||||
for (const t of types) {
|
||||
dict.push({
|
||||
typeId: t.id,
|
||||
typeCode: t.type,
|
||||
typeCode: t.code, // 保存 code 用于后备匹配
|
||||
orgType: t.type, // 保存 type 用于后备匹配
|
||||
typeName: t.name, // 保存 name
|
||||
categoryName: t.name,
|
||||
items: []
|
||||
});
|
||||
@@ -425,7 +512,15 @@ async function loadCategoryList() {
|
||||
nationalCode: p.nationalCode || '',
|
||||
checked: false
|
||||
};
|
||||
const target = dict.find(d => d.typeCode === p.checkType);
|
||||
|
||||
// 增强匹配逻辑:部位的 checkType (如 'ECG', 'CT') 优先去匹配大类的 orgType,
|
||||
// 如果大类的 type 字段脏了(比如填了中文),则尝试匹配 code,甚至是分类名称
|
||||
const target = dict.find(d =>
|
||||
d.orgType === p.checkType ||
|
||||
d.typeCode === p.checkType ||
|
||||
d.typeName === p.checkType
|
||||
);
|
||||
|
||||
if (target) target.items.push(mapped);
|
||||
else unclassified.push(mapped);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user