hookehuyr

feat: 新增消息中心功能模块

添加消息列表页和消息详情页,支持查看系统消息详情
在个人中心菜单中增加消息入口,并更新组件类型声明
......@@ -16,8 +16,8 @@ declare module 'vue' {
NavHeader: typeof import('./src/components/NavHeader.vue')['default']
NutAvatar: typeof import('@nutui/nutui-taro')['Avatar']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutEmpty: typeof import('@nutui/nutui-taro')['Empty']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutLoading: typeof import('@nutui/nutui-taro')['Loading']
NutPicker: typeof import('@nutui/nutui-taro')['Picker']
NutPopup: typeof import('@nutui/nutui-taro')['Popup']
NutRadio: typeof import('@nutui/nutui-taro')['Radio']
......
......@@ -26,6 +26,8 @@ const pages = [
'pages/feedback/index',
'pages/login/index',
'pages/help-center/index',
'pages/message/index',
'pages/message-detail/index',
]
if (process.env.NODE_ENV === 'development') {
......
export default {
navigationBarTitleText: '消息详情',
backgroundColor: '#ffffff',
navigationStyle: 'custom'
}
<!--
* @Date: 2026-02-03 21:26:58
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-02-03 21:30:21
* @FilePath: /manulife-weapp/src/pages/message-detail/index.vue
* @Description: 文件描述
-->
<template>
<view class="min-h-screen bg-white pb-safe">
<NavHeader title="消息详情" />
<view v-if="detail" class="p-5">
<!-- 标题 -->
<view class="text-xl font-bold text-gray-900 mb-3 leading-tight">
{{ detail.title }}
</view>
<!-- 元信息 -->
<view class="flex items-center text-xs text-gray-400 mb-6">
<text>{{ detail.create_time }}</text>
<text class="mx-2">·</text>
<text>Manulife</text>
</view>
<!-- 内容区域 -->
<view class="rich-text-content">
<rich-text :nodes="formattedContent" />
</view>
</view>
<!-- 加载中 -->
<view v-else-if="loading" class="flex justify-center py-10">
<view class="w-8 h-8 border-4 border-gray-200 border-t-blue-600 rounded-full animate-spin"></view>
</view>
<!-- 错误/空状态 -->
<nut-empty v-else description="未找到消息内容" image="error" />
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useLoad } from '@tarojs/taro'
import NavHeader from '@/components/NavHeader.vue'
import { detailAPI } from '@/api/news'
const detail = ref(null)
const loading = ref(true)
/**
* @description 格式化富文本内容,处理图片宽度等问题
*/
const formattedContent = computed(() => {
if (!detail.value?.content) return ''
// 简单的正则替换,确保图片宽度不超过容器
return detail.value.content.replace(
/<img/g,
'<img style="max-width:100%;height:auto;display:block;"'
)
})
/**
* @description 获取消息详情
* @param {string|number} id 消息ID
*/
const fetchDetail = async (id) => {
loading.value = true
try {
const res = await detailAPI({ i: id })
if (res.code === 1) {
detail.value = res.data
}
} catch (err) {
console.error('获取消息详情失败:', err)
} finally {
loading.value = false
}
}
useLoad((options) => {
if (options.id) {
fetchDetail(options.id)
}
})
</script>
<style lang="less">
.rich-text-content {
font-size: 28rpx;
color: #333;
line-height: 1.8;
/* 确保富文本样式正确 */
p {
margin-bottom: 20rpx;
}
}
</style>
/*
* @Date: 2026-02-03 21:26:43
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-02-03 21:33:27
* @FilePath: /manulife-weapp/src/pages/message/index.config.js
* @Description: 文件描述
*/
export default {
navigationBarTitleText: '我的消息',
enablePullDownRefresh: true,
backgroundColor: '#f9fafb',
navigationStyle: 'custom'
}
<template>
<view class="min-h-screen bg-[#F9FAFB] pb-safe">
<NavHeader title="我的消息" />
<!-- 列表区域 -->
<view class="p-4">
<template v-if="messageList.length > 0">
<view
v-for="item in messageList"
:key="item.id"
class="bg-white rounded-xl p-4 mb-3 shadow-sm active:opacity-70 transition-opacity"
@tap="handleItemClick(item)"
>
<view class="flex justify-between items-start mb-2">
<view class="flex-1 mr-2">
<view class="text-base font-bold text-gray-900 line-clamp-1">
{{ item.title }}
</view>
</view>
<text class="text-xs text-gray-400 shrink-0 mt-1">
{{ item.create_time }}
</text>
</view>
<view class="text-sm text-gray-600 line-clamp-2 leading-relaxed">
{{ item.intro || item.content || '暂无简介' }}
</view>
</view>
<!-- 加载更多/没有更多 -->
<view class="py-4 text-center text-xs text-gray-400">
<text v-if="loading">加载中...</text>
<text v-else-if="!hasMore">没有更多了</text>
<text v-else>上拉加载更多</text>
</view>
</template>
<!-- 空状态 -->
<nut-empty
v-else-if="!loading && messageList.length === 0"
description="暂无消息"
image="empty"
/>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { useLoad, usePullDownRefresh, useReachBottom, stopPullDownRefresh } from '@tarojs/taro'
import { useGo } from '@/hooks/useGo'
import NavHeader from '@/components/NavHeader.vue'
import { myListAPI } from '@/api/news'
const go = useGo()
const messageList = ref([])
const page = ref(1)
const limit = ref(10)
const hasMore = ref(true)
const loading = ref(false)
/**
* @description 加载消息列表
* @param {boolean} refresh 是否刷新
*/
const fetchMessageList = async (refresh = false) => {
if (loading.value) return
if (refresh) {
page.value = 1
hasMore.value = true
} else if (!hasMore.value) {
return
}
loading.value = true
try {
const res = await myListAPI({
page: page.value,
limit: limit.value
})
if (res.code === 1) {
const list = res.data?.list || []
if (refresh) {
messageList.value = list
} else {
messageList.value = [...messageList.value, ...list]
}
if (list.length < limit.value) {
hasMore.value = false
} else {
page.value++
}
}
} catch (err) {
console.error('获取消息列表失败:', err)
} finally {
loading.value = false
if (refresh) {
stopPullDownRefresh()
}
}
}
/**
* @description 跳转到详情页
* @param {Object} item 消息对象
*/
const handleItemClick = (item) => {
go('/pages/message-detail/index', { id: item.id })
}
// 页面加载
useLoad(() => {
fetchMessageList(true)
})
// 下拉刷新
usePullDownRefresh(() => {
fetchMessageList(true)
})
// 上拉加载更多
useReachBottom(() => {
fetchMessageList()
})
</script>
<style lang="less">
/* Scoped styles if needed */
</style>
......@@ -128,6 +128,7 @@ useDidShow(() => {
const menuItems = [
{ title: '我的计划书', icon: 'order', path: '/pages/plan/index' },
{ title: '我的消息', icon: 'message', path: '/pages/message/index' },
{ title: '我的收藏', icon: 'star', path: '/pages/favorites/index' },
{ title: '帮助中心', icon: 'service', path: '/pages/help-center/index' },
{ title: '意见反馈', icon: 'edit', path: '/pages/feedback-list/index' }
......