From e9d4f5781565d80773061bd029698033bb5d427b Mon Sep 17 00:00:00 2001 From: Ranyunqiao <2499115710@qq.com> Date: Tue, 7 Apr 2026 17:49:26 +0800 Subject: [PATCH] =?UTF-8?q?bug=E9=87=8D=E6=96=B0=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../outpatientAppointment/index.vue | 337 ++++++++++++------ 1 file changed, 231 insertions(+), 106 deletions(-) diff --git a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue index 869d573a..99176dac 100644 --- a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue +++ b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue @@ -196,6 +196,7 @@ {{ index + 1 }} {{ patient.name }} {{ patient.identifierNo || patient.medicalCard || patient.id }} + {{ patient.identifierNo }} {{ getGenderText(patient.genderEnum_enumText || patient.genderEnum || patient.gender || patient.sex) }} {{ patient.idCard }} {{ patient.phone }} @@ -312,6 +313,37 @@ export default { } }, computed: { + // 全部号源经过日期过期过滤后的数据(不按医生过滤,不按患者搜索过滤),用于统计医生余号 + allTicketsForDoctorCount() { + let filtered = [...this.tickets]; + + // 🎯 只过滤过期号源,不按医生过滤,不按患者搜索过滤 + // 这样余号统计总是基于该日期下所有号源,得到正确的每个医生余号 + const now = new Date(); + + filtered = filtered.filter(ticket => { + // dateTime 格式示例:"2024-01-01 08:00-09:00" + const parts = (ticket.dateTime || '').split(' '); + if (parts.length < 2) return true; // 如果格式不正确,保留显示 + + const dateStr = parts[0]; + const timeRangeStr = parts[1]; + if (!dateStr || !timeRangeStr) return true; + + // 提取开始时间 + const startTimeStr = timeRangeStr.split('-')[0]; // "08:00" + if (!startTimeStr) return true; + + // 构建号源开始时间的完整 Date 对象 + const ticketStartStr = `${dateStr} ${startTimeStr}`; + const ticketStart = new Date(ticketStartStr); + + // 只显示开始时间晚于当前时间的号源 + return ticketStart > now; + }); + + return filtered; + }, filteredDoctors() { let filtered = [...this.doctors]; @@ -329,10 +361,96 @@ export default { ); } + // 🎯 实时更新余号数量:统计该医生当前筛选条件下剩余可预约(未预约 + 未过期)号源数量 + // 使用全部未过期号源统计(不按选中医生过滤),这样所有医生余号都正确显示 + const availableCountMap = {}; + this.allTicketsForDoctorCount.forEach(ticket => { + const doctorId = String(ticket.doctorId || ticket.doctor_id); + if (!availableCountMap[doctorId]) { + availableCountMap[doctorId] = 0; + } + // 只有未预约的号源才算作可预约余号 + if (ticket.status === '未预约') { + availableCountMap[doctorId]++; + } + }); + + // 更新每个医生的余号数量 + filtered = filtered.map(doctor => { + const actualAvailable = availableCountMap[String(doctor.id)] || 0; + return { + ...doctor, + available: actualAvailable + }; + }); + return filtered; }, + // 过滤并排序后的完整号源列表(用于右侧显示) + filteredAndSortedTickets() { + // 从已经过滤掉过期的全部数据开始 + let filtered = [...this.allTicketsForDoctorCount]; + + // 🎯 根据选中的医生过滤(右侧只显示选中医生的号源) + if (this.selectedDoctorId) { + const doctorIdStr = String(this.selectedDoctorId); + filtered = filtered.filter(ticket => { + const ticketDoctorId = String(ticket.doctorId || ticket.doctor_id || ''); + return ticketDoctorId === doctorIdStr; + }); + } + + // 🎯 根据患者搜索条件过滤 + if (this.patientName?.trim()) { + const keyword = this.patientName.trim().toLowerCase(); + filtered = filtered.filter(ticket => + (ticket.patientName || '').toLowerCase().includes(keyword) + ); + } + if (this.patientCard?.trim()) { + const keyword = this.patientCard.trim().toLowerCase(); + filtered = filtered.filter(ticket => + (ticket.patientId || '').toLowerCase().includes(keyword) || + (ticket.medicalCard || '').toLowerCase().includes(keyword) + ); + } + if (this.patientPhone?.trim()) { + const keyword = this.patientPhone.trim().toLowerCase(); + filtered = filtered.filter(ticket => + (ticket.phone || '').toLowerCase().includes(keyword) + ); + } + + // 🎯 按开始时间升序排序 → 较早的号源排在前面 + filtered.sort((a, b) => { + const getStartTime = (ticket) => { + const parts = (ticket.dateTime || '').split(' '); + if (parts.length < 2) return new Date(0).getTime(); + const dateStr = parts[0]; + const timeRangeStr = parts[1]; + const startTimeStr = (timeRangeStr || '').split('-')[0]; + if (!startTimeStr) return new Date(0).getTime(); + const ticketStartStr = `${dateStr} ${startTimeStr}`; + return new Date(ticketStartStr).getTime(); + }; + + const timeA = getStartTime(a); + const timeB = getStartTime(b); + return timeA - timeB; + }); + + return filtered; + }, + // 🎯 分页:按照用户选择的每页条数分页,返回当前页的数据 filteredTickets() { - return [...this.tickets]; + const filtered = this.filteredAndSortedTickets; + const startIndex = (this.currentPage - 1) * this.pageSize; + const endIndex = startIndex + this.pageSize; + return filtered.slice(startIndex, endIndex); + }, + // 更新总条数为过滤后的实际条数,分页自动处理 + totalTickets() { + return this.filteredAndSortedTickets.length; }, hasSearchCriteria() { return !!this.patientKeyword?.trim(); @@ -342,6 +460,8 @@ export default { selectDoctor(doctorId) { this.selectedDoctorId = this.selectedDoctorId === doctorId ? null : doctorId; this.currentPage = 1; + // 🔧 BugFix: 选择医生后不改变医生列表,余号计算基于 filteredAndSortedTickets 已经正确过滤 + // 只需要重新获取号源,医生列表保持不变,余号计算会自动正确 this.fetchTickets({ refreshDepartments: false, refreshDoctors: false }).catch(() => {}); }, onTypeChange() { @@ -433,21 +553,21 @@ export default { this.selectedPatient = null; this.searchPatients(); }, - + // 双击未预约卡片触发患者选择流程 handleDoubleClick(ticket) { if (ticket.status === '未预约') { this.currentTicket = ticket; this.patientKeyword = ''; this.selectedPatientId = null; - this.selectedPatient = null; + this.selectedPatient = null; // 先打开弹窗,再加载患者数据,避免等待 this.showPatientModal = true; // 调用患者搜索接口,加载患者列表 this.searchPatients(); } }, - + // 右键已预约卡片显示取消预约菜单 handleRightClick(event, ticket) { if (ticket.status === '已预约') { @@ -456,13 +576,13 @@ export default { this.contextMenuVisible = true; } }, - + // 关闭右键菜单 closeContextMenu() { this.contextMenuVisible = false; this.selectedTicketForCancel = null; }, - + // 确认取消预约 confirmCancelAppointment() { if (this.selectedTicketForCancel) { @@ -484,7 +604,7 @@ export default { }); } }, - + // 取消预约API调用 cancelAppointment(ticket) { if (!ticket || !ticket.slot_id) { @@ -492,13 +612,13 @@ export default { this.closeContextMenu(); return; } - + // 使用真实API调用取消预约,传递slot_id - cancelTicket(ticket.slot_id).then(response => { + cancelTicket(ticket.slot_id).then(response => { // 根据后端返回判断是否成功 if (response.code === 200 || response.msg === '取消成功' || response.message === '取消成功') { console.log('取消预约成功,更新前端状态'); - + // API调用成功后,更新当前卡片状态 const ticketIndex = this.tickets.findIndex(t => t.slot_id === ticket.slot_id); if (ticketIndex !== -1) { @@ -513,7 +633,7 @@ export default { } this.fetchTickets({ refreshDepartments: false, refreshDoctors: true }).catch(() => {}); - + // 关闭上下文菜单 this.closeContextMenu(); ElMessage.success('预约已取消,号源已释放'); @@ -630,24 +750,24 @@ export default { if (genderValue === null || genderValue === undefined) { return '未知'; } - + // 将值转换为字符串进行比较 const strValue = String(genderValue).toLowerCase(); - + // 处理男性值 - if (strValue === '0' || strValue === '男' || strValue === 'male' || strValue === 'm' || - strValue === 'malegender' || strValue === 'man' || strValue === 'boy' || + if (strValue === '0' || strValue === '男' || strValue === 'male' || strValue === 'm' || + strValue === 'malegender' || strValue === 'man' || strValue === 'boy' || strValue === '男性' || strValue === '男士') { return '男'; } - + // 处理女性值 - if (strValue === '1' || strValue === '女' || strValue === 'female' || strValue === 'f' || - strValue === 'femalegender' || strValue === 'woman' || strValue === 'girl' || + if (strValue === '1' || strValue === '女' || strValue === 'female' || strValue === 'f' || + strValue === 'femalegender' || strValue === 'woman' || strValue === 'girl' || strValue === '女性' || strValue === '女士') { return '女'; } - + // 如果都不是,返回"未知" return '未知'; }, @@ -660,7 +780,7 @@ export default { if (patient.gender !== undefined && patient.gender !== null) { return patient.gender; } - + // 如果genderEnum_enumText是"男性"或"女性",转换为对应的数字 if (patient.genderEnum_enumText) { const text = patient.genderEnum_enumText.toLowerCase(); @@ -673,7 +793,7 @@ export default { // 默认返回0(男性) return 0; }, - + // 检测是否为移动设备 checkMobileDevice() { this.isMobile = window.innerWidth <= 768; @@ -682,21 +802,20 @@ export default { return STATUS_CLASS_MAP[status] || 'status-unbooked'; }, buildQueryParams(page = this.currentPage) { - const doctorId = - this.selectedDoctorId === null || this.selectedDoctorId === undefined || this.selectedDoctorId === '' - ? null - : String(this.selectedDoctorId); return { date: this.selectedDate, - status: this.selectedStatus === 'all' ? null : this.selectedStatus, + status: null, // 状态过滤在前端做 type: this.selectedType === 'all' ? null : this.selectedType, department: this.selectedDepartment === 'all' ? null : this.selectedDepartment, - doctorId, - name: this.patientName?.trim() || null, - card: this.patientCard?.trim() || null, - phone: this.patientPhone?.trim() || null, - page, - limit: this.pageSize + doctorId: null, // 🎯 关键:永远不传 doctorId 给后端,后端返回全量数据 + // 医生过滤、状态过滤、患者搜索都在前端做,才能保证所有医生余号统计正确 + name: null, + card: null, + phone: null, + // 🎯 获取全量数据到前端,由前端做过滤和分页,保证余号统计总是正确 + // 号源数量每个日期每个科室不会太多,全量获取可行 + page: 1, + limit: 10000 }; }, buildDoctorQueryParams() { @@ -724,20 +843,14 @@ export default { if (!payload) { this.tickets = []; this.allTickets = []; - this.totalTickets = 0; return; } - const records = payload.list || payload.records || []; - const filteredRecords = this.applyStatusFilter(records); - const total = Number(payload.total); - this.tickets = [...filteredRecords]; - this.allTickets = [...filteredRecords]; - // 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部” - if (this.selectedStatus && this.selectedStatus !== 'all') { - this.totalTickets = this.tickets.length; - } else { - this.totalTickets = Number.isFinite(total) ? total : this.tickets.length; - } + let records = payload.list || payload.records || []; + // 获取全量数据,应用状态过滤后保存所有数据到 tickets + // 过滤、余号统计、分页都由前端完成,保证余号计算正确 + records = this.applyStatusFilter(records); + this.tickets = [...records]; + this.allTickets = [...records]; }, applyStatusFilter(records = []) { if (!Array.isArray(records) || records.length === 0) { @@ -892,8 +1005,8 @@ export default {