From 881f8b990e508919bfec0094047c3f15c84fc1d5 Mon Sep 17 00:00:00 2001 From: WindowBird <13870814+windows-bird@user.noreply.gitee.com> Date: Thu, 6 Nov 2025 15:27:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=8E=B7=E5=8F=96=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=AF=A6=E7=BB=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/api.js | 13 ++ pages/task-detail/index.vue | 281 +++++++++++++++++++++++++++++++++--- 2 files changed, 273 insertions(+), 21 deletions(-) diff --git a/common/api.js b/common/api.js index cbf5292..95a4a2f 100644 --- a/common/api.js +++ b/common/api.js @@ -72,3 +72,16 @@ export const getTaskList = ({ overdue, statusList, expireTimeStart, expireTimeEn }); }; +/** + * 获取任务详情 + * @param {string} id 任务ID + * @returns {Promise} 返回任务详情 + */ +export const getTaskDetail = (id) => { + return uni.$uv.http.get(`bst/task/${id}`, { + custom: { + auth: true // 启用 token 认证 + } + }); +}; + diff --git a/pages/task-detail/index.vue b/pages/task-detail/index.vue index 320b165..c26dcdd 100644 --- a/pages/task-detail/index.vue +++ b/pages/task-detail/index.vue @@ -29,7 +29,15 @@ 创建人: - {{ task.creator }} + + + {{ task.creator }} + 负责人: @@ -81,7 +89,13 @@ @@ -150,6 +164,7 @@ import { ref, onMounted, } from 'vue'; import { onLoad,onShow } from '@dcloudio/uni-app'; import { getStatusFromTagText, getTaskStatusType, getTaskStatusStyle } from '@/utils/taskConfig.js'; import { useTaskStore } from '@/store/task'; +import { getTaskDetail } from '@/common/api.js'; // 当前激活的标签 const activeTab = ref('info'); @@ -157,10 +172,12 @@ const showMenuIndex = ref(-1); // 格式化时间为中文格式:年月日星期几时分秒 const formatTimeToChinese = (date) => { + if (!date) return ''; if (typeof date === 'string') { // 如果是字符串,尝试解析 date = new Date(date); } + if (isNaN(date.getTime())) return ''; const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); @@ -173,6 +190,152 @@ const formatTimeToChinese = (date) => { return `${year}年${month}月${day}日 ${weekday} ${hour}:${minute}:${second}`; }; +// 格式化日期:将 "2024-10-31 23:59:59" 转换为 "2024-10-31" +const formatDate = (dateStr) => { + if (!dateStr) return ''; + // 如果包含空格,取日期部分 + return dateStr.split(' ')[0]; +}; + +// 格式化日期时间:格式化为 yyyy-MM-dd HH:mm:ss +const formatDateTime = (date) => { + if (!date) return ''; + const d = new Date(date); + if (isNaN(d.getTime())) return ''; + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + const hours = String(d.getHours()).padStart(2, '0'); + const minutes = String(d.getMinutes()).padStart(2, '0'); + const seconds = String(d.getSeconds()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +}; + +// 计算剩余天数 +const calculateRemainingDays = (expireTime) => { + if (!expireTime) return null; + const expireDate = new Date(expireTime); + const now = new Date(); + now.setHours(0, 0, 0, 0); + expireDate.setHours(0, 0, 0, 0); + const diffTime = expireDate.getTime() - now.getTime(); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + return diffDays; +}; + +// 根据过期时间和状态判断任务状态 +const determineTaskStatus = (status, expireTime) => { + // 如果任务已完成(状态为4),直接返回 completed + const taskStatus = status !== undefined ? status : null; + const isCompleted = taskStatus === 4 || + taskStatus === '4' || + taskStatus === 'completed' || + String(taskStatus) === '4'; + + if (isCompleted) { + return 'completed'; + } + + // 如果没有过期时间,返回待完成 + if (!expireTime) { + return 'pending'; + } + + const expireDate = new Date(expireTime); + const now = new Date(); + + // 设置时间到当天0点,便于日期比较 + now.setHours(0, 0, 0, 0); + expireDate.setHours(23, 59, 59, 999); + + // 如果已过期,标记为逾期 + if (expireDate.getTime() < now.getTime()) { + return 'overdue'; + } + + // 计算距离过期的天数 + const diffTime = expireDate.getTime() - now.getTime(); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + + // 如果3天内到期,标记为即将逾期 + if (diffDays <= 3 && diffDays > 0) { + return 'imminent'; + } + + // 否则返回待完成状态 + return 'pending'; +}; + +// 获取状态标签数组 +const getStatusTags = (status, expireTime) => { + const taskStatus = determineTaskStatus(status, expireTime); + const tags = []; + + if (taskStatus === 'completed') { + tags.push('已完成'); + } else if (taskStatus === 'overdue') { + tags.push('已逾期', '紧急'); + } else if (taskStatus === 'imminent') { + tags.push('即将逾期'); + } else { + tags.push('待完成'); + } + + return tags; +}; + +// 提取负责人:从 memberList 中提取所有成员的名称 +const getOwnerNames = (memberList) => { + if (!Array.isArray(memberList) || memberList.length === 0) return ''; + return memberList.map(member => member.userName || member.name || '').filter(name => name).join('、'); +}; + +// 转换提交记录数据 +const transformSubmitRecords = (submitList) => { + if (!Array.isArray(submitList) || submitList.length === 0) { + return []; + } + + return submitList.map(item => { + // 处理附件 + let attachments = []; + if (item.attaches) { + try { + // 如果 attaches 是字符串,尝试解析 + const attachData = typeof item.attaches === 'string' ? JSON.parse(item.attaches) : item.attaches; + if (Array.isArray(attachData)) { + attachments = attachData.map(att => { + // 根据文件扩展名判断类型 + const fileName = att.name || att.fileName || ''; + const filePath = att.path || att.url || att.filePath || ''; + const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(fileName); + + return { + type: isImage ? 'image' : 'file', + name: fileName, + path: filePath + }; + }); + } + } catch (e) { + console.error('解析附件数据失败:', e); + } + } + + return { + id: item.id || '', + userName: item.userName || '', + userAvatar: item.userAvatar || '', + time: formatTimeToChinese(item.createTime) || '', + content: item.remark || item.description || item.taskDescription || '', // 如果没有提交内容,可能显示任务描述 + progress: null, // API 返回的数据中没有进度字段 + attachments: attachments, + showDelayBtn: false, // 根据业务需求决定是否显示 + canEdit: true // 根据业务需求决定是否可以编辑 + }; + }); +}; + // 任务数据 const task = ref({ @@ -308,13 +471,59 @@ const applyDelay = () => { }); }; -// 加载任务数据(模拟) -const loadTaskData = (taskId) => { - // 这里可以根据 taskId 从 API 加载任务数据 - // 暂时使用模拟数据 - if (taskId) { - // 可以根据 taskId 加载不同的任务数据 - console.log('加载任务数据:', taskId); +// 加载任务数据 +const loadTaskData = async (taskId) => { + if (!taskId) { + uni.showToast({ + title: '任务ID不能为空', + icon: 'none' + }); + return; + } + + try { + // 显示加载提示 + uni.showLoading({ + title: '加载中...' + }); + + // 调用 API 获取任务详情 + const res = await getTaskDetail(taskId); + console.log('任务详情数据:', res); + + // 转换数据格式 + const taskStatus = res.status !== undefined ? res.status : null; + const expireTime = res.expireTime || null; + const statusTags = getStatusTags(taskStatus, expireTime); + + // 转换提交记录 + const submitRecords = transformSubmitRecords(res.submitList || []); + + // 更新任务数据 + task.value = { + id: res.id || taskId, + name: res.description || '任务名称', + project: res.projectName || '', + statusTags: statusTags, + deadline: expireTime ? expireTime : '无', + creator: res.createName || '', + creatorAvatar: res.createAvatar || '', + responsible: getOwnerNames(res.memberList || []), + publishTime: res.createTime ? formatTimeToChinese(res.createTime) : '', + content: res.description || '', + submitRecords: submitRecords, + // 保存原始数据,供其他功能使用 + rawData: res + }; + + uni.hideLoading(); + } catch (err) { + console.error('加载任务详情失败:', err); + uni.hideLoading(); + uni.showToast({ + title: '加载任务详情失败', + icon: 'none' + }); } }; @@ -323,19 +532,26 @@ onLoad((options) => { const taskId = options.id || options.taskId; if (taskId) { task.value.id = taskId; + // 优先从 API 加载数据 loadTaskData(taskId); - } - - // 从 Pinia store 获取任务详情数据 - const taskStore = useTaskStore(); - const storedTask = taskStore.getTaskDetail; - if (storedTask) { - task.value = { - ...task.value, - ...storedTask - }; - // 清除 store 中的数据(因为已经使用完毕) - // taskStore.clearTaskDetail(); + } else { + // 如果没有 taskId,尝试从 Pinia store 获取任务详情数据(兼容旧逻辑) + const taskStore = useTaskStore(); + const storedTask = taskStore.getTaskDetail; + if (storedTask) { + task.value = { + ...task.value, + ...storedTask + }; + } else { + uni.showToast({ + title: '缺少任务ID', + icon: 'none' + }); + setTimeout(() => { + uni.navigateBack(); + }, 1500); + } } }); @@ -596,6 +812,29 @@ onShow(() => { flex-shrink: 0; } +.avatar-img { + width: 32px; + height: 32px; + border-radius: 50%; + flex-shrink: 0; +} + +.info-value-with-avatar { + flex: 1; + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: #333; +} + +.creator-avatar { + width: 24px; + height: 24px; + border-radius: 50%; + flex-shrink: 0; +} + .user-name { font-size: 15px; color: #333;