9.2 zjd提交

This commit is contained in:
zoujiandong
2025-09-02 19:03:45 +08:00
parent 1cc611d68d
commit f89d0e6295
16 changed files with 1338 additions and 301 deletions
+209 -78
View File
@@ -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;