From a74875eedad0012bb4bb3167bf1e0f5cdf467b4d Mon Sep 17 00:00:00 2001 From: zhangjf Date: Sun, 1 Mar 2026 22:23:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=A7=BB=E5=8A=A8=E7=AB=AF):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=94=AF=E5=87=BA=E4=BD=BF=E7=94=A8=20COS=20=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E9=99=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. API 增强 (src/api/index.ts): - 新增 uploadFile 函数:支持文件上传到腾讯云 COS - 新增 getFileList 函数:获取文件列表 - 新增 deleteFile 函数:删除文件 2. 新增支出页面优化 (src/views/expense/Add.vue): - 修改附件上传逻辑:从 base64 改为 COS 上传 - onAfterRead: 调用 uploadFile API 上传到 COS - 获取 COS 返回的文件路径并存储 - 提交时将 COS 路径数组转为逗号分隔字符串 - 图片预览直接使用 COS URL - 添加上传进度提示和成功/失败反馈 技术实现: - 使用 FormData 进行 multipart/form-data 上传 - 业务类型标识为'expense' - 附件以 COS 完整 URL 形式存储(逗号分隔) - 支持多图片上传(最多 9 张) - 每张图片独立上传到 COS,获得永久可访问链接 --- fund-mobile/src/api/index.ts | 24 ++++++++++++++ fund-mobile/src/views/expense/Add.vue | 47 ++++++++++++++++----------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/fund-mobile/src/api/index.ts b/fund-mobile/src/api/index.ts index 5ffd08b..4dd61fe 100644 --- a/fund-mobile/src/api/index.ts +++ b/fund-mobile/src/api/index.ts @@ -23,6 +23,30 @@ export function updatePassword(data: { oldPassword: string; newPassword: string; return request.put('/sys/profile/password', data) } +// ===================== 文件管理 ===================== + +export function uploadFile(file: File, businessType?: string, businessId?: number, description?: string) { + const formData = new FormData() + formData.append('file', file) + if (businessType) formData.append('businessType', businessType) + if (businessId) formData.append('businessId', String(businessId)) + if (description) formData.append('description', description) + + return request.post('/file/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +export function getFileList(params?: { pageNum: number; pageSize: number; businessType?: string; businessId?: number; fileType?: string }) { + return request.get('/file/page', { params }) +} + +export function deleteFile(id: number) { + return request.delete(`/file/${id}`) +} + // ===================== 项目管理 ===================== export function getProjectList(params?: { pageNum: number; pageSize: number; keyword?: string }) { diff --git a/fund-mobile/src/views/expense/Add.vue b/fund-mobile/src/views/expense/Add.vue index 3ae272e..867964e 100644 --- a/fund-mobile/src/views/expense/Add.vue +++ b/fund-mobile/src/views/expense/Add.vue @@ -87,14 +87,14 @@ import { ref, reactive, onMounted } from 'vue' import { useRouter } from 'vue-router' import { showToast, showSuccessToast, showFailToast, ImagePreview } from 'vant' -import { createExpense, getExpenseTypeTree } from '@/api' +import { createExpense, getExpenseTypeTree, uploadFile } from '@/api' const router = useRouter() const loading = ref(false) const showTypePicker = ref(false) const showDatePicker = ref(false) const fileList = ref([]) -const uploadedAttachments = ref([]) +const uploadedAttachments = ref([]) // 存储 COS 返回的文件路径 const form = reactive({ title: '', @@ -120,22 +120,29 @@ const onDateConfirm = ({ selectedValues }: any) => { showDatePicker.value = false } -// 附件上传处理 +// 附件上传处理 - 使用 COS 上传 const onAfterRead = async (file: any) => { - // 这里模拟上传,实际应该调用文件上传 API - // 由于项目使用 fund-file 服务,暂时先保存为 base64 - if (file.file) { - const reader = new FileReader() - reader.onload = (e) => { - const base64 = e.target?.result as string - // 提取 base64 数据部分(去掉 data:image/jpeg;base64,前缀) - const base64Data = base64.split(',')[1] - if (base64Data) { - uploadedAttachments.value.push(base64Data) - console.log('已添加附件:', base64Data.substring(0, 50) + '...') - } + if (!file.file) return + + try { + // 显示上传提示 + showToast('上传中...') + + // 调用 uploadFile API 上传到 COS + const res: any = await uploadFile(file.file, 'expense', undefined, '支出附件') + + // 从响应中获取文件路径 + const filePath = res.data?.filePath || res.data?.url + if (filePath) { + uploadedAttachments.value.push(filePath) + console.log('文件上传成功:', filePath) + showSuccessToast('上传成功') + } else { + showFailToast('上传失败:未获取文件路径') } - reader.readAsDataURL(file.file) + } catch (error: any) { + console.error('上传失败:', error) + showFailToast(error.message || '上传失败') } } @@ -145,10 +152,12 @@ const onBeforeDelete = (file: any, detail: any) => { return true } -// 预览图片 +// 预览图片(通过后端下载 URL) const onPreviewImage = (index: number) => { + // 这里需要后端提供文件预览或下载接口 + // 暂时使用后端返回的完整 URL ImagePreview.show({ - images: uploadedAttachments.value.map(b64 => `data:image/jpeg;base64,${b64}`), + images: uploadedAttachments.value, startPosition: index, }) } @@ -176,7 +185,7 @@ const handleSubmit = async () => { // 转换日期格式为 LocalDateTime 格式 const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null - // 处理附件:将 base64 数组转为逗号分隔的字符串 + // 处理附件:将 COS 文件路径数组转为逗号分隔的字符串 const attachmentsStr = uploadedAttachments.value.length > 0 ? uploadedAttachments.value.join(',') : null