Merge branch 'master' of https://gitea.igandanyiyuan.com/gdxz/uniapp-app
This commit is contained in:
commit
b068fb00cc
293
TODO.md
293
TODO.md
@ -1,272 +1,29 @@
|
||||
# 精品课页面功能改进完成
|
||||
# TODO List
|
||||
|
||||
## ✅ 已完成功能
|
||||
## 已完成任务
|
||||
- [x] 调用API里的指南标签接口并集成到筛选功能
|
||||
- [x] 为指南列表页面添加scroll-view的上拉加载和下拉刷新功能
|
||||
- [x] 为指南列表标题添加超过2行显示省略号功能
|
||||
- [x] 修复PPT页面的上拉加载功能
|
||||
- [x] 修复页码递增逻辑问题
|
||||
- [x] 添加详细的调试日志
|
||||
- [x] 添加测试按钮用于手动触发
|
||||
- [x] 修复排序功能
|
||||
|
||||
### 🎯 **主横幅板块升级**
|
||||
1. **多个横幅图片** - 从单个图片改为5个轮播横幅
|
||||
2. **自动滑动功能** - 使用swiper组件实现自动轮播
|
||||
3. **丰富的内容展示** - 每个横幅包含标题、副标题、专家信息等
|
||||
4. **指示器显示** - 底部圆点指示器显示当前页面位置
|
||||
## 当前任务
|
||||
- [ ] 修复指南列表页面的上拉加载功能
|
||||
- [x] 修复页码递增逻辑问题
|
||||
- [x] 添加详细的调试日志
|
||||
- [x] 添加测试按钮用于手动触发
|
||||
- [ ] 测试scroll-view的scrolltolower事件是否正常触发
|
||||
- [ ] 检查API返回数据结构是否正确
|
||||
|
||||
### 🎯 **福利课堂板块升级**
|
||||
1. **多个福利项目** - 从单个横幅改为5个福利项目
|
||||
2. **左右滑动功能** - 使用scroll-view实现横向滚动
|
||||
3. **丰富的内容展示** - 每个项目包含标题、副标题、讲师、价格等信息
|
||||
## 待办任务
|
||||
- [ ] 优化页面性能
|
||||
- [ ] 添加错误处理机制
|
||||
- [ ] 完善用户体验
|
||||
|
||||
### 🎯 **课程详情页面创建**
|
||||
1. **100%还原设计图** - 完全按照图片设计实现
|
||||
2. **完整的页面结构** - 包含所有设计元素和功能
|
||||
3. **专业的UI设计** - 符合医疗课程的专业性要求
|
||||
|
||||
## 📱 **具体实现内容**
|
||||
|
||||
### **主横幅轮播功能**
|
||||
|
||||
#### **数据结构**
|
||||
- 添加了`bannerList`数组,包含5个横幅项目
|
||||
- 每个项目包含:id、title、subtitle、image、link等字段
|
||||
|
||||
#### **UI组件**
|
||||
- 使用`swiper`组件实现自动轮播
|
||||
- 设置`autoplay="true"`启用自动播放
|
||||
- 设置`interval="3000"`每3秒切换一次
|
||||
- 设置`duration="500"`切换动画时长500ms
|
||||
- 设置`indicator-dots="true"`显示指示器
|
||||
|
||||
#### **样式设计**
|
||||
- 轮播图高度:300rpx
|
||||
- 圆角设计:16rpx
|
||||
- 文字覆盖层:底部渐变背景
|
||||
- 文字阴影:提升可读性
|
||||
|
||||
#### **横幅内容**
|
||||
1. **小懂医生讲HIV和感染** - 黄湛镰 副主任医师
|
||||
2. **肝脏肿瘤临床影像学习** - 王学浩 教授
|
||||
3. **慢性肝病营养治疗指南** - 段钟平 教授
|
||||
4. **肝移植术后管理要点** - 郑树森 院士
|
||||
5. **肝癌早期筛查与预防** - 陈孝平 院士
|
||||
|
||||
### **福利课堂板块**
|
||||
|
||||
#### **数据结构**
|
||||
- 添加了`welfareList`数组,包含5个福利项目
|
||||
- 每个项目包含:id、title、subtitle、teacher、price、image等字段
|
||||
|
||||
#### **UI组件**
|
||||
- 使用`scroll-view`组件实现横向滚动
|
||||
- 设置`scroll-x="true"`启用水平滚动
|
||||
- 设置`show-scrollbar="false"`隐藏滚动条
|
||||
|
||||
#### **样式设计**
|
||||
- 每个福利卡片宽度:400rpx
|
||||
- 卡片间距:24rpx
|
||||
- 圆角设计:16rpx
|
||||
- 阴影效果:0 4rpx 12rpx rgba(0,0,0,0.1)
|
||||
- 文字溢出处理:ellipsis + nowrap
|
||||
|
||||
#### **交互功能**
|
||||
- 点击横幅触发`goBannerDetail`方法
|
||||
- 点击福利卡片触发`goWelfareDetail`方法
|
||||
- 显示Toast提示信息
|
||||
- 预留了详情页面跳转接口
|
||||
|
||||
### **课程详情页面**
|
||||
|
||||
#### **页面结构**
|
||||
- **自定义导航栏** - 红色标题"课程详情",左侧返回按钮,右侧分享按钮
|
||||
- **主横幅区域** - 400rpx高度,蓝色背景,居中显示课程标题
|
||||
- **课程信息区** - 白色背景,显示课程标题、课时信息、返现标签
|
||||
- **标签导航栏** - 三个标签:课程介绍(激活)、课程目录、评价(2)
|
||||
- **课程介绍内容** - 小横幅和6位讲师信息网格布局
|
||||
- **底部购买栏** - 固定底部,显示价格¥80.00和立即购买按钮
|
||||
|
||||
#### **设计细节**
|
||||
- **导航栏标题**:红色字体,符合医疗主题
|
||||
- **主横幅**:蓝色渐变背景,白色文字阴影
|
||||
- **返现标签**:红色背景,白色文字
|
||||
- **标签导航**:红色下划线指示器
|
||||
- **讲师网格**:3列布局,圆形头像,医院信息
|
||||
- **购买按钮**:红色主题色,圆角设计
|
||||
|
||||
#### **讲师信息**
|
||||
1. **王建设 教授** - 复旦大学附属儿童医院
|
||||
2. **黄燕 教授** - 中南大学湘雅医院
|
||||
3. **田沂 教授** - 中南大学湘雅二医院
|
||||
4. **李教授** - 知名医院
|
||||
5. **张教授** - 知名医院
|
||||
6. **刘教授** - 知名医院
|
||||
|
||||
## 🖼️ **图片资源**
|
||||
- 使用现有的static目录图片
|
||||
- 包括:lunbo_bg.png、paper_bg.png、pap_bg.png、bo_bg.png、livebg.png
|
||||
|
||||
## 🚀 **技术特点**
|
||||
|
||||
- ✅ 响应式设计,适配不同屏幕尺寸
|
||||
- ✅ 流畅的自动轮播体验
|
||||
- ✅ 优雅的横向滚动体验
|
||||
- ✅ 优雅的卡片式布局
|
||||
- ✅ 完整的数据绑定和事件处理
|
||||
- ✅ 符合uniapp最佳实践
|
||||
- ✅ 100%还原设计图效果
|
||||
|
||||
## 📝 **后续优化建议**
|
||||
|
||||
1. **添加分页指示器** - 显示当前滚动位置
|
||||
2. **实现手动滑动** - 支持用户手动滑动轮播图
|
||||
3. **添加加载动画** - 提升用户体验
|
||||
4. **图片懒加载** - 优化性能
|
||||
5. **缓存机制** - 减少重复请求
|
||||
6. **轮播图暂停** - 触摸时暂停自动播放
|
||||
7. **课程目录功能** - 实现课程目录标签页
|
||||
8. **评价系统** - 实现评价标签页功能
|
||||
9. **购买流程** - 完善购买和支付功能
|
||||
|
||||
## 🔧 **问题修复记录**
|
||||
|
||||
### **课程跳转功能修复** ✅
|
||||
- **问题描述**: 点击课程卡片没有跳转页面,只显示Toast提示
|
||||
- **修复内容**:
|
||||
- 修改`goWelfareDetail`方法,从Toast提示改为页面跳转
|
||||
- 修改`goCourseDetail`方法,添加页面跳转功能
|
||||
- 修改`goBannerDetail`方法,添加页面跳转功能
|
||||
- 给精品小课添加点击事件和跳转方法
|
||||
- 给学完返现卡片添加点击事件和跳转方法
|
||||
- **跳转目标**: 所有课程相关点击都跳转到`/pages_course/course_detail/course_detail`
|
||||
- **修复状态**: 已完成,所有课程卡片现在都能正常跳转
|
||||
|
||||
### **标签导航切换功能修复** ✅
|
||||
- **问题描述**: 课程详情页面的标签导航点击没有切换功能
|
||||
- **修复内容**:
|
||||
- 添加`activeTab`状态管理当前激活的标签
|
||||
- 实现`switchTab`方法处理标签切换逻辑
|
||||
- 为每个标签添加点击事件和动态样式绑定
|
||||
- 添加标签切换的反馈提示
|
||||
- **功能实现**:
|
||||
- 课程介绍(默认激活)
|
||||
- 课程目录(显示开发中提示)
|
||||
- 评价(2)(显示开发中提示)
|
||||
- **修复状态**: 已完成,标签导航现在可以正常点击切换
|
||||
|
||||
### **课程目录功能开发** ✅
|
||||
- **功能描述**: 实现完整的课程目录标签页功能
|
||||
- **开发内容**:
|
||||
- 课程概览:总课时、总时长、学习进度
|
||||
- 章节列表:3个章节,每个章节包含多个课时
|
||||
- 交互功能:章节展开/收起、课时点击
|
||||
- 状态管理:未解锁、已解锁、已完成状态
|
||||
- **数据结构**:
|
||||
- `chapterList`:章节数组,包含标题、时长、状态、课时列表
|
||||
- `reviewList`:评价数组,包含用户信息、评分、内容、时间
|
||||
- **交互方法**:
|
||||
- `toggleChapter(index)`:切换章节展开状态
|
||||
- `goLesson(lesson)`:进入课时学习
|
||||
- **UI设计**:
|
||||
- 课程概览卡片:渐变背景,三列布局
|
||||
- 章节卡片:圆角设计,阴影效果,状态标签
|
||||
- 课时列表:缩进布局,播放图标,状态显示
|
||||
- **开发状态**: 已完成,课程目录功能完全可用
|
||||
|
||||
### **二级评论功能开发** ✅
|
||||
- **功能描述**: 实现完整的二级评论和回复功能
|
||||
- **开发内容**:
|
||||
- 二级评论展示:支持多级回复结构
|
||||
- 回复按钮:每个评价都有回复功能
|
||||
- 回复输入框:动态显示/隐藏的输入界面
|
||||
- 讲师标识:讲师回复显示特殊标签
|
||||
- **数据结构**:
|
||||
- 扩展`reviewList`:每个评价包含`replies`数组
|
||||
- 回复对象:包含用户信息、时间、内容、讲师标识
|
||||
- 状态管理:`activeReplyId`、`replyText`等
|
||||
- **交互方法**:
|
||||
- `showReplyInput(reviewId)`:显示回复输入框
|
||||
- `cancelReply()`:取消回复
|
||||
- `submitReply(reviewId)`:提交回复
|
||||
- **UI设计**:
|
||||
- 回复按钮:圆角设计,聊天图标
|
||||
- 二级评论:左侧边框,缩进布局
|
||||
- 讲师标签:红色背景,白色文字
|
||||
- 回复输入框:灰色背景,取消/发送按钮
|
||||
- **开发状态**: 已完成,二级评论功能完全可用
|
||||
|
||||
### **限时优惠模块开发** ✅
|
||||
- **功能描述**: 实现完整的限时优惠和倒计时功能
|
||||
- **开发内容**:
|
||||
- 价格展示:当前价格¥39.00,原价¥390.00(划线显示)
|
||||
- 优惠标签:白色背景的"限时优惠"标签
|
||||
- 倒计时功能:实时倒计时显示剩余时间
|
||||
- 自动更新:每秒更新倒计时数据
|
||||
- **数据结构**:
|
||||
- `countdown`:包含天、时、分、秒的倒计时对象
|
||||
- `countdownTimer`:倒计时定时器引用
|
||||
- **交互方法**:
|
||||
- `startCountdown()`:启动倒计时,计算剩余时间
|
||||
- 自动清理:页面卸载时清除定时器
|
||||
- **UI设计**:
|
||||
- 渐变背景:红色到橙色的渐变效果
|
||||
- 左侧布局:价格信息和优惠标签(占2/3宽度)
|
||||
- 右侧布局:倒计时文字和时间显示(占1/3宽度)
|
||||
- 时间盒子:橙色背景的时分秒显示框
|
||||
- **开发状态**: 已完成,限时优惠功能完全可用
|
||||
|
||||
### **课程评论页面开发** ✅
|
||||
- **功能描述**: 创建专门的课程评价提交页面
|
||||
- **开发内容**:
|
||||
- 自定义导航栏:红色"评价课程"标题,左侧返回按钮
|
||||
- 星级评分系统:5颗星星,点击选择评分
|
||||
- 动态反馈文字:根据评分显示对应的评价反馈
|
||||
- 评价输入框:300字限制,多行提示文字
|
||||
- 字数统计:实时显示已输入字数/总字数
|
||||
- 提交按钮:青色主题色,圆角设计
|
||||
- 奖励信息:显示积分奖励说明
|
||||
- **具体实现**:
|
||||
- **评分系统**:1-5星评分,对应不同反馈文字
|
||||
- **输入验证**:必须选择评分和填写内容才能提交
|
||||
- **页面跳转**:从课程详情页"评价"标签页进入
|
||||
- **返回逻辑**:提交成功后自动返回上一页
|
||||
- **数据结构**:
|
||||
- `currentRating`:当前选择的星级评分
|
||||
- `reviewContent`:评价内容文本
|
||||
- `ratingFeedback`:评分对应的反馈文字
|
||||
- **交互方法**:
|
||||
- `setRating(rating)`:设置星级评分
|
||||
- `updateRatingFeedback()`:更新评分反馈文字
|
||||
- `submitReview()`:提交评价内容
|
||||
- `goToReview()`:跳转到评论页面
|
||||
- **UI设计**:
|
||||
- 导航栏:红色标题,符合医疗主题
|
||||
- 星星图标:48rpx大小,黄色填充,灰色未填充
|
||||
- 输入框:浅灰色背景,圆角设计,300rpx高度
|
||||
- 提交按钮:青色背景,白色文字,88rpx高度
|
||||
- **开发状态**: 已完成,课程评论功能完全可用
|
||||
|
||||
### **我的课程页面开发** ✅
|
||||
- **功能描述**: 创建完整的"我的课程"页面,包含标签切换和课程管理
|
||||
- **开发内容**:
|
||||
- 自定义导航栏:红色"我的课程"标题,左侧返回按钮,右侧搜索按钮
|
||||
- 标签切换功能:学习中/已学完两个标签,带分隔线设计
|
||||
- 课程列表展示:课程图片、标题、课时信息、学习进度
|
||||
- 特殊标签显示:福利课堂、学完返现等特殊标识
|
||||
- 订单记录浮动按钮:青色圆形按钮,固定位置显示
|
||||
- 底部导航栏:精品课(未激活)、我的课程(激活状态)
|
||||
- **具体实现**:
|
||||
- **标签系统**:默认激活"学习中"标签,点击切换显示不同课程列表
|
||||
- **课程数据结构**:包含id、标题、课时数、状态、已学课时、图片、标签等
|
||||
- **响应式布局**:课程卡片自适应,文字溢出处理,标签动态显示
|
||||
- **交互功能**:点击课程跳转详情页,点击标签切换内容,底部导航跳转
|
||||
- **数据结构**:
|
||||
- `activeTab`:当前激活的标签(learning/completed)
|
||||
- `courseList`:按标签分组的课程数据
|
||||
- `currentCourseList`:计算属性,根据当前标签返回对应课程列表
|
||||
- **交互方法**:
|
||||
- `switchTab(tab)`:切换标签,更新课程列表显示
|
||||
- `goCourseDetail(course)`:跳转到课程详情页面
|
||||
- `goOrderRecord()`:进入订单记录功能
|
||||
- `goPremiumCourses()`:跳转到精品课页面
|
||||
- **UI设计**:
|
||||
- 导航栏:红色主题色,符合医疗应用风格
|
||||
- 标签设计:居中布局,分隔线分隔,激活状态红色高亮
|
||||
- 课程卡片:白色背景,圆角设计,阴影效果,左侧图片右侧信息
|
||||
- 浮动按钮:青色背景,圆形图标,固定位置,阴影效果
|
||||
- 底部导航:红色激活状态,图标+文字布局
|
||||
- **开发状态**: 已完成,"我的课程"页面功能完全可用
|
||||
## 技术债务
|
||||
- [ ] 代码重构和优化
|
||||
- [ ] 添加单元测试
|
||||
- [ ] 文档更新
|
||||
|
||||
24
api/api.js
24
api/api.js
@ -248,7 +248,7 @@ const api = {
|
||||
},
|
||||
//指南标签
|
||||
guideTag(data){
|
||||
return request('/expertApp/tagList', data, 'post', false);
|
||||
return request('/expertAPI/tagList', data, 'post', false);
|
||||
},
|
||||
|
||||
// 会议列表
|
||||
@ -319,7 +319,27 @@ const api = {
|
||||
return request('/expertAPI/newsTagList', data, 'post', false);
|
||||
},
|
||||
|
||||
|
||||
meetingListV2U(data){
|
||||
return request('/expertAPI/meetingListV2U', data, 'post', false);
|
||||
},
|
||||
applyList(data){
|
||||
return request('/expertAPI/applyList', data, 'post', false);
|
||||
},
|
||||
patientListByGBK(data){
|
||||
return request('/expertAPI/patientListByGBK', data, 'post', false);
|
||||
},
|
||||
followUpList(data){
|
||||
return request('/expertAPI/followUpList', data, 'post', false);
|
||||
},
|
||||
relationRecordLately(data){
|
||||
return request('/expertAPI/relationRecordLately', data, 'post', false);
|
||||
},
|
||||
applyListOperate(data){
|
||||
return request('/expertAPI/applyListOperate', data, 'post', false);
|
||||
},
|
||||
groupList(data){
|
||||
return request('/expertAPI/groupListU', data, 'post', false);
|
||||
},
|
||||
}
|
||||
|
||||
export default api
|
||||
@ -12,6 +12,7 @@
|
||||
"dayjs": "^1.11.18",
|
||||
"js-base64": "^3.7.8",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinyin": "^4.0.0",
|
||||
"uview-plus": "^3.4.73"
|
||||
}
|
||||
}
|
||||
|
||||
41
pages.json
41
pages.json
@ -238,6 +238,47 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "patientSetting/patientSetting",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "patientRemark/patientRemark",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "groupEdit/groupEdit",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "selectPatient/selectPatient",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "videoDetail/videoDetail",
|
||||
"style": {
|
||||
|
||||
@ -36,8 +36,8 @@
|
||||
<view class="time-header">2025年08月</view>
|
||||
|
||||
<!-- 会议列表 -->
|
||||
<view class="meeting-list">
|
||||
<view class="meeting-item" v-for="(item, index) in meetingList" :key="index">
|
||||
<view class="meeting-list" v-if="meetingList.length > 0">
|
||||
<view class="meeting-item" v-for="(item, index) in meetingList" :key="item.id || index">
|
||||
<!-- 左侧日期标识 -->
|
||||
<view class="date-tag" :style="{backgroundColor: item.tagColor}">
|
||||
<text class="date-text">{{ item.date }}</text>
|
||||
@ -47,11 +47,20 @@
|
||||
<view class="meeting-content">
|
||||
<view class="meeting-title">{{ item.title }}</view>
|
||||
<view class="meeting-poster" @click="playVideo(item)">
|
||||
<image :src="item.poster" class="poster-image"></image>
|
||||
<image
|
||||
:src="docUrl+item.liveimg"
|
||||
class="poster-image"
|
||||
mode="aspectFill"
|
||||
@error="onImageError"
|
||||
@load="onImageLoad"
|
||||
:data-item-id="item.id"
|
||||
></image>
|
||||
<view class="play-btn">
|
||||
<up-image :src="playImg" width="108rpx" height="108rpx" ></up-image>
|
||||
</view>
|
||||
<view class="preview-tag">预告</view>
|
||||
<view class="preview-tag" v-if="item.status === 'upcoming'">预告</view>
|
||||
<view class="live-tag" v-else-if="item.status === 'live'">直播中</view>
|
||||
<view class="replay-tag" v-else-if="item.status === 'replay'">回放</view>
|
||||
</view>
|
||||
<view class="meeting-info">
|
||||
<view class="info-item">
|
||||
@ -69,6 +78,15 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" v-else-if="!isRefreshing">
|
||||
<view class="empty-icon">
|
||||
<uni-icons type="calendar" size="80" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
<text class="empty-text">暂无会议数据</text>
|
||||
<text class="empty-subtext">请稍后再试或调整筛选条件</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view class="load-more" v-if="showLoadMore">
|
||||
<view class="load-more-content" v-if="isLoadingMore">
|
||||
@ -138,10 +156,13 @@
|
||||
import { ref,nextTick} from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import CustomTabbar from '@/components/tabBar/tabBar.vue';
|
||||
import api from '@/api/api.js';
|
||||
import select from "@/static/triangle_normal.png"
|
||||
import selectOn from "@/static/triangle_normal.png"
|
||||
import playImg from "@/static/bofang.png"
|
||||
import timeImg from "@/static/play_long.png"
|
||||
import docUrl from "@/utils/docUrl"
|
||||
|
||||
// 弹窗状态
|
||||
const isTimePopupShow = ref(false);
|
||||
const isLocationPopupShow = ref(false);
|
||||
@ -214,32 +235,153 @@
|
||||
]);
|
||||
|
||||
// 会议列表数据
|
||||
const meetingList = ref([
|
||||
{
|
||||
date: '13',
|
||||
tagColor: '#FF4444',
|
||||
title: '"天山论·见"—疑难危重病患维训练营',
|
||||
poster: '/static/meeting-poster-1.jpg',
|
||||
time: '2025.08.13',
|
||||
location: '线上'
|
||||
},
|
||||
{
|
||||
date: '13',
|
||||
tagColor: '#FFA500',
|
||||
title: '护肝新声大咖谈',
|
||||
poster: '/static/meeting-poster-2.jpg',
|
||||
time: '2025.08.13',
|
||||
location: '线上'
|
||||
},
|
||||
{
|
||||
date: '15',
|
||||
tagColor: '#00BCD4',
|
||||
title: '小罐医生讲HIV和感染|专题二:抗菌药物-抗真菌药物特性解读',
|
||||
poster: '/static/meeting-poster-3.jpg',
|
||||
time: '2025.08.15',
|
||||
location: '线上'
|
||||
const meetingList = ref([]);
|
||||
|
||||
// 页面显示时获取会议列表数据
|
||||
onShow(() => {
|
||||
getMeetingList(true);
|
||||
});
|
||||
|
||||
// 获取会议列表数据的函数
|
||||
const getMeetingList = async (isRefresh = false) => {
|
||||
if (isRefresh) {
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
}
|
||||
]);
|
||||
|
||||
const params = {
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
month: selectedMonth.value !== 'all' ? selectedMonth.value : '',
|
||||
province: selectedProvince.value !== 'all' ? selectedProvince.value : ''
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('获取会议列表参数:', params);
|
||||
const response = await api.meetingListV2U(params);
|
||||
console.log('会议列表API响应:', response);
|
||||
|
||||
if (response && response.code === 200 && response.data) {
|
||||
let newItems = [];
|
||||
let totalCount = 0;
|
||||
|
||||
// 处理不同的数据结构
|
||||
if (response.data.list && Array.isArray(response.data.list)) {
|
||||
newItems = response.data.list;
|
||||
totalCount = response.data.total || response.data.totalRow || 0;
|
||||
console.log('使用 res.data.list 结构');
|
||||
} else if (response.data && Array.isArray(response.data)) {
|
||||
newItems = response.data;
|
||||
totalCount = response.total || response.totalRow || newItems.length;
|
||||
console.log('使用 res.data 结构');
|
||||
} else if (Array.isArray(response)) {
|
||||
newItems = response;
|
||||
totalCount = newItems.length;
|
||||
console.log('使用 res 数组结构');
|
||||
}
|
||||
|
||||
console.log('解析后的数据:', { newItems, totalCount });
|
||||
console.log('图片字段映射示例:', newItems.slice(0, 2).map(item => ({
|
||||
liveimg: item.liveimg || item.live_image || item.live_img,
|
||||
poster: item.poster || item.cover_image || item.image
|
||||
})));
|
||||
|
||||
if (Array.isArray(newItems) && newItems.length > 0) {
|
||||
// 处理会议数据,添加必要的字段
|
||||
const processedItems = newItems.map(item => ({
|
||||
id: item.id || item.meeting_id || Math.random().toString(36).substr(2, 9),
|
||||
date: item.date || item.meeting_date || item.start_time || '13',
|
||||
tagColor: getTagColor(item.status || item.meeting_status || 'upcoming'),
|
||||
title: item.title || item.meeting_title || item.name || '会议标题',
|
||||
liveimg: item.liveimg || item.live_image || item.live_img || '',
|
||||
poster: item.poster || item.cover_image || item.image || '/static/meeting-poster-1.jpg',
|
||||
time: formatMeetingTime(item.start_time || item.meeting_time || item.time),
|
||||
location: item.location || item.address || item.venue || '线上',
|
||||
status: item.status || item.meeting_status || 'upcoming',
|
||||
description: item.description || item.content || '',
|
||||
organizer: item.organizer || item.host || '',
|
||||
speakers: item.speakers || item.experts || []
|
||||
}));
|
||||
|
||||
if (isRefresh) {
|
||||
meetingList.value = processedItems;
|
||||
} else {
|
||||
meetingList.value.push(...processedItems);
|
||||
}
|
||||
|
||||
// 检查是否还有更多数据
|
||||
if (meetingList.value.length >= totalCount) {
|
||||
hasMoreData.value = false;
|
||||
}
|
||||
|
||||
console.log('会议列表更新成功,当前总数:', meetingList.value.length);
|
||||
console.log('图片字段详情:', processedItems.slice(0, 2).map(item => ({
|
||||
id: item.id,
|
||||
liveimg: item.liveimg,
|
||||
poster: item.poster,
|
||||
finalImage: item.liveimg || item.poster
|
||||
})));
|
||||
} else {
|
||||
console.log('API返回的数据为空');
|
||||
if (isRefresh) {
|
||||
meetingList.value = [];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('API响应格式不正确:', response);
|
||||
if (isRefresh) {
|
||||
meetingList.value = [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取会议列表失败:', error);
|
||||
if (isRefresh) {
|
||||
meetingList.value = [];
|
||||
}
|
||||
uni.showToast({
|
||||
title: '获取会议列表失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 根据会议状态获取标签颜色
|
||||
const getTagColor = (status) => {
|
||||
const colorMap = {
|
||||
'upcoming': '#FF4444', // 预告
|
||||
'live': '#00BCD4', // 直播中
|
||||
'replay': '#9C27B0', // 回放
|
||||
'finished': '#4CAF50', // 已结束
|
||||
'cancelled': '#FF9800' // 已取消
|
||||
};
|
||||
return colorMap[status] || '#FF4444';
|
||||
};
|
||||
|
||||
// 格式化会议时间
|
||||
const formatMeetingTime = (timeStr) => {
|
||||
if (!timeStr) return '2025.08.13';
|
||||
|
||||
try {
|
||||
// 如果是时间戳
|
||||
if (typeof timeStr === 'number') {
|
||||
const date = new Date(timeStr);
|
||||
return `${date.getFullYear()}.${String(date.getMonth() + 1).padStart(2, '0')}.${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// 如果是字符串,尝试解析
|
||||
const date = new Date(timeStr);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return `${date.getFullYear()}.${String(date.getMonth() + 1).padStart(2, '0')}.${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// 如果解析失败,返回原字符串
|
||||
return timeStr;
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error);
|
||||
return timeStr;
|
||||
}
|
||||
};
|
||||
|
||||
// 显示时间选择弹窗
|
||||
const showTimePopup = () => {
|
||||
@ -255,7 +397,8 @@
|
||||
const selectMonth = (month) => {
|
||||
selectedMonth.value = month.value;
|
||||
console.log('选择月份:', month.label);
|
||||
// 这里可以根据选择的月份筛选会议数据
|
||||
// 选择月份后重新加载数据
|
||||
getMeetingList(true);
|
||||
hideTimePopup();
|
||||
};
|
||||
|
||||
@ -273,7 +416,8 @@
|
||||
const selectProvince = (province) => {
|
||||
selectedProvince.value = province.code;
|
||||
console.log('选择省份:', province.name);
|
||||
// 这里可以根据选择的省份筛选会议数据
|
||||
// 选择省份后重新加载数据
|
||||
getMeetingList(true);
|
||||
hideLocationPopup();
|
||||
};
|
||||
|
||||
@ -283,102 +427,64 @@
|
||||
// 这里可以实现视频播放逻辑
|
||||
};
|
||||
|
||||
// 图片加载失败处理
|
||||
const onImageError = (e) => {
|
||||
const itemId = e.currentTarget.dataset.itemId;
|
||||
console.log('图片加载失败,项目ID:', itemId);
|
||||
|
||||
// 找到对应的项目并设置默认图片
|
||||
const itemIndex = meetingList.value.findIndex(item => item.id === itemId);
|
||||
if (itemIndex !== -1) {
|
||||
// 如果liveimg加载失败,尝试使用poster
|
||||
if (meetingList.value[itemIndex].liveimg && meetingList.value[itemIndex].liveimg !== meetingList.value[itemIndex].poster) {
|
||||
console.log('liveimg加载失败,切换到poster');
|
||||
meetingList.value[itemIndex].liveimg = meetingList.value[itemIndex].poster;
|
||||
} else {
|
||||
// 如果poster也失败,使用默认图片
|
||||
console.log('设置默认图片');
|
||||
meetingList.value[itemIndex].poster = '/static/meeting-poster-1.jpg';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 图片加载成功处理
|
||||
const onImageLoad = (e) => {
|
||||
const itemId = e.currentTarget.dataset.itemId;
|
||||
console.log('图片加载成功,项目ID:', itemId, '图片地址:', e.currentTarget.src);
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = () => {
|
||||
isRefreshing.value = true;
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
// 重置会议列表为初始数据
|
||||
meetingList.value = [
|
||||
{
|
||||
date: '13',
|
||||
tagColor: '#FF4444',
|
||||
title: '"天山论·见"—疑难危重病患维训练营',
|
||||
poster: '/static/meeting-poster-1.jpg',
|
||||
time: '2025.08.13',
|
||||
location: '线上'
|
||||
},
|
||||
{
|
||||
date: '13',
|
||||
tagColor: '#FFA500',
|
||||
title: '护肝新声大咖谈',
|
||||
poster: '/static/meeting-poster-2.jpg',
|
||||
time: '2025.08.13',
|
||||
location: '线上'
|
||||
},
|
||||
{
|
||||
date: '15',
|
||||
tagColor: '#00BCD4',
|
||||
title: '小罐医生讲HIV和感染|专题二:抗菌药物-抗真菌药物特性解读',
|
||||
poster: '/static/meeting-poster-3.jpg',
|
||||
time: '2025.08.15',
|
||||
location: '线上'
|
||||
}
|
||||
];
|
||||
isRefreshing.value = false;
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
}, 1500);
|
||||
// 调用获取会议列表函数
|
||||
getMeetingList(true).finally(() => {
|
||||
// 延迟关闭刷新状态,给用户更好的体验
|
||||
setTimeout(() => {
|
||||
isRefreshing.value = false;
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
// 上拉加载更多
|
||||
const onLoadMore = () => {
|
||||
console.log('上拉加载');
|
||||
console.log('上拉加载更多');
|
||||
if (isLoadingMore.value || !hasMoreData.value) return;
|
||||
|
||||
isLoadingMore.value = true;
|
||||
currentPage.value++;
|
||||
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
// 模拟新增数据
|
||||
const newMeetings = [
|
||||
{
|
||||
date: '16',
|
||||
tagColor: '#9C27B0',
|
||||
title: '肝胆外科微创技术研讨会',
|
||||
poster: '/static/meeting-poster-4.jpg',
|
||||
time: '2025.08.16',
|
||||
location: '北京'
|
||||
},
|
||||
{
|
||||
date: '17',
|
||||
tagColor: '#FF9800',
|
||||
title: '胆囊疾病诊疗新进展',
|
||||
poster: '/static/meeting-poster-5.jpg',
|
||||
time: '2025.08.17',
|
||||
location: '上海'
|
||||
},
|
||||
{
|
||||
date: '18',
|
||||
tagColor: '#4CAF50',
|
||||
title: '肝移植术后管理专题讲座',
|
||||
poster: '/static/meeting-poster-6.jpg',
|
||||
time: '2025.08.18',
|
||||
location: '广州'
|
||||
}
|
||||
];
|
||||
|
||||
meetingList.value.push(...newMeetings);
|
||||
|
||||
// 模拟没有更多数据的情况(第3页后)
|
||||
if (currentPage.value >= 3) {
|
||||
hasMoreData.value = false;
|
||||
}
|
||||
|
||||
// 调用API获取更多数据
|
||||
getMeetingList(false).finally(() => {
|
||||
isLoadingMore.value = false;
|
||||
|
||||
// 强制更新页面,确保scroll-view可以正常滚动
|
||||
nextTick(() => {
|
||||
console.log('数据加载完成,列表长度:', meetingList.value.length);
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -701,7 +807,29 @@ $shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
border: 4rpx solid #fff;
|
||||
background-color: #FF4444;
|
||||
color: $white;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.live-tag {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
background-color: #00BCD4;
|
||||
color: $white;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.replay-tag {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
background-color: #9C27B0;
|
||||
color: $white;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
@ -730,6 +858,31 @@ $shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 30rpx;
|
||||
|
||||
.empty-icon {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: $gray;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: $gray-text;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多提示
|
||||
.load-more {
|
||||
padding: 30rpx;
|
||||
|
||||
196
pages_app/groupEdit/groupEdit.vue
Normal file
196
pages_app/groupEdit/groupEdit.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view class="group-edit-page">
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="编辑分组"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
<template #right>
|
||||
<text class="save-text" @click="saveGroup">保存</text>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<!-- 分组名称 -->
|
||||
<view class="section-header">分组名称</view>
|
||||
<view class="name-row">
|
||||
<input class="name-input" v-model.trim="groupName" placeholder="请输入分组名称" maxlength="20" />
|
||||
<view class="icon-btn" v-if="groupName" @click="clearName">
|
||||
<up-image :src="delImg" width="48rpx" height="48rpx" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分组成员 -->
|
||||
<view class="section-header">分组成员</view>
|
||||
<view class="add-member" @click="addMember">
|
||||
<view class="add-circle">
|
||||
<up-icon name="plus" size="34" color="#bfbfbf" />
|
||||
</view>
|
||||
<text class="add-text">添加组患者</text>
|
||||
</view>
|
||||
|
||||
<view class="member-list">
|
||||
<view class="member-item" v-for="(m, idx) in members" :key="m.uuid || idx">
|
||||
<image class="avatar" :src="docUrl + (m.photo || '')" mode="aspectFill" />
|
||||
<text class="member-name">{{ m.realName || '未知' }}</text>
|
||||
<view class="remove-btn" @click="removeMember(idx)">
|
||||
<view class="remove-circle">
|
||||
<up-icon name="minus" color="#fff" size="28rpx" bold></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部删除按钮 -->
|
||||
<view class="bottom-danger">
|
||||
<button class="danger-btn" @click="deleteGroup">删除分组</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import navTo from '@/utils/navTo.js'
|
||||
import docUrl from '@/utils/docUrl.js'
|
||||
import delImg from "@/static/iv_delete.png"
|
||||
|
||||
const groupUuid = ref('')
|
||||
const groupName = ref('')
|
||||
const members = ref([])
|
||||
|
||||
onLoad((query) => {
|
||||
groupUuid.value = query?.uuid || ''
|
||||
// TODO: 根据 uuid 拉取分组详情
|
||||
// 预置示例
|
||||
groupName.value = '看看'
|
||||
members.value = [
|
||||
{ uuid: 'u1', realName: '测试', photo: '' }
|
||||
]
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 兜底读取从选择页写入的缓存
|
||||
try {
|
||||
const cached = uni.getStorageSync('patientsSelectedPayload')
|
||||
if (cached && Array.isArray(cached.list) && cached.list.length) {
|
||||
mergeSelected(cached.list)
|
||||
uni.removeStorageSync('patientsSelectedPayload')
|
||||
}
|
||||
} catch (e) {}
|
||||
})
|
||||
|
||||
const goBack = () => uni.navigateBack()
|
||||
const clearName = () => { groupName.value = '' }
|
||||
|
||||
const saveGroup = () => {
|
||||
// TODO: 调用保存接口: { uuid: groupUuid, name: groupName, members }
|
||||
uni.showToast({ title: '已保存', icon: 'success' })
|
||||
}
|
||||
const addMember = () => {
|
||||
// 跳转选择患者页并监听事件通道返回
|
||||
uni.navigateTo({
|
||||
url: '/pages_app/selectPatient/selectPatient',
|
||||
events: {
|
||||
onPatientsSelected: ({ ids, list }) => {
|
||||
if (Array.isArray(list)) mergeSelected(list)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const removeMember = (idx) => {
|
||||
members.value.splice(idx, 1)
|
||||
}
|
||||
const deleteGroup = () => {
|
||||
uni.showModal({
|
||||
title: '删除分组',
|
||||
content: '确定要删除该分组吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// TODO: 调用删除接口
|
||||
uni.showToast({ title: '已删除', icon: 'success' })
|
||||
setTimeout(() => goBack(), 700)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 将选择结果合并到成员列表,按 uuid 去重
|
||||
const mergeSelected = (selectedList) => {
|
||||
const existIds = new Set(members.value.map(m => m.uuid))
|
||||
selectedList.forEach(s => {
|
||||
if (!existIds.has(s.uuid)) {
|
||||
existIds.add(s.uuid)
|
||||
members.value.push({ uuid: s.uuid, realName: s.realName, photo: s.photo || '' })
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.group-edit-page{
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 160rpx;
|
||||
}
|
||||
.save-text{ color:#8B2316; font-size: 30rpx; }
|
||||
|
||||
.section-header{
|
||||
background:#d9d9d9;
|
||||
color:#333;
|
||||
padding: 22rpx 30rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.name-row{
|
||||
background:#fff;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
.name-input{
|
||||
flex:1;
|
||||
font-size: 32rpx;
|
||||
color:#333;
|
||||
}
|
||||
.icon-btn{ padding-left: 20rpx; }
|
||||
}
|
||||
|
||||
.add-member{
|
||||
background:#fff;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:20rpx;
|
||||
padding: 26rpx 30rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
.add-circle{
|
||||
width: 96rpx; height: 96rpx; border-radius: 50%;
|
||||
border: 4rpx solid #e5e5e5;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
background:#fff;
|
||||
}
|
||||
.add-text{ font-size: 32rpx; color:#666; }
|
||||
}
|
||||
|
||||
.member-list{
|
||||
background:#fff;
|
||||
.member-item{
|
||||
display:flex; align-items:center; justify-content:space-between;
|
||||
padding: 26rpx 30rpx; border-bottom: 1rpx solid #eee;
|
||||
.avatar{ width: 100rpx; height: 100rpx; border-radius: 16rpx; background:#ffe; }
|
||||
.member-name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; }
|
||||
.remove-btn{ padding-left: 20rpx; }
|
||||
.remove-circle{ width: 48rpx; height:48rpx; background:#8B2316; border-radius:50%; display:flex; align-items:center; justify-content:center; }
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-danger{
|
||||
position: fixed; left:30rpx; right:30rpx; bottom: 30rpx;
|
||||
background:#fff; border-top: 1rpx solid #eee;
|
||||
.danger-btn{ width:100%; height: 96rpx; background:#8B2316; color:#fff; border:none; border-radius: 12rpx; font-size: 32rpx;display: flex; align-items: center; justify-content: center; }
|
||||
}
|
||||
</style>
|
||||
@ -24,20 +24,20 @@
|
||||
</view>
|
||||
|
||||
<!-- 随访申请 -->
|
||||
<view class="follow-up-section">
|
||||
<view class="follow-up-section" v-if="applyList.length > 0">
|
||||
<view class="section-title">随访申请</view>
|
||||
<view class="pending-request" v-if="pendingRequest">
|
||||
<view class="pending-request" v-for="(item, index) in applyList" :key="index">
|
||||
<view class="request-item">
|
||||
<view class="avatar">
|
||||
<view class="avatar-icon"></view>
|
||||
<up-image :src="docUrl+item.photo" radius="10rpx" width="80rpx" height="80rpx" ></up-image>
|
||||
</view>
|
||||
<view class="request-content">
|
||||
<view class="request-time">2025-08-18 15:55:03</view>
|
||||
<view class="request-text">我是陈新华,在线上和您沟通过,请您同意我作为您的随访患者</view>
|
||||
<view class="request-time">{{ item.createDate }}</view>
|
||||
<view class="request-text">{{ item.content }}</view>
|
||||
|
||||
<view class="action-buttons">
|
||||
<button class="reject-btn" @click="rejectRequest">拒绝</button>
|
||||
<button class="agree-btn" @click="agreeRequest">同意</button>
|
||||
<button class="reject-btn" @click="applyListOperate(item.uuid,3)">拒绝</button>
|
||||
<button class="agree-btn" @click="applyListOperate(item.uuid,2)">同意</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -45,22 +45,22 @@
|
||||
</view>
|
||||
|
||||
<!-- 申请记录 -->
|
||||
<view class="history-section">
|
||||
<view class="history-section" v-if="historyList.length > 0">
|
||||
<view class="section-title">申请记录(近一月)</view>
|
||||
<view class="history-list">
|
||||
<view class="history-item" v-for="(item, index) in historyList" :key="index">
|
||||
<view class="avatar">
|
||||
<view class="avatar-icon"></view>
|
||||
<up-image v-if="docUrl+item.patient_photo" :src="docUrl + item.patient_photo" radius="10rpx" width="80rpx" height="80rpx"></up-image>
|
||||
|
||||
</view>
|
||||
<view class="history-content">
|
||||
<view class="history-time">{{ item.time }}</view>
|
||||
<view class="nickname">昵称: {{ item.nickname }}</view>
|
||||
<view class="history-text">{{ item.message }}</view>
|
||||
<view class="history-time">{{ formatDate(item.createDate) }}</view>
|
||||
<view class="nickname">{{ item.nickname || item.patient_name }}</view>
|
||||
<view class="history-text">{{ item.content}}</view>
|
||||
<view class="status-info">
|
||||
<up-image :src="goImg" width="30rpx" height="30rpx" ></up-image>
|
||||
<text class="status-text">已同意</text>
|
||||
<up-image :src="goImg" width="30rpx" height="30rpx" v-if="item.status==2"></up-image>
|
||||
<text class="status-text">{{ getStatusText(item.status) }}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -75,36 +75,19 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onLoad,onShow} from '@dcloudio/uni-app';
|
||||
import goImg from "@/static/go_big.png"
|
||||
import api from '@/api/api.js'
|
||||
import docUrl from "@/utils/docUrl"
|
||||
import navTo from "@/utils/navTo.js"
|
||||
// 响应式数据
|
||||
const pendingRequest = ref({
|
||||
name: '陈新华',
|
||||
message: '我是陈新华,在线上和您沟通过,请您同意我作为您的随访患者',
|
||||
time: '2025-08-18 15:55:03'
|
||||
});
|
||||
|
||||
const historyList = ref([
|
||||
{
|
||||
nickname: '韩夫臣',
|
||||
message: '我是韩夫臣,在线上和您沟通过,请您...',
|
||||
time: '2025-08-19 22:41:35'
|
||||
},
|
||||
{
|
||||
nickname: '鲁保山',
|
||||
message: '我是鲁保山,在线上和您沟通过,请您...',
|
||||
time: '2025-08-19 22:41:27'
|
||||
},
|
||||
{
|
||||
nickname: '蒋宁宁',
|
||||
message: '我是蒋宁宁,在线上和您沟通过,请您...',
|
||||
time: '2025-08-19 11:25:46'
|
||||
},
|
||||
{
|
||||
nickname: '蒋宁',
|
||||
message: '我是蒋宁,在线上和您沟通过,请您同...',
|
||||
time: '2025-08-19 11:25:39'
|
||||
}
|
||||
]);
|
||||
const applyList = ref([]);
|
||||
const historyList = ref([]);
|
||||
|
||||
// 方法
|
||||
const goBack = () => {
|
||||
@ -142,11 +125,90 @@ const agreeRequest = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
const getRelationRecordLately = async () => {
|
||||
const res = await api.relationRecordLately({
|
||||
page:1,
|
||||
pageSize:100
|
||||
});
|
||||
console.log('随访记录API响应:', res);
|
||||
if(res.code === 200){
|
||||
historyList.value = res.data.list;
|
||||
}
|
||||
};
|
||||
const getApplyList = async () => { // 申请列表
|
||||
|
||||
try {
|
||||
|
||||
let userInfo=uni.getStorageSync('userInfo')
|
||||
const res = await api.applyList();
|
||||
console.log('申请列表API响应:', res);
|
||||
|
||||
if (res && res.code === 200) {
|
||||
applyList.value = res.data;
|
||||
} else {
|
||||
console.log('申请列表API响应异常:', res);
|
||||
uni.showToast({
|
||||
title: res.message || '获取申请列表失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取申请列表失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
const applyListOperate = async (uuid,status) => {
|
||||
let data = {
|
||||
uuid: uuid,
|
||||
status: status
|
||||
}
|
||||
const res = await api.applyListOperate(data);
|
||||
if(res.code === 200){
|
||||
uni.showToast({
|
||||
title: '操作成功',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
getApplyList();
|
||||
getRelationRecordLately();
|
||||
}
|
||||
};
|
||||
onShow(() => {
|
||||
getApplyList();
|
||||
getRelationRecordLately();
|
||||
|
||||
});
|
||||
|
||||
// 辅助方法
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
switch (status) {
|
||||
case 1: return '待审核';
|
||||
case 2: return '已同意';
|
||||
case 3: return '已拒绝';
|
||||
}
|
||||
};
|
||||
|
||||
const addPatient = () => {
|
||||
uni.showToast({
|
||||
title: '跳转到添加患者页面',
|
||||
icon: 'none'
|
||||
navTo({
|
||||
url:'/pages_app/myCode/myCode'
|
||||
});
|
||||
// 这里可以跳转到添加患者页面
|
||||
};
|
||||
@ -378,7 +440,7 @@ const addPatient = () => {
|
||||
}
|
||||
|
||||
.history-list {
|
||||
padding-bottom: 100rpx;
|
||||
margin-bottom: 100rpx;
|
||||
.history-item {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
backgroundColor="#eeeeee"
|
||||
>
|
||||
<template #right>
|
||||
<view class="nav-right" @click="editPatient">
|
||||
<view class="nav-right" @click.stop="editPatient">
|
||||
<uni-icons type="compose" size="22" color="#8B2316"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -17,14 +17,14 @@
|
||||
<!-- 筛选排序栏 -->
|
||||
<view class="filter-sort-bar">
|
||||
<view class="sort-section" @click="toggleGroupSort">
|
||||
<text class="sort-label">分组排序</text>
|
||||
<text class="sort-label">{{ groupSortTitle }}</text>
|
||||
<view class="imgbox">
|
||||
<up-image :src="upImg" width="26rpx" height="26rpx" ></up-image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="current-sort" @click="toggleInnerSort">
|
||||
<text class="sort-text">按首字母</text>
|
||||
<text class="sort-text">{{ innerSortTitle }}</text>
|
||||
<view class="imgbox">
|
||||
<up-image :src="upImg" width="26rpx" height="26rpx" ></up-image>
|
||||
</view>
|
||||
@ -33,12 +33,12 @@
|
||||
|
||||
<!-- 分组排序弹窗 -->
|
||||
<view v-if="showGroupSort" class="popup-panel">
|
||||
<view class="popup-item" :class="{ active: selectedGroupSort==='letter' }" @click.stop="chooseGroupSort('letter')">
|
||||
<view class="popup-item" :class="{ active: group_sort==0 }" @click.stop="chooseGroupSort('letter')">
|
||||
<text class="item-text">按首字母</text>
|
||||
<uni-icons v-if="selectedGroupSort==='letter'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||
</view>
|
||||
<view class="popup-divider"></view>
|
||||
<view class="popup-item" :class="{ active: selectedGroupSort==='count' }" @click.stop="chooseGroupSort('count')">
|
||||
<view class="popup-item" :class="{ active:group_sort==1}" @click.stop="chooseGroupSort('count')">
|
||||
<text class="item-text">分组人数</text>
|
||||
<uni-icons v-if="selectedGroupSort==='count'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||
</view>
|
||||
@ -47,13 +47,13 @@
|
||||
|
||||
<!-- 组内排序弹窗 -->
|
||||
<view v-if="showInnerSort" class="popup-panel">
|
||||
<view class="popup-item" :class="{ active: selectedInnerSort==='letter' }" @click.stop="chooseInnerSort('letter')">
|
||||
<view class="popup-item" :class="{ active:list_sort==0 }" @click.stop="chooseInnerSort('letter')">
|
||||
<text class="item-text">按首字母</text>
|
||||
<uni-icons v-if="selectedInnerSort==='letter'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||
</view>
|
||||
<view class="popup-divider"></view>
|
||||
<view class="popup-item" :class="{ active: selectedInnerSort==='count' }" @click.stop="chooseInnerSort('count')">
|
||||
<text class="item-text">分组人数</text>
|
||||
<view class="popup-item" :class="{ active:list_sort==1 }" @click.stop="chooseInnerSort('count')">
|
||||
<text class="item-text">随访时间</text>
|
||||
<uni-icons v-if="selectedInnerSort==='count'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@ -61,22 +61,29 @@
|
||||
|
||||
<!-- 患者列表 -->
|
||||
<scroll-view class="patient-list-section" scroll-y="true" :style="{ height: scrollViewHeight }">
|
||||
<view class="groupcell">
|
||||
<view class="section-title">
|
||||
|
||||
<view class="imgbox">
|
||||
<up-image :src="groupRightImg" width="19rpx" height="32rpx" ></up-image>
|
||||
<!-- 分组循环渲染 -->
|
||||
<view class="groupcell" v-for="(group, gi) in groups" :key="group.uuid || gi">
|
||||
<view class="section-title" @click="toggleGroup(gi)">
|
||||
<view class="left">
|
||||
<view class="imgbox">
|
||||
<up-image v-if="!openGroups[gi]" :src="groupRightImg" width="19rpx" height="32rpx" ></up-image>
|
||||
<up-image v-else :src="groupDownImg" width="32rpx" height="19rpx" ></up-image>
|
||||
</view>
|
||||
<view class="title">{{ group.name || '未命名分组' }} | {{ group.patientNum || (group.patientList ? group.patientList.length : 0) }}</view>
|
||||
</view>
|
||||
<view class="right-edit" @click.stop="editGroup(group)">
|
||||
|
||||
<text class="edit-text">编辑</text>
|
||||
</view>
|
||||
<view class="title">待分组患者 | 5</view>
|
||||
</view>
|
||||
<view class="patient-list" >
|
||||
<view class="patient-item" v-for="(patient, index) in patientList" :key="index">
|
||||
<view class="patient-list" v-if="openGroups[gi]">
|
||||
<view class="patient-item" v-for="(patient, index) in (group.patientList || [])" :key="patient.uuid || index">
|
||||
<view class="patient-avatar">
|
||||
<up-image :src="patient.avatar" width="80rpx" height="80rpx" mode="aspectFill"></up-image>
|
||||
<up-image :src="docUrl + patient.photo" width="80rpx" height="80rpx" mode="aspectFill"></up-image>
|
||||
</view>
|
||||
<view class="patient-info">
|
||||
<view class="patient-name">{{ patient.name }}</view>
|
||||
<view class="follow-up-time">随访于{{ patient.lastFollowUp }}</view>
|
||||
<view class="patient-name">{{ patient.realName || patient.nickname || '-' }}</view>
|
||||
<view class="follow-up-time">随访于{{ formatYMD(patient.join_date) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -87,53 +94,59 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import upImg from "@/static/triangle_green_theme.png"
|
||||
import downImg from "@/static/triangle_normal.png"
|
||||
import groupRightImg from "@/static/groupright_big.png"
|
||||
import groupDownImg from "@/static/groupup_big.png"
|
||||
// 响应式数据
|
||||
const patientList = ref([
|
||||
{
|
||||
name: 'aa',
|
||||
lastFollowUp: '2020-12-02',
|
||||
avatar: '/static/avatar1.png' // 云朵天空头像
|
||||
},
|
||||
{
|
||||
name: '测试',
|
||||
lastFollowUp: '2023-10-08',
|
||||
avatar: '/static/avatar2.png' // 粉色背景白色人像
|
||||
},
|
||||
{
|
||||
name: '刘三多',
|
||||
lastFollowUp: '2021-12-16',
|
||||
avatar: '/static/avatar3.png' // 绿色山水风景
|
||||
},
|
||||
{
|
||||
name: '路测试',
|
||||
lastFollowUp: '2019-10-28',
|
||||
avatar: '/static/avatar4.png' // 粉色背景卡通人像
|
||||
},
|
||||
{
|
||||
name: '哦哦哦',
|
||||
lastFollowUp: '2023-10-08',
|
||||
avatar: '/static/avatar5.png' // 粉色背景白色人像
|
||||
}
|
||||
]);
|
||||
import api from '@/api/api.js';
|
||||
import docUrl from '@/utils/docUrl'
|
||||
import dayjs from 'dayjs'
|
||||
const list_sort = ref(0);
|
||||
const group_sort = ref(0);
|
||||
|
||||
// 计算滚动视图高度
|
||||
const scrollViewHeight = computed(() => {
|
||||
// 获取系统信息计算可用高度
|
||||
// 导航栏高度(140rpx) + 筛选栏高度(约80rpx) + 标题高度(约80rpx) = 300rpx
|
||||
// 转换为px并减去,得到可用高度
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const windowHeight = systemInfo.windowHeight;
|
||||
const navHeight = 140 / 2; // rpx转px
|
||||
const filterHeight = 80 / 2;
|
||||
const titleHeight = 80 / 2;
|
||||
const availableHeight = windowHeight - navHeight - filterHeight - titleHeight;
|
||||
return `${availableHeight}px`;
|
||||
// 分组数据与展开状态
|
||||
const groups = ref([]);
|
||||
const openGroups = ref({});
|
||||
|
||||
const formatYMD = (val) => {
|
||||
if (!val) return '';
|
||||
const d = dayjs(val);
|
||||
return d.isValid() ? d.format('YYYY-MM-DD') : '';
|
||||
};
|
||||
|
||||
// 排序栏标题
|
||||
const groupSortTitle = computed(() => group_sort.value === 0 ? '按首字母' : '分组人数');
|
||||
const innerSortTitle = computed(() => list_sort.value === 0 ? '按首字母' : '随访时间');
|
||||
|
||||
onShow(() => {
|
||||
fetchGroupList();
|
||||
});
|
||||
|
||||
const fetchGroupList = async () => {
|
||||
const res = await api.groupList({
|
||||
list_sort:list_sort.value,
|
||||
page:1,
|
||||
pageSize:10,
|
||||
group_sort:group_sort.value
|
||||
});
|
||||
if(res.code === 200){
|
||||
groups.value = Array.isArray(res.data) ? res.data : [];
|
||||
// 初始化展开状态:默认收起
|
||||
openGroups.value = {};
|
||||
groups.value.forEach((_, idx) => openGroups.value[idx] = false);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleGroup = (idx) => {
|
||||
openGroups.value[idx] = !openGroups.value[idx];
|
||||
};
|
||||
|
||||
const editGroup = (group) => {
|
||||
// TODO: 跳转到分组编辑页或弹窗编辑
|
||||
uni.showToast({ title: `编辑:${group.name || '未命名分组'}`, icon: 'none' });
|
||||
};
|
||||
|
||||
// 分组排序弹窗状态
|
||||
const showGroupSort = ref(false);
|
||||
const selectedGroupSort = ref('letter'); // letter | count
|
||||
@ -142,18 +155,23 @@ const selectedGroupSort = ref('letter'); // letter | count
|
||||
const showInnerSort = ref(false);
|
||||
const selectedInnerSort = ref('letter');
|
||||
|
||||
// 待分组展开/收起
|
||||
const showPending = ref(false);
|
||||
|
||||
const toggleGroupSort = () => {
|
||||
showGroupSort.value = !showGroupSort.value;
|
||||
if (showGroupSort.value) showInnerSort.value = false;
|
||||
};
|
||||
|
||||
const closeGroupSort = () => {
|
||||
showGroupSort.value = false;
|
||||
};
|
||||
|
||||
const chooseGroupSort = (type) => {
|
||||
selectedGroupSort.value = type;
|
||||
closeGroupSort();
|
||||
selectedGroupSort.value = type;
|
||||
// 修改接口字段:按首字母=0,分组人数=1
|
||||
group_sort.value = type === 'letter' ? 0 : 1;
|
||||
closeGroupSort();
|
||||
fetchGroupList();
|
||||
};
|
||||
|
||||
const toggleInnerSort = () => {
|
||||
@ -166,8 +184,11 @@ const closeInnerSort = () => {
|
||||
};
|
||||
|
||||
const chooseInnerSort = (type) => {
|
||||
selectedInnerSort.value = type;
|
||||
closeInnerSort();
|
||||
selectedInnerSort.value = type;
|
||||
// 组内排序:按首字母=0,随访时间=1
|
||||
list_sort.value = type === 'letter' ? 0 : 1;
|
||||
closeInnerSort();
|
||||
fetchGroupList();
|
||||
};
|
||||
|
||||
// 方法
|
||||
@ -182,6 +203,7 @@ const createNew = () => {
|
||||
});
|
||||
// 这里可以跳转到新建分组页面
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -356,6 +378,7 @@ const createNew = () => {
|
||||
z-index: 9;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
.imgbox{
|
||||
width: 32rpx;
|
||||
margin-top: -10rpx;
|
||||
}
|
||||
.sort-section {
|
||||
@ -418,14 +441,28 @@ const createNew = () => {
|
||||
.section-title {
|
||||
padding: 30rpx 30rpx 20rpx;
|
||||
font-size: 32rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-weight: normal;
|
||||
display: flex;
|
||||
color: #333;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
}
|
||||
.imgbox{
|
||||
margin-top: 4rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.right-edit{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 8rpx;
|
||||
.edit-text{ font-size: 28rpx; color:#8B2316; }
|
||||
}
|
||||
}
|
||||
|
||||
.patient-list {
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#ffffff"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
<template #right>
|
||||
<view class="nav-right">
|
||||
@ -20,31 +20,43 @@
|
||||
</uni-nav-bar>
|
||||
|
||||
<!-- 消息列表区域 -->
|
||||
<view class="message-list" v-if="activeTab === 'message'">
|
||||
<scroll-view
|
||||
class="message-list"
|
||||
v-if="activeTab === 'message'"
|
||||
scroll-y="true"
|
||||
refresher-enabled="true"
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
>
|
||||
<!-- 消息项 -->
|
||||
<view class="message-item" @click="openMessage">
|
||||
<view class="message-item" v-for="(item, index) in messageList" :key="item.id || index" @click="openMessage(item)">
|
||||
<view class="message-avatar">
|
||||
<view class="avatar-placeholder">
|
||||
<view class="avatar-placeholder" v-if="!item.avatar">
|
||||
<uni-icons type="person" size="32" color="#ffffff"></uni-icons>
|
||||
</view>
|
||||
<image v-else :src="item.avatar" class="patient-avatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="message-content">
|
||||
<view class="message-header">
|
||||
<text class="patient-name">测试</text>
|
||||
<text class="message-time">2025-08-11</text>
|
||||
<text class="patient-name">{{ item.patientName }}</text>
|
||||
<text class="message-time">{{ item.time }}</text>
|
||||
</view>
|
||||
<view class="message-preview">
|
||||
<text class="preview-text">[图片]</text>
|
||||
<text class="preview-text">{{ item.messagePreview }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态提示 -->
|
||||
<view class="empty-state" v-if="messageList.length === 0">
|
||||
<view class="empty-state" v-if="messageList.length === 0 && !isRefreshing">
|
||||
<uni-icons type="chat" size="80" color="#cccccc"></uni-icons>
|
||||
<text class="empty-text">暂无患者消息</text>
|
||||
<text class="empty-subtext">下拉刷新获取最新申请</text>
|
||||
<view class="debug-actions">
|
||||
<button class="debug-btn" @click="getApplyList">测试API调用</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 患者列表区域 -->
|
||||
<view class="patient-list" v-if="activeTab === 'list'">
|
||||
@ -55,7 +67,7 @@
|
||||
<uni-icons type="person" size="24" color="#ffffff"></uni-icons>
|
||||
<uni-icons type="plus" size="16" color="#ffffff" style="position: absolute; right: 8rpx; bottom: 8rpx;"></uni-icons>
|
||||
</view>
|
||||
<text class="action-text">新的患者</text>
|
||||
<text class="action-text">新的患者<text class="new-patient-count" v-if="applyList.length > 0">(待审核{{ applyList.length }}人)</text></text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
|
||||
@ -68,7 +80,7 @@
|
||||
<view class="grid-item"></view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="action-text">患者分组 (随访5人)</text>
|
||||
<text class="action-text">患者分组 <text class="new-patient-count" v-if="patientList.length > 0">(随访{{ patientList.length }}人)</text></text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@ -81,24 +93,24 @@
|
||||
<up-index-anchor :text="group.letter" />
|
||||
|
||||
<view class="group-section">
|
||||
<view class="patient-item" v-for="item in group.items" :key="item.name" @click="openPatient(item.name)">
|
||||
<view class="patient-item" v-for="item in group.items" :key="item.uuid || item.id" >
|
||||
<template v-if="item.placeholder">
|
||||
<view class="patient-avatar-placeholder">
|
||||
<uni-icons type="person" size="32" color="#ffffff"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<image class="patient-avatar" :src="item.avatar" mode="aspectFill"></image>
|
||||
<image class="patient-avatar" :src="docUrl+item.photo" mode="aspectFill"></image>
|
||||
</template>
|
||||
<view class="patient-info">
|
||||
<text class="patient-name">{{ item.name }}</text>
|
||||
<view class="patient-info" @click="goPatientDetail(item.uuid)">
|
||||
<text class="patient-name">{{ item.realName }}</text>
|
||||
<view class="patient-badge" v-if="item.badge">
|
||||
<text class="badge-text">{{ item.badge }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="patient-status">
|
||||
<uni-icons type="compose" size="20" color="#8B2316"></uni-icons>
|
||||
<text class="follow-date">随访于{{ item.date }}</text>
|
||||
<uni-icons type="compose" size="20" color="#8B2316" @click.stop="editPatient(item.uuid)"></uni-icons>
|
||||
<text class="follow-date">随访于{{ formatYMD(item.join_date) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -108,7 +120,15 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="plan" v-if="activeTab === 'plan'">
|
||||
<empty></empty>
|
||||
|
||||
<view class="apply-list" v-if="applyList.length > 0">
|
||||
<view class="apply-item" v-for="item in applyList" :key="item.id">
|
||||
<view class="apply-content">
|
||||
<text class="apply-text">{{ item.messagePreview }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<empty v-else></empty>
|
||||
|
||||
<!-- 悬浮添加按钮 -->
|
||||
<view class="floating-add-btn" @click="showAddMenu">
|
||||
@ -159,8 +179,30 @@
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import dayImg from "@/static/visit_data11.png"
|
||||
import planImg from "@/static/visitplan.png"
|
||||
|
||||
// 消息列表数据
|
||||
import api from '@/api/api.js';
|
||||
import navTo from '@/utils/navTo.js';
|
||||
import docUrl from '@/utils/docUrl.js';
|
||||
const patientList = ref([]);
|
||||
import pinyin from 'pinyin';
|
||||
import dayjs from 'dayjs'
|
||||
const goPatientDetail = (uuid) => {
|
||||
navTo({
|
||||
url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}`
|
||||
})
|
||||
}
|
||||
const editPatient = (uuid) => {
|
||||
console.log(uuid)
|
||||
navTo({
|
||||
url: `/pages_app/patientSetting/patientSetting?uuid=${uuid}`
|
||||
})
|
||||
}
|
||||
// 仅保留年月日
|
||||
const formatYMD = (input) => {
|
||||
if (!input) return '';
|
||||
const d = dayjs(input);
|
||||
return d.isValid() ? d.format('YYYY-MM-DD') : '';
|
||||
}
|
||||
// 申请列表数据
|
||||
const messageList = ref([
|
||||
{
|
||||
id: 1,
|
||||
@ -184,8 +226,9 @@
|
||||
// 组件实例(用于 selectorQuery 作用域)
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// up-index-list 数据源(与当前示例分组一致)
|
||||
const indexList = ref([]);
|
||||
// up-index-list 索引固定为 26 个大写字母
|
||||
// 索引固定为 A-Z
|
||||
const indexList = ref('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''));
|
||||
// 列表高度(像素):窗口高度 - 顶部导航(rpx转px) - 底部tab(约100rpx->px) - 操作区(约200rpx->px)
|
||||
const groupsListHeight = ref(0);
|
||||
const rpxToPx = (rpx) => {
|
||||
@ -201,59 +244,117 @@
|
||||
// 分组数据(可从接口返回后赋值)
|
||||
const patientGroups = ref([]);
|
||||
|
||||
// 动态生成索引字母
|
||||
// 重建索引时保持固定 A-Z
|
||||
const rebuildIndexList = () => {
|
||||
// 固定 A-Z,不随分组变化
|
||||
indexList.value = patientGroups.value.map(g => g.letter);
|
||||
};
|
||||
|
||||
// 生成模拟数据
|
||||
const generateMockDate = () => {
|
||||
const start = new Date(2019, 0, 1).getTime();
|
||||
const end = new Date().getTime();
|
||||
const d = new Date(start + Math.random() * (end - start));
|
||||
const y = d.getFullYear();
|
||||
const m = `${d.getMonth() + 1}`.padStart(2, '0');
|
||||
const day = `${d.getDate()}`.padStart(2, '0');
|
||||
return `${y}-${m}-${day}`;
|
||||
};
|
||||
|
||||
const generateMockGroups = () => {
|
||||
const lettersPool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
const avatarSamples = ['/static/avatar-a.png','/static/avatar-l.png','/static/avatar-l2.png'];
|
||||
const groups = [];
|
||||
lettersPool.forEach((L) => {
|
||||
// 随机产生0-3个患者
|
||||
const count = Math.floor(Math.random() * 3);
|
||||
if (count === 0) return;
|
||||
const items = Array.from({length: count}).map((_, i) => {
|
||||
const usePlaceholder = Math.random() < 0.4;
|
||||
return {
|
||||
name: `${L}患者${i+1}`,
|
||||
avatar: usePlaceholder ? '' : avatarSamples[Math.floor(Math.random()*avatarSamples.length)],
|
||||
placeholder: usePlaceholder,
|
||||
badge: Math.random() < 0.2 ? 'GOOD' : '',
|
||||
date: generateMockDate()
|
||||
};
|
||||
});
|
||||
groups.push({ letter: L, items });
|
||||
// 工具:获取首字母
|
||||
const generateMockDate = () => '';
|
||||
const getFirstLetter = (chineseName) => {
|
||||
// 假设名字由一个或多个汉字组成,我们取第一个汉字的首字母
|
||||
const firstChar = chineseName.charAt(0); // 获取第一个汉字
|
||||
const pinyinArray = pinyin(firstChar, { style: pinyin.STYLE_NORMAL }); // 获取拼音数组
|
||||
return pinyinArray[0][0].charAt(0); // 返回拼音的第一个字母
|
||||
}
|
||||
// 根据 patientList 构建按拼音首字母的分组
|
||||
const buildGroupsFromPatients = () => {
|
||||
const map = new Map();
|
||||
patientList.value.forEach((p) => {
|
||||
const name = p.realName;
|
||||
const first = getFirstLetter(name).toUpperCase();
|
||||
const letter = /^[A-Z]$/.test(first) ? first : '#';
|
||||
if (!map.has(letter)) map.set(letter, []);
|
||||
map.get(letter).push(p);
|
||||
});
|
||||
return groups;
|
||||
};
|
||||
|
||||
// 加载模拟数据
|
||||
const loadMockGroups = () => {
|
||||
uni.showLoading({ title: '加载中' });
|
||||
setTimeout(() => {
|
||||
patientGroups.value = generateMockGroups();
|
||||
rebuildIndexList();
|
||||
uni.hideLoading();
|
||||
}, 300);
|
||||
const letters = Array.from(map.keys()).sort((a,b) => a.localeCompare(b));
|
||||
patientGroups.value = letters.map(l => ({ letter: l, items: map.get(l) }));
|
||||
rebuildIndexList();
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
// 申请列表数据
|
||||
const applyList = ref([]);
|
||||
|
||||
// 刷新状态
|
||||
const isRefreshing = ref(false);
|
||||
|
||||
const getApplyList = async () => { // 申请列表
|
||||
|
||||
try {
|
||||
|
||||
let userInfo=uni.getStorageSync('userInfo')
|
||||
const res = await api.applyList();
|
||||
|
||||
if (res && res.code === 200) {
|
||||
applyList.value = res.data;
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.message || '获取申请列表失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取申请列表失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const patientListByGBK = async () => {
|
||||
|
||||
const res = await api.patientListByGBK();
|
||||
if(res.code == 1){
|
||||
patientList.value = res.data;
|
||||
|
||||
buildGroupsFromPatients()
|
||||
}
|
||||
|
||||
};
|
||||
const page=ref(1)
|
||||
const followUpList = async () => {
|
||||
let userInfo=uni.getStorageSync('userInfo')
|
||||
const res = await api.followUpList({
|
||||
page:page.value,
|
||||
pageSize:10
|
||||
});
|
||||
if(res.code === '1'){
|
||||
followUpList.value = res.data;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = async () => {
|
||||
isRefreshing.value = true;
|
||||
|
||||
try {
|
||||
await getApplyList();
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('刷新失败:', error);
|
||||
uni.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'error',
|
||||
duration: 1500
|
||||
});
|
||||
} finally {
|
||||
isRefreshing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索患者
|
||||
const searchPatients = () => {
|
||||
@ -272,11 +373,15 @@
|
||||
};
|
||||
|
||||
// 打开消息
|
||||
const openMessage = () => {
|
||||
const openMessage = (item) => {
|
||||
uni.showToast({
|
||||
title: '打开消息',
|
||||
title: `打开患者 ${item?.patientName || '未知'} 的消息`,
|
||||
icon: 'none'
|
||||
});
|
||||
// 这里可以跳转到消息详情页面
|
||||
// uni.navigateTo({
|
||||
// url: `/pages_app/messageDetail/messageDetail?id=${item?.id}`
|
||||
// });
|
||||
};
|
||||
|
||||
// 切换标签
|
||||
@ -285,7 +390,8 @@
|
||||
|
||||
switch(tab) {
|
||||
case 'message':
|
||||
// 患者消息页面逻辑
|
||||
// 患者消息页面逻辑 - 刷新申请列表
|
||||
getApplyList();
|
||||
break;
|
||||
case 'list':
|
||||
// 显示患者列表
|
||||
@ -302,18 +408,16 @@
|
||||
|
||||
// 添加新患者
|
||||
const addNewPatient = () => {
|
||||
uni.showToast({
|
||||
title: '添加新患者',
|
||||
icon: 'none'
|
||||
});
|
||||
navTo({
|
||||
url: '/pages_app/myPatient/myPatient'
|
||||
})
|
||||
};
|
||||
|
||||
// 管理患者分组
|
||||
const managePatientGroups = () => {
|
||||
uni.showToast({
|
||||
title: '管理患者分组',
|
||||
icon: 'none'
|
||||
});
|
||||
navTo({
|
||||
url: '/pages_app/patientGroup/patientGroup'
|
||||
})
|
||||
};
|
||||
|
||||
// 打开患者详情
|
||||
@ -354,14 +458,15 @@
|
||||
// 页面显示时加载数据
|
||||
onShow(() => {
|
||||
loadMessageList();
|
||||
loadMockGroups();
|
||||
computeListHeight();
|
||||
getApplyList();
|
||||
patientListByGBK();
|
||||
followUpList();
|
||||
});
|
||||
|
||||
// 加载消息列表
|
||||
const loadMessageList = () => {
|
||||
// 这里可以调用API获取消息列表
|
||||
console.log('加载患者消息列表');
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -446,13 +551,32 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
padding: 100rpx 30rpx;
|
||||
|
||||
.empty-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.debug-actions {
|
||||
margin-top: 30rpx;
|
||||
|
||||
.debug-btn {
|
||||
background-color: #8B2316;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,6 +634,7 @@
|
||||
|
||||
// 患者列表样式
|
||||
.patient-list {
|
||||
|
||||
height:calc(100vh - 265rpx);
|
||||
|
||||
display: flex;
|
||||
@ -576,6 +701,11 @@
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
.new-patient-count {
|
||||
color: red;
|
||||
font-size: 32rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -670,7 +800,8 @@
|
||||
|
||||
.patient-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
|
||||
.follow-date {
|
||||
font-size: 24rpx;
|
||||
|
||||
112
pages_app/patientRemark/patientRemark.vue
Normal file
112
pages_app/patientRemark/patientRemark.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<view class="remark-page">
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="设置备注和分组"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eee"
|
||||
/>
|
||||
<view class="form-block">
|
||||
<view class="label">备注</view>
|
||||
<input class="input" v-model.trim="remark" placeholder="给患者添加备注名" placeholder-class="ph" maxlength="20"/>
|
||||
</view>
|
||||
<view class="form-block">
|
||||
<view class="label">分组</view>
|
||||
<view class="iptbox">
|
||||
<input class="input" v-model.trim="remark" placeholder="通过分组给患者分类" placeholder-class="ph" maxlength="20" readonly/>
|
||||
<uni-icons type="right" size="20" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-block">
|
||||
<view class="label">描述</view>
|
||||
<textarea class="textarea" v-model.trim="note" placeholder="补充患者关键信息,方便随访患者" placeholder-class="ph" auto-height maxlength="140"/>
|
||||
</view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<button class="save-btn" @click="saveRemark">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const remark = ref('')
|
||||
const note = ref('')
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const saveRemark = () => {
|
||||
if (!remark.value) {
|
||||
uni.showToast({ title: '请输入备注名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// TODO: 调用保存接口
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
setTimeout(() => goBack(), 700)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-page{
|
||||
min-height: 100vh;
|
||||
background: #f7f7f7;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
.form-block{
|
||||
.iptbox{
|
||||
background: #f8f8f8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
background: #fff;
|
||||
padding: 24rpx 30rpx 30rpx;
|
||||
.label{
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.input{
|
||||
flex:1;
|
||||
background: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
.textarea{
|
||||
background: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
min-height: 180rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.ph{
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
.bottom-bar{
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
padding: 20rpx 24rpx env(safe-area-inset-bottom);
|
||||
.save-btn{
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
background: #8B2316;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
136
pages_app/patientSetting/patientSetting.vue
Normal file
136
pages_app/patientSetting/patientSetting.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<view class="setting-page">
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="常用设置"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
|
||||
</uni-nav-bar>
|
||||
|
||||
<view class="list-block">
|
||||
<view class="cell" @click="goRemark">
|
||||
<text class="cell-left">设置备注</text>
|
||||
<view class="cell-right">
|
||||
<text class="cell-desc">给患者添加备注名</text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cell" @click="goRemark">
|
||||
<text class="cell-left">设置分组</text>
|
||||
<view class="cell-right">
|
||||
<text class="cell-desc">通过分组给患者分类</text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cell" @click="goRemark">
|
||||
<text class="cell-left">患者描述</text>
|
||||
<view class="cell-right">
|
||||
<text class="cell-desc">补充患者关键信息,方便随访患者</text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cell" @click="setNextFollow">
|
||||
<text class="cell-left">下次随访时间</text>
|
||||
<view class="cell-right">
|
||||
<text class="cell-desc">添加随访提醒</text>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cell" @click="goFeedback">
|
||||
<text class="cell-left">投诉反馈</text>
|
||||
<view class="cell-right">
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="danger-block">
|
||||
<text class="danger-text" @click="unbindPatient">解除随访</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import navTo from '@/utils/navTo.js'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
}
|
||||
const goRemark = () => {
|
||||
navTo({ url: '/pages_app/patientRemark/patientRemark' })
|
||||
}
|
||||
|
||||
const setNextFollow = () => {
|
||||
navTo({ url: '/pages_app/visit/visit' })
|
||||
}
|
||||
const goFeedback = () => {
|
||||
navTo({ url: '/pages_app/feedback/feedback' })
|
||||
}
|
||||
const unbindPatient = () => {
|
||||
uni.showModal({
|
||||
title: '确认解除',
|
||||
content: '确定要解除随访关系吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '已提交解除', icon: 'success' })
|
||||
// TODO: 调用解除随访接口
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-page{
|
||||
min-height: 100vh;
|
||||
background:#f7f7f7;
|
||||
}
|
||||
.list-block{
|
||||
margin-top: 20rpx;
|
||||
background: #fff;
|
||||
.cell{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
&:last-child{ border-bottom: none; }
|
||||
.cell-left{
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
.cell-right{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
.cell-desc{
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.danger-block{
|
||||
margin-top: 30rpx;
|
||||
background: #fff;
|
||||
padding: 40rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.align-center{ align-items: center; }
|
||||
.danger-text{
|
||||
color: #8B2316;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -9,14 +9,20 @@
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
></uni-nav-bar>
|
||||
>
|
||||
<template v-slot:right>
|
||||
<view class="nav-right" @click="testLoadMore">
|
||||
<text style="font-size: 24rpx; color: #8B2316;">测试加载</text>
|
||||
</view>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<view class="courseware-container">
|
||||
|
||||
<!-- 排序和筛选栏 -->
|
||||
<view class="filter-bar">
|
||||
<view class="filter-item" @click="toggleSort">
|
||||
<text class="filter-text">{{sort==2?'最新':'最热'}}</text>
|
||||
<text class="filter-text">{{sort==0?'最新':'最热'}}</text>
|
||||
<view class="newbox">
|
||||
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
|
||||
</view>
|
||||
@ -47,17 +53,24 @@
|
||||
<view class="pdf-icon">📄</view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-author">{{ item.author }}</view>
|
||||
<view class="item-title">{{item.title}}</view>
|
||||
<view class="info">
|
||||
<view >{{ item.providername }}</view>
|
||||
<view class="item-author">{{ item.hospitalname }}</view>
|
||||
</view>
|
||||
<view class="item-stats">
|
||||
<view class="views">
|
||||
<uni-icons type="eye" size="30rpx" color="#999"></uni-icons>
|
||||
<text class="view-count">{{ item.views }}人阅读</text>
|
||||
<text class="view-count">{{ item.readnum }}人阅读</text>
|
||||
</view>
|
||||
|
||||
<view class="price">
|
||||
<up-image :src="downLoadImg" width="32rpx" height="32rpx" ></up-image>
|
||||
<text class="price-value">{{ item.price }}</text>
|
||||
<view class="priceImg">
|
||||
<up-image :src="downLoadImg" width="32rpx" height="32rpx" ></up-image>
|
||||
</view>
|
||||
<text class="price-value" v-if="item.price>0"><text class="money-unit">¥</text>{{ item.price>item.discount?fromatPrice(item.discount):fromatPrice(item.price) }}</text>
|
||||
<text class="yuanjia" v-if="item.price>0 && item.price>item.discount">原价<text class="jiaprice">{{fromatPrice(item.price/100)}}</text></text>
|
||||
<text v-else class="free">免费</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -75,7 +88,7 @@
|
||||
:class="{ active: tag.selected }"
|
||||
@click="toggleTag(index)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
{{ tag.DES}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -106,7 +119,7 @@
|
||||
import downLoadImg from "@/static/wdxz.png"
|
||||
import api from '@/api/api.js';
|
||||
const isFilterActive=ref(false)
|
||||
const sort = ref(2);
|
||||
const sort = ref(0); // 默认最新排序
|
||||
// 响应式数据
|
||||
const refreshing = ref(false);
|
||||
const loading = ref(false);
|
||||
@ -136,7 +149,10 @@
|
||||
};
|
||||
|
||||
const toggleSort = () => {
|
||||
sort.value=sort.value==1?2:1
|
||||
sort.value = sort.value === 0 ? 1 : 0;
|
||||
console.log('切换排序:', sort.value);
|
||||
// 切换排序后重新加载数据
|
||||
loadData(true);
|
||||
};
|
||||
|
||||
|
||||
@ -154,7 +170,24 @@
|
||||
icon: 'none'
|
||||
});
|
||||
};
|
||||
|
||||
const loadGuideTags = async () => {
|
||||
try {
|
||||
const res = await api.guideTag({
|
||||
type:6
|
||||
});
|
||||
console.log('指南标签API响应:', res);
|
||||
if(res && res.code === 200 && res.data) {
|
||||
// 将API返回的标签数据转换为筛选标签格式
|
||||
filterTags.value = res.data.map(tag => ({
|
||||
...tag,
|
||||
selected: false
|
||||
}));
|
||||
console.log('指南标签加载成功:', filterTags.value);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载指南标签失败:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = async () => {
|
||||
@ -184,22 +217,24 @@
|
||||
|
||||
// 上拉加载更多
|
||||
const onLoadMore = async () => {
|
||||
console.log('加载更多')
|
||||
if (loading.value || noMore.value) return;
|
||||
console.log('=== onLoadMore 触发 ===');
|
||||
console.log('当前状态:', {
|
||||
loading: loading.value,
|
||||
noMore: noMore.value,
|
||||
page: page.value,
|
||||
listLength: coursewareList.value.length
|
||||
});
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
// 加载下一页数据
|
||||
await loadData(false);
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'error'
|
||||
if (loading.value || noMore.value) {
|
||||
console.log('条件不满足,跳过加载:', {
|
||||
loading: loading.value,
|
||||
noMore: noMore.value
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('条件满足,开始加载更多数据');
|
||||
await loadData(false);
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
@ -219,7 +254,6 @@
|
||||
// 调用API获取课件列表
|
||||
const res = await api.ganDanFileByKeyWords({
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
sort: sort.value,
|
||||
// 可以根据需要添加其他筛选参数
|
||||
keywords: '',
|
||||
@ -237,7 +271,7 @@
|
||||
// 解析API返回的数据结构
|
||||
if (res.data && res.data.list) {
|
||||
newItems = res.data.list;
|
||||
totalCount = res.data.total || res.data.totalRow || 0;
|
||||
totalCount = res.data.totalRow;
|
||||
console.log('使用 res.data.list 结构');
|
||||
} else if (res.data && Array.isArray(res.data)) {
|
||||
newItems = res.data;
|
||||
@ -265,8 +299,11 @@
|
||||
noMore.value = true;
|
||||
console.log('没有更多数据了');
|
||||
} else {
|
||||
page.value++;
|
||||
console.log(`还有更多数据,下一页: ${page.value}`);
|
||||
// 只有在加载更多模式下才递增页码
|
||||
if (!isRefresh) {
|
||||
page.value=page.value+1;
|
||||
console.log(`还有更多数据,下一页: ${page.value}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('API返回的数据不是数组格式:', newItems);
|
||||
@ -332,11 +369,40 @@
|
||||
hideFilterPopup();
|
||||
// 这里可以根据选中的标签进行数据筛选
|
||||
};
|
||||
const fromatPrice=(price)=>{
|
||||
if(price<1){
|
||||
return price.toFixed(2)
|
||||
}else{
|
||||
return price.toFixed(1)
|
||||
}
|
||||
}
|
||||
onShow(() => {
|
||||
// 页面显示时加载数据
|
||||
console.log('页面显示,开始加载课件数据');
|
||||
loadData(true);
|
||||
loadGuideTags()
|
||||
});
|
||||
|
||||
// 测试加载更多功能
|
||||
const testLoadMore = () => {
|
||||
console.log('=== 手动测试加载更多 ===');
|
||||
console.log('当前状态:', {
|
||||
loading: loading.value,
|
||||
noMore: noMore.value,
|
||||
page: page.value,
|
||||
listLength: coursewareList.value.length
|
||||
});
|
||||
|
||||
if (!loading.value && !noMore.value) {
|
||||
onLoadMore();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: `加载中: ${loading.value}, 无更多: ${noMore.value}`,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// 变量定义
|
||||
@ -438,15 +504,16 @@
|
||||
top:240rpx;
|
||||
flex: 1;
|
||||
bottom:0;
|
||||
width:100%;
|
||||
left:30rpx;
|
||||
right:30rpx;
|
||||
padding-top: 20rpx;
|
||||
margin: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
width:auto;
|
||||
.courseware-item {
|
||||
background-color: $white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
padding: 30rpx 0;
|
||||
margin-bottom: 20rpx;
|
||||
@include shadow;
|
||||
display: flex;
|
||||
@ -464,18 +531,23 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
|
||||
.item-title {
|
||||
.item-title{
|
||||
font-size: 32rpx;
|
||||
color: $text-primary;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
color:#333;
|
||||
}
|
||||
.info{
|
||||
display: flex;
|
||||
font-size: 26rpx;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
|
||||
.item-author {
|
||||
font-size: 28rpx;
|
||||
color: $text-secondary;
|
||||
line-height: 1.3;
|
||||
margin-left: 10rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200rpx; /* 限制最大宽度 */
|
||||
}
|
||||
|
||||
.item-stats {
|
||||
@ -518,10 +590,28 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.free{
|
||||
font-size: 26rpx;
|
||||
color:#999;
|
||||
}
|
||||
.yuanjia{
|
||||
font-size: 26rpx;
|
||||
margin-left: 10rpx;
|
||||
color:#999;
|
||||
.jiaprice{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
.priceImg{
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
.price-value {
|
||||
font-size: 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: $primary-color;
|
||||
font-weight: bold;
|
||||
|
||||
.money-unit{
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#ffffff"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
<template #right>
|
||||
<view class="nav-right" @click="submitSchedule">
|
||||
|
||||
130
pages_app/selectPatient/selectPatient.vue
Normal file
130
pages_app/selectPatient/selectPatient.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<view class="select-page">
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="选择患者"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
<template #right>
|
||||
<view class="confirm-btn" :class="{ active: selectedIds.length > 0 }" @click="confirmSelect">
|
||||
<text class="confirm-text">确定({{ selectedIds.length }})</text>
|
||||
</view>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-bar">
|
||||
<view class="input-wrap">
|
||||
<input class="search-input" v-model.trim="keyword" placeholder="搜索患者的备注名、昵称或手机号" placeholder-class="ph" @confirm="onSearch" />
|
||||
</view>
|
||||
<view class="search-btn" @click="onSearch">
|
||||
<uni-icons type="search" size="50rpx" color="#999" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<scroll-view class="list" scroll-y>
|
||||
<view class="item" @click="toggle(p.uuid)" v-for="p in patientList" :key="p.uuid">
|
||||
<image class="avatar" :src="docUrl + (p.photo || '')" mode="aspectFill" />
|
||||
<view class="name">{{ p.realName || '-' }}</view>
|
||||
<view class="check" >
|
||||
<view class="circle" :class="{ active: selectedIds.includes(p.uuid) }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import docUrl from '@/utils/docUrl.js'
|
||||
import { onShow,onLoad} from "@dcloudio/uni-app";
|
||||
import api from '@/api/api.js'
|
||||
|
||||
const keyword = ref('')
|
||||
const selectedIds = ref([])
|
||||
const patientList = ref([])
|
||||
const selectedDetail = ref([])
|
||||
const patientListByGBK = async () => {
|
||||
|
||||
const res = await api.patientListByGBK();
|
||||
if(res.code == 1){
|
||||
patientList.value = res.data;
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
onLoad(() => {
|
||||
|
||||
})
|
||||
onShow(() => {
|
||||
patientListByGBK();
|
||||
});
|
||||
|
||||
|
||||
|
||||
const toggle = (id) => {
|
||||
const i = selectedIds.value.indexOf(id)
|
||||
if (i > -1) {
|
||||
selectedIds.value.splice(i, 1)
|
||||
const di = selectedDetail.value.findIndex(it => it.uuid === id)
|
||||
if (di > -1) selectedDetail.value.splice(di, 1)
|
||||
} else {
|
||||
selectedIds.value.push(id)
|
||||
const p = patientList.value.find(x => x.uuid === id)
|
||||
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '' })
|
||||
}
|
||||
}
|
||||
|
||||
const onSearch = () => {}
|
||||
const goBack = () => uni.navigateBack()
|
||||
const confirmSelect = () => {
|
||||
const payload = { ids: selectedIds.value, list: selectedDetail.value }
|
||||
// 通过事件通道回传
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const curr = pages[pages.length - 1]
|
||||
const ec = curr?.getOpenerEventChannel?.()
|
||||
ec?.emit && ec.emit('onPatientsSelected', payload)
|
||||
} catch (e) {}
|
||||
// 兜底:使用本地存储
|
||||
try { uni.setStorageSync('patientsSelectedPayload', payload) } catch (e) {}
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-page{
|
||||
min-height: 100vh; background:#fefefe;
|
||||
}
|
||||
.confirm-text{ color:#fff; font-size: 28rpx;white-space: nowrap; }
|
||||
.confirm-btn{ background:#7f7f7f; padding: 10rpx 18rpx; border-radius: 26rpx; }
|
||||
.confirm-btn.active{ background:#8B2316; }
|
||||
.search-bar{
|
||||
border: 2rpx solid #eee;
|
||||
margin: 20rpx 30rpx; display:flex; align-items:center; gap: 16rpx;
|
||||
.input-wrap{ flex:1; background:#fff; border-radius: 12rpx; padding: 16rpx 20rpx; }
|
||||
.search-input{ font-size: 28rpx; color:#333; }
|
||||
.ph{ color:#bfbfbf; }
|
||||
.search-btn{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 88rpx; height: 72rpx; background:#fff;
|
||||
}
|
||||
}
|
||||
.list{ border-radius: 12rpx; }
|
||||
.item{background:#fff; display:flex; align-items:center;padding: 24rpx 30rpx; border-bottom: 2rpx solid #eee; }
|
||||
.avatar{ width: 96rpx; height:96rpx; border-radius: 16rpx; background:#ffe; }
|
||||
.name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; }
|
||||
.check{ padding-left: 12rpx; }
|
||||
.circle{ width: 40rpx; height: 40rpx; border-radius: 50%; border: 2rpx solid #cfcfcf; }
|
||||
.circle.active{ background:#8B2316; border-color:#8B2316; position: relative; }
|
||||
.circle.active::after{ content:''; position:absolute; left: 14rpx; top: 6rpx; width: 10rpx; height: 18rpx; border: 4rpx solid #fff; border-top: 0; border-left: 0; transform: rotate(45deg); }
|
||||
</style>
|
||||
@ -9,7 +9,7 @@
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#ffffff"
|
||||
backgroundColor="#eee"
|
||||
>
|
||||
<template #right>
|
||||
<view class="nav-right" @click="submitPlan">
|
||||
|
||||
@ -263,6 +263,7 @@
|
||||
import delImg from "@/static/delete_paper.png"
|
||||
import api from '@/api/api.js'
|
||||
import docUrl from "@/utils/docUrl.js"
|
||||
import navTo from '../../utils/navTo';
|
||||
const total=ref(0);
|
||||
const tab=ref('zhinan')
|
||||
// 响应式数据
|
||||
@ -317,7 +318,9 @@
|
||||
keywords.value=selectedTags[i].DES
|
||||
}
|
||||
|
||||
|
||||
navTo({
|
||||
url:'/pages_app/zhinanList/zhinanList?keywords='+encodeURIComponent(keywords.value)
|
||||
})
|
||||
|
||||
}
|
||||
isFilterActive.value=true;
|
||||
@ -366,7 +369,9 @@
|
||||
// 加载指南标签列表
|
||||
const loadGuideTags = async () => {
|
||||
try {
|
||||
const res = await api.guideTag({});
|
||||
const res = await api.guideTag({
|
||||
type:3
|
||||
});
|
||||
console.log('指南标签API响应:', res);
|
||||
if(res && res.code === 200 && res.data) {
|
||||
// 将API返回的标签数据转换为筛选标签格式
|
||||
|
||||
@ -10,115 +10,149 @@
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
></uni-nav-bar>
|
||||
>
|
||||
<template v-slot:right>
|
||||
<view class="nav-right" @click="testLoadMore">
|
||||
<text style="font-size: 24rpx; color: #8B2316;">测试加载</text>
|
||||
</view>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<!-- 指南列表 -->
|
||||
<view class="guidelines-list">
|
||||
<view
|
||||
class="guideline-item"
|
||||
v-for="(item, index) in guidelinesList"
|
||||
:key="item.uuid || index"
|
||||
>
|
||||
<!-- 指南信息 -->
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-bottom">
|
||||
<view class="item-date">{{ formatDate(item.releaseTime) }}</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="item-action">
|
||||
<view
|
||||
v-if="item.can_download"
|
||||
class="download-btn"
|
||||
@click="downloadGuideline(item)"
|
||||
>
|
||||
<up-icon name="download" color="#8D2316" size="28"></up-icon>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="view-btn"
|
||||
@click="viewGuideline(item)"
|
||||
>
|
||||
查看
|
||||
<!-- 使用scroll-view实现列表 -->
|
||||
<scroll-view
|
||||
class="guidelines-scroll-view"
|
||||
scroll-y="true"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="onScrollToLower"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<!-- 指南列表 -->
|
||||
<view class="guidelines-list">
|
||||
<view
|
||||
class="guideline-item"
|
||||
v-for="(item, index) in guidelinesList"
|
||||
:key="item.uuid || index"
|
||||
>
|
||||
<!-- 指南信息 -->
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-bottom">
|
||||
<view class="item-date">{{ formatDate(item.releaseTime) }}</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="item-action">
|
||||
<view
|
||||
v-if="item.can_download"
|
||||
class="download-btn"
|
||||
@click="downloadGuideline(item)"
|
||||
>
|
||||
<up-icon name="download" color="#8D2316" size="28"></up-icon>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="view-btn"
|
||||
@click="viewGuideline(item)"
|
||||
>
|
||||
查看
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-status">
|
||||
<view v-if="isLoading && currentPage === 1" class="loading">
|
||||
<uni-load-more status="loading" :content-text="loadingText"></uni-load-more>
|
||||
<!-- 加载更多状态 -->
|
||||
<view class="load-more-status">
|
||||
<view v-if="loadMoreStatus === 'loading'" class="loading">
|
||||
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="loadMoreStatus === 'more'" class="more">
|
||||
<text>上拉加载更多</text>
|
||||
</view>
|
||||
<view v-else-if="loadMoreStatus === 'noMore'" class="no-more">
|
||||
<text>没有更多数据了</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="hasMoreData && !isLoading" class="load-more" @click="loadMoreData">
|
||||
<uni-load-more status="more" :content-text="loadingText"></uni-load-more>
|
||||
</view>
|
||||
<view v-else-if="!hasMoreData && guidelinesList.length > 0" class="no-more">
|
||||
<uni-load-more status="noMore" :content-text="loadingText"></uni-load-more>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<view class="no-data" v-if="guidelinesList.length === 0 && !isLoading">
|
||||
<uni-icons type="info" size="60" color="#999"></uni-icons>
|
||||
<text>暂无诊疗指南数据</text>
|
||||
</view>
|
||||
<!-- 无数据提示 -->
|
||||
<view class="no-data" v-if="guidelinesList.length === 0 && !isLoading">
|
||||
<uni-icons type="info" size="60" color="#999"></uni-icons>
|
||||
<text>暂无诊疗指南数据</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted} from 'vue'
|
||||
import api from '@/api/api.js'
|
||||
import { onShow, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
|
||||
import { onShow,onLoad } from "@dcloudio/uni-app";
|
||||
|
||||
// 响应式数据
|
||||
const guidelinesList = ref([])
|
||||
const isLoading = ref(false)
|
||||
const hasMoreData = ref(true)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const pageSize = ref(10)
|
||||
const isRefreshing = ref(false)
|
||||
const keywords=ref('');
|
||||
const loadMoreStatus = ref('more') // 'loading', 'more', 'noMore'
|
||||
|
||||
// 加载文本配置
|
||||
const loadingText = {
|
||||
contentdown: '上拉显示更多',
|
||||
contentrefresh: '正在加载...',
|
||||
contentnomore: '没有更多数据了'
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
loadGuidelinesList()
|
||||
onLoad((options)=>{
|
||||
console.log(options)
|
||||
keywords.value=decodeURIComponent(options.keywords);
|
||||
console.log(keywords.value);
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 页面显示时可以刷新数据
|
||||
loadGuidelinesList()
|
||||
})
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
if (hasMoreData.value && !isLoading.value) {
|
||||
loadMoreData()
|
||||
// 页面显示时,如果列表为空则加载数据
|
||||
if (guidelinesList.value.length === 0) {
|
||||
loadGuidelinesList(true)
|
||||
}
|
||||
})
|
||||
|
||||
// scroll-view 下拉刷新
|
||||
const onRefresh = async () => {
|
||||
console.log('scroll-view 下拉刷新触发');
|
||||
isRefreshing.value = true;
|
||||
await refreshData();
|
||||
isRefreshing.value = false;
|
||||
};
|
||||
|
||||
// scroll-view 滚动到底部
|
||||
const onScrollToLower = () => {
|
||||
console.log('=== onScrollToLower 触发 ===');
|
||||
console.log('当前状态:', {
|
||||
loadMoreStatus: loadMoreStatus.value,
|
||||
isRefreshing: isRefreshing.value,
|
||||
currentPage: currentPage.value,
|
||||
listLength: guidelinesList.value.length,
|
||||
hasMoreData: hasMoreData.value,
|
||||
isLoading: isLoading.value
|
||||
});
|
||||
|
||||
if (loadMoreStatus.value === 'more' && !isRefreshing.value && !isLoading.value) {
|
||||
console.log('条件满足,开始加载更多数据');
|
||||
loadMoreData();
|
||||
} else {
|
||||
console.log('条件不满足,跳过加载:', {
|
||||
loadMoreStatus: loadMoreStatus.value,
|
||||
isRefreshing: isRefreshing.value,
|
||||
isLoading: isLoading.value
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = async () => {
|
||||
if (isRefreshing.value) return
|
||||
|
||||
try {
|
||||
isRefreshing.value = true
|
||||
currentPage.value = 1
|
||||
hasMoreData.value = true
|
||||
loadMoreStatus.value = 'more'
|
||||
|
||||
await loadGuidelinesList()
|
||||
await loadGuidelinesList(true)
|
||||
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
@ -130,42 +164,59 @@ const refreshData = async () => {
|
||||
title: '刷新失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
uni.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载指南列表
|
||||
const loadGuidelinesList = async (isLoadMore = false) => {
|
||||
const loadGuidelinesList = async (isRefresh = false) => {
|
||||
console.log('=== loadGuidelinesList 开始 ===');
|
||||
console.log('当前参数:', { isRefresh, page: currentPage.value, keywords: keywords.value });
|
||||
|
||||
if (isLoading.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
loadMoreStatus.value = 'loading'
|
||||
|
||||
const params = {
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
type: 1, // 诊疗指南类型
|
||||
keywords: ''
|
||||
sort:2,
|
||||
keywords:keywords.value
|
||||
}
|
||||
|
||||
const res = await api.searchLibraryU(params)
|
||||
console.log('指南列表响应:', res)
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
const newData = res.data
|
||||
const newData = res.data.list || res.data
|
||||
|
||||
if (isLoadMore) {
|
||||
guidelinesList.value = [...guidelinesList.value, ...newData]
|
||||
} else {
|
||||
if (isRefresh) {
|
||||
guidelinesList.value = newData
|
||||
console.log('刷新模式:替换列表数据');
|
||||
} else {
|
||||
guidelinesList.value = [...guidelinesList.value, ...newData]
|
||||
console.log('加载更多:追加数据到列表');
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
hasMoreData.value = newData.length === pageSize.value
|
||||
if (newData.length < pageSize.value) {
|
||||
loadMoreStatus.value = 'noMore'
|
||||
hasMoreData.value = false
|
||||
console.log('没有更多数据了');
|
||||
} else {
|
||||
loadMoreStatus.value = 'more'
|
||||
hasMoreData.value = true
|
||||
// 只有在加载更多模式下才递增页码
|
||||
if (!isRefresh) {
|
||||
currentPage.value++
|
||||
console.log(`还有更多数据,下一页: ${currentPage.value}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('加载指南列表失败:', res.message)
|
||||
loadMoreStatus.value = 'noMore'
|
||||
uni.showToast({
|
||||
title: res.message || '加载失败',
|
||||
icon: 'none'
|
||||
@ -173,20 +224,36 @@ const loadGuidelinesList = async (isLoadMore = false) => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载指南列表异常:', error)
|
||||
loadMoreStatus.value = 'noMore'
|
||||
uni.showToast({
|
||||
title: '网络异常,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
console.log('=== loadGuidelinesList 结束 ===');
|
||||
console.log('最终状态:', {
|
||||
loadMoreStatus: loadMoreStatus.value,
|
||||
page: currentPage.value,
|
||||
listLength: guidelinesList.value.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多数据
|
||||
const loadMoreData = () => {
|
||||
console.log('=== loadMoreData 被调用 ===');
|
||||
console.log('检查条件:', {
|
||||
hasMoreData: hasMoreData.value,
|
||||
isLoading: isLoading.value,
|
||||
currentPage: currentPage.value
|
||||
});
|
||||
|
||||
if (hasMoreData.value && !isLoading.value) {
|
||||
currentPage.value++
|
||||
loadGuidelinesList(true)
|
||||
console.log('条件满足,开始加载更多数据');
|
||||
loadGuidelinesList(false);
|
||||
} else {
|
||||
console.log('条件不满足,跳过加载更多');
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,6 +303,28 @@ const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 测试加载更多功能
|
||||
const testLoadMore = () => {
|
||||
console.log('=== 手动测试加载更多 ===');
|
||||
console.log('当前状态:', {
|
||||
loadMoreStatus: loadMoreStatus.value,
|
||||
hasMoreData: hasMoreData.value,
|
||||
isLoading: isLoading.value,
|
||||
currentPage: currentPage.value,
|
||||
listLength: guidelinesList.value.length
|
||||
});
|
||||
|
||||
if (loadMoreStatus.value === 'more' && !isLoading.value) {
|
||||
loadMoreData();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: `状态: ${loadMoreStatus.value}, 加载中: ${isLoading.value}`,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return ''
|
||||
@ -264,7 +353,12 @@ $white: #ffffff;
|
||||
|
||||
.zhinan-list-page {
|
||||
background-color: $bg-color;
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.guidelines-scroll-view {
|
||||
height: calc(100vh - 140rpx);
|
||||
background-color: $bg-color;
|
||||
}
|
||||
|
||||
// 指南列表
|
||||
@ -291,6 +385,11 @@ $white: #ffffff;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16rpx;
|
||||
font-weight: 500;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
@ -341,18 +440,22 @@ $white: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
.loading-status {
|
||||
// 加载更多状态
|
||||
.load-more-status {
|
||||
padding: 20rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.loading, .load-more, .no-more {
|
||||
.loading, .more, .no-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
padding: 20rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: $text-light;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 无数据提示
|
||||
.no-data {
|
||||
display: flex;
|
||||
|
||||
BIN
static/iv_delete.png
Normal file
BIN
static/iv_delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Loading…
x
Reference in New Issue
Block a user