All checks were successful
continuous-integration/drone/push Build is passing
302 lines
5.8 KiB
Vue
302 lines
5.8 KiB
Vue
<template>
|
|
<view class="system-msg-page">
|
|
<!-- 自定义导航栏 -->
|
|
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="navbar-content">
|
|
<view class="nav-back" @click="goBack">
|
|
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
|
</view>
|
|
<text class="navbar-title">系统消息</text>
|
|
<view class="nav-placeholder"></view>
|
|
</view>
|
|
</view>
|
|
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
|
<!-- 系统消息列表 -->
|
|
<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>
|
|
|
|
<!-- 通知详情弹窗 -->
|
|
<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: {},
|
|
statusBarHeight: 0
|
|
}
|
|
},
|
|
onLoad() {
|
|
const sysInfo = uni.getSystemInfoSync()
|
|
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
|
this.loadMessages()
|
|
},
|
|
methods: {
|
|
goBack() { uni.navigateBack() },
|
|
/** 加载系统消息列表 */
|
|
async loadMessages() {
|
|
this.loading = true
|
|
try {
|
|
const res = await getSystemMessages()
|
|
this.messages = res || []
|
|
} catch (e) {
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/** 查看消息详情 */
|
|
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(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : 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>
|
|
/* 自定义导航栏 */
|
|
.custom-navbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
z-index: 999;
|
|
background: #FFB700;
|
|
}
|
|
.navbar-content {
|
|
height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20rpx;
|
|
}
|
|
.nav-back {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.back-icon {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
}
|
|
.navbar-title {
|
|
font-size: 34rpx;
|
|
font-weight: bold;
|
|
color: #363636;
|
|
}
|
|
.nav-placeholder {
|
|
width: 60rpx;
|
|
}
|
|
.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>
|