9.2 zjd提交
This commit is contained in:
parent
1cc611d68d
commit
f89d0e6295
22
api/api.js
22
api/api.js
@ -261,7 +261,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
@ -218,6 +218,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>
|
||||
@ -88,7 +88,7 @@
|
||||
:class="{ active: tag.selected }"
|
||||
@click="toggleTag(index)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
{{ tag.DES}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -170,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 () => {
|
||||
@ -363,6 +380,7 @@
|
||||
// 页面显示时加载数据
|
||||
console.log('页面显示,开始加载课件数据');
|
||||
loadData(true);
|
||||
loadGuideTags()
|
||||
});
|
||||
|
||||
// 测试加载更多功能
|
||||
|
||||
@ -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">
|
||||
|
||||
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