因为 elment-ui 好像没有维护了, 就采用了 Ant Design Vue 组件库。
因为项目里面好多地方使用了 Upload(a-upload) 上传组件,所以就进行二次封装(比如上传路径等等)!
并且交付 Form(a-form) 组件进行托管
老实说交付 Form(a-form) 自动接管这里的坑真大, 百度了好久没有解决方案(都没有进行二次封装)
先看 Form(a-form) 组件的一段描述
经过 getFieldDecorator
或 v-decorator
包装的控件,表单控件会自动添加 value
(或 valuePropName
指定的其他属性) onChange
(或 trigger
指定的其他属性),数据同步将被 Form(a-form) 接管,这会导致以下结果:
- 你不再需要也不应该用
onChange
来做同步,但还是可以继续监听onChange
等事件。 - 你不能用控件的
value
defaultValue
等属性来设置表单域的值,默认值可以用getFieldDecorator
或v-decorator
里的initialValue
。 - 你不应该用
v-model
,可以使用this.form.setFieldsValue
来动态改变表单值。
这里坑就坑在
onChange
的这段描述,因为之前一直在写React
也是使用Ant Design
进行二次组件封装时候写的this.props.onChange(xxx)
, 所以Vue
这边上传成功后发送事件我就写成了this.$emit("onChange", info.file.response.data)
导致 Form(a-form) 一直接管不到!
也看了 Vue 官网的自定义组件:
一个组件上的
v-model
默认会利用名为value
的 prop 和名为input
的事件,但是像单选框、复选框等类型的输入控件可能会将value
attribute 用于不同的目的。
所以我又改成了这样: this.$emit("input", info.file.response.data)
还是不行, Form(a-form) 也接管不到!
于是想到了
Vue
在接受事件处理的习惯写法上@change => onChange
,所以就改成了this.$emit("change", info.file.response.data)
,就成功了。。。
至于为什么 Ant Design Vue 没有使用
input
事件的原因,应该是通过了自定义组件中的model
属性进行了修改,以达到和React
版本的Ant Design
组件库的API一致。
Ant Design Vue 还出了一个v-model
的 FormModel 的版本的From
表单,有时间可以看看!
完整代码(记得看到最下面如何使用):
<template>
<a-upload
name="file"
list-type="picture-card"
class="avatar-uploader"
:withCredentials="true"
:show-upload-list="false"
:action="url"
:before-upload="beforeUpload"
@change="handleChange"
:headers="headers"
>
<img class="pre-img" v-if="value" :src="value" alt="avatar" />
<div v-else>
<a-icon :type="loading ? 'loading' : 'plus'" />
<div class="ant-upload-text">
Upload
</div>
</div>
</a-upload>
</template>
<script>
import { getToken } from '@/utils/localStorage'
// 图片文件上传
export default {
name: 'upload',
props: {
value: {
type: String,
default: ''
},
},
data() {
return {
// 上传请求地址
url: process.env.VUE_APP_IMG_UPLOAD_URL,
loading: false,
headers: {
Authorization: `bearer ${getToken()}`
}
}
},
methods: {
handleChange(info) {
console.log(info)
if (info.file.status === 'uploading') {
this.loading = true;
return;
}
if (info.file.status === 'done') { // 上传成功会有有一个 done 状态标识成功
this.loading = false;
this.imageUrl = info.file.response.data;
// 发送事件
// 重点来了这里必须是 change 事件
this.$emit("change", info.file.response.data);
}
},
beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
this.$message.error('You can only upload JPG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.$message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
},
}
}
</script>
<style>
.avatar-uploader > .ant-upload, .pre-img {
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>
<style lang="less" scoped>
</style>
如何使用:
// 引入定义好的组件
import ImgUpload from '@/components/upload/index.vue'
export default {
// 并且在这里注册一下
components: { ImgUpload },
...省略其他代码
}
<a-form :form="form" :label-col="{ span: 3 }" :wrapper-col="{ span: 8 }" @submit="handleSubmit">
...省略其他组件
<a-form-item label="公司Logo">
<img-upload v-decorator="['logo', { getValueFromEvent: normFile, initialValue: '' }]"/>
</a-form-item>
</a-form>
// 函数,Form 获取结果
normFile(e) {
console.log('Upload event:', e);
return e;
},
重点来了
getValueFromEvent
是 Form(a-form) 接管后, 可以把onChange
事件的参数(如event
)转化为控件的值!
如果不能理解这句话请看看双向绑定
的原理以及实现!
网友评论