campus-errand/miniapp/pages/message/system-msg.vue
2026-03-01 05:01:47 +08:00

252 lines
4.7 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>
<view class="system-msg-page">
<!-- 系统消息列表Requirements 15.1 -->
<view
class="msg-item"
v-for="item in messages"
:key="item.id"
@click="goDetail(item)"
>
<view class="msg-content">
<view class="msg-header">
<text class="msg-title">{{ item.title }}</text>
<view v-if="!item.isRead" class="unread-dot"></view>
</view>
<text class="msg-preview">{{ item.contentPreview }}</text>
<text class="msg-time">{{ formatTime(item.createdAt) }}</text>
</view>
<image
v-if="item.thumbnailUrl"
class="msg-thumb"
:src="item.thumbnailUrl"
mode="aspectFill"
></image>
</view>
<!-- 空状态 -->
<view v-if="messages.length === 0 && !loading" class="empty">
<text class="empty-text">暂无系统消息</text>
</view>
<!-- 通知详情弹窗Requirements 15.2 -->
<view v-if="showDetail" class="detail-mask" @click="closeDetail">
<view class="detail-popup" @click.stop>
<view class="detail-header">
<text class="detail-title">{{ detailData.title }}</text>
<text class="detail-close" @click="closeDetail">✕</text>
</view>
<scroll-view scroll-y class="detail-body">
<image
v-if="detailData.thumbnailUrl"
class="detail-image"
:src="detailData.thumbnailUrl"
mode="widthFix"
></image>
<rich-text :nodes="detailData.content || ''"></rich-text>
</scroll-view>
<text class="detail-time">{{ formatTime(detailData.createdAt) }}</text>
</view>
</view>
</view>
</template>
<script>
import { getSystemMessages, getSystemMessageDetail } from '../../utils/api'
export default {
data() {
return {
messages: [],
loading: false,
showDetail: false,
detailData: {}
}
},
onLoad() {
this.loadMessages()
},
methods: {
/** 加载系统消息列表 */
async loadMessages() {
this.loading = true
try {
const res = await getSystemMessages()
this.messages = res || []
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.loading = false
}
},
/** 查看消息详情Requirements 15.2 */
async goDetail(item) {
try {
const res = await getSystemMessageDetail(item.id)
this.detailData = res
this.showDetail = true
// 标记为已读
item.isRead = true
} catch (e) {
uni.showToast({ title: '加载详情失败', icon: 'none' })
}
},
/** 关闭详情弹窗 */
closeDetail() {
this.showDetail = false
this.detailData = {}
},
/** 格式化时间 */
formatTime(dateStr) {
if (!dateStr) return ''
const d = new Date(dateStr)
const pad = (n) => String(n).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
}
}
</script>
<style scoped>
.system-msg-page {
padding: 20rpx 24rpx;
}
.msg-item {
display: flex;
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx 30rpx;
margin-bottom: 16rpx;
}
.msg-content {
flex: 1;
overflow: hidden;
}
.msg-header {
display: flex;
align-items: center;
}
.msg-title {
font-size: 30rpx;
color: #333333;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.unread-dot {
width: 14rpx;
height: 14rpx;
background-color: #ff4d4f;
border-radius: 50%;
margin-left: 12rpx;
flex-shrink: 0;
}
.msg-preview {
font-size: 26rpx;
color: #999999;
margin-top: 10rpx;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
display: block;
}
.msg-time {
font-size: 22rpx;
color: #cccccc;
margin-top: 10rpx;
display: block;
}
.msg-thumb {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-left: 20rpx;
flex-shrink: 0;
}
.empty {
text-align: center;
padding: 120rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #cccccc;
}
/* 详情弹窗 */
.detail-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.detail-popup {
width: 85%;
max-height: 80vh;
background-color: #ffffff;
border-radius: 20rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
}
.detail-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.detail-title {
font-size: 34rpx;
color: #333333;
font-weight: bold;
flex: 1;
}
.detail-close {
font-size: 36rpx;
color: #999999;
padding: 10rpx;
}
.detail-body {
flex: 1;
max-height: 60vh;
}
.detail-image {
width: 100%;
margin-bottom: 20rpx;
border-radius: 8rpx;
}
.detail-time {
font-size: 22rpx;
color: #cccccc;
margin-top: 20rpx;
text-align: right;
display: block;
}
</style>