antd表单和上传组件
如果对antd的From和Upload组件不太熟悉,推荐去官网上先阅读下API。
Antd
本项目采用3.26.16版本
需求描述
表单项操作提示中,每个操作包含一个文本输入框,三个文件上传,还包含提示信息,每个提示信息,包含一个文本输入框,一个音频文件上传。提示信息可以动态增加和删除。操作提示也同理。文件回显时,通过返回的数组进行重新渲染。通过getFieldDecorator定义存储数组的名字,initialValue设置默认值。getFieldDecorator用于和表单进行双向绑定。
如有不想看文字描述,可以直接看下图:
首先,在本地的state中初始化了一个数组
boxList: [
{
toolStep: '名字',
id: 0,
promptSteps: [
{
toolStep: '工器具使用提示步骤1',
id: 0,
}
]
}
], //boxList,
该数组中的id,新增时拼接于文件名后,保证getFieldDecorator中的id(文件名)具有唯一性。如果使用index拼接,在删除时会显示的错误的数据。
点击确定按钮后,将文件名和地址从文件数组中取出,传给后端。
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let submitData = {}
submitData.toolShow = this.state.boxList.map((boxItem, idx) => {
let data = {}
data.name = boxItem.name
data.demoSrf = values[`demoSrfList${boxItem.id}`][0].response.data.url
data.realDemoSrf = values[`demoSrfList${boxItem.id}`][0].response.data.name
data.demoLua = values[`demoLuaList${boxItem.id}`][0].response.data.url
data.realDemoLua = values[`demoLuaList${boxItem.id}`][0].response.data.name
data.demoSdf = values[`demoSdfList${boxItem.id}`][0].response.data.url
data.realDemoSdf = values[`demoSdfList${boxItem.id}`][0].response.data.name
data.promptSteps = boxItem.promptSteps.map((subItem, index) => {
let subData = {}
subData.toolStep = subItem.toolStep
subData.toolIntroduction = subItem.toolIntroduction
subData.toolAudio = values[`audioStepsName${boxItem.id}${subItem.id}`][0].response.data.url
subData.realToolAudio = values[`audioStepsName${boxItem.id}${subItem.id}`][0].response.data.name
subData.toolStepId = index
return subData
})
return data
})
}
});
点击编辑按钮,会把当前工具的数据传入。主要理清层级关系,将文件正确的存入其对应的文件数组。最后更新外层动态数组,渲染回显的数据。
showDrawer = (item, title) => {
if (item) {
record = JSON.parse(JSON.stringify(item))
let { dynamicPartList, synopsisList, boxList, operationList } = this.state
// getToolLabel({ nameUrl: record.name }).then(res => {
// this.setState({ labelsList: res.data || [] })
// })
if (title === '编辑工器具' || title === '标签配置') {
boxList = JSON.parse(JSON.stringify(item.toolShow))
boxList.forEach((boxItem, index) => {
if (item.toolShow.length !== 0) {
record[`demoSrfList${boxItem.id}`] = [{ name: item.toolShow[index].realDemoSrf, url: item.toolShow[index].demoSrf }]
record[`demoLuaList${boxItem.id}`] = [{ name: item.toolShow[index].realDemoLua, url: item.toolShow[index].demoLua }]
record[`demoSdfList${boxItem.id}`] = [{ name: item.toolShow[index].realDemoSdf, url: item.toolShow[index].demoSdf }]
if (item.toolShow[index].promptSteps.length !== 0) {
record.tipIds = item.toolShow[index].promptSteps
item.toolShow[index].promptSteps.forEach((subItem, idx) => {
record[`audioStepsName${boxItem.id}${subItem.id}`] = [{ name: item.toolShow[index].promptSteps[idx].realToolAudio, url: item.toolShow[index].promptSteps[idx].toolAudio }]
})
}
}
})
this.setState({
drawerTitle: title,
boxList,
editData: record || {}
})
}
通过遍历这个demoSrfList这个文件数组,将name和url展现在界面上,并且存入response中。
<Form.Item>
{getFieldDecorator(`demoSrfList${boxitem.id}`, {
initialValue: (editData[`demoSrfList${boxitem.id}`] || []).map((f, index) => ({
// 为了提供给上传组件回显
uid: index, // 这是上传组件规定的文件唯一标识,内部会提供给Form以便正常渲染回显列表
name: f.name,
status: 'done',
url: f.url,
response: {
data: {
name: f.name,
url: f.url,
}
}
})),
valuePropName: 'fileList',
})(
<Upload name="file" {...upLoadProps} accept=".srf">
<Button>
<Icon type="upload" />上传文件
</Button>
<p style={{ fontSize: '12px', color: '#999' }}>支持上传格式srf</p>
</Upload>
)}
</Form.Item>
效果如下:
但是这样还不算完。我在新增的时候还发现一个问题。假如开始我新增了很多提示,每个提示都上传了文件。但是最后我觉得有点多了,想删除一个提示,这个时候,其它的所有文件都消失了。所以,在上传文件的时候,需要通过onChange事件,将文件名进行存储。我认为这里不需要存url,因为这里不用点击文件。
<Upload name="file" {...upLoadProps} accept=".srf" onChange={(f) => {
if (f.file.status === 'done') {
this.state.editData[`demoSrfList${boxitem.id}`] = [{ name: f.file.response.data.name }]
}
}}>
<Button>
<Icon type="upload" />上传文件
</Button>
<p style={{ fontSize: '12px', color: '#999' }}>支持上传格式srf</p>
</Upload>
再提一下这个Upload中的accept属性。它只是帮你 默认选择了上传文件类型,并没有做校验。所以要进行手动校验。像校验图片那样,在Upload的beforeUpload方法中,将文件的名字进行截取,通过后缀名判断文件是否合法。
下面是上传图片前校验:
//上传图片前校验
beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
if (!isJpgOrPng) {
message.error('文件格式错误,请重新选择');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('文件太大,请重新选择');
}
return isJpgOrPng && isLt2M;
}
以上只是我个人想法,如有不足,欢迎指出,感谢你的阅读!