Showing
6 changed files
with
120 additions
and
62 deletions
| ... | @@ -5,6 +5,18 @@ | ... | @@ -5,6 +5,18 @@ |
| 5 | 5 | ||
| 6 | --- | 6 | --- |
| 7 | 7 | ||
| 8 | +## [2026-02-12] - 优化消息列表卡片布局 | ||
| 9 | + | ||
| 10 | +### 优化 | ||
| 11 | +- 重构消息列表卡片布局,提升信息可读性 | ||
| 12 | +- 简化卡片结构:第一行展示消息内容,第二行左侧展示时间,右侧展示状态 | ||
| 13 | +- 调整标题、时间、状态的视觉层级 | ||
| 14 | +- 使用 `IconFont` 组件替换旧的图标实现 | ||
| 15 | +- 优化消息详情页布局:移除标题提取逻辑,仅展示“发送时间”(右上角)和完整内容,避免内容重复显示 | ||
| 16 | +- 增加未读消息红点提示 | ||
| 17 | + | ||
| 18 | +--- | ||
| 19 | + | ||
| 8 | ## [2026-02-12] - 修复结果页返回后表单未重置 | 20 | ## [2026-02-12] - 修复结果页返回后表单未重置 |
| 9 | 21 | ||
| 10 | ### 修复 | 22 | ### 修复 | ... | ... |
| ... | @@ -8,7 +8,7 @@ info: | ... | @@ -8,7 +8,7 @@ info: |
| 8 | title: '' | 8 | title: '' |
| 9 | version: 1.0.0 | 9 | version: 1.0.0 |
| 10 | paths: | 10 | paths: |
| 11 | - /: | 11 | + /srv/: |
| 12 | get: | 12 | get: |
| 13 | summary: 消息详情 | 13 | summary: 消息详情 |
| 14 | deprecated: false | 14 | deprecated: false |
| ... | @@ -41,7 +41,7 @@ paths: | ... | @@ -41,7 +41,7 @@ paths: |
| 41 | in: query | 41 | in: query |
| 42 | description: 消息ID | 42 | description: 消息ID |
| 43 | required: true | 43 | required: true |
| 44 | - example: '817677' | 44 | + example: '834791' |
| 45 | schema: | 45 | schema: |
| 46 | type: string | 46 | type: string |
| 47 | responses: | 47 | responses: |
| ... | @@ -57,8 +57,6 @@ paths: | ... | @@ -57,8 +57,6 @@ paths: |
| 57 | msg: | 57 | msg: |
| 58 | type: string | 58 | type: string |
| 59 | data: | 59 | data: |
| 60 | - type: array | ||
| 61 | - items: | ||
| 62 | type: object | 60 | type: object |
| 63 | properties: | 61 | properties: |
| 64 | id: | 62 | id: |
| ... | @@ -74,15 +72,20 @@ paths: | ... | @@ -74,15 +72,20 @@ paths: |
| 74 | type: string | 72 | type: string |
| 75 | title: 状态 | 73 | title: 状态 |
| 76 | description: send=以发送未读取,read=已读取 | 74 | description: send=以发送未读取,read=已读取 |
| 75 | + pk_id: | ||
| 76 | + type: integer | ||
| 77 | + title: 计划书订单ID | ||
| 77 | x-apifox-orders: | 78 | x-apifox-orders: |
| 78 | - id | 79 | - id |
| 79 | - note | 80 | - note |
| 80 | - created_time | 81 | - created_time |
| 81 | - status | 82 | - status |
| 83 | + - pk_id | ||
| 82 | required: | 84 | required: |
| 83 | - note | 85 | - note |
| 84 | - created_time | 86 | - created_time |
| 85 | - status | 87 | - status |
| 88 | + - pk_id | ||
| 86 | required: | 89 | required: |
| 87 | - code | 90 | - code |
| 88 | - msg | 91 | - msg |
| ... | @@ -96,7 +99,7 @@ paths: | ... | @@ -96,7 +99,7 @@ paths: |
| 96 | x-apifox-ordering: 0 | 99 | x-apifox-ordering: 0 |
| 97 | security: [] | 100 | security: [] |
| 98 | x-apifox-folder: 消息 | 101 | x-apifox-folder: 消息 |
| 99 | - x-apifox-status: developing | 102 | + x-apifox-status: testing |
| 100 | x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-413906673-run | 103 | x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-413906673-run |
| 101 | components: | 104 | components: |
| 102 | schemas: {} | 105 | schemas: {} | ... | ... |
| ... | @@ -64,6 +64,9 @@ paths: | ... | @@ -64,6 +64,9 @@ paths: |
| 64 | msg: | 64 | msg: |
| 65 | type: string | 65 | type: string |
| 66 | data: | 66 | data: |
| 67 | + type: object | ||
| 68 | + properties: | ||
| 69 | + list: | ||
| 67 | type: array | 70 | type: array |
| 68 | items: | 71 | items: |
| 69 | type: object | 72 | type: object |
| ... | @@ -81,15 +84,28 @@ paths: | ... | @@ -81,15 +84,28 @@ paths: |
| 81 | type: string | 84 | type: string |
| 82 | title: 状态 | 85 | title: 状态 |
| 83 | description: send=以发送未读取,read=已读取 | 86 | description: send=以发送未读取,read=已读取 |
| 87 | + pk_id: | ||
| 88 | + type: integer | ||
| 89 | + title: 计划书订单ID | ||
| 84 | x-apifox-orders: | 90 | x-apifox-orders: |
| 85 | - id | 91 | - id |
| 86 | - note | 92 | - note |
| 87 | - created_time | 93 | - created_time |
| 88 | - status | 94 | - status |
| 95 | + - pk_id | ||
| 89 | required: | 96 | required: |
| 90 | - note | 97 | - note |
| 91 | - created_time | 98 | - created_time |
| 92 | - status | 99 | - status |
| 100 | + - pk_id | ||
| 101 | + total: | ||
| 102 | + type: integer | ||
| 103 | + x-apifox-orders: | ||
| 104 | + - list | ||
| 105 | + - total | ||
| 106 | + required: | ||
| 107 | + - list | ||
| 108 | + - total | ||
| 93 | required: | 109 | required: |
| 94 | - code | 110 | - code |
| 95 | - msg | 111 | - msg |
| ... | @@ -103,7 +119,7 @@ paths: | ... | @@ -103,7 +119,7 @@ paths: |
| 103 | x-apifox-ordering: 0 | 119 | x-apifox-ordering: 0 |
| 104 | security: [] | 120 | security: [] |
| 105 | x-apifox-folder: 消息 | 121 | x-apifox-folder: 消息 |
| 106 | - x-apifox-status: developing | 122 | + x-apifox-status: testing |
| 107 | x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-413906672-run | 123 | x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-413906672-run |
| 108 | components: | 124 | components: |
| 109 | schemas: {} | 125 | schemas: {} | ... | ... |
| ... | @@ -13,12 +13,13 @@ const Api = { | ... | @@ -13,12 +13,13 @@ const Api = { |
| 13 | * @returns {Promise<{ | 13 | * @returns {Promise<{ |
| 14 | * code: number; // 状态码 | 14 | * code: number; // 状态码 |
| 15 | * msg: string; // 消息 | 15 | * msg: string; // 消息 |
| 16 | - * data: Array<{ | 16 | + * data: { |
| 17 | id: integer; // 消息id | 17 | id: integer; // 消息id |
| 18 | note: string; // 消息内容 | 18 | note: string; // 消息内容 |
| 19 | created_time: string; // 发消息的时间 | 19 | created_time: string; // 发消息的时间 |
| 20 | status: string; // send=以发送未读取,read=已读取 | 20 | status: string; // send=以发送未读取,read=已读取 |
| 21 | - * }>; | 21 | + pk_id: integer; // 计划书订单ID |
| 22 | + * }; | ||
| 22 | * }>} | 23 | * }>} |
| 23 | */ | 24 | */ |
| 24 | export const detailAPI = (params) => fn(fetch.get(Api.Detail, params)); | 25 | export const detailAPI = (params) => fn(fetch.get(Api.Detail, params)); |
| ... | @@ -32,12 +33,16 @@ export const detailAPI = (params) => fn(fetch.get(Api.Detail, params)); | ... | @@ -32,12 +33,16 @@ export const detailAPI = (params) => fn(fetch.get(Api.Detail, params)); |
| 32 | * @returns {Promise<{ | 33 | * @returns {Promise<{ |
| 33 | * code: number; // 状态码 | 34 | * code: number; // 状态码 |
| 34 | * msg: string; // 消息 | 35 | * msg: string; // 消息 |
| 35 | - * data: Array<{ | 36 | + * data: { |
| 37 | + list: Array<{ | ||
| 36 | id: integer; // 消息id | 38 | id: integer; // 消息id |
| 37 | note: string; // 消息内容 | 39 | note: string; // 消息内容 |
| 38 | created_time: string; // 发消息的时间 | 40 | created_time: string; // 发消息的时间 |
| 39 | status: string; // send=以发送未读取,read=已读取 | 41 | status: string; // send=以发送未读取,read=已读取 |
| 40 | - * }>; | 42 | + pk_id: integer; // 计划书订单ID |
| 43 | + }>; | ||
| 44 | + total: integer; // | ||
| 45 | + * }; | ||
| 41 | * }>} | 46 | * }>} |
| 42 | */ | 47 | */ |
| 43 | export const myListAPI = (params) => fn(fetch.get(Api.MyList, params)); | 48 | export const myListAPI = (params) => fn(fetch.get(Api.MyList, params)); | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | - * @Date: 2026-02-03 21:26:58 | 2 | + * @Date:2026-02-03 21:26:58 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2026-02-03 21:30:21 | 4 | + * @LastEditTime: 2026-02-12 18:24:35 |
| 5 | * @FilePath: /manulife-weapp/src/pages/message-detail/index.vue | 5 | * @FilePath: /manulife-weapp/src/pages/message-detail/index.vue |
| 6 | - * @Description: 文件描述 | 6 | + * @Description: 消息详情页 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <view class="min-h-screen bg-white pb-safe"> | 9 | <view class="min-h-screen bg-white pb-safe"> |
| 10 | <NavHeader title="消息详情" /> | 10 | <NavHeader title="消息详情" /> |
| 11 | 11 | ||
| 12 | <view v-if="detail" class="p-5"> | 12 | <view v-if="detail" class="p-5"> |
| 13 | - <!-- 标题 --> | ||
| 14 | - <view class="text-xl font-bold text-gray-900 mb-3 leading-tight"> | ||
| 15 | - {{ detail.title }} | ||
| 16 | - </view> | ||
| 17 | - | ||
| 18 | - <!-- 元信息 --> | ||
| 19 | - <view class="flex items-center text-xs text-gray-400 mb-6"> | ||
| 20 | - <text>{{ detail.create_time }}</text> | ||
| 21 | - <text class="mx-2">·</text> | ||
| 22 | - <text>Manulife</text> | ||
| 23 | - </view> | ||
| 24 | 13 | ||
| 25 | <!-- 内容区域 --> | 14 | <!-- 内容区域 --> |
| 26 | <view class="rich-text-content"> | 15 | <view class="rich-text-content"> |
| 27 | <rich-text :nodes="formattedContent" /> | 16 | <rich-text :nodes="formattedContent" /> |
| 28 | </view> | 17 | </view> |
| 18 | + | ||
| 19 | + <!-- 顶部时间 --> | ||
| 20 | + <view class="mt-4 text-right"> | ||
| 21 | + <text class="text-xs text-gray-400">{{ detail.created_time }}</text> | ||
| 22 | + </view> | ||
| 23 | + | ||
| 24 | + <!-- 关联计划书(可选) --> | ||
| 25 | + <!-- <view v-if="detail.pk_id" class="mt-2 pt-4 border-t border-gray-200"> | ||
| 26 | + <view class="text-sm text-gray-500 mb-2">关联计划书</view> | ||
| 27 | + <view class="text-xs text-gray-400">订单ID: {{ detail.pk_id }}</view> | ||
| 28 | + </view> --> | ||
| 29 | </view> | 29 | </view> |
| 30 | 30 | ||
| 31 | <!-- 加载中 --> | 31 | <!-- 加载中 --> |
| ... | @@ -51,12 +51,15 @@ const loading = ref(true) | ... | @@ -51,12 +51,15 @@ const loading = ref(true) |
| 51 | * @description 格式化富文本内容,处理图片宽度等问题 | 51 | * @description 格式化富文本内容,处理图片宽度等问题 |
| 52 | */ | 52 | */ |
| 53 | const formattedContent = computed(() => { | 53 | const formattedContent = computed(() => { |
| 54 | - if (!detail.value?.content) return '' | 54 | + if (!detail.value?.note) return '' |
| 55 | + | ||
| 55 | // 简单的正则替换,确保图片宽度不超过容器 | 56 | // 简单的正则替换,确保图片宽度不超过容器 |
| 56 | - return detail.value.content.replace( | 57 | + const content = detail.value.note.replace( |
| 57 | /<img/g, | 58 | /<img/g, |
| 58 | '<img style="max-width:100%;height:auto;display:block;"' | 59 | '<img style="max-width:100%;height:auto;display:block;"' |
| 59 | ) | 60 | ) |
| 61 | + | ||
| 62 | + return content | ||
| 60 | }) | 63 | }) |
| 61 | 64 | ||
| 62 | /** | 65 | /** | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | - * @Date: 2026-02-08 | 2 | + * @Date:2026-02-08 |
| 3 | * @Description: 我的消息页 - 使用 LoadMoreList 组件重构版本 | 3 | * @Description: 我的消息页 - 使用 LoadMoreList 组件重构版本 |
| 4 | --> | 4 | --> |
| 5 | <template> | 5 | <template> |
| ... | @@ -27,19 +27,26 @@ | ... | @@ -27,19 +27,26 @@ |
| 27 | class="message-item bg-white rounded-xl p-4 mb-3 shadow-sm active:opacity-70 transition-opacity" | 27 | class="message-item bg-white rounded-xl p-4 mb-3 shadow-sm active:opacity-70 transition-opacity" |
| 28 | @tap="handleItemClick(item)" | 28 | @tap="handleItemClick(item)" |
| 29 | > | 29 | > |
| 30 | - <view class="flex justify-between items-start mb-2"> | 30 | + <!-- 第一行:内容(带红点) --> |
| 31 | - <view class="flex-1 mr-2"> | 31 | + <view class="text-base font-bold text-gray-900 line-clamp-1 mb-3 flex items-center"> |
| 32 | - <view class="text-base font-bold text-gray-900 line-clamp-1"> | 32 | + <view v-if="item.status === 'send'" class="w-2 h-2 bg-red-500 rounded-full mr-2 shrink-0"></view> |
| 33 | - {{ item.title }} | 33 | + {{ getItemTitle(item.note) }} |
| 34 | </view> | 34 | </view> |
| 35 | + | ||
| 36 | + <!-- 第二行:时间(左)与 状态(右) --> | ||
| 37 | + <view class="flex justify-between items-center"> | ||
| 38 | + <!-- 左边:时间 --> | ||
| 39 | + <text class="text-xs text-gray-400 font-medium">{{ item.created_time }}</text> | ||
| 40 | + | ||
| 41 | + <!-- 右边:状态 --> | ||
| 42 | + <view class="flex items-center"> | ||
| 43 | + <view v-if="item.status === 'send'" class="px-2 py-0.5 bg-red-50 text-red-500 rounded text-xs font-medium"> | ||
| 44 | + 未读 | ||
| 45 | + </view> | ||
| 46 | + <view v-else-if="item.status === 'read'" class="px-2 py-0.5 bg-gray-100 text-gray-400 rounded text-xs"> | ||
| 47 | + 已读 | ||
| 35 | </view> | 48 | </view> |
| 36 | - <text class="text-xs text-gray-400 shrink-0 mt-1"> | ||
| 37 | - {{ item.create_time }} | ||
| 38 | - </text> | ||
| 39 | </view> | 49 | </view> |
| 40 | - | ||
| 41 | - <view class="text-sm text-gray-600 line-clamp-2 leading-relaxed"> | ||
| 42 | - {{ item.intro || item.content || '暂无简介' }} | ||
| 43 | </view> | 50 | </view> |
| 44 | </view> | 51 | </view> |
| 45 | </template> | 52 | </template> |
| ... | @@ -58,6 +65,7 @@ import { useLoad } from '@tarojs/taro' | ... | @@ -58,6 +65,7 @@ import { useLoad } from '@tarojs/taro' |
| 58 | import { useGo } from '@/hooks/useGo' | 65 | import { useGo } from '@/hooks/useGo' |
| 59 | import LoadMoreList from '@/components/list/LoadMoreList' | 66 | import LoadMoreList from '@/components/list/LoadMoreList' |
| 60 | import NavHeader from '@/components/navigation/NavHeader.vue' | 67 | import NavHeader from '@/components/navigation/NavHeader.vue' |
| 68 | +import IconFont from '@/components/icons/IconFont.vue' | ||
| 61 | import { myListAPI } from '@/api/news' | 69 | import { myListAPI } from '@/api/news' |
| 62 | import { mockMessageListAPI } from '@/utils/mockData' | 70 | import { mockMessageListAPI } from '@/utils/mockData' |
| 63 | 71 | ||
| ... | @@ -67,41 +75,52 @@ const USE_MOCK_DATA = false | ... | @@ -67,41 +75,52 @@ const USE_MOCK_DATA = false |
| 67 | 75 | ||
| 68 | const go = useGo() | 76 | const go = useGo() |
| 69 | 77 | ||
| 70 | -/** | 78 | +// 响应式状态 |
| 71 | - * 当前列表数据 | ||
| 72 | - * @type {Ref<Array<any>>} | ||
| 73 | - */ | ||
| 74 | const currentList = ref([]) | 79 | const currentList = ref([]) |
| 75 | - | ||
| 76 | -/** | ||
| 77 | - * 当前页码(从0开始) | ||
| 78 | - * @type {Ref<number>} | ||
| 79 | - */ | ||
| 80 | const currentPage = ref(0) | 80 | const currentPage = ref(0) |
| 81 | - | ||
| 82 | -/** | ||
| 83 | - * 每页数量 | ||
| 84 | - * @type {number} | ||
| 85 | - */ | ||
| 86 | const pageSize = 10 | 81 | const pageSize = 10 |
| 87 | - | ||
| 88 | -/** | ||
| 89 | - * 是否还有更多数据 | ||
| 90 | - * @type {Ref<boolean>} | ||
| 91 | - */ | ||
| 92 | const hasMore = ref(true) | 82 | const hasMore = ref(true) |
| 83 | +const loading = ref(false) | ||
| 84 | +const loadingMore = ref(false) | ||
| 93 | 85 | ||
| 94 | /** | 86 | /** |
| 95 | - * 首次加载状态 | 87 | + * 提取消息标题(第一行或截取) |
| 96 | - * @type {Ref<boolean>} | 88 | + * |
| 89 | + * @param {string} note - 消息内容 | ||
| 90 | + * @returns {string} 标题 | ||
| 97 | */ | 91 | */ |
| 98 | -const loading = ref(false) | 92 | +const getItemTitle = (note) => { |
| 93 | + if (!note) return '暂无消息内容' | ||
| 94 | + | ||
| 95 | + // 提取第一行作为标题 | ||
| 96 | + const firstLine = note.split('\n')[0] | ||
| 97 | + | ||
| 98 | + // 移除富文本标签(简单处理) | ||
| 99 | + const textOnly = firstLine.replace(/<[^>]+>/g, '').trim() | ||
| 100 | + | ||
| 101 | + // 如果第一行太长,截取前50个字符 | ||
| 102 | + return textOnly.length > 50 ? textOnly.substring(0, 50) + '...' : textOnly | ||
| 103 | +} | ||
| 99 | 104 | ||
| 100 | /** | 105 | /** |
| 101 | - * 加载更多状态 | 106 | + * 提取消息预览(移除第一行后的内容) |
| 102 | - * @type {Ref<boolean>} | 107 | + * |
| 108 | + * @param {string} note - 消息内容 | ||
| 109 | + * @returns {string} 预览内容 | ||
| 103 | */ | 110 | */ |
| 104 | -const loadingMore = ref(false) | 111 | +const getItemPreview = (note) => { |
| 112 | + if (!note) return '' | ||
| 113 | + | ||
| 114 | + // 移除第一行(已在标题显示) | ||
| 115 | + const lines = note.split('\n') | ||
| 116 | + if (lines.length > 1) { | ||
| 117 | + // 移除富文本标签(简单处理) | ||
| 118 | + const preview = lines.slice(1).join('\n').replace(/<[^>]+>/g, '').trim() | ||
| 119 | + return preview.substring(0, 100) // 限制预览长度 | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + return '' // 只有一行时不显示预览 | ||
| 123 | +} | ||
| 105 | 124 | ||
| 106 | /** | 125 | /** |
| 107 | * 获取消息列表 | 126 | * 获取消息列表 | ... | ... |
-
Please register or login to post a comment