340 预约管理-门诊预约挂号:选择患者弹窗列表数据字段显示错位
This commit is contained in:
@@ -196,7 +196,6 @@
|
|||||||
<td>{{ index + 1 }}</td>
|
<td>{{ index + 1 }}</td>
|
||||||
<td>{{ patient.name }}</td>
|
<td>{{ patient.name }}</td>
|
||||||
<td>{{ patient.identifierNo || patient.medicalCard || patient.id }}</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>{{ getGenderText(patient.genderEnum_enumText || patient.genderEnum || patient.gender || patient.sex) }}</td>
|
||||||
<td>{{ patient.idCard }}</td>
|
<td>{{ patient.idCard }}</td>
|
||||||
<td>{{ patient.phone }}</td>
|
<td>{{ patient.phone }}</td>
|
||||||
@@ -313,37 +312,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
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() {
|
filteredDoctors() {
|
||||||
let filtered = [...this.doctors];
|
let filtered = [...this.doctors];
|
||||||
|
|
||||||
@@ -361,96 +329,10 @@ 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;
|
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() {
|
filteredTickets() {
|
||||||
const filtered = this.filteredAndSortedTickets;
|
return [...this.tickets];
|
||||||
const startIndex = (this.currentPage - 1) * this.pageSize;
|
|
||||||
const endIndex = startIndex + this.pageSize;
|
|
||||||
return filtered.slice(startIndex, endIndex);
|
|
||||||
},
|
|
||||||
// 更新总条数为过滤后的实际条数,分页自动处理
|
|
||||||
totalTickets() {
|
|
||||||
return this.filteredAndSortedTickets.length;
|
|
||||||
},
|
},
|
||||||
hasSearchCriteria() {
|
hasSearchCriteria() {
|
||||||
return !!this.patientKeyword?.trim();
|
return !!this.patientKeyword?.trim();
|
||||||
@@ -460,8 +342,6 @@ export default {
|
|||||||
selectDoctor(doctorId) {
|
selectDoctor(doctorId) {
|
||||||
this.selectedDoctorId = this.selectedDoctorId === doctorId ? null : doctorId;
|
this.selectedDoctorId = this.selectedDoctorId === doctorId ? null : doctorId;
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
// 🔧 BugFix: 选择医生后不改变医生列表,余号计算基于 filteredAndSortedTickets 已经正确过滤
|
|
||||||
// 只需要重新获取号源,医生列表保持不变,余号计算会自动正确
|
|
||||||
this.fetchTickets({ refreshDepartments: false, refreshDoctors: false }).catch(() => {});
|
this.fetchTickets({ refreshDepartments: false, refreshDoctors: false }).catch(() => {});
|
||||||
},
|
},
|
||||||
onTypeChange() {
|
onTypeChange() {
|
||||||
@@ -794,7 +674,7 @@ export default {
|
|||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 检测是否为移动设备
|
// 检测是否为移动设备。
|
||||||
checkMobileDevice() {
|
checkMobileDevice() {
|
||||||
this.isMobile = window.innerWidth <= 768;
|
this.isMobile = window.innerWidth <= 768;
|
||||||
},
|
},
|
||||||
@@ -802,20 +682,21 @@ export default {
|
|||||||
return STATUS_CLASS_MAP[status] || 'status-unbooked';
|
return STATUS_CLASS_MAP[status] || 'status-unbooked';
|
||||||
},
|
},
|
||||||
buildQueryParams(page = this.currentPage) {
|
buildQueryParams(page = this.currentPage) {
|
||||||
|
const doctorId =
|
||||||
|
this.selectedDoctorId === null || this.selectedDoctorId === undefined || this.selectedDoctorId === ''
|
||||||
|
? null
|
||||||
|
: String(this.selectedDoctorId);
|
||||||
return {
|
return {
|
||||||
date: this.selectedDate,
|
date: this.selectedDate,
|
||||||
status: null, // 状态过滤在前端做
|
status: this.selectedStatus === 'all' ? null : this.selectedStatus,
|
||||||
type: this.selectedType === 'all' ? null : this.selectedType,
|
type: this.selectedType === 'all' ? null : this.selectedType,
|
||||||
department: this.selectedDepartment === 'all' ? null : this.selectedDepartment,
|
department: this.selectedDepartment === 'all' ? null : this.selectedDepartment,
|
||||||
doctorId: null, // 🎯 关键:永远不传 doctorId 给后端,后端返回全量数据
|
doctorId,
|
||||||
// 医生过滤、状态过滤、患者搜索都在前端做,才能保证所有医生余号统计正确
|
name: this.patientName?.trim() || null,
|
||||||
name: null,
|
card: this.patientCard?.trim() || null,
|
||||||
card: null,
|
phone: this.patientPhone?.trim() || null,
|
||||||
phone: null,
|
page,
|
||||||
// 🎯 获取全量数据到前端,由前端做过滤和分页,保证余号统计总是正确
|
limit: this.pageSize
|
||||||
// 号源数量每个日期每个科室不会太多,全量获取可行
|
|
||||||
page: 1,
|
|
||||||
limit: 10000
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
buildDoctorQueryParams() {
|
buildDoctorQueryParams() {
|
||||||
@@ -843,14 +724,20 @@ export default {
|
|||||||
if (!payload) {
|
if (!payload) {
|
||||||
this.tickets = [];
|
this.tickets = [];
|
||||||
this.allTickets = [];
|
this.allTickets = [];
|
||||||
|
this.totalTickets = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let records = payload.list || payload.records || [];
|
const records = payload.list || payload.records || [];
|
||||||
// 获取全量数据,应用状态过滤后保存所有数据到 tickets
|
const filteredRecords = this.applyStatusFilter(records);
|
||||||
// 过滤、余号统计、分页都由前端完成,保证余号计算正确
|
const total = Number(payload.total);
|
||||||
records = this.applyStatusFilter(records);
|
this.tickets = [...filteredRecords];
|
||||||
this.tickets = [...records];
|
this.allTickets = [...filteredRecords];
|
||||||
this.allTickets = [...records];
|
// 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部”
|
||||||
|
if (this.selectedStatus && this.selectedStatus !== 'all') {
|
||||||
|
this.totalTickets = this.tickets.length;
|
||||||
|
} else {
|
||||||
|
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
applyStatusFilter(records = []) {
|
applyStatusFilter(records = []) {
|
||||||
if (!Array.isArray(records) || records.length === 0) {
|
if (!Array.isArray(records) || records.length === 0) {
|
||||||
@@ -1005,8 +892,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 颜色变量定义 */
|
/* 颜色变量定义 - 使用组件内CSS变量 */
|
||||||
:root {
|
.ticket-management-container {
|
||||||
--primary-color: #1890FF;
|
--primary-color: #1890FF;
|
||||||
--secondary-color: #FF6B35;
|
--secondary-color: #FF6B35;
|
||||||
--status-unbooked: #5A8DEE;
|
--status-unbooked: #5A8DEE;
|
||||||
@@ -1080,7 +967,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.date-picker {
|
.date-picker {
|
||||||
width: 100%;
|
width: 160px;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索控件样式 */
|
/* 搜索控件样式 */
|
||||||
@@ -1151,12 +1039,6 @@ export default {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 日期选择器样式 */
|
|
||||||
.date-picker {
|
|
||||||
width: 160px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 状态筛选器样式 */
|
/* 状态筛选器样式 */
|
||||||
.status-filter {
|
.status-filter {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@@ -1242,50 +1124,50 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
/* 顶部搜索区域改为纵向排列 */
|
/* 顶部搜索区域改为纵向排列 */
|
||||||
.top-search-area {
|
.top-search-area {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 汉堡菜单显示 */
|
/* 汉堡菜单显示 */
|
||||||
.hamburger-menu {
|
.hamburger-menu {
|
||||||
display: block;
|
display: block;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索区域元素宽度100% */
|
/* 搜索区域元素宽度100% */
|
||||||
.date-picker,
|
.date-picker,
|
||||||
.status-filter,
|
.status-filter,
|
||||||
.patient-search,
|
.patient-search,
|
||||||
.card-search,
|
.card-search,
|
||||||
.phone-search,
|
.phone-search,
|
||||||
.search-button,
|
.search-button,
|
||||||
.add-patient {
|
.add-patient {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区卡片布局调整 */
|
/* 右侧内容区卡片布局调整 */
|
||||||
.virtual-list {
|
.virtual-list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧边栏默认隐藏 */
|
/* 左侧边栏默认隐藏 */
|
||||||
.left-sidebar {
|
.left-sidebar {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区占满屏幕 */
|
/* 右侧内容区占满屏幕 */
|
||||||
.right-content {
|
.right-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 确保卡片样式正确应用 */
|
/* 确保卡片样式正确应用 */
|
||||||
.ticket-card {
|
.ticket-card {
|
||||||
@@ -1711,9 +1593,28 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -1781,32 +1682,6 @@ export default {
|
|||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
padding: 16px 20px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-btn {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
font-size: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-body {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-search-toolbar {
|
.patient-search-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user