From 37f9b0b6e8510869ca54a7b919b24af28dca9576 Mon Sep 17 00:00:00 2001 From: WindowBird <13870814+windows-bird@user.noreply.gitee.com> Date: Fri, 14 Nov 2025 17:41:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/task/add/index.vue | 619 ++++++++++++++++++++++++++++++++------- 1 file changed, 510 insertions(+), 109 deletions(-) diff --git a/pages/task/add/index.vue b/pages/task/add/index.vue index 9293f66..b4c3b03 100644 --- a/pages/task/add/index.vue +++ b/pages/task/add/index.vue @@ -69,24 +69,44 @@ 附件 请上传不超过200MB的文件,支持常见图片、Office、PDF、压缩包等格式。 - + + + 🏔️ + 添加照片 + + + + - {{ getAttachmentIcon(file) }} - {{ file.displayName }} - + + + + + + + 📄 + 添加文件 + + + + - - 上传 + {{ getFileIcon(file.name) }} + + {{ file.name }} + {{ formatFileSize(file.size) }} + + @@ -203,7 +223,9 @@ const formData = ref({ description: '', expireTime: '', attachments: [], - members: [] + members: [], + images: [], + files: [] }); @@ -223,6 +245,28 @@ const projectPicker=ref(null); const expirePickerRef = ref(null); const expirePickerValue = ref(Date.now()); +const getFileIcon = (fileName) => { + if (!fileName) return '📄'; + const ext = fileName.split('.').pop().toLowerCase(); + const iconMap = { + 'pdf': '📕', + 'doc': '📘', + 'docx': '📘', + 'xls': '📗', + 'xlsx': '📗', + 'ppt': '📙', + 'pptx': '📙', + 'txt': '📄', + 'zip': '📦', + 'rar': '📦', + 'jpg': '🖼️', + 'jpeg': '🖼️', + 'png': '🖼️', + 'gif': '🖼️' + }; + return iconMap[ext] || '📄'; +}; + const typeOptions = computed(() => { return dictStore.getDictByType('task_type').map(item => ({ label: item.dictLabel, @@ -388,159 +432,359 @@ const handleAddAttachment = () => { itemList: ['上传图片', '上传文件'], success: ({ tapIndex }) => { if (tapIndex === 0) { - chooseImages(remaining); + chooseImages(); } else if (tapIndex === 1) { - chooseFiles(remaining); + chooseFiles(); } } }); }; -const chooseImages = async (count) => { +// 选择图片并自动上传到七牛云 +const chooseImages = async () => { try { + const remainingCount = 9 - formData.value.images.length; + if (remainingCount <= 0) { + uni.showToast({ + title: '最多只能添加9张图片', + icon: 'none' + }); + return; + } + + // 使用封装好的选择并上传功能 const urls = await chooseAndUploadImages({ - count + count: remainingCount, + sizeType: ['original', 'compressed'], + sourceType: ['album', 'camera'] }); - const newItems = urls.map((url, idx) => ({ - type: 'image', - url, - displayName: `图片${formData.value.attachments.length + idx + 1}` - })); - formData.value.attachments = [...formData.value.attachments, ...newItems]; - } catch (error) { - console.error('上传图片失败:', error); + + // 将上传后的URL添加到图片列表 + formData.value.images = [...formData.value.images, ...urls]; + } catch (err) { + console.error('选择或上传图片失败:', err); uni.showToast({ - title: error.message || '上传图片失败', + title: err.message || '选择图片失败', icon: 'none' }); } }; -const chooseFiles = async (count) => { - const remainingCount = Math.min(count, ATTACHMENT_LIMIT - formData.value.attachments.length); +// 预览图片 +const previewImage = (index) => { + uni.previewImage({ + urls: formData.value.images, + current: index + }); +}; + +// 删除图片 +const removeImage = (index) => { + formData.value.images.splice(index, 1); +}; +const chooseFiles = async () => { + const remainingCount = 5 - formData.value.files.length; if (remainingCount <= 0) { uni.showToast({ - title: `最多上传${ATTACHMENT_LIMIT}个附件`, + title: '最多只能添加5个文件', icon: 'none' }); return; } + // 优先使用 uni.chooseFile(H5和部分平台支持) + // #ifdef H5 || MP-WEIXIN || APP-PLUS try { uni.chooseFile({ count: remainingCount, + extension: ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.zip', '.rar','.jpg','.png'], success: async (res) => { try { uni.showLoading({ title: '上传中...', mask: true }); + + // 批量上传文件到七牛云 const uploadResults = await batchUploadFilesToQiniu( - res.tempFiles.map(file => ({ - path: file.path, - name: file.name - })) + res.tempFiles.map(file => ({ + path: file.path, + name: file.name + })) ); + + // 将上传结果添加到文件列表 const newFiles = uploadResults.map(result => ({ - type: 'file', - url: result.url, - displayName: result.name || '附件' + name: result.name, + path: result.url, // 保存七牛云URL + size: result.size })); - formData.value.attachments = [...formData.value.attachments, ...newFiles]; + + formData.value.files = [...formData.value.files, ...newFiles]; + uni.hideLoading(); uni.showToast({ - title: '上传成功', + title: `成功添加${newFiles.length}个文件`, icon: 'success' }); } catch (error) { uni.hideLoading(); console.error('上传文件失败:', error); uni.showToast({ - title: error.message || '上传失败', + title: error.message || '上传文件失败', icon: 'none' }); } }, fail: (err) => { console.error('选择文件失败:', err); + // 如果uni.chooseFile不支持,尝试使用原生方法 + chooseFilesNative(); + } + }); + } catch (error) { + // 如果不支持uni.chooseFile,使用原生方法 + chooseFilesNative(); + } + // #endif + + // #ifndef H5 || MP-WEIXIN || APP-PLUS + // 其他平台使用原生方法 + chooseFilesNative(); + // #endif +}; + +// 原生文件选择方法(安卓平台) +const chooseFilesNative = async () => { + const remainingCount = 5 - formData.value.files.length; + + // 安卓平台使用 plus API 调用原生文件选择器 + if (typeof plus !== 'undefined') { + try { + const Intent = plus.android.importClass('android.content.Intent'); + const main = plus.android.runtimeMainActivity(); + + // 创建文件选择 Intent + const intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType('*/*'); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // 允许多选 + + // 启动文件选择器 + main.startActivityForResult(intent, 1001); + + // 监听文件选择结果 + const originalOnActivityResult = main.onActivityResult; + main.onActivityResult = async (requestCode, resultCode, data) => { + if (requestCode === 1001) { + if (resultCode === -1 && data) { // RESULT_OK = -1 + try { + const clipData = data.getClipData(); + const files = []; + + // 获取文件名的方法 + const getFileName = (uri) => { + try { + const cursor = main.getContentResolver().query(uri, null, null, null, null); + if (cursor && cursor.moveToFirst()) { + const nameIndex = cursor.getColumnIndex('_display_name'); + if (nameIndex !== -1) { + const fileName = cursor.getString(nameIndex); + cursor.close(); + return fileName; + } + cursor.close(); + } + } catch (e) { + console.error('获取文件名失败:', e); + } + return null; + }; + + if (clipData) { + // 多选文件 + const count = clipData.getItemCount(); + for (let i = 0; i < count && files.length < remainingCount; i++) { + const item = clipData.getItemAt(i); + const uri = item.getUri(); + const uriString = uri.toString(); + + // 获取文件名 + let fileName = getFileName(uri) || `file_${Date.now()}_${i}`; + + files.push({ + name: fileName, + path: uriString, // 保存 URI 字符串 + size: 0 + }); + } + } else { + // 单选文件 + const uri = data.getData(); + if (uri) { + const uriString = uri.toString(); + let fileName = getFileName(uri) || `file_${Date.now()}`; + + files.push({ + name: fileName, + path: uriString, // 保存 URI 字符串 + size: 0 + }); + } + } + + if (files.length > 0) { + // 显示上传中提示 + uni.showLoading({ + title: '上传中...', + mask: true + }); + + try { + // 批量上传文件到七牛云 + const uploadResults = await batchUploadFilesToQiniu(files); + + // 将上传结果添加到文件列表 + const newFiles = uploadResults.map(result => ({ + name: result.name, + path: result.url, // 保存七牛云URL + size: result.size + })); + + formData.value.files = [...formData.value.files, ...newFiles]; + + uni.hideLoading(); + uni.showToast({ + title: `成功添加${newFiles.length}个文件`, + icon: 'success' + }); + } catch (uploadError) { + uni.hideLoading(); + console.error('上传文件失败:', uploadError); + uni.showToast({ + title: uploadError.message || '上传文件失败', + icon: 'none' + }); + } + } + + // 恢复原始的 onActivityResult + if (originalOnActivityResult) { + main.onActivityResult = originalOnActivityResult; + } + } catch (error) { + uni.hideLoading(); + console.error('处理文件选择结果失败:', error); + uni.showToast({ + title: '处理文件失败', + icon: 'none' + }); + } + } + } else { + // 调用原始的 onActivityResult + if (originalOnActivityResult) { + originalOnActivityResult(requestCode, resultCode, data); + } + } + }; + } catch (error) { + console.error('打开文件选择器失败:', error); + uni.showToast({ + title: '文件选择功能暂不可用', + icon: 'none' + }); + } + } else { + uni.showToast({ + title: '当前环境不支持文件选择', + icon: 'none' + }); + } +}; + +// 删除文件 +const removeFile = (index) => { + formData.value.files.splice(index, 1); +}; + +// 格式化文件大小 +const formatFileSize = (bytes) => { + if (!bytes || bytes === 0) return ''; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; +}; + +// 预览/下载文件 +const previewFile = (file) => { + if (!file.path) { + uni.showToast({ + title: '文件路径不存在', + icon: 'none' + }); + return; + } + + // 如果是图片,使用预览图片功能 + const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + const ext = file.name.split('.').pop().toLowerCase(); + + if (imageExts.includes(ext)) { + uni.previewImage({ + urls: [file.path], + current: file.path + }); + } else { + // 其他文件类型,尝试打开或下载 + // #ifdef H5 + window.open(file.path, '_blank'); + // #endif + + // #ifdef APP-PLUS + plus.runtime.openURL(file.path); + // #endif + + // #ifndef H5 || APP-PLUS + uni.showToast({ + title: '点击下载文件', + icon: 'none' + }); + // 可以调用下载API + uni.downloadFile({ + url: file.path, + success: (res) => { + if (res.statusCode === 200) { + uni.openDocument({ + filePath: res.tempFilePath, + success: () => { + console.log('打开文档成功'); + }, + fail: (err) => { + console.error('打开文档失败:', err); + uni.showToast({ + title: '无法打开此文件', + icon: 'none' + }); + } + }); + } + }, + fail: (err) => { + console.error('下载文件失败:', err); uni.showToast({ - title: '当前环境暂不支持选文件', + title: '下载文件失败', icon: 'none' }); } }); - } catch (error) { - console.error('调用选择文件失败:', error); - uni.showToast({ - title: '当前环境暂不支持选文件', - icon: 'none' - }); + // #endif } }; -const getAttachmentIcon = (file) => { - if (file.type === 'image') { - return '🖼️'; - } - const name = file.displayName || ''; - const ext = name.includes('.') ? name.split('.').pop().toLowerCase() : ''; - const map = { - pdf: '📕', - doc: '📘', - docx: '📘', - xls: '📗', - xlsx: '📗', - ppt: '📙', - pptx: '📙', - zip: '📦', - rar: '📦' - }; - return map[ext] || '📄'; -}; -const removeAttachment = (index) => { - formData.value.attachments.splice(index, 1); -}; -const previewAttachment = (file) => { - if (file.type === 'image') { - const imageUrls = formData.value.attachments.filter(att => att.type === 'image').map(att => att.url); - uni.previewImage({ - urls: imageUrls, - current: file.url - }); - return; - } - - if (!file.url) { - uni.showToast({ - title: '文件地址无效', - icon: 'none' - }); - return; - } - - // #ifdef H5 - window.open(file.url, '_blank'); - // #endif - - // #ifdef APP-PLUS - plus.runtime.openURL(file.url); - // #endif - - // #ifndef H5 || APP-PLUS - uni.downloadFile({ - url: file.url, - success: (res) => { - if (res.statusCode === 200) { - uni.openDocument({ - filePath: res.tempFilePath - }); - } - } - }); - // #endif -}; const openMemberModal = () => { selectedMemberIds.value = formData.value.members.map(member => member.userId); @@ -588,12 +832,22 @@ const handleSubmit = async () => { return; } + // 合并所有附件URL(图片和文件) + // 图片和文件都已经上传到七牛云,直接使用URL + const allAttaches = [ + ...formData.value.images, // 图片已经是七牛云URL + ...formData.value.files.map(file => file.path) // 文件已经是七牛云URL + ].filter(url => url && url.trim() !== ''); // 过滤空值 + + // 将附件数组转换为逗号分隔的字符串 + const submitAttaches = allAttaches.join(','); + const payload = { id: null, projectId: formData.value.projectId, type: formData.value.type, level: formData.value.level, - picture: formData.value.attachments.map(item => item.url).join(','), + picture: submitAttaches, description: formData.value.description.trim(), expireTime: formData.value.expireTime, memberList: formData.value.members.map(member => ({ @@ -982,5 +1236,152 @@ onLoad(async (options) => { color: #999; font-size: 14px; } + +/* 表单项 */ +.form-item { + display: flex; + align-items: center; + padding: 16px; + background-color: #fff; + border-radius: 8px; + margin-bottom: 12px; + gap: 12px; +} + +.clickable-item { + cursor: pointer; + + &:active { + background-color: #f5f5f5; + } +} + +.form-icon { + font-size: 20px; + flex-shrink: 0; +} + +.form-content { + flex: 1; + display: flex; + flex-direction: column; +} + +.form-label { + flex: 1; + font-size: 15px; + color: #333; +} + +.form-value { + font-size: 15px; + color: #333; + font-weight: 500; +} + +.form-placeholder { + font-size: 15px; + color: #999; +} + +.arrow { + font-size: 20px; + color: #999; + flex-shrink: 0; +} + +.description-input { + flex: 1; + min-height: 80px; + font-size: 15px; + color: #333; + line-height: 1.6; +} + +/* 图片预览 */ +.images-preview { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 12px; + padding: 0 16px; +} + +.image-item { + position: relative; + width: 100px; + height: 100px; + border-radius: 8px; + overflow: hidden; +} + +.preview-image { + width: 100%; + height: 100%; +} + +.remove-btn { + position: relative; + + width: 24px; + height: 24px; + background-color: rgba(0, 0, 0, 0.6); + color: #fff; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + cursor: pointer; +} + +/* 文件列表 */ +.files-list { + padding: 0 16px; + margin-bottom: 12px; +} + +.file-item { + display: flex; + align-items: center; + padding: 12px; + background-color: #fff; + border-radius: 8px; + margin-bottom: 8px; + gap: 12px; + cursor: pointer; + + &:active { + background-color: #f5f5f5; + } +} + +.file-icon { + font-size: 24px; + flex-shrink: 0; +} + +.file-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.file-name { + text-align: left; + font-size: 14px; + color: #333; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; +} + +.file-size { + font-size: 12px; + color: #999; +}