feat(移动端): 新增支出使用 COS 上传附件
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,获得永久可访问链接
This commit is contained in:
parent
da4488dccc
commit
a74875eeda
@ -23,6 +23,30 @@ export function updatePassword(data: { oldPassword: string; newPassword: string;
|
|||||||
return request.put('/sys/profile/password', data)
|
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 }) {
|
export function getProjectList(params?: { pageNum: number; pageSize: number; keyword?: string }) {
|
||||||
|
|||||||
@ -87,14 +87,14 @@
|
|||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast, showSuccessToast, showFailToast, ImagePreview } from 'vant'
|
import { showToast, showSuccessToast, showFailToast, ImagePreview } from 'vant'
|
||||||
import { createExpense, getExpenseTypeTree } from '@/api'
|
import { createExpense, getExpenseTypeTree, uploadFile } from '@/api'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const showTypePicker = ref(false)
|
const showTypePicker = ref(false)
|
||||||
const showDatePicker = ref(false)
|
const showDatePicker = ref(false)
|
||||||
const fileList = ref<any[]>([])
|
const fileList = ref<any[]>([])
|
||||||
const uploadedAttachments = ref<string[]>([])
|
const uploadedAttachments = ref<string[]>([]) // 存储 COS 返回的文件路径
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
@ -120,22 +120,29 @@ const onDateConfirm = ({ selectedValues }: any) => {
|
|||||||
showDatePicker.value = false
|
showDatePicker.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 附件上传处理
|
// 附件上传处理 - 使用 COS 上传
|
||||||
const onAfterRead = async (file: any) => {
|
const onAfterRead = async (file: any) => {
|
||||||
// 这里模拟上传,实际应该调用文件上传 API
|
if (!file.file) return
|
||||||
// 由于项目使用 fund-file 服务,暂时先保存为 base64
|
|
||||||
if (file.file) {
|
try {
|
||||||
const reader = new FileReader()
|
// 显示上传提示
|
||||||
reader.onload = (e) => {
|
showToast('上传中...')
|
||||||
const base64 = e.target?.result as string
|
|
||||||
// 提取 base64 数据部分(去掉 data:image/jpeg;base64,前缀)
|
// 调用 uploadFile API 上传到 COS
|
||||||
const base64Data = base64.split(',')[1]
|
const res: any = await uploadFile(file.file, 'expense', undefined, '支出附件')
|
||||||
if (base64Data) {
|
|
||||||
uploadedAttachments.value.push(base64Data)
|
// 从响应中获取文件路径
|
||||||
console.log('已添加附件:', base64Data.substring(0, 50) + '...')
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预览图片
|
// 预览图片(通过后端下载 URL)
|
||||||
const onPreviewImage = (index: number) => {
|
const onPreviewImage = (index: number) => {
|
||||||
|
// 这里需要后端提供文件预览或下载接口
|
||||||
|
// 暂时使用后端返回的完整 URL
|
||||||
ImagePreview.show({
|
ImagePreview.show({
|
||||||
images: uploadedAttachments.value.map(b64 => `data:image/jpeg;base64,${b64}`),
|
images: uploadedAttachments.value,
|
||||||
startPosition: index,
|
startPosition: index,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -176,7 +185,7 @@ const handleSubmit = async () => {
|
|||||||
// 转换日期格式为 LocalDateTime 格式
|
// 转换日期格式为 LocalDateTime 格式
|
||||||
const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null
|
const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null
|
||||||
|
|
||||||
// 处理附件:将 base64 数组转为逗号分隔的字符串
|
// 处理附件:将 COS 文件路径数组转为逗号分隔的字符串
|
||||||
const attachmentsStr = uploadedAttachments.value.length > 0
|
const attachmentsStr = uploadedAttachments.value.length > 0
|
||||||
? uploadedAttachments.value.join(',')
|
? uploadedAttachments.value.join(',')
|
||||||
: null
|
: null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user