2025-10-09 16:48:46 +08:00

460 lines
11 KiB
Vue
Raw 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>
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="消息"
right-text="清除"
@clickRight="clearMsg"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<view class="msg-page">
<!-- 四宫格入口 -->
<view class="shortcut-grid">
<view class="grid-item" @click="goBenefit">
<view class="icon-wrap gift">
<uni-icons type="gift" size="34" color="#fff"></uni-icons>
<view class="badge" v-if="badgeData.Module_Welfare > 0">{{ badgeData.Module_Welfare }}</view>
</view>
<text class="label">福利</text>
</view>
<view class="grid-item" @click="goOrder">
<view class="icon-wrap order">
<uni-icons type="list" size="34" color="#fff"></uni-icons>
<view class="badge" v-if="badgeData.Module_Order > 0">{{ badgeData.Module_Order }}</view>
</view>
<text class="label">订单</text>
</view>
<view class="grid-item" @click="goFollow">
<view class="icon-wrap visit">
<uni-icons type="heart" size="34" color="#fff"></uni-icons>
<view class="badge" v-if="badgeData.Module_Relation > 0">{{ badgeData.Module_Relation }}</view>
</view>
<text class="label">随访</text>
</view>
<view class="grid-item" @click="goReply">
<view class="icon-wrap reply">
<uni-icons type="chatbubble" size="34" color="#fff"></uni-icons>
<view class="badge" v-if="badgeData.Module_Comment > 0">{{ badgeData.Module_Comment }}</view>
</view>
<text class="label">回复我的</text>
</view>
</view>
<!-- 消息列表 -->
<scroll-view
class="msg-list"
scroll-y="true"
:show-scrollbar="false"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="80"
>
<!-- 回复我的模块 - 聊天列表样式 -->
<view v-if="currentModule === 4" class="chat-list">
<view class="chat-item" v-for="(msg, idx) in msgList" :key="msg.id" @click="goMsgDetail(msg)">
<view class="avatar">
<image :src="msg.avatar || '/static/default-avatar.png'" mode="aspectFill" @error="handleImageError"></image>
</view>
<view class="chat-content">
<view class="chat-header">
<text class="sender-name">{{ msg.sender_name || msg.title }}</text>
<text class="chat-time">{{ msg.create_date }}</text>
</view>
<view class="message-preview">{{ msg.content }}</view>
</view>
</view>
</view>
<!-- 其他模块 - 卡片样式 -->
<view v-else>
<view class="card" v-for="(msg, idx) in msgList" :key="msg.id" @click="goMsgDetail(msg)">
<view class="card-title">{{ msg.title }}</view>
<view class="card-content">{{ msg.content }}</view>
<view class="card-time">{{ msg.create_date }}</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="msgList.length === 0" class="empty-state">
<text class="empty-text">暂无消息</text>
</view>
<!-- 加载更多提示 -->
<view v-if="loading" class="loading-more">
<text class="loading-text">加载中...</text>
</view>
<!-- 没有更多数据提示 -->
<view v-if="noMore" class="no-more">
<text class="no-more-text">没有更多数据了</text>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import api from '@/api/api.js';
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
// 角标数据
const badgeData = ref({
Module_Order: 0, // 订单
Module_Comment: 0, // 回复我的
Module_Welfare: 0, // 福利
Module_Relation: 0 // 随访
});
// 消息列表数据
const msgList = ref([]);
const currentModule = ref(1); // 当前选中的模块默认显示模块1的消息
// 刷新/加载状态
const refreshing = ref(false);
const loading = ref(false);
const noMore = ref(false);
const page = ref(1);
const pageSize = ref(10);
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({
url: '/pages/index/index'
});
}
});
};
const goBenefit = () => {
currentModule.value = 1;
getAppMesageList(1);
};
const goOrder = () => {
currentModule.value = 2;
getAppMesageList(2);
};
const goFollow = () => {
currentModule.value = 3;
getAppMesageList(3);
};
const goReply = () => {
currentModule.value = 4;
getAppMesageList(4);
};
const clearMsg = () => {
uni.showModal({
title: '提醒',
content: '是否要清除所有未读消息?',
success: (res) => {
if (res.confirm) {
api.appMesageRead({}).then(res => {
console.log(res);
});
getUnReadList();
}
}
});
}
const goMsgDetail = (msg) => {
console.log(msg);
let url="";
if(msg.extra.module==1 && msg.extra.type==36){
url=`/pages_app/myPoint/myPoint`
}else if(msg.extra.module==3 && msg.extra.type==12){
url=`/pages_app/myPatient/myPatient`
}
navTo({
url: url
})
}
// 下拉刷新=
const onRefresh = async () => {
if (refreshing.value) return;
refreshing.value = true;
try {
// 重新获取当前模块的消息列表
getAppMesageList(currentModule.value);
// 重新获取未读消息数量
getUnReadList();
} finally {
refreshing.value = false;
}
};
// 上拉加载
const onLoadMore = async () => {
if (loading.value || noMore.value) return;
loading.value = true;
try {
// 这里可以根据需要实现分页加载
// 目前接口返回的数据已经包含了分页信息
// 如果需要加载更多,可以调用 getAppMesageList 并传入页码参数
noMore.value = true; // 暂时设置为没有更多数据
} finally {
loading.value = false;
}
};
const getUnReadList = () => {
api.unReadList({}).then(res => {
console.log(res);
if (res.code === 200 && res.data) {
// 更新角标数据
badgeData.value = {
Module_Order: parseInt(res.data.Module_Order) || 0,
Module_Comment: parseInt(res.data.Module_Comment) || 0,
Module_Welfare: parseInt(res.data.Module_Welfare) || 0,
Module_Relation: parseInt(res.data.Module_Relation) || 0
};
}
}).catch(err => {
console.error('获取未读消息列表失败:', err);
});
};
const getAppMesageList = (module) => {
api.appMesageList({module: module}).then(res => {
console.log(res);
if (res.code === 200 && res.data && res.data.list) {
// 更新消息列表数据
msgList.value = res.data.list.map(item => ({
id: item.id,
title: item.title,
content: item.content,
create_date: item.create_date,
is_read: item.is_read,
extra: item.extra,
// 从extra字段提取用户信息
sender_name: item.extra?.user_name || item.title,
avatar: item.extra?.user_photo ?
(item.extra.user_photo.startsWith('http') ?
item.extra.user_photo :
`${getBaseUrl()}${item.extra.user_photo}`) :
'/static/default-avatar.png'
}));
}
}).catch(err => {
console.error('获取消息列表失败:', err);
});
};
// 获取基础URL
const getBaseUrl = () => {
return docUrl;
};
// 图片加载错误处理
const handleImageError = (e) => {
// 设置默认头像
e.target.src = '/static/default-avatar.png';
};
onMounted(() => {
getUnReadList();
getAppMesageList(1);
});
</script>
<style lang="scss" scoped>
$bg: #f5f6f7;
$text-primary: #333;
$text-secondary: #666;
$muted: #999;
$divider: #eeeeee;
$theme: #8B2316;
$nav-h: 140rpx; // 与 uni-nav-bar 保持一致
$grid-h: 200rpx; // 四宫格区域高度
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.msg-page {
min-height: 100vh;
background: $bg;
padding-top: $nav-h; // 与导航栏高度一致
}
.shortcut-grid {
position: fixed;
top: $nav-h;
left: 0;
right: 0;
z-index: 9;
height: $grid-h;
background: #fff;
display: grid;
grid-template-columns: repeat(4, 1fr);
padding: 30rpx 20rpx 10rpx;
column-gap: 10rpx;
row-gap: 10rpx;
border-bottom: 1rpx solid $divider;
.grid-item {
@include flex-center;
flex-direction: column;
gap: 12rpx;
.icon-wrap {
position: relative;
width: 92rpx;
height: 92rpx;
border-radius: 20rpx;
@include flex-center;
&.gift { background: linear-gradient(180deg, #ff6680, #ff4d6a); }
&.order { background: linear-gradient(180deg, #ffcc66, #ffb300); }
&.visit { background: linear-gradient(180deg, #4dd0e1, #2ec4b6); }
&.reply { background: linear-gradient(180deg, #6edc82, #2ecc71); }
.badge {
position: absolute;
right: -6rpx;
top: -6rpx;
min-width: 32rpx;
height: 32rpx;
border-radius: 50rpx;
background: #ff3b30;
color: #fff;
font-size: 22rpx;
@include flex-center;
padding: 0 6rpx;
}
}
.label {
font-size: 26rpx;
color: $text-secondary;
}
}
}
.msg-list {
position: fixed;
// 减去导航与宫格的高度
top: 360rpx; // 让列表起始位置在宫格下方
padding: 20rpx 24rpx 40rpx;
box-sizing: border-box;
bottom: 0rpx;
// 加载状态样式
.loading-more, .no-more, .empty-state {
@include flex-center;
padding: 28rpx 0;
.loading-text, .no-more-text, .empty-text {
font-size: 26rpx;
color: $muted;
}
}
.card {
background: #fff;
border-radius: 16rpx;
padding: 26rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.04);
margin-top: 26rpx;
.card-title {
font-size: 30rpx;
color: $text-primary;
font-weight: 600;
margin-bottom: 14rpx;
}
.card-content {
font-size: 28rpx;
color: $text-secondary;
line-height: 1.6;
margin-bottom: 14rpx;
}
.card-time {
font-size: 24rpx;
color: $muted;
text-align: right;
}
}
// 聊天列表样式
.chat-list {
.chat-item {
display: flex;
align-items: center;
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
background: #fff;
margin-top: 24rpx;
&:last-child {
border-bottom: none;
}
.avatar {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 24rpx;
flex-shrink: 0;
image {
width: 100%;
height: 100%;
}
}
.chat-content {
flex: 1;
min-width: 0;
.chat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8rpx;
.sender-name {
font-size: 30rpx;
color: $text-primary;
font-weight: 500;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-time {
font-size: 24rpx;
color: $muted;
flex-shrink: 0;
margin-left: 16rpx;
}
}
.message-preview {
font-size: 28rpx;
color: $text-secondary;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
</style>