From 37b81d188a295b850873594415b52637d890ba98 Mon Sep 17 00:00:00 2001 From: WindowBird <13870814+windows-bird@user.noreply.gitee.com> Date: Tue, 18 Nov 2025 14:46:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E5=85=B6?= =?UTF-8?q?=E4=B8=AD=E5=8C=85=E5=90=AB=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/task/AttachmentFileUploader.vue | 196 ++++++++++++++++++--- 1 file changed, 171 insertions(+), 25 deletions(-) diff --git a/components/task/AttachmentFileUploader.vue b/components/task/AttachmentFileUploader.vue index c476117..a423f09 100644 --- a/components/task/AttachmentFileUploader.vue +++ b/components/task/AttachmentFileUploader.vue @@ -6,19 +6,38 @@ - + + + + + + + + - {{ getFileIcon(file.name) }} + + {{ getFileTypeLabel(file.name) }} + {{ file.name }} {{ formatFileSize(file.size) }} - + {{ getFileIcon(file.name) }} + @@ -61,6 +80,17 @@ const files = computed({ } }); +const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic', 'heif', 'svg']; + +const isImageFile = (file) => { + if (!file) return false; + const ext = getFileExtension(file.name); + return imageExtensions.includes(ext); +}; + +const imageFiles = computed(() => files.value.filter((file) => isImageFile(file))); +const otherFiles = computed(() => files.value.filter((file) => !isImageFile(file))); + const handleChooseFiles = async () => { const remainingCount = props.maxCount - files.value.length; if (remainingCount <= 0) { @@ -95,9 +125,10 @@ const chooseFilesWithUni = (remainingCount) => { extension: props.extensions, success: async (res) => { try { - await uploadFiles(res.tempFiles.map(file => ({ + await uploadFiles(res.tempFiles.map((file) => ({ path: file.path, - name: file.name + name: file.name, + size: file.size || 0 }))); resolve(); } catch (error) { @@ -251,15 +282,18 @@ const uploadFiles = async (fileList) => { } }; -const removeFile = (index) => { - const next = [...files.value]; - next.splice(index, 1); +const removeFile = (file) => { + const next = files.value.filter((item) => item !== file); files.value = next; }; +const getFileExtension = (fileName = '') => { + if (!fileName) return ''; + return fileName.split('.').pop().toLowerCase(); +}; + const getFileIcon = (fileName) => { - if (!fileName) return '📄'; - const ext = fileName.split('.').pop().toLowerCase(); + const ext = getFileExtension(fileName); const iconMap = { pdf: '📕', doc: '📘', @@ -279,6 +313,36 @@ const getFileIcon = (fileName) => { return iconMap[ext] || '📄'; }; +const getFileTypeKey = (fileName) => { + const ext = getFileExtension(fileName); + if (!ext) return 'other'; + if (imageExtensions.includes(ext)) return 'image'; + if (['pdf'].includes(ext)) return 'pdf'; + if (['doc', 'docx', 'wps'].includes(ext)) return 'doc'; + if (['xls', 'xlsx', 'csv'].includes(ext)) return 'xls'; + if (['ppt', 'pptx'].includes(ext)) return 'ppt'; + if (['zip', 'rar', '7z'].includes(ext)) return 'zip'; + return 'other'; +}; + +const getFileTypeLabel = (fileName) => { + const type = getFileTypeKey(fileName); + const labelMap = { + image: 'IMG', + pdf: 'PDF', + doc: 'DOC', + xls: 'XLS', + ppt: 'PPT', + zip: 'ZIP', + other: 'FILE' + }; + return labelMap[type] || 'FILE'; +}; + +const getFileTypeClass = (fileName) => { + return `badge-${getFileTypeKey(fileName)}`; +}; + const formatFileSize = (bytes) => { if (!bytes || bytes === 0) return ''; const k = 1024; @@ -287,6 +351,15 @@ const formatFileSize = (bytes) => { return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; }; +const previewImage = (index) => { + const urls = imageFiles.value.map((file) => file.path); + if (!urls.length) return; + uni.previewImage({ + urls, + current: urls[index] || urls[0] + }); +}; + const previewFile = (file) => { if (!file?.path) { uni.showToast({ @@ -296,14 +369,9 @@ const previewFile = (file) => { 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 - }); + const ext = getFileExtension(file.name); + if (imageExtensions.includes(ext)) { + previewImage(imageFiles.value.findIndex((item) => item === file)); return; } @@ -392,6 +460,9 @@ const previewFile = (file) => { .files-list { margin-top: 12px; + display: flex; + flex-direction: column; + gap: 8px; } .file-item { @@ -408,11 +479,6 @@ const previewFile = (file) => { } } -.file-icon { - font-size: 24px; - flex-shrink: 0; -} - .file-info { flex: 1; display: flex; @@ -447,5 +513,85 @@ const previewFile = (file) => { font-size: 14px; cursor: pointer; } + +.images-preview { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; +} + +.image-item { + position: relative; + width: calc((100% - 16px) / 3); + aspect-ratio: 1; + border-radius: 8px; + overflow: hidden; + background-color: #f4f4f5; +} + +.preview-image { + width: 100%; + height: 100%; +} + +.image-item .remove-btn { + position: absolute; + top: 6px; + right: 6px; + width: 22px; + height: 22px; +} + +.file-type-badge { + padding: 4px 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + color: #333; + background-color: #f5f5f5; + flex-shrink: 0; +} + +.badge-pdf { + background-color: #fff1f0; + color: #f5222d; +} + +.badge-doc { + background-color: #f0f5ff; + color: #2f54eb; +} + +.badge-xls { + background-color: #f6ffed; + color: #52c41a; +} + +.badge-ppt { + background-color: #fff7e6; + color: #fa8c16; +} + +.badge-zip { + background-color: #f9f0ff; + color: #722ed1; +} + +.badge-image { + background-color: #fff7e6; + color: #d48806; +} + +.badge-other { + background-color: #f5f5f5; + color: #888; +} + +.file-icon { + font-size: 20px; + color: #999; + flex-shrink: 0; +}