feat: 图片上传功能

This commit is contained in:
caoqianming 2026-03-13 10:56:12 +08:00
parent 3f2d627641
commit b1c0e94ab1
3 changed files with 36 additions and 36 deletions

1
backend/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/media/*

View File

@ -1,17 +1,4 @@
import api from './client' import api from './client'
const toFormData = (payload) => {
const form = new FormData()
Object.entries(payload).forEach(([key, value]) => {
if (value === undefined || value === null) return
if (Array.isArray(value)) {
form.append(key, JSON.stringify(value))
return
}
form.append(key, value)
})
return form
}
export const fetchMaterials = async (params) => { export const fetchMaterials = async (params) => {
const { data } = await api.get('/material/', { params }) const { data } = await api.get('/material/', { params })
@ -23,18 +10,21 @@ export const fetchMaterialDetail = async (id) => {
return data return data
} }
export const createMaterial = async (payload, withFile = false) => { export const createMaterial = async (payload) => {
const dataPayload = withFile ? toFormData(payload) : payload const { data } = await api.post('/material/', payload)
const { data } = await api.post('/material/', dataPayload, {
headers: withFile ? { 'Content-Type': 'multipart/form-data' } : undefined
})
return data return data
} }
export const updateMaterial = async (id, payload, withFile = false) => { export const updateMaterial = async (id, payload) => {
const dataPayload = withFile ? toFormData(payload) : payload const { data } = await api.put(`/material/${id}/`, payload)
const { data } = await api.put(`/material/${id}/`, dataPayload, { return data
headers: withFile ? { 'Content-Type': 'multipart/form-data' } : undefined }
export const uploadImage = async (file) => {
const formData = new FormData()
formData.append('file', file)
const { data } = await api.post('/upload/', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
}) })
return data return data
} }

View File

@ -105,11 +105,12 @@
<el-form-item label="宣传页"> <el-form-item label="宣传页">
<el-upload <el-upload
class="upload" class="upload"
:auto-upload="false" :auto-upload="true"
:show-file-list="true" :show-file-list="false"
:on-change="onFileChange" :http-request="handleUpload"
accept="image/*"
> >
<el-button>选择图片</el-button> <el-button :loading="uploading">{{ uploading ? '上传中...' : '选择图片' }}</el-button>
</el-upload> </el-upload>
<div v-if="form.brochure_url" class="preview"> <div v-if="form.brochure_url" class="preview">
<img :src="form.brochure_url" alt="预览" /> <img :src="form.brochure_url" alt="预览" />
@ -168,7 +169,7 @@ import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuth } from '@/store/auth' import { useAuth } from '@/store/auth'
import { fetchMaterials, fetchMaterialDetail, createMaterial, updateMaterial, deleteMaterial, submitMaterial, approveMaterial, rejectMaterial, fetchMaterialChoices } from '@/api/material' import { fetchMaterials, fetchMaterialDetail, createMaterial, updateMaterial, deleteMaterial, submitMaterial, approveMaterial, rejectMaterial, fetchMaterialChoices, uploadImage } from '@/api/material'
import { fetchCategories, fetchSubcategories } from '@/api/category' import { fetchCategories, fetchSubcategories } from '@/api/category'
import { fetchFactorySimple } from '@/api/factory' import { fetchFactorySimple } from '@/api/factory'
@ -186,7 +187,7 @@ const dialogVisible = ref(false)
const dialogTitle = ref('') const dialogTitle = ref('')
const isEdit = ref(false) const isEdit = ref(false)
const currentId = ref(null) const currentId = ref(null)
const fileRef = ref(null) const uploading = ref(false)
const filters = reactive({ const filters = reactive({
name: '', name: '',
@ -312,7 +313,6 @@ const resetForm = () => {
limit_condition: '', limit_condition: '',
factory: null factory: null
}) })
fileRef.value = null
} }
const onCategoryChange = async (val, resetSub = true) => { const onCategoryChange = async (val, resetSub = true) => {
@ -352,22 +352,31 @@ const openEdit = async (row) => {
dialogVisible.value = true dialogVisible.value = true
} }
const onFileChange = (file) => { const handleUpload = async (options) => {
fileRef.value = file.raw uploading.value = true
form.brochure = file.raw try {
const result = await uploadImage(options.file)
form.brochure = result.path
form.brochure_url = result.url
ElMessage.success('图片上传成功')
} catch {
ElMessage.error('图片上传失败')
} finally {
uploading.value = false
}
} }
const onSave = async () => { const onSave = async () => {
try { try {
const payload = { ...form } const payload = { ...form }
const withFile = !!fileRef.value delete payload.brochure_url
if (!isAdmin.value) { if (!isAdmin.value) {
delete payload.factory delete payload.factory
} }
if (isEdit.value) { if (isEdit.value) {
await updateMaterial(currentId.value, payload, withFile) await updateMaterial(currentId.value, payload)
} else { } else {
await createMaterial(payload, withFile) await createMaterial(payload)
} }
ElMessage.success('保存成功') ElMessage.success('保存成功')
dialogVisible.value = false dialogVisible.value = false