Files
xk-client-wx/pages/index/index.vue
李琦 8b455b1920 feat(home/chat): 列表骨架屏、聊天室己方头像与协议/医生详情优化
- 新增分包组件 ListSkeleton(doctor/message/product/article/category/zone)
- 首页/消息/资讯首次加载改用骨架屏,刷新不再清空旧数据避免闪空
  · 去掉 home getList、消息 onShow 中的数组清空
  · 三页 pages.json 增加 easycom 与 componentPlaceholder 主包异步引用
- 聊天室己方头像改为读取本地 userinfo.avatarurl(登录后微信头像),
  mycollection 上传头像成功后同步写回 storage
- 修复协议抽屉点不开:page-container 改为 v-if + :show;
  协议名单独可点节点,匹配放宽 name/desc 双向 includes
- 医生气泡头像可点击进入 doctor-detail?readonly=1 只读医生详情,
  隐藏预约挂号区
2026-07-21 16:05:09 +08:00

518 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="content safe-area-inset-bottom">
<!-- 全局消息通知组件 -->
<MessageNotification />
<!-- 标题 -->
<view class="title">
<text>全部消息</text>
<view class="clear" @click="getClearInfo">
<text class="iconfont icon-qingchu"></text>
清除未读
</view>
</view>
<!-- 首次加载四条固定入口用骨架避免 num 未定义时闪暂无新消息 -->
<ListSkeleton v-if="noticeInitialLoading" type="message" :rows="4" />
<template v-else>
<!-- 消息列表 -->
<view class="list" @click="goSystem">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/xx/xt.png" mode=""></image>
</view>
<u-badge size="default" v-if="system.num>=0" :count="system.num" :is-dot="system.num==0?true:false"
type="error" :offset="system.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">系统消息</text>
<text class="time">{{ system.time }}</text>
</view>
<view class="preview" v-if="system.num >0">
您有{{ system.num }}条未读信息
</view>
<view class="preview" v-if="system.num==undefined">
暂无新消息
</view>
</view>
</view>
<view class="list" @click="goOrder">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/xx/cp.png" mode=""></image>
</view>
<u-badge size="default" v-if="order.num>=0" :count="order.num" :is-dot="order.num==0?true:false"
type="error" :offset="order.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">产品订单</text>
<text class="time">{{ order.time }}</text>
</view>
<view class="preview" v-if="order.num>0">
您有{{ order.num }}条未读信息
</view>
<view class="preview" v-if="order.num==undefined">
暂无新消息
</view>
</view>
</view>
<view class="list" @click="goDoctor">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/xx/ysxx.png" mode=""></image>
</view>
<u-badge size="default" v-if="doctor.num>=0" :count="doctor.num" :is-dot="doctor.num==0?true:false"
type="error" :offset="doctor.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">医生消息</text>
<text class="time">{{ doctor.time }}</text>
</view>
<view class="preview" v-if="doctor.num>0">
您有{{ doctor.num }}条新的消息
</view>
<view class="preview" v-if="doctor.num==undefined">
暂无新消息
</view>
</view>
</view>
<!-- 转诊消息 -->
<view class="list" @click="goTransfer">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/xx/ysxx.png" mode=""></image>
</view>
<u-badge size="default" v-if="transfer.num>=0" :count="transfer.num" :is-dot="transfer.num==0?true:false"
type="error" :offset="transfer.num>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">转诊消息</text>
<text class="time">{{ transfer.time }}</text>
</view>
<view class="preview" v-if="transfer.num>0">
您有{{ transfer.num }}条转诊消息待处理
</view>
<view class="preview" v-if="transfer.num==undefined || transfer.num==0">
暂无转诊消息
</view>
</view>
</view>
</template>
<!-- 标题 - 在线咨询 -->
<view v-if="checkDev('open-im') && onlineConsultationList.length > 0" class="title">
<text>在线咨询</text>
</view>
<!-- IM 会话首次骨架有旧数据时直接展示旧列表直至替换 -->
<ListSkeleton
v-if="checkDev('open-im') && chatInitialLoading && onlineConsultationList.length === 0 && onlineReconsultationList.length === 0"
type="message"
:rows="3"
/>
<!-- 在线咨询 - 用户 (type=1) -->
<view v-if="checkDev('open-im')" v-for="(item, index) in onlineConsultationList" :key="item.id" class="list" @click="goChat(item, index)">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image :src="item.avatar" mode=""></image>
</view>
<u-badge size="default" v-if="item.un_read_count>=0" :count="item.un_read_count"
:is-dot="item.un_read_count==0?true:false"
type="error" :offset="item.un_read_count>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">{{ item.nick_name }}</text>
<text class="time">{{ item.last_message_time }}</text>
</view>
<view class="preview">
<p>{{ item.last_message }}</p>
</view>
<view class="preview" v-if="item.last_message===''">
暂无新消息
</view>
</view>
</view>
<!-- 标题 - 在线复诊 -->
<view v-if="checkDev('open-im') && onlineReconsultationList.length > 0" class="title">
<text>在线复诊</text>
</view>
<!-- 在线复诊 - 用户 (type=3) -->
<view v-if="checkDev('open-im')" v-for="(item, index) in onlineReconsultationList" :key="item.id" class="list" @click="goChat(item, index)">
<!-- 头像 -->
<view class="avator">
<view class="img">
<image :src="item.avatar" mode=""></image>
</view>
<u-badge size="default" v-if="item.un_read_count>=0" :count="item.un_read_count"
:is-dot="item.un_read_count==0?true:false"
type="error" :offset="item.un_read_count>0?[-4,-8]:[0,0]"></u-badge>
</view>
<!-- 描述 -->
<view class="info">
<view class="infos_title">
<text class="infos">{{ item.nick_name }}</text>
<text class="time">{{ item.last_message_time }}</text>
</view>
<view class="preview">
<p>{{ item.last_message }}</p>
</view>
<view class="preview" v-if="item.last_message===''">
暂无新消息
</view>
</view>
</view>
</view>
</template>
<script>
import {
getseesionRead
} from '../../request/api/api'
import {getSystemNoticeIndexApi} from '../../request/api/systemNotice'
import {getChatFriendsListApi} from "@/request/api/im";
import {getTransferPrescriptionListApi} from "@/request/api/transferPrescription";
import {checkDev} from "@/utils/utils";
import MessageNotification from "@/components/MessageNotification/MessageNotification.vue";
export default {
components: {
MessageNotification
},
data() {
return {
order: [],
system: [],
doctor: [],
transfer: {
num: 0,
time: ''
},
imChat: [],
onlineConsultationList: [], // 在线咨询列表 (type=1)
onlineReconsultationList: [], // 在线复诊列表 (type=3)
type: "",
// 首次加载骨架:刷新时不再清空数据,故仅首次为 true
noticeInitialLoading: true,
chatInitialLoading: true
}
},
onLoad() {
// 监听聊天列表更新事件
uni.$on('chat-list-update', this.handleChatListUpdate);
},
onUnload() {
// 移除事件监听
uni.$off('chat-list-update', this.handleChatListUpdate);
},
methods: {
checkDev,
/**
* 处理聊天列表更新事件
* 收到新消息时自动刷新列表
*/
handleChatListUpdate() {
this.getMyChatFriendsList();
},
// 清除未读信息
getClearInfo() {
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "all"
}
}).then((res) => {
uni.showToast({
title: '清除成功',
duration: 2000,
icon: "success"
});
this.getInfo()
})
},
// 系统信息
goSystem() {
uni.navigateTo({
url: '/pages/index/system'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "system"
}
}).then((res) => {
})
},
// 详情
goOrder() {
uni.navigateTo({
url: '/pages/index/order-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "order"
}
}).then((res) => {
})
},
goDoctor() {
uni.navigateTo({
url: '/pages/index/doctor-info'
})
getseesionRead({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || 11001,
type: "doctor_news"
}
}).then((res) => {
})
},
// 转诊消息
goTransfer() {
uni.navigateTo({
url: '/subPackages/transfer/transfer-message'
})
},
goChat(item, index) {
uni.navigateTo({
url: `/subPackages/chat/chat?room_id=${item.room_id}&nick_name=${item.nick_name}&user_id=${item.id}&avatar=${encodeURIComponent(item.avatar || '')}&room_status=${item.room_status || 0}`
})
},
// 获取系统/订单/医生消息概览;成功才覆盖,失败保留旧数据
async getInfo() {
try {
const res = await getSystemNoticeIndexApi({
store_id: uni.getStorageSync('store_id') || 11001,
})
if (res.data && res.data.result) {
this.order = res.data.result.order
this.system = res.data.result.system
this.doctor = res.data.result.doctor_news
}
} catch (error) {
console.error('获取消息概览失败:', error)
} finally {
this.noticeInitialLoading = false
}
},
// 拉取 IM 好友会话列表,成功后整份替换,失败不写空
getMyChatFriendsList() {
return getChatFriendsListApi().then((res) => {
const allChatList = res.data.result || []
// 根据type字段区分在线咨询和在线复诊
this.onlineConsultationList = allChatList.filter(item => item.type === 1 || !item.type) // type=1或未设置type的为在线咨询
this.onlineReconsultationList = allChatList.filter(item => (item.type === 3 || item.type === 2)) // type=3的为在线复诊
// 保持imChat兼容性包含所有
this.imChat = allChatList
}).catch((error) => {
console.error('获取聊天列表失败:', error)
}).finally(() => {
this.chatInitialLoading = false
})
},
// 获取转诊消息列表;失败不重置 transfer避免闪空
getTransferList() {
return getTransferPrescriptionListApi().then((res) => {
if (res.data && res.data.code == 0) {
const transferList = res.data.result || []
// 统计待确认的转诊消息数量
const pendingCount = transferList.filter(item => item.status === 0).length
this.transfer.num = pendingCount
// 获取最新的转诊时间
if (transferList.length > 0) {
const latestTransfer = transferList[0]
if (latestTransfer.transfer_time) {
this.transfer.time = latestTransfer.transfer_time
} else if (latestTransfer.created_at) {
this.transfer.time = latestTransfer.created_at
}
}
}
}).catch((error) => {
console.error('获取转诊消息列表失败:', error)
})
},
/**
* 下拉刷新:重置骨架并并行重拉消息概览 / IM / 转诊
*/
refreshMessagePage() {
this.noticeInitialLoading = true
if (checkDev('open-im')) {
this.chatInitialLoading = true
this.onlineConsultationList = []
this.onlineReconsultationList = []
this.imChat = []
}
const tasks = [
this.getInfo(),
this.getTransferList()
]
if (checkDev('open-im')) {
tasks.push(this.getMyChatFriendsList())
} else {
this.chatInitialLoading = false
}
return Promise.all(tasks)
},
onPullDownRefresh() {
this.refreshMessagePage().finally(() => {
uni.stopPullDownRefresh()
})
}
},
mounted() {
this.getInfo()
},
onShow() {
// 不再清空列表:保留旧 UI接口成功后再覆盖
this.getInfo()
this.getMyChatFriendsList()
this.getTransferList()
}
}
</script>
<style lang="scss" scoped>
.content {
width: 750rpx;
max-height: 100%;
min-height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
background: #F5F5F5;
.title {
padding: 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
text {
height: 50rpx;
font-size: 36rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #0F172A;
}
.clear {
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #536DFE;
line-height: 33rpx;
.iconfont {
color: #536DFE;
margin-right: 8rpx;
vertical-align: text-top;
}
}
}
.list {
width: 750rpx;
display: flex;
padding: 32rpx 38rpx;
justify-content: space-between;
background: #fff;
.avator {
position: relative;
.img {
width: 84rpx;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 50%;
background: #E6EAFF;
image {
width: 100%;
height: 100%;
}
}
}
.info {
width: 570rpx;
height: 94rpx;
font-family: PingFang SC-Medium, PingFang SC;
.infos_title {
width: 570rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
.doctor {
.name {
height: 42rpx;
font-size: 32rpx;
font-weight: 500;
color: #1E293B;
}
.infos {
height: 42rpx;
font-size: 28rpx;
font-weight: 500;
color: #475569;
}
}
.time {
font-size: 24rpx;
font-weight: 400;
color: #94A3B8;
}
}
.preview {
width: 570rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #475569;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
}
</style>