- 新增分包组件 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 只读医生详情, 隐藏预约挂号区
518 lines
15 KiB
Vue
518 lines
15 KiB
Vue
<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>
|