From 3c2365660b3e576278d5a7ae79071db46812f078 Mon Sep 17 00:00:00 2001 From: WindowBird <13870814+windows-bird@user.noreply.gitee.com> Date: Thu, 6 Nov 2025 16:49:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/api.js | 20 +++ pages/submit-task/index.vue | 82 +++++----- pages/task-detail/index.vue | 299 ++++++++++++++++++++++++++++-------- 3 files changed, 295 insertions(+), 106 deletions(-) diff --git a/common/api.js b/common/api.js index 69e1b00..20bda64 100644 --- a/common/api.js +++ b/common/api.js @@ -97,3 +97,23 @@ export const getQiniuUploadToken = () => { }); }; +/** + * 提交任务 + * @param {Object} params 请求参数 + * @param {string} params.id 任务ID + * @param {string} params.submitAttaches 附件, 逗号分隔的URL字符串 + * @param {string} params.submitRemark 备注 + * @returns {Promise} 返回提交结果 + */ +export const submitTask = ({ id, submitAttaches, submitRemark }) => { + return uni.$uv.http.put('/bst/task/submit', { + id: id, + submitAttaches: submitAttaches || '', + submitRemark: submitRemark || '' + }, { + custom: { + auth: true // 启用 token 认证 + } + }); +}; + diff --git a/pages/submit-task/index.vue b/pages/submit-task/index.vue index fd827cf..d795f4c 100644 --- a/pages/submit-task/index.vue +++ b/pages/submit-task/index.vue @@ -122,6 +122,7 @@ import { ref, computed, onMounted } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import { chooseAndUploadImages } from '@/utils/qiniu.js'; +import { submitTask } from '@/common/api.js'; // 表单数据 const formData = ref({ @@ -416,7 +417,7 @@ const formatTimeToChinese = (date) => { }; // 提交任务 -const handleSubmit = () => { +const handleSubmit = async () => { if (!canSubmit.value) { uni.showToast({ title: '请至少填写提交说明或添加附件', @@ -425,51 +426,48 @@ const handleSubmit = () => { return; } + if (!taskId.value) { + uni.showToast({ + title: '任务ID不能为空', + icon: 'none' + }); + return; + } + uni.showLoading({ title: isEditMode.value ? '更新中...' : '提交中...' }); - // 准备提交数据 - const submitData = { - taskId: taskId.value, - description: formData.value.description.trim(), - progress: formData.value.progress, - images: formData.value.images, - files: formData.value.files.map(file => ({ - name: file.name, - path: file.path, - size: file.size - })) - }; + try { + // 合并所有附件URL(图片和文件) + const allAttaches = [ + ...formData.value.images, // 图片已经是七牛云URL + ...formData.value.files.map(file => file.path) // 文件路径(如果是URL则直接使用,如果是本地路径可能需要先上传) + ].filter(url => url && url.trim() !== ''); // 过滤空值 - // TODO: 调用提交接口 - // 实际使用时,应该调用API接口上传数据 - // uni.request({ - // url: isEditMode.value ? '/api/task/submit/update' : '/api/task/submit', - // method: isEditMode.value ? 'PUT' : 'POST', - // data: submitData, - // success: (res) => { - // // 处理成功响应 - // }, - // fail: (err) => { - // // 处理错误 - // } - // }); + // 将附件数组转换为逗号分隔的字符串 + const submitAttaches = allAttaches.join(','); + + // 调用提交接口 + await submitTask({ + id: taskId.value, + submitAttaches: submitAttaches, + submitRemark: formData.value.description.trim() + }); - // 模拟提交请求 - setTimeout(() => { uni.hideLoading(); - + + // 提交成功后的处理 if (isEditMode.value) { // 编辑模式:更新现有记录 const updatedRecord = { userName: editRecordData.value?.userName || '当前用户', // 保持原用户名 time: formatTimeToChinese(new Date()), // 更新时间 - content: submitData.description || '', - progress: submitData.progress, + content: formData.value.description.trim() || '', + progress: formData.value.progress, attachments: [ - ...submitData.images.map(img => ({ type: 'image', path: img })), - ...submitData.files.map(file => ({ type: 'file', name: file.name, path: file.path })) + ...formData.value.images.map(img => ({ type: 'image', path: img })), + ...formData.value.files.map(file => ({ type: 'file', name: file.name, path: file.path })) ], canEdit: true, // 保持可编辑权限 showDelayBtn: editRecordData.value?.showDelayBtn || false @@ -490,11 +488,11 @@ const handleSubmit = () => { const submitRecord = { userName: '当前用户', // TODO: 从用户信息获取 time: formatTimeToChinese(new Date()), - content: submitData.description || '', - progress: submitData.progress, + content: formData.value.description.trim() || '', + progress: formData.value.progress, attachments: [ - ...submitData.images.map(img => ({ type: 'image', path: img })), - ...submitData.files.map(file => ({ type: 'file', name: file.name, path: file.path })) + ...formData.value.images.map(img => ({ type: 'image', path: img })), + ...formData.value.files.map(file => ({ type: 'file', name: file.name, path: file.path })) ], canEdit: true, showDelayBtn: false @@ -513,7 +511,15 @@ const handleSubmit = () => { setTimeout(() => { uni.navigateBack(); }, 1500); - }, 1000); + } catch (error) { + uni.hideLoading(); + console.error('提交任务失败:', error); + uni.showToast({ + title: error.message || '提交失败,请重试', + icon: 'none', + duration: 2000 + }); + } }; diff --git a/pages/task-detail/index.vue b/pages/task-detail/index.vue index c26dcdd..47767c0 100644 --- a/pages/task-detail/index.vue +++ b/pages/task-detail/index.vue @@ -72,8 +72,21 @@ {{ task.content }} - {{ task.content }} - {{ task.content }} + + + + + + 申请延期 @@ -120,23 +133,30 @@ 任务进度: {{ record.progress }}% - + + - - 📄 - {{ attachment.name }} - + + + + + + 📄 + {{ file.name }} @@ -290,6 +310,21 @@ const getOwnerNames = (memberList) => { return memberList.map(member => member.userName || member.name || '').filter(name => name).join('、'); }; +// 解析逗号分隔的URL字符串 +const parseAttachUrls = (attachStr) => { + if (!attachStr) return []; + if (typeof attachStr !== 'string') return []; + + // 按逗号分割,过滤空值 + return attachStr.split(',').map(url => url.trim()).filter(url => url); +}; + +// 判断URL是否为图片 +const isImageUrl = (url) => { + if (!url || typeof url !== 'string') return false; + return /\.(jpg|jpeg|png|gif|bmp|webp)(\?|$)/i.test(url); +}; + // 转换提交记录数据 const transformSubmitRecords = (submitList) => { if (!Array.isArray(submitList) || submitList.length === 0) { @@ -297,28 +332,62 @@ const transformSubmitRecords = (submitList) => { } return submitList.map(item => { - // 处理附件 - let attachments = []; + // 处理附件:可能是逗号分隔的URL字符串 + let imageAttachments = []; + let fileAttachments = []; + if (item.attaches) { try { - // 如果 attaches 是字符串,尝试解析 - const attachData = typeof item.attaches === 'string' ? JSON.parse(item.attaches) : item.attaches; + // 先尝试作为JSON解析 + let attachData = null; + try { + attachData = typeof item.attaches === 'string' ? JSON.parse(item.attaches) : item.attaches; + } catch (e) { + // 如果不是JSON,则作为逗号分隔的URL字符串处理 + attachData = null; + } + if (Array.isArray(attachData)) { - attachments = attachData.map(att => { - // 根据文件扩展名判断类型 - const fileName = att.name || att.fileName || ''; + // 如果是数组格式 + attachData.forEach(att => { const filePath = att.path || att.url || att.filePath || ''; - const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(fileName); + const fileName = att.name || att.fileName || ''; + const isImage = fileName ? /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(fileName) : isImageUrl(filePath); - return { - type: isImage ? 'image' : 'file', - name: fileName, - path: filePath - }; + if (isImage && filePath) { + imageAttachments.push(filePath); + } else if (filePath) { + fileAttachments.push({ + name: fileName || '文件', + path: filePath + }); + } + }); + } else { + // 作为逗号分隔的URL字符串处理 + const urls = parseAttachUrls(item.attaches); + urls.forEach(url => { + if (isImageUrl(url)) { + imageAttachments.push(url); + } else { + // 从URL中提取文件名 + const fileName = url.split('/').pop().split('?')[0] || '文件'; + fileAttachments.push({ + name: fileName, + path: url + }); + } }); } } catch (e) { console.error('解析附件数据失败:', e); + // 如果解析失败,尝试作为逗号分隔的URL字符串处理 + const urls = parseAttachUrls(item.attaches); + urls.forEach(url => { + if (isImageUrl(url)) { + imageAttachments.push(url); + } + }); } } @@ -329,7 +398,8 @@ const transformSubmitRecords = (submitList) => { time: formatTimeToChinese(item.createTime) || '', content: item.remark || item.description || item.taskDescription || '', // 如果没有提交内容,可能显示任务描述 progress: null, // API 返回的数据中没有进度字段 - attachments: attachments, + imageAttachments: imageAttachments, + fileAttachments: fileAttachments, showDelayBtn: false, // 根据业务需求决定是否显示 canEdit: true // 根据业务需求决定是否可以编辑 }; @@ -402,17 +472,22 @@ const deleteRecord = (index) => { }); }; -// 预览附件图片 -const previewAttachmentImage = (attachments, index) => { - const imageUrls = attachments - .filter(att => att.type === 'image') - .map(att => att.path); - const currentIndex = attachments.slice(0, index).filter(att => att.type === 'image').length; - - if (imageUrls.length > 0) { +// 预览任务图片 +const previewTaskImages = (imageUrls, index) => { + if (imageUrls && imageUrls.length > 0) { uni.previewImage({ urls: imageUrls, - current: currentIndex + current: index + }); + } +}; + +// 预览提交记录图片 +const previewRecordImages = (imageUrls, index) => { + if (imageUrls && imageUrls.length > 0) { + uni.previewImage({ + urls: imageUrls, + current: index }); } }; @@ -427,7 +502,7 @@ const getTagType = (tagText) => { const getTagStyle = (tagText) => { const status = getStatusFromTagText(tagText); const styleConfig = getTaskStatusStyle(status); - return { + return { backgroundColor: styleConfig.backgroundColor, color: styleConfig.color, borderColor: styleConfig.borderColor @@ -499,6 +574,9 @@ const loadTaskData = async (taskId) => { // 转换提交记录 const submitRecords = transformSubmitRecords(res.submitList || []); + // 解析任务图片(逗号分隔的URL字符串) + const taskPictures = res.picture ? parseAttachUrls(res.picture) : []; + // 更新任务数据 task.value = { id: res.id || taskId, @@ -511,6 +589,7 @@ const loadTaskData = async (taskId) => { responsible: getOwnerNames(res.memberList || []), publishTime: res.createTime ? formatTimeToChinese(res.createTime) : '', content: res.description || '', + pictures: taskPictures, // 任务图片数组 submitRecords: submitRecords, // 保存原始数据,供其他功能使用 rawData: res @@ -555,13 +634,52 @@ onLoad((options) => { } }); +// 转换旧格式的提交记录为新格式(兼容本地存储的数据) +const convertOldFormatRecord = (record) => { + // 如果已经是新格式(有 imageAttachments 和 fileAttachments),直接返回 + if (record.imageAttachments !== undefined || record.fileAttachments !== undefined) { + return record; + } + + // 如果是旧格式(有 attachments 数组),转换为新格式 + if (record.attachments && Array.isArray(record.attachments)) { + const imageAttachments = []; + const fileAttachments = []; + + record.attachments.forEach(att => { + if (att.type === 'image' && att.path) { + imageAttachments.push(att.path); + } else if (att.type === 'file') { + fileAttachments.push({ + name: att.name || '文件', + path: att.path || '' + }); + } + }); + + return { + ...record, + imageAttachments: imageAttachments, + fileAttachments: fileAttachments + }; + } + + // 如果都没有,返回空数组 + return { + ...record, + imageAttachments: [], + fileAttachments: [] + }; +}; + // 页面显示时检查是否有新的提交记录或更新的记录 onShow(() => { // 检查是否有新的提交记录 const newSubmitRecord = uni.getStorageSync('newSubmitRecord'); if (newSubmitRecord) { - // 将新提交记录添加到列表开头 - task.value.submitRecords.unshift(newSubmitRecord); + // 转换格式并添加到列表开头 + const convertedRecord = convertOldFormatRecord(newSubmitRecord); + task.value.submitRecords.unshift(convertedRecord); // 切换到提交记录标签页 activeTab.value = 'records'; // 清除存储的记录 @@ -573,8 +691,9 @@ onShow(() => { if (updatedSubmitRecord) { const { recordIndex, record } = updatedSubmitRecord; if (recordIndex !== undefined && recordIndex >= 0 && recordIndex < task.value.submitRecords.length) { - // 更新指定索引的记录 - task.value.submitRecords[recordIndex] = record; + // 转换格式并更新指定索引的记录 + const convertedRecord = convertOldFormatRecord(record); + task.value.submitRecords[recordIndex] = convertedRecord; // 切换到提交记录标签页 activeTab.value = 'records'; } @@ -935,38 +1054,82 @@ onShow(() => { font-weight: 600; } -.record-attachments { +/* 任务图片展示(一行最多三张) */ +.task-images-wrapper { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 16px; +} + +.task-image-item { + /* 一行三个:每个图片宽度 = (100% - 2个gap) / 3 */ + width: calc((100% - 16px) / 3); + aspect-ratio: 1; + border-radius: 4px; + overflow: hidden; + background-color: #e0e0e0; + flex-shrink: 0; +} + +.task-image { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* 提交记录图片展示(一行三个) */ +.record-images-wrapper { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } -.attachment-item { - .attachment-image { - width: 80px; - height: 80px; - border-radius: 4px; - background-color: #e0e0e0; - } - - .file-attachment { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - background-color: #f5f5f5; - border-radius: 4px; - } - - .file-icon { - font-size: 16px; - } - - .file-name { - font-size: 14px; - color: #333; - } +.record-image-item { + /* 一行三个:每个图片宽度 = (100% - 2个gap) / 3 */ + width: calc((100% - 16px) / 3); + aspect-ratio: 1; + border-radius: 4px; + overflow: hidden; + background-color: #e0e0e0; + flex-shrink: 0; +} + +.record-image { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* 文件附件展示 */ +.record-files-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 12px; +} + +.file-attachment-item { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background-color: #f5f5f5; + border-radius: 4px; +} + +.file-icon { + font-size: 16px; +} + +.file-name { + font-size: 14px; + color: #333; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .no-record {