// 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); } }, });