xiangyixiangqin/miniapp/pages/message/system.vue
2026-01-07 17:52:35 +08:00

378 lines
8.1 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-message-page">
<!-- 页面加载状态 -->
<Loading type="page" :loading="pageLoading" />
<!-- 顶部背景图 -->
<view class="top-bg">
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
</view>
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<view class="navbar-back" @click="handleBack">
<text class="back-icon"></text>
</view>
<text class="navbar-title">系统消息</text>
<view class="navbar-placeholder"></view>
</view>
</view>
<!-- 筛选标签 -->
<view class="filter-section" :style="{ top: (statusBarHeight + 44) + 'px' }">
<view
class="filter-tag"
:class="{ active: currentFilter === 'all' }"
@click="handleFilterChange('all')"
>
全部
</view>
</view>
<!-- 消息列表 -->
<scroll-view
class="message-scroll"
scroll-y
:style="{
top: (statusBarHeight + 44 + 60) + 'px',
height: scrollHeight + 'px'
}"
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="handleRefresh"
@scrolltolower="handleLoadMore"
>
<view class="message-list" v-if="messageList.length > 0">
<view
class="message-item"
v-for="item in messageList"
:key="item.id"
@click="handleMessageClick(item)"
>
<view class="message-title">{{ item.title }}</view>
<view class="message-content">{{ item.content }}</view>
<view class="message-time">{{ formatTime(item.createTime) }}</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-wrapper" v-else-if="!pageLoading">
<Empty text="暂无系统消息" :showButton="false" />
</view>
<!-- 加载更多 -->
<Loading type="more" :loading="listLoading" :noMore="noMoreData" />
</scroll-view>
</view>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/store/user.js'
import { getSystemMessages } from '@/api/message.js'
import { formatTimestamp } from '@/utils/format.js'
import Loading from '@/components/Loading/index.vue'
import Empty from '@/components/Empty/index.vue'
export default {
name: 'SystemMessagePage',
components: {
Loading,
Empty
},
setup() {
const userStore = useUserStore()
// 状态栏高度
const statusBarHeight = ref(20)
const scrollHeight = ref(500)
// 页面状态
const pageLoading = ref(true)
const listLoading = ref(false)
const noMoreData = ref(false)
const isRefreshing = ref(false)
// 筛选
const currentFilter = ref('all')
// 分页
const pageIndex = ref(1)
const pageSize = 20
// 数据
const messageList = ref([])
// 获取系统信息
const getSystemInfo = () => {
uni.getSystemInfo({
success: (res) => {
statusBarHeight.value = res.statusBarHeight || 20
// 计算滚动区域高度:窗口高度 - 导航栏高度 - 筛选区域高度
const navbarHeight = statusBarHeight.value + 44
const filterHeight = 60
scrollHeight.value = res.windowHeight - navbarHeight - filterHeight
}
})
}
// 返回
const handleBack = () => {
uni.navigateBack()
}
// 加载消息列表
const loadMessages = async (isLoadMore = false) => {
if (listLoading.value) return
if (isLoadMore && noMoreData.value) return
listLoading.value = true
try {
if (!isLoadMore) {
pageIndex.value = 1
messageList.value = []
noMoreData.value = false
}
const res = await getSystemMessages({
pageIndex: pageIndex.value,
pageSize: pageSize
})
if (res && (res.code === 0 || res.success) && res.data) {
const items = res.data.items || res.data.list || []
const total = res.data.total || 0
if (isLoadMore) {
messageList.value = [...messageList.value, ...items]
} else {
messageList.value = items
}
noMoreData.value = messageList.value.length >= total || items.length < pageSize
pageIndex.value++
}
} catch (error) {
console.error('加载系统消息失败:', error)
} finally {
listLoading.value = false
}
}
// 初始化页面
const initPage = async () => {
pageLoading.value = true
try {
getSystemInfo()
userStore.restoreFromStorage()
await loadMessages()
} catch (error) {
console.error('初始化页面失败:', error)
} finally {
pageLoading.value = false
}
}
// 筛选切换
const handleFilterChange = (filter) => {
if (currentFilter.value === filter) return
currentFilter.value = filter
loadMessages()
}
// 下拉刷新
const handleRefresh = async () => {
isRefreshing.value = true
try {
await loadMessages()
} finally {
isRefreshing.value = false
}
}
// 加载更多
const handleLoadMore = () => {
if (!noMoreData.value && !listLoading.value) {
loadMessages(true)
}
}
// 点击消息
const handleMessageClick = (item) => {
// 如果有跳转链接
if (item.linkUrl) {
uni.navigateTo({ url: item.linkUrl })
}
}
// 格式化时间
const formatTime = (timestamp) => {
return formatTimestamp(timestamp)
}
onMounted(() => {
initPage()
})
return {
statusBarHeight,
scrollHeight,
pageLoading,
listLoading,
noMoreData,
isRefreshing,
currentFilter,
messageList,
handleBack,
handleFilterChange,
handleRefresh,
handleLoadMore,
handleMessageClick,
formatTime
}
}
}
</script>
<style lang="scss" scoped>
.system-message-page {
height: 100vh;
background-color: #f5f6fa;
}
// 顶部背景图
.top-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 400rpx;
z-index: 0;
.bg-img {
width: 100%;
height: 100%;
}
}
// 自定义导航栏
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
.navbar-content {
position: relative;
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
.navbar-back {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
.back-icon {
font-size: 56rpx;
color: #333;
font-weight: 300;
}
}
.navbar-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.navbar-placeholder {
width: 80rpx;
}
}
}
// 筛选标签
.filter-section {
position: fixed;
left: 0;
right: 0;
z-index: 10;
padding: 16rpx 24rpx;
.filter-tag {
display: inline-block;
padding: 12rpx 32rpx;
font-size: 28rpx;
color: #ff6b6b;
background: #fff;
border: 2rpx solid #ff6b6b;
border-radius: 32rpx;
&.active {
color: #ff6b6b;
background: #fff;
}
}
}
// 消息滚动区域
.message-scroll {
position: fixed;
left: 0;
right: 0;
background-color: #f5f6fa;
}
// 消息列表
.message-list {
padding: 24rpx;
min-height: 100%;
.message-item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
&:active {
background: #f8f8f8;
}
.message-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
line-height: 1.4;
}
.message-content {
font-size: 28rpx;
color: #666;
line-height: 1.8;
margin-bottom: 16rpx;
}
.message-time {
font-size: 24rpx;
color: #999;
}
}
}
// 空状态
.empty-wrapper {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style>