项目介绍
项目是模仿王者荣耀mobile端的后台admin管理项目,前端使用ant Design Vue2.0+vite+vue3+typescript来做,后端是node。
需求
添加英雄技能界面截图。
一开始使用ant design vue的upload的change来做,后来发现我这个界面的upload是需要循环遍历出来的,而change函数里面只有三个参数,无法获取每个upload的特定参数,挣扎了好久看官方文档,终于发现了customRequest这个参数,官方说明是【通过覆盖默认的上传行为,可以自定义自己的上传实现】,然并卵,官方并没有给出例子,查了半天,别人写的全都是vue2.x,没法只能自己看着2.x版本的来修改,奋斗了一天多,终于搞出了个基础版本,在此贴出一些核心实现代码,仅供各位大佬参考,还请各位大佬多多提供优化方案。
HeroEdit.vue代码
代码太多就贴出技能这块的好了,说明一下,点击添加技能按钮就会新建一个空的技能进formState.skills数组里面,然后循环遍历这个数组生成每个技能customRequest(options:any)里面的options对象中有data,可以通过:data="{index}"来携带参数,这个我们就知道每个upload对应的哪一个技能。
<a-tab-pane key="2" tab="技能">
<a-button type="primary" @click="formState.skills.push({})">
<template #icon><PlusOutlined /></template>添加技能</a-button
>
<a-row type="flex">
<a-col
:span="10"
:offset="2"
v-for="(item, index) of formState.skills"
:key="index"
>
<a-form-item label="名称">
<a-input
v-model:value="item.name"
placeholder="请输入技能名称"
></a-input>
</a-form-item>
<!-- :customRequest="customRequest" -->
<a-form-item label="图标" name="icon">
<a-upload
v-model:avatar="item.icon"
name="avatar"
:data="{ index }"
list-type="picture-card"
class="avatar-uploader"
:show-upload-list="false"
action="/admin/api/upload"
:before-upload="beforeUpload"
:customRequest="customRequest"
>
<img
v-if="formState.skills[index].icon"
:src="formState.skills[index].icon"
alt="avatar"
/>
<div v-else>
<loading-outlined v-if="loading"></loading-outlined>
<plus-outlined v-else></plus-outlined>
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
</a-form-item>
<a-form-item label="描述">
<a-textarea
v-model:value="item.description"
placeholder="请输入技能描述"
:rows="4"
/>
</a-form-item>
</a-col>
</a-row>
</a-tab-pane>
import { message } from 'ant-design-vue'
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
import {
defineComponent,
onMounted,
reactive,
ref,
toRaw,
UnwrapRef,
} from 'vue'
import { useRouter, useRoute } from 'vue-router'
import {
reqAdd,
reqGetDetail,
reqGetList,
reqUpdate,
uploadImage,
} from '../../api'
interface FormState {
name: string
avatar: string | undefined
title: string
categories: string[]
scores: object
items1: string[]
items2: string[]
usageTips: string
battleTips: string
teamTips: string
skills: any[]
}
function getBase64(img: any, callback: (base64Url: string) => void) {
const reader = new FileReader()
reader.addEventListener('load', () => callback(reader.result as string))
reader.readAsDataURL(img)
}
export default defineComponent({
setup() {
const formState: UnwrapRef<FormState> = reactive({
name: '',
avatar: undefined,
title: '',
categories: [],
scores: {},
items1: [],
items2: [],
usageTips: '',
battleTips: '',
teamTips: '',
skills: [],
})
/**自定义上传图片 */
const customRequest = async (options: any) => {
// console.log('customRequest', options)
const { filename, file, action, data, onSuccess } = options
// 定义上传的文件
const formData = new FormData()
formData.append(filename, file)
// 发送请求
const result: any = await uploadImage(action, formData)
// console.log(result)
getBase64(file, (base64Url: string) => {
formState.skills[data.index].icon = base64Url
})
// 调用 onSuccess 不然会一直显示loadning
onSuccess()
}
const beforeUpload = (file: FileItem) => {
// console.log('beforeUpload')
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
if (!isJpgOrPng) {
message.error('You can only upload JPG file!')
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
message.error('Image must smaller than 2MB!')
}
return isJpgOrPng && isLt2M
}
return {
customRequest,
beforeUpload,
formState,
customRequest
}
}
})
<style>
.avatar-uploader > .ant-upload {
width: 128px;
height: 128px;
}
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>
index.ts请求的代码
import ajax from "./ajax";
/**自定义图片上传接口 */
export const uploadImage = (url: string, data: object) => ajax(url, data, 'POST')
服务端接收请求的代码
const express = require('express')
const multer = require('multer')
const upload = multer({ dest: __dirname + '/../../public/uploads' })
const routerFile = express.Router()
// 上传图片
routerFile.post('/upload', upload.single('avatar'), async (req, res) => {
console.log('上传图片成功')
const file = req.file
file.url = `http://localhost:8000/public/uploads/${file.filename}`
res.send(file)
})
module.exports = routerFile