559 lines
14 KiB
JavaScript
559 lines
14 KiB
JavaScript
// pages/surgery/index.js
|
||
const { surgeryApi } = require("../../utils/api");
|
||
const dayjs = require("../../miniprogram_npm/dayjs/index");
|
||
const Toast = require("../../miniprogram_npm/tdesign-miniprogram/toast/index");
|
||
|
||
Page({
|
||
/**
|
||
* 页面的初始数据
|
||
*/
|
||
data: {
|
||
surgeries: [], // 手术列表数据
|
||
surgeriesList: [],
|
||
// 分页参数
|
||
page: 1,
|
||
pageSize: 10,
|
||
total: 0,
|
||
|
||
// 状态控制
|
||
loading: true, // 初始加载状态
|
||
refreshing: false, // 下拉刷新状态
|
||
loadingMore: false, // 加载更多状态
|
||
loadError: false, // 加载错误状态
|
||
noMoreData: false, // 没有更多数据状态
|
||
isFinish: false, // 是否选中未完成
|
||
|
||
confirmDialogVisible: false, // 确认删除对话框可见性
|
||
currentSurgeryId: null, // 当前操作的手术ID
|
||
|
||
// 数据变更状态管理
|
||
dataHasChanged: false, // 标记数据是否已变更(从编辑/创建页面返回时)
|
||
lastShowTime: 0, // 上次显示页面的时间戳
|
||
showTimeout: null, // 防抖定时器
|
||
|
||
// TDesign 组件配置
|
||
loadingProps: {
|
||
theme: 'circular',
|
||
size: '40rpx',
|
||
layout: 'horizontal'
|
||
},
|
||
|
||
loadingTexts: [
|
||
'下拉刷新',
|
||
'松手刷新',
|
||
'正在刷新...',
|
||
'刷新完成'
|
||
]
|
||
|
||
// 不再在这里全局定义swipeButtons,而是为每个手术项动态生成
|
||
},
|
||
|
||
/**
|
||
* 生命周期函数--监听页面加载
|
||
*/
|
||
onLoad() {
|
||
this.loadSurgeries();
|
||
},
|
||
|
||
/**
|
||
* 生命周期函数--监听页面显示
|
||
*/
|
||
onShow() {
|
||
const currentTime = Date.now();
|
||
|
||
// 清除之前的定时器(防抖)
|
||
if (this.data.showTimeout) {
|
||
clearTimeout(this.data.showTimeout);
|
||
}
|
||
|
||
// 检查是否需要刷新数据
|
||
const shouldRefresh = this.shouldRefreshData(currentTime);
|
||
|
||
if (shouldRefresh) {
|
||
// 设置新的定时器,延迟500ms执行刷新,避免快速切换页面导致的重复请求
|
||
const timeoutId = setTimeout(() => {
|
||
this.performSmartRefresh();
|
||
}, 500);
|
||
|
||
this.setData({
|
||
showTimeout: timeoutId,
|
||
lastShowTime: currentTime,
|
||
dataHasChanged: false
|
||
});
|
||
} else {
|
||
// 更新最后显示时间
|
||
this.setData({ lastShowTime: currentTime });
|
||
}
|
||
},
|
||
/**
|
||
* 点击未完成
|
||
*/
|
||
clickunfinished ({detail}) {
|
||
this.setData({isFinish: detail.checked})
|
||
this.filterSurgeriesList()
|
||
},
|
||
/**
|
||
* 过滤手术未完成
|
||
*/
|
||
filterSurgeriesList(arr = []) {
|
||
if (arr.length > 0) this.data.surgeriesList.push(...arr)
|
||
let list = []
|
||
if (this.data.isFinish) {
|
||
list = this.data.surgeries.filter((item) => {
|
||
if (item.surgerySubDevices && item.surgerySubDevices.length > 0){
|
||
if (item.surgerySubDevices.some(subDevice => !subDevice.end_time)) {
|
||
return item
|
||
}
|
||
}
|
||
})
|
||
this.setData({
|
||
surgeries: list
|
||
})
|
||
} else {
|
||
this.setData({
|
||
surgeries: this.data.surgeriesList
|
||
})
|
||
}
|
||
},
|
||
/**
|
||
* 判断是否需要刷新数据
|
||
*/
|
||
shouldRefreshData(currentTime) {
|
||
// 如果正在加载或刷新,则不执行
|
||
if (this.data.loading || this.data.refreshing || this.data.loadingMore) {
|
||
return false;
|
||
}
|
||
// 如果数据已变更(从编辑页面返回的标记),则需要刷新
|
||
if (this.data.dataHasChanged) {
|
||
return true;
|
||
}
|
||
|
||
// 如果还没有加载过数据,则需要加载
|
||
if (this.data.surgeries.length === 0 && !this.data.loadError) {
|
||
return true;
|
||
}
|
||
|
||
// 如果距离上次显示超过5分钟,考虑刷新(防止数据过期)
|
||
const timeSinceLastShow = currentTime - this.data.lastShowTime;
|
||
if (timeSinceLastShow > 5 * 60 * 1000) { // 5分钟
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* 执行智能刷新策略
|
||
*/
|
||
async performSmartRefresh() {
|
||
try {
|
||
// 1. 首先尝试增量更新(只刷新第一页,检查是否有新数据)
|
||
const freshData = await this.fetchSurgeriesData(1, this.data.pageSize);
|
||
const freshFirstPage = freshData.formattedSurgeries;
|
||
|
||
// 2. 比较数据是否有变化
|
||
const hasChanges = this.detectChanges(this.data.surgeries, freshFirstPage);
|
||
|
||
if (hasChanges) {
|
||
console.log('📋 检测到数据变化,执行增量更新');
|
||
await this.performIncrementalUpdate(freshData);
|
||
} else {
|
||
console.log('📋 数据无变化,保持当前状态');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('📋 智能刷新失败,回退到完整刷新:', error);
|
||
// 如果智能刷新失败,回退到完整的刷新
|
||
this.loadSurgeries();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 检测数据是否有变化
|
||
*/
|
||
detectChanges(currentData, freshData) {
|
||
// 如果数据数量不同,肯定有变化
|
||
if (currentData.length !== freshData.length) {
|
||
return true;
|
||
}
|
||
|
||
// 如果当前没有数据,而新数据有,则有变化
|
||
if (currentData.length === 0 && freshData.length > 0) {
|
||
return true;
|
||
}
|
||
|
||
// 比较前几个项目的ID和时间戳
|
||
const compareCount = Math.min(currentData.length, freshData.length, 3);
|
||
for (let i = 0; i < compareCount; i++) {
|
||
const currentItem = currentData[i];
|
||
const freshItem = freshData[i];
|
||
|
||
// 比较ID
|
||
if (currentItem.id !== freshItem.id) {
|
||
return true;
|
||
}
|
||
|
||
// 比较更新时间(如果有的话)
|
||
if (currentItem.updated_at !== freshItem.updated_at) {
|
||
return true;
|
||
}
|
||
|
||
// 比较手术时间
|
||
if (currentItem.surgery_time !== freshItem.surgery_time) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* 执行增量更新
|
||
*/
|
||
async performIncrementalUpdate(freshFirstPage) {
|
||
// 只在第1页时自动更新,其他页面不进行任何操作
|
||
if (this.data.page === 1) {
|
||
this.setData({
|
||
surgeries: freshFirstPage.formattedSurgeries,
|
||
total: freshFirstPage.total,
|
||
noMoreData: freshFirstPage.formattedSurgeries.length < this.data.pageSize
|
||
});
|
||
|
||
// 简单的数据更新提示
|
||
wx.showToast({
|
||
title: '数据已更新',
|
||
icon: 'success',
|
||
duration: 1000
|
||
});
|
||
}
|
||
// 如果用户在第2页+,不做任何操作,让用户自己选择是否下拉刷新
|
||
},
|
||
|
||
/**
|
||
* 设置数据变更标记(从其他页面调用)
|
||
*/
|
||
markDataChanged() {
|
||
this.setData({ dataHasChanged: true });
|
||
},
|
||
|
||
/**
|
||
* 初始加载手术数据
|
||
*/
|
||
async loadSurgeries() {
|
||
this.setData({
|
||
loading: true,
|
||
loadError: false,
|
||
page: 1,
|
||
noMoreData: false
|
||
});
|
||
|
||
try {
|
||
const res = await this.fetchSurgeriesData(1, this.data.pageSize);
|
||
|
||
this.setData({
|
||
surgeries: res.formattedSurgeries,
|
||
surgeriesList: res.formattedSurgeries,
|
||
total: res.total,
|
||
page: 1,
|
||
noMoreData: res.formattedSurgeries.length < this.data.pageSize,
|
||
loading: false
|
||
});
|
||
|
||
} catch (error) {
|
||
this.setData({
|
||
loading: false,
|
||
loadError: true,
|
||
});
|
||
wx.showToast({
|
||
title: "获取手术列表失败",
|
||
icon: "error",
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 下拉刷新
|
||
*/
|
||
async onRefresh() {
|
||
if (this.data.refreshing || this.data.loadingMore) return;
|
||
this.setData({
|
||
refreshing: true,
|
||
page: 1,
|
||
noMoreData: false
|
||
});
|
||
|
||
try {
|
||
const res = await this.fetchSurgeriesData(1, this.data.pageSize);
|
||
|
||
this.setData({
|
||
surgeries: res.formattedSurgeries,
|
||
total: res.total,
|
||
page: 1,
|
||
noMoreData: res.formattedSurgeries.length < this.data.pageSize
|
||
});
|
||
wx.showToast({
|
||
title: '刷新成功',
|
||
icon: 'success',
|
||
duration: 1500
|
||
});
|
||
} catch (error) {
|
||
wx.showToast({
|
||
title: '刷新失败',
|
||
icon: 'error'
|
||
});
|
||
} finally {
|
||
this.setData({ refreshing: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 滑动底部加载更多
|
||
*/
|
||
async onScrollToLower() {
|
||
// 如果正在加载或没有更多数据,则返回
|
||
if (this.data.loadingMore || this.data.noMoreData || this.data.refreshing) {
|
||
return;
|
||
}
|
||
|
||
this.setData({
|
||
loadingMore: true,
|
||
page: this.data.page + 1
|
||
});
|
||
try {
|
||
const res = await this.fetchSurgeriesData(this.data.page, this.data.pageSize);
|
||
this.filterSurgeriesList(res.formattedSurgeries)
|
||
// 合并数据
|
||
this.setData({
|
||
// surgeries: this.data.surgeries.concat(res.formattedSurgeries),
|
||
total: res.total,
|
||
noMoreData: res.formattedSurgeries.length < this.data.pageSize
|
||
});
|
||
} catch (error) {
|
||
// 恢复页码
|
||
this.setData({
|
||
page: this.data.page - 1
|
||
});
|
||
|
||
wx.showToast({
|
||
title: '加载更多失败',
|
||
icon: 'error'
|
||
});
|
||
} finally {
|
||
this.setData({ loadingMore: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取手术数据的核心方法
|
||
*/
|
||
async fetchSurgeriesData(page, pageSize) {
|
||
const res = await surgeryApi.getSurgeries({
|
||
page: page,
|
||
pageSize: pageSize
|
||
});
|
||
|
||
// 处理手术数据,格式化日期并确保医生和科室数据存在
|
||
// 后端返回的是分页格式:{list: [...], total: ...}
|
||
const surgeryList = res.data.list || res.data || [];
|
||
const formattedSurgeries = surgeryList.map((surgery) => {
|
||
// 诊断医生信息缺失问题
|
||
const hasDoctor = !!surgery.doctor;
|
||
const hasDepartment = !!surgery.doctor?.department;
|
||
|
||
// 确保医生和科室信息存在并设置默认值
|
||
const doctorName = hasDoctor ? surgery.doctor.name : "未知医生";
|
||
const departmentName = hasDepartment ? surgery.doctor.department.name : "未知科室";
|
||
|
||
// 计算使用的设备数量
|
||
const deviceCount = surgery.surgerySubDevices ? surgery.surgerySubDevices.length : 0;
|
||
|
||
// 检查是否有未设置结束时间的设备
|
||
let hasUnfinishedDevice = false;
|
||
if (surgery.surgerySubDevices && surgery.surgerySubDevices.length > 0) {
|
||
hasUnfinishedDevice = surgery.surgerySubDevices.some(subDevice => !subDevice.end_time);
|
||
}
|
||
|
||
// 为每个手术记录添加专属的侧滑按钮配置
|
||
const surgerySwipeButtons = [
|
||
{
|
||
text: "编辑",
|
||
style: "background-color: #0052d9; color: white; width: 120rpx; height: 100%; display: flex; align-items: center; justify-content: center;",
|
||
data: { id: surgery.id, type: "edit" },
|
||
},
|
||
{
|
||
text: "删除",
|
||
style: "background-color: #e34d59; color: white; width: 120rpx; height: 100%; display: flex; align-items: center; justify-content: center;",
|
||
data: { id: surgery.id, type: "delete" },
|
||
},
|
||
];
|
||
|
||
return {
|
||
...surgery,
|
||
formattedTime: dayjs(surgery.surgery_time).format("YYYY年MM月DD日 HH:mm"),
|
||
// 添加独立的属性,确保即使嵌套对象不存在也能显示
|
||
doctorName: doctorName,
|
||
departmentName: departmentName,
|
||
// 添加设备数量
|
||
deviceCount: deviceCount,
|
||
// 添加样式状态标记
|
||
hasUnfinishedDevice: hasUnfinishedDevice,
|
||
// 添加侧滑按钮配置
|
||
swipeButtons: surgerySwipeButtons,
|
||
};
|
||
});
|
||
|
||
return {
|
||
formattedSurgeries,
|
||
total: res.data.total || surgeryList.length
|
||
};
|
||
},
|
||
|
||
/**
|
||
* 查看手术详情
|
||
*/
|
||
viewSurgeryDetail(e) {
|
||
const surgeryId = e.currentTarget.dataset.id;
|
||
// 跳转到手术详情页
|
||
wx.navigateTo({
|
||
url: `/pages/surgery/detail/index?id=${surgeryId}`,
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 侧滑单元格点击事件处理
|
||
*/
|
||
onSwipeCellClick(e) {
|
||
const { index } = e.detail;
|
||
const btnData = e.detail.data;
|
||
|
||
if (!btnData || !btnData.id) {
|
||
Toast({
|
||
message: "操作失败,请重试",
|
||
theme: "error",
|
||
});
|
||
return;
|
||
}
|
||
|
||
const surgeryId = btnData.id;
|
||
const actionType = btnData.type;
|
||
|
||
|
||
// 查找对应的手术记录数据
|
||
const surgery = this.data.surgeries.find((item) => item.id === surgeryId);
|
||
if (!surgery) {
|
||
Toast({
|
||
message: "找不到对应的手术记录",
|
||
theme: "error",
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 根据操作类型执行相应的动作
|
||
if (actionType === "edit" || index === 0) {
|
||
// 编辑操作
|
||
this.handleEdit(surgery);
|
||
} else if (actionType === "delete" || index === 1) {
|
||
// 删除操作
|
||
this.handleDelete(surgery);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 处理编辑操作
|
||
*/
|
||
handleEdit(surgery) {
|
||
// 跳转到编辑页面,实际上是复用创建页面
|
||
wx.navigateTo({
|
||
url: `/pages/surgery/create/index?id=${surgery.id}&mode=edit`,
|
||
success: () => {
|
||
},
|
||
fail: (err) => {
|
||
Toast({
|
||
message: "页面跳转失败",
|
||
theme: "error",
|
||
});
|
||
},
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 处理删除操作
|
||
*/
|
||
handleDelete(surgery) {
|
||
// 显示确认对话框
|
||
this.setData({
|
||
currentSurgeryId: surgery.id,
|
||
confirmDialogVisible: true,
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 确认删除
|
||
*/
|
||
async confirmDelete() {
|
||
const surgeryId = this.data.currentSurgeryId;
|
||
|
||
try {
|
||
await surgeryApi.deleteSurgery(surgeryId);
|
||
// 删除成功,刷新列表
|
||
wx.showToast({
|
||
title: "删除成功",
|
||
icon: "success",
|
||
});
|
||
|
||
// 重新加载数据
|
||
this.loadSurgeries();
|
||
} catch (error) {
|
||
wx.showToast({
|
||
title: "删除失败",
|
||
icon: "error",
|
||
});
|
||
} finally {
|
||
// 关闭对话框
|
||
this.setData({
|
||
confirmDialogVisible: false,
|
||
currentSurgeryId: null,
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 取消删除
|
||
*/
|
||
cancelDelete() {
|
||
this.setData({
|
||
confirmDialogVisible: false,
|
||
currentSurgeryId: null,
|
||
});
|
||
},
|
||
|
||
|
||
/**
|
||
* 浮动按钮点击事件处理函数
|
||
*/
|
||
handleClick() {
|
||
// 跳转到手术创建页面
|
||
wx.navigateTo({
|
||
url: "/pages/surgery/create/index",
|
||
success: () => {
|
||
},
|
||
fail: (err) => {
|
||
wx.showToast({
|
||
title: "页面跳转失败",
|
||
icon: "error",
|
||
});
|
||
},
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 页面卸载时清理资源
|
||
*/
|
||
onUnload() {
|
||
// 清除定时器,避免内存泄漏
|
||
if (this.data.showTimeout) {
|
||
clearTimeout(this.data.showTimeout);
|
||
}
|
||
},
|
||
});
|