Showing
3 changed files
with
222 additions
and
103 deletions
| ... | @@ -5,6 +5,29 @@ | ... | @@ -5,6 +5,29 @@ |
| 5 | 5 | ||
| 6 | --- | 6 | --- |
| 7 | 7 | ||
| 8 | +## [2026-02-11] - 统一分页起始页码从0开始 | ||
| 9 | + | ||
| 10 | +### 修复 | ||
| 11 | +- **计划书页**: | ||
| 12 | + - 完善状态筛选功能(5个状态tab:全部、待处理、处理中、已生成、已查看) | ||
| 13 | + - 实现查看计划书功能(支持多文件选择,使用 ActionSheet) | ||
| 14 | + - 实现删除计划书功能(调用 deleteAPI) | ||
| 15 | + - 添加状态标记显示(4种状态颜色区分) | ||
| 16 | + - 修复分页从0开始 | ||
| 17 | + | ||
| 18 | +- **消息页**: | ||
| 19 | + - 修复分页从0开始 | ||
| 20 | + | ||
| 21 | +--- | ||
| 22 | + | ||
| 23 | +**详细信息**: | ||
| 24 | +- **影响文件**: src/pages/plan/index.vue, src/pages/message/index.vue | ||
| 25 | +- **技术栈**: Taro 4, Vue 3, NutUI | ||
| 26 | +- **测试状态**: 已通过 ESLint 检查 | ||
| 27 | +- **备注**: 统一项目所有列表页的分页规范,确保与 API 文档一致 | ||
| 28 | + | ||
| 29 | +--- | ||
| 30 | + | ||
| 8 | ## [2026-02-11] - 完善计划书 API 接口定义 | 31 | ## [2026-02-11] - 完善计划书 API 接口定义 |
| 9 | 32 | ||
| 10 | ### 新增 | 33 | ### 新增 | ... | ... |
| ... | @@ -72,10 +72,10 @@ const go = useGo() | ... | @@ -72,10 +72,10 @@ const go = useGo() |
| 72 | const currentList = ref([]) | 72 | const currentList = ref([]) |
| 73 | 73 | ||
| 74 | /** | 74 | /** |
| 75 | - * 当前页码(从1开始) | 75 | + * 当前页码(从0开始) |
| 76 | * @type {Ref<number>} | 76 | * @type {Ref<number>} |
| 77 | */ | 77 | */ |
| 78 | -const currentPage = ref(1) | 78 | +const currentPage = ref(0) |
| 79 | 79 | ||
| 80 | /** | 80 | /** |
| 81 | * 每页数量 | 81 | * 每页数量 |
| ... | @@ -105,7 +105,7 @@ const loadingMore = ref(false) | ... | @@ -105,7 +105,7 @@ const loadingMore = ref(false) |
| 105 | * 获取消息列表 | 105 | * 获取消息列表 |
| 106 | * | 106 | * |
| 107 | * @param {Object} params - 请求参数 | 107 | * @param {Object} params - 请求参数 |
| 108 | - * @param {number} params.page - 页码(从1开始) | 108 | + * @param {number} params.page - 页码(从0开始) |
| 109 | * @param {number} params.limit - 每页数量 | 109 | * @param {number} params.limit - 每页数量 |
| 110 | * @param {boolean} isLoadMore - 是否为加载更多 | 110 | * @param {boolean} isLoadMore - 是否为加载更多 |
| 111 | * @returns {Promise<void>} | 111 | * @returns {Promise<void>} |
| ... | @@ -174,11 +174,11 @@ useLoad(async (options) => { | ... | @@ -174,11 +174,11 @@ useLoad(async (options) => { |
| 174 | console.log('[Message] 页面参数:', options) | 174 | console.log('[Message] 页面参数:', options) |
| 175 | 175 | ||
| 176 | // 重置分页状态 | 176 | // 重置分页状态 |
| 177 | - currentPage.value = 1 | 177 | + currentPage.value = 0 |
| 178 | hasMore.value = true | 178 | hasMore.value = true |
| 179 | 179 | ||
| 180 | // 获取消息列表 | 180 | // 获取消息列表 |
| 181 | - await fetchMessageList({ page: 1, limit: pageSize }) | 181 | + await fetchMessageList({ page: 0, limit: pageSize }) |
| 182 | }) | 182 | }) |
| 183 | 183 | ||
| 184 | /** | 184 | /** |
| ... | @@ -207,11 +207,11 @@ const handleRefresh = async () => { | ... | @@ -207,11 +207,11 @@ const handleRefresh = async () => { |
| 207 | console.log('[Message] 下拉刷新') | 207 | console.log('[Message] 下拉刷新') |
| 208 | 208 | ||
| 209 | // 重置分页状态 | 209 | // 重置分页状态 |
| 210 | - currentPage.value = 1 | 210 | + currentPage.value = 0 |
| 211 | hasMore.value = true | 211 | hasMore.value = true |
| 212 | 212 | ||
| 213 | // 刷新数据 | 213 | // 刷新数据 |
| 214 | - await fetchMessageList({ page: 1, limit: pageSize }) | 214 | + await fetchMessageList({ page: 0, limit: pageSize }) |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | /** | 217 | /** | ... | ... |
| ... | @@ -79,10 +79,13 @@ | ... | @@ -79,10 +79,13 @@ |
| 79 | <view | 79 | <view |
| 80 | :class="[ | 80 | :class="[ |
| 81 | 'status-badge', | 81 | 'status-badge', |
| 82 | - item.status === 'processing' ? 'status-processing' : 'status-generated' | 82 | + item.status === 'pending' ? 'status-pending' : '', |
| 83 | + item.status === 'processing' ? 'status-processing' : '', | ||
| 84 | + item.status === 'generated' ? 'status-generated' : '', | ||
| 85 | + item.status === 'viewed' ? 'status-viewed' : '' | ||
| 83 | ]" | 86 | ]" |
| 84 | > | 87 | > |
| 85 | - {{ item.status === 'processing' ? '生成中' : '已完成' }} | 88 | + {{ getStatusText(item.status) }} |
| 86 | </view> | 89 | </view> |
| 87 | </view> | 90 | </view> |
| 88 | </view> | 91 | </view> |
| ... | @@ -133,7 +136,7 @@ | ... | @@ -133,7 +136,7 @@ |
| 133 | import { ref, nextTick } from 'vue' | 136 | import { ref, nextTick } from 'vue' |
| 134 | import Taro, { useLoad, useReachBottom } from '@tarojs/taro' | 137 | import Taro, { useLoad, useReachBottom } from '@tarojs/taro' |
| 135 | import { useFileOperation } from '@/composables/useFileOperation' | 138 | import { useFileOperation } from '@/composables/useFileOperation' |
| 136 | -import { listAPI } from '@/api/plan' | 139 | +import { listAPI, viewAPI, deleteAPI } from '@/api/plan' |
| 137 | import NavHeader from '@/components/navigation/NavHeader.vue' | 140 | import NavHeader from '@/components/navigation/NavHeader.vue' |
| 138 | import ListItemActions from '@/components/list/ListItemActions/index.vue' | 141 | import ListItemActions from '@/components/list/ListItemActions/index.vue' |
| 139 | import SearchBar from '@/components/forms/SearchBar.vue' | 142 | import SearchBar from '@/components/forms/SearchBar.vue' |
| ... | @@ -154,29 +157,55 @@ const pageSize = 20 // 每页数量 | ... | @@ -154,29 +157,55 @@ const pageSize = 20 // 每页数量 |
| 154 | /** | 157 | /** |
| 155 | * Tab 数据源 | 158 | * Tab 数据源 |
| 156 | * @description 包含分类信息和对应的计划书列表 | 159 | * @description 包含分类信息和对应的计划书列表 |
| 157 | - * @description 状态值说明:空字符串=全部,"3"=生成中,"5"=已生成 | 160 | + * @description 状态值说明(根据API文档): |
| 161 | + * - 空字符串 = 全部 | ||
| 162 | + * - "3" = 待处理 | ||
| 163 | + * - "5" = 处理中 | ||
| 164 | + * - "7" = 已生成 | ||
| 165 | + * - "9" = 已查看 | ||
| 158 | */ | 166 | */ |
| 159 | const tabsData = ref([ | 167 | const tabsData = ref([ |
| 160 | { id: '', name: '全部', list: [] }, | 168 | { id: '', name: '全部', list: [] }, |
| 161 | - { id: '3', name: '生成中', list: [] }, | 169 | + { id: '3', name: '待处理', list: [] }, |
| 162 | - { id: '5', name: '已生成', list: [] }, | 170 | + { id: '5', name: '处理中', list: [] }, |
| 171 | + { id: '7', name: '已生成', list: [] }, | ||
| 172 | + { id: '9', name: '已查看', list: [] }, | ||
| 163 | ]) | 173 | ]) |
| 164 | 174 | ||
| 165 | /** | 175 | /** |
| 166 | * 订单状态映射 | 176 | * 订单状态映射 |
| 167 | * @description 将 API 返回的 order_status 映射到前端使用的状态 | 177 | * @description 将 API 返回的 order_status 映射到前端使用的状态 |
| 168 | * @param {string} orderStatus - API 返回的状态值 | 178 | * @param {string} orderStatus - API 返回的状态值 |
| 169 | - * @returns {string} 前端状态:'processing' | 'generated' | 179 | + * @returns {string} 前端状态:'pending' | 'processing' | 'generated' | 'viewed' |
| 170 | */ | 180 | */ |
| 171 | const mapOrderStatus = (orderStatus) => { | 181 | const mapOrderStatus = (orderStatus) => { |
| 172 | - // 根据业务需求: | 182 | + // 根据API文档: |
| 173 | - // "3" = 生成中 | 183 | + // "3" = 待处理 |
| 174 | - // "5" = 已生成 | 184 | + // "5" = 处理中 |
| 175 | - // 其他值 = 生成中(默认) | 185 | + // "7" = 已生成 |
| 176 | - if (orderStatus === '5') { | 186 | + // "9" = 已查看 |
| 177 | - return 'generated' | 187 | + const statusMap = { |
| 188 | + '3': 'pending', | ||
| 189 | + '5': 'processing', | ||
| 190 | + '7': 'generated', | ||
| 191 | + '9': 'viewed' | ||
| 178 | } | 192 | } |
| 179 | - return 'processing' | 193 | + return statusMap[orderStatus] || 'pending' |
| 194 | +} | ||
| 195 | + | ||
| 196 | +/** | ||
| 197 | + * 获取状态文本 | ||
| 198 | + * @param {string} status - 前端状态值 | ||
| 199 | + * @returns {string} 状态文本 | ||
| 200 | + */ | ||
| 201 | +const getStatusText = (status) => { | ||
| 202 | + const textMap = { | ||
| 203 | + 'pending': '待处理', | ||
| 204 | + 'processing': '处理中', | ||
| 205 | + 'generated': '已生成', | ||
| 206 | + 'viewed': '已查看' | ||
| 207 | + } | ||
| 208 | + return textMap[status] || '待处理' | ||
| 180 | } | 209 | } |
| 181 | 210 | ||
| 182 | /** | 211 | /** |
| ... | @@ -186,8 +215,11 @@ const mapOrderStatus = (orderStatus) => { | ... | @@ -186,8 +215,11 @@ const mapOrderStatus = (orderStatus) => { |
| 186 | * @returns {Object} 组件使用的计划书对象 | 215 | * @returns {Object} 组件使用的计划书对象 |
| 187 | */ | 216 | */ |
| 188 | const transformApiItem = (apiItem) => { | 217 | const transformApiItem = (apiItem) => { |
| 189 | - // 获取第一个文件(如果有) | 218 | + // 获取所有文件 |
| 190 | - const firstFile = apiItem.proposal_files && apiItem.proposal_files[0] | 219 | + const proposalFiles = apiItem.proposal_files || [] |
| 220 | + | ||
| 221 | + // 获取第一个文件(作为默认显示) | ||
| 222 | + const firstFile = proposalFiles[0] | ||
| 191 | 223 | ||
| 192 | // 获取第一个分类名称(categories 是对象数组) | 224 | // 获取第一个分类名称(categories 是对象数组) |
| 193 | const categoryName = apiItem.categories && apiItem.categories.length > 0 | 225 | const categoryName = apiItem.categories && apiItem.categories.length > 0 |
| ... | @@ -201,8 +233,11 @@ const transformApiItem = (apiItem) => { | ... | @@ -201,8 +233,11 @@ const transformApiItem = (apiItem) => { |
| 201 | date: apiItem.created_time || '', | 233 | date: apiItem.created_time || '', |
| 202 | tag: categoryName, | 234 | tag: categoryName, |
| 203 | status: mapOrderStatus(apiItem.order_status), | 235 | status: mapOrderStatus(apiItem.order_status), |
| 236 | + // 默认显示第一个文件 | ||
| 204 | fileName: firstFile?.file_name || '', | 237 | fileName: firstFile?.file_name || '', |
| 205 | downloadUrl: firstFile?.file_url || '', | 238 | downloadUrl: firstFile?.file_url || '', |
| 239 | + // 保存所有文件(用于多文件选择) | ||
| 240 | + proposalFiles: proposalFiles, | ||
| 206 | // 保存完整的原始数据 | 241 | // 保存完整的原始数据 |
| 207 | _raw: apiItem | 242 | _raw: apiItem |
| 208 | } | 243 | } |
| ... | @@ -210,54 +245,34 @@ const transformApiItem = (apiItem) => { | ... | @@ -210,54 +245,34 @@ const transformApiItem = (apiItem) => { |
| 210 | 245 | ||
| 211 | /** | 246 | /** |
| 212 | * 加载计划书列表(调用真实 API) | 247 | * 加载计划书列表(调用真实 API) |
| 213 | - * @param {number} page - 页码(从1开始,API 要求) | 248 | + * @param {number} page - 页码(从0开始,API 要求) |
| 214 | * @param {number} limit - 每页数量 | 249 | * @param {number} limit - 每页数量 |
| 215 | * @param {boolean} isLoadMore - 是否为加载更多 | 250 | * @param {boolean} isLoadMore - 是否为加载更多 |
| 216 | * | 251 | * |
| 217 | - * @description 状态过滤说明(前端过滤,因为后端不支持): | 252 | + * @description 状态筛选说明(使用服务端 status 参数筛选): |
| 218 | * - activeTabId = '':不过滤,显示全部 | 253 | * - activeTabId = '':不过滤,显示全部 |
| 219 | - * - activeTabId = '3':只显示生成中的计划书(order_status = "3") | 254 | + * - activeTabId = '3':只显示待处理的计划书 |
| 220 | - * - activeTabId = '5':只显示已生成的计划书(order_status = "5") | 255 | + * - activeTabId = '5':只显示处理中的计划书 |
| 256 | + * - activeTabId = '7':只显示已生成的计划书 | ||
| 257 | + * - activeTabId = '9':只显示已查看的计划书 | ||
| 221 | * | 258 | * |
| 222 | - * @todo ⚠️ 后端API缺少关键查询参数,需要添加以下支持: | 259 | + * ✅ **API参数说明**: |
| 223 | - * | ||
| 224 | - * 1️⃣ **分页参数**(必须): | ||
| 225 | - * - `page` - 页码,从 1 开始 | ||
| 226 | - * - `limit` - 每页数量,默认 20 | ||
| 227 | - * - 当前影响:无法实现真正的分页,只能一次性加载所有数据 | ||
| 228 | - * | ||
| 229 | - * 2️⃣ **状态筛选参数**(必须): | ||
| 230 | - * - `order_status` - 订单状态筛选 | ||
| 231 | - * - ⚠️ **状态值不确定**:当前推测 `"3"` = 生成中,`"5"` = 已生成(需与后端确认) | ||
| 232 | - * - 当前影响:需要前端过滤,性能和分页准确性差 | ||
| 233 | - * | ||
| 234 | - * 3️⃣ **搜索参数**(建议): | ||
| 235 | - * - `keyword` - 搜索关键字(搜索申请人、产品名等) | ||
| 236 | - * - 当前影响:搜索功能可能不准确 | ||
| 237 | - * | ||
| 238 | - * 🔴 **严重问题**: | ||
| 239 | - * - 当前后端API不支持任何查询参数 | ||
| 240 | - * - 前端只能一次性获取全部数据,然后在本地进行分页和过滤 | ||
| 241 | - * - 这导致: | ||
| 242 | - * - 性能问题:数据量大时加载慢 | ||
| 243 | - * - 分页失效:无法实现真正的服务端分页 | ||
| 244 | - * - 内存占用:所有数据都在前端 | ||
| 245 | - * | ||
| 246 | - * ✅ **建议的API参数规范**: | ||
| 247 | * ```javascript | 260 | * ```javascript |
| 248 | * GET /srv/?a=proposal&t=list | 261 | * GET /srv/?a=proposal&t=list |
| 249 | * Query Parameters: | 262 | * Query Parameters: |
| 250 | - * - page: number (必需) - 页码,从 1 开始 | 263 | + * - page: number (必需) - 页码,从 0 开始 |
| 251 | * - limit: number (必需) - 每页数量,默认 20 | 264 | * - limit: number (必需) - 每页数量,默认 20 |
| 252 | - * - order_status: string (可选) - 状态筛选,需与后端确认具体值 | 265 | + * - status: string (可选) - 状态筛选 |
| 253 | * - keyword: string (可选) - 搜索关键字 | 266 | * - keyword: string (可选) - 搜索关键字 |
| 254 | * ``` | 267 | * ``` |
| 255 | * | 268 | * |
| 256 | - * ⚠️ **重要**:order_status 的具体值需要与后端确认! | 269 | + * ⚠️ **重要**:status 参数的具体值(根据API文档): |
| 257 | - * - 当前前端推测:`"3"` = 生成中,`"5"` = 已生成 | 270 | + * - "3" = 待处理 |
| 258 | - * - 实际值可能不同,请后端提供准确的状态值定义 | 271 | + * - "5" = 处理中 |
| 272 | + * - "7" = 已生成 | ||
| 273 | + * - "9" = 已查看 | ||
| 259 | */ | 274 | */ |
| 260 | -const fetchPlanList = async (page = 1, limit = pageSize, isLoadMore = false) => { | 275 | +const fetchPlanList = async (page = 0, limit = pageSize, isLoadMore = false) => { |
| 261 | try { | 276 | try { |
| 262 | // 如果是加载更多,使用 loadingMore 状态,否则使用 loading 状态 | 277 | // 如果是加载更多,使用 loadingMore 状态,否则使用 loading 状态 |
| 263 | if (isLoadMore) { | 278 | if (isLoadMore) { |
| ... | @@ -267,12 +282,16 @@ const fetchPlanList = async (page = 1, limit = pageSize, isLoadMore = false) => | ... | @@ -267,12 +282,16 @@ const fetchPlanList = async (page = 1, limit = pageSize, isLoadMore = false) => |
| 267 | } | 282 | } |
| 268 | 283 | ||
| 269 | // 构建请求参数 | 284 | // 构建请求参数 |
| 270 | - // 注意:后端不支持 order_status 参数,需要在前端过滤 | ||
| 271 | const params = { | 285 | const params = { |
| 272 | page: page, | 286 | page: page, |
| 273 | limit: limit | 287 | limit: limit |
| 274 | } | 288 | } |
| 275 | 289 | ||
| 290 | + // 状态筛选(使用 status 参数) | ||
| 291 | + if (activeTabId.value !== '') { | ||
| 292 | + params.status = activeTabId.value | ||
| 293 | + } | ||
| 294 | + | ||
| 276 | // 搜索关键字 | 295 | // 搜索关键字 |
| 277 | if (searchValue.value) { | 296 | if (searchValue.value) { |
| 278 | params.keyword = searchValue.value | 297 | params.keyword = searchValue.value |
| ... | @@ -283,12 +302,6 @@ const fetchPlanList = async (page = 1, limit = pageSize, isLoadMore = false) => | ... | @@ -283,12 +302,6 @@ const fetchPlanList = async (page = 1, limit = pageSize, isLoadMore = false) => |
| 283 | 302 | ||
| 284 | if (res.code === 1 && res.data) { | 303 | if (res.code === 1 && res.data) { |
| 285 | let apiList = res.data.list || [] | 304 | let apiList = res.data.list || [] |
| 286 | - | ||
| 287 | - // ⚠️ 前端过滤:因为后端不支持 order_status 参数 | ||
| 288 | - if (activeTabId.value !== '') { | ||
| 289 | - apiList = apiList.filter(item => item.order_status === activeTabId.value) | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | const transformedList = apiList.map(transformApiItem) | 305 | const transformedList = apiList.map(transformApiItem) |
| 293 | 306 | ||
| 294 | if (isLoadMore) { | 307 | if (isLoadMore) { |
| ... | @@ -336,8 +349,8 @@ const onTabClick = (id) => { | ... | @@ -336,8 +349,8 @@ const onTabClick = (id) => { |
| 336 | activeTabId.value = id | 349 | activeTabId.value = id |
| 337 | listVisible.value = false | 350 | listVisible.value = false |
| 338 | 351 | ||
| 339 | - // 重置分页状态(从第 1 页开始) | 352 | + // 重置分页状态(从第 0 页开始) |
| 340 | - currentPage.value = 1 | 353 | + currentPage.value = 0 |
| 341 | hasMore.value = true | 354 | hasMore.value = true |
| 342 | 355 | ||
| 343 | nextTick(() => { | 356 | nextTick(() => { |
| ... | @@ -345,7 +358,7 @@ const onTabClick = (id) => { | ... | @@ -345,7 +358,7 @@ const onTabClick = (id) => { |
| 345 | listVisible.value = true | 358 | listVisible.value = true |
| 346 | 359 | ||
| 347 | // 重新加载数据 | 360 | // 重新加载数据 |
| 348 | - fetchPlanList(1, pageSize, false) | 361 | + fetchPlanList(0, pageSize, false) |
| 349 | }) | 362 | }) |
| 350 | } | 363 | } |
| 351 | 364 | ||
| ... | @@ -353,14 +366,14 @@ const onTabClick = (id) => { | ... | @@ -353,14 +366,14 @@ const onTabClick = (id) => { |
| 353 | * 搜索处理 | 366 | * 搜索处理 |
| 354 | */ | 367 | */ |
| 355 | const onSearch = () => { | 368 | const onSearch = () => { |
| 356 | - // 重置分页状态(从第 1 页开始) | 369 | + // 重置分页状态(从第 0 页开始) |
| 357 | - currentPage.value = 1 | 370 | + currentPage.value = 0 |
| 358 | hasMore.value = true | 371 | hasMore.value = true |
| 359 | 372 | ||
| 360 | listRenderKey.value += 1 | 373 | listRenderKey.value += 1 |
| 361 | 374 | ||
| 362 | // 重新加载数据 | 375 | // 重新加载数据 |
| 363 | - fetchPlanList(1, pageSize, false) | 376 | + fetchPlanList(0, pageSize, false) |
| 364 | } | 377 | } |
| 365 | 378 | ||
| 366 | /** | 379 | /** |
| ... | @@ -372,14 +385,14 @@ const onClear = () => { | ... | @@ -372,14 +385,14 @@ const onClear = () => { |
| 372 | // 清空搜索值 | 385 | // 清空搜索值 |
| 373 | searchValue.value = '' | 386 | searchValue.value = '' |
| 374 | 387 | ||
| 375 | - // 重置分页状态(从第 1 页开始) | 388 | + // 重置分页状态(从第 0 页开始) |
| 376 | - currentPage.value = 1 | 389 | + currentPage.value = 0 |
| 377 | hasMore.value = true | 390 | hasMore.value = true |
| 378 | 391 | ||
| 379 | listRenderKey.value += 1 | 392 | listRenderKey.value += 1 |
| 380 | 393 | ||
| 381 | // 重新加载数据 | 394 | // 重新加载数据 |
| 382 | - fetchPlanList(1, pageSize, false) | 395 | + fetchPlanList(0, pageSize, false) |
| 383 | 396 | ||
| 384 | console.log('[Plan Page] onClear 完成,列表已刷新') | 397 | console.log('[Plan Page] onClear 完成,列表已刷新') |
| 385 | } | 398 | } |
| ... | @@ -388,8 +401,8 @@ const onClear = () => { | ... | @@ -388,8 +401,8 @@ const onClear = () => { |
| 388 | * 页面加载时初始化数据 | 401 | * 页面加载时初始化数据 |
| 389 | */ | 402 | */ |
| 390 | useLoad(() => { | 403 | useLoad(() => { |
| 391 | - // 加载第一页数据 | 404 | + // 加载第一页数据(page = 0) |
| 392 | - fetchPlanList(1, pageSize, false) | 405 | + fetchPlanList(0, pageSize, false) |
| 393 | }) | 406 | }) |
| 394 | 407 | ||
| 395 | /** | 408 | /** |
| ... | @@ -428,46 +441,119 @@ useReachBottom(() => { | ... | @@ -428,46 +441,119 @@ useReachBottom(() => { |
| 428 | * @param {Object} item - 计划书对象 | 441 | * @param {Object} item - 计划书对象 |
| 429 | */ | 442 | */ |
| 430 | const onView = async (item) => { | 443 | const onView = async (item) => { |
| 431 | - // 检查是否已生成 | 444 | + // 检查是否已生成(只有"已生成"或"已查看"状态才能查看) |
| 432 | - if (item.status === 'processing') { | 445 | + if (item.status === 'pending' || item.status === 'processing') { |
| 446 | + Taro.showToast({ | ||
| 447 | + title: '计划书尚未生成,请稍后', | ||
| 448 | + icon: 'none' | ||
| 449 | + }) | ||
| 450 | + return | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + // 检查是否有计划书文件 | ||
| 454 | + if (!item.proposalFiles || item.proposalFiles.length === 0) { | ||
| 433 | Taro.showToast({ | 455 | Taro.showToast({ |
| 434 | - title: '计划书生成中,请稍后', | 456 | + title: '暂无可查看的计划书', |
| 435 | icon: 'none' | 457 | icon: 'none' |
| 436 | }) | 458 | }) |
| 437 | return | 459 | return |
| 438 | } | 460 | } |
| 439 | 461 | ||
| 440 | - // 使用 useFileOperation 查看文档 | 462 | + /** |
| 441 | - await viewFile({ | 463 | + * 处理文件查看 |
| 442 | - downloadUrl: item.downloadUrl, | 464 | + * @param {Object} file - 文件对象 |
| 443 | - fileName: item.fileName | 465 | + */ |
| 466 | + const handleFileView = async (file) => { | ||
| 467 | + try { | ||
| 468 | + // 调用 viewAPI 标记为已查看 | ||
| 469 | + const viewRes = await viewAPI({ i: item.id }) | ||
| 470 | + | ||
| 471 | + if (viewRes.code === 1) { | ||
| 472 | + // 更新本地状态为"已查看" | ||
| 473 | + item.status = 'viewed' | ||
| 474 | + | ||
| 475 | + // 显示提示 | ||
| 476 | + Taro.showToast({ | ||
| 477 | + title: '已标记为查看', | ||
| 478 | + icon: 'success', | ||
| 479 | + duration: 1000 | ||
| 480 | + }) | ||
| 481 | + } | ||
| 482 | + } catch (error) { | ||
| 483 | + console.error('标记查看失败:', error) | ||
| 484 | + // 即使标记失败,也继续查看文档 | ||
| 485 | + } | ||
| 486 | + | ||
| 487 | + // 使用 useFileOperation 查看文档 | ||
| 488 | + await viewFile({ | ||
| 489 | + downloadUrl: file.file_url, | ||
| 490 | + fileName: file.file_name | ||
| 491 | + }) | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + // 如果只有一个文件,直接查看 | ||
| 495 | + if (item.proposalFiles.length === 1) { | ||
| 496 | + await handleFileView(item.proposalFiles[0]) | ||
| 497 | + return | ||
| 498 | + } | ||
| 499 | + | ||
| 500 | + // 如果有多个文件,显示选择列表 | ||
| 501 | + const fileList = item.proposalFiles.map((file, index) => ({ | ||
| 502 | + text: file.file_name || `计划书 ${index + 1}`, | ||
| 503 | + file: file | ||
| 504 | + })) | ||
| 505 | + | ||
| 506 | + // 使用 Taro.showActionSheet 显示文件选择列表 | ||
| 507 | + Taro.showActionSheet({ | ||
| 508 | + itemList: fileList.map(f => f.text), | ||
| 509 | + success: async (res) => { | ||
| 510 | + const selectedIndex = res.tapIndex | ||
| 511 | + if (selectedIndex !== undefined && selectedIndex >= 0) { | ||
| 512 | + const selectedFile = fileList[selectedIndex].file | ||
| 513 | + await handleFileView(selectedFile) | ||
| 514 | + } | ||
| 515 | + } | ||
| 444 | }) | 516 | }) |
| 445 | } | 517 | } |
| 446 | 518 | ||
| 447 | /** | 519 | /** |
| 448 | * 删除计划书 | 520 | * 删除计划书 |
| 449 | - * @description 注意:当前后端可能未提供删除接口,暂时只做前端提示 | 521 | + * @description 调用删除API删除计划书 |
| 450 | */ | 522 | */ |
| 451 | -const onDelete = (item) => { | 523 | +const onDelete = async (item) => { |
| 452 | Taro.showModal({ | 524 | Taro.showModal({ |
| 453 | - title: '提示', | 525 | + title: '确认删除', |
| 454 | - content: '删除功能需要后端支持,是否继续?', | 526 | + content: `确定要删除"${item.title}"吗?`, |
| 455 | - success: (res) => { | 527 | + success: async (res) => { |
| 456 | if (res.confirm) { | 528 | if (res.confirm) { |
| 457 | - // TODO: 调用删除 API | 529 | + try { |
| 458 | - // const res = await deletePlanAPI({ id: item.id }) | 530 | + // 调用删除API |
| 459 | - // if (res.code === 1) { | 531 | + const deleteRes = await deleteAPI({ i: item.id }) |
| 460 | - // Taro.showToast({ title: '已删除', icon: 'success' }) | 532 | + |
| 461 | - // // 重新加载列表 | 533 | + if (deleteRes.code === 1) { |
| 462 | - // currentPage.value = 1 | 534 | + Taro.showToast({ |
| 463 | - // fetchPlanList(1, pageSize, false) | 535 | + title: '删除成功', |
| 464 | - // } | 536 | + icon: 'success', |
| 465 | - | 537 | + duration: 1500 |
| 466 | - Taro.showToast({ | 538 | + }) |
| 467 | - title: '删除功能待实现', | 539 | + |
| 468 | - icon: 'none', | 540 | + // 重新加载列表 |
| 469 | - duration: 2000 | 541 | + currentPage.value = 0 |
| 470 | - }) | 542 | + hasMore.value = true |
| 543 | + await fetchPlanList(0, pageSize, false) | ||
| 544 | + } else { | ||
| 545 | + Taro.showToast({ | ||
| 546 | + title: deleteRes.msg || '删除失败', | ||
| 547 | + icon: 'none' | ||
| 548 | + }) | ||
| 549 | + } | ||
| 550 | + } catch (error) { | ||
| 551 | + console.error('删除失败:', error) | ||
| 552 | + Taro.showToast({ | ||
| 553 | + title: '网络异常,请重试', | ||
| 554 | + icon: 'none' | ||
| 555 | + }) | ||
| 556 | + } | ||
| 471 | } | 557 | } |
| 472 | } | 558 | } |
| 473 | }) | 559 | }) |
| ... | @@ -585,13 +671,23 @@ const onDelete = (item) => { | ... | @@ -585,13 +671,23 @@ const onDelete = (item) => { |
| 585 | white-space: nowrap; | 671 | white-space: nowrap; |
| 586 | } | 672 | } |
| 587 | 673 | ||
| 588 | -.status-processing { | 674 | +.status-pending { |
| 589 | background-color: #FEF3C7; | 675 | background-color: #FEF3C7; |
| 590 | color: #D97706; | 676 | color: #D97706; |
| 591 | } | 677 | } |
| 592 | 678 | ||
| 679 | +.status-processing { | ||
| 680 | + background-color: #DBEAFE; | ||
| 681 | + color: #2563EB; | ||
| 682 | +} | ||
| 683 | + | ||
| 593 | .status-generated { | 684 | .status-generated { |
| 594 | background-color: #D1FAE5; | 685 | background-color: #D1FAE5; |
| 595 | color: #059669; | 686 | color: #059669; |
| 596 | } | 687 | } |
| 688 | + | ||
| 689 | +.status-viewed { | ||
| 690 | + background-color: #E5E7EB; | ||
| 691 | + color: #6B7280; | ||
| 692 | +} | ||
| 597 | </style> | 693 | </style> | ... | ... |
-
Please register or login to post a comment