在这里记录一下这个解决了2天1夜,中途问过七八个人,在五六个QQ群里刷屏,几十个人给过我解决方案,但最终还是失败。这也是我接触技术以来遇到的最难的一个问题。记录在这里,留待以后思考。
需求
我需要在小程序里实现自定义表单。后台设定规则,小程序根据规则自动生成页面。
最终会给前端返回的信息如下:
{
"data": {
"id": 1,
"title": "企业认证",
"logo": "http://supply.test/storage/images/0d398edacf11e9e3ad27c12a5608a658.png",
"fields": [
{
"tip": "请输入企业名称",
"mark": "qi_ye_ming_cheng",
"type": "field",
"title": "企业名称"
},
{
"tip": "请上传营业执照",
"mark": "ying_ye_zhi_zhao",
"type": "uploader",
"title": "营业执照"
}
],
"role": 0,
"front": 0
}
}
小程序会根据上面的数据格式,自动生成需要用户填写的信息。其中mark
字段是系统自动将title的汉字生成了拼音。作为标识。
比如判断type
如果是field
,那么就使用input
表单。如果判断type
是uploader
,那么就使用图片上传组件。
代码如下:
<form bindsubmit="addInfo">
<van-cell-group>
<block wx:for="{{certtypeshow.fields}}" wx:key="index">
<view wx:if="{{item.type == 'field'}}">
<view class="fromsec_title"><text>{{item.title}}</text></view>
<van-field name="{{item.mark}}" value="" required clearable label="{{item.title}}"
placeholder="{{item.tip}}" />
</view>
<view wx:if="{{item.type == 'uploader'}}">
<view class="fromsec_title"><text>{{item.title}}</text></view>
<view class="uploader">
<van-uploader upload-text="{{item.tip}}" file-list="{{item.mark}}"
max-count="1" bind:after-read="afterRead" deletable="true" />
</view>
</view>
</block>
</van-cell-group>
<view class="postbutton fixed">
<van-button type="primary" data-fun="toMyNeed" block form-type="submit">保存</van-button>
</view>
</form>
上面这段代码的问题:
1,input表单,可以通过from表单事件获取到用户提交的内容。
2,但是图片上传组件,通过from表单是无法获取到的。
我的图片上传使用的是vant的小程序组件。图片上传的组件使用方式:
<van-uploader file-list="{{ fileList }}" bind:after-read="afterRead" />
文档见:https://youzan.github.io/vant-weapp/#/uploader
用户在上传图片的时候,会触发bind:after-read
参数内的事件,比如上面代码的afterRead
;
afterRead(event) {
const { file } = event.detail;
// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
wx.uploadFile({
url: 'https://example.weixin.qq.com/upload', // 仅为示例,非真实的接口地址
filePath: file.url,
name: 'file',
formData: { user: 'test' },
success(res) {
// 上传完成需要更新 fileList
const { fileList = [] } = this.data;
fileList.push({ ...file, url: res.data });
this.setData({ fileList });
},
});
},
上面这段代码,会将用户上传的图片,赋值给data
的fileList
内。
问题来了
因为表单是用户自定义生成的。比如用户在后台自定义表单的时候,使用了两个图片上传。一个叫做营业执照,一个叫做相册。
那么两个图片上传,存放在data里的数据应该是分开的。而不应该存储在一个fileList
内。应该区分保存。
那怎么区分呢?回头在看一下我们用来生成表单的接口数据:
{
"data": {
"id": 1,
"title": "企业认证",
"logo": "http://supply.test/storage/images/0d398edacf11e9e3ad27c12a5608a658.png",
"fields": [
{
"tip": "请输入企业名称",
"mark": "qi_ye_ming_cheng",
"type": "field",
"title": "企业名称"
},
{
"tip": "请上传营业执照",
"mark": "ying_ye_zhi_zhao",
"type": "uploader",
"title": "营业执照"
}
],
"role": 0,
"front": 0
}
}
每个类型的表单,都有一个mark
字段作为标识,那么我的解决方式就是,把每个图片上传组件的数据,保存在以mark
为名称的data内。
所以图片上传的组件得这么改下。
<van-uploader name="{{item.mark}}" file-list="{{item.mark}}" bind:after-read="afterRead"/>
给组件加了个name属性。然后在js里面,可以通过event.detail.name
获取到这个属性。
所以js里面保存数据的逻辑,也变成了下面这样的代码:
afterRead(event) {
let name = event.detail.name;
this.setData({
[name]:[],
});
const {
file
} = event.detail;
// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
wx.uploadFile({
url: baseUrl+'/api/weapp/upload_image', // 仅为示例,非真实的接口地址
filePath: file.url,
name: 'file',
success: (res) => {
// 上传完成需要更新 fileList
let fileList = this.data[name];
fileList.push({
...file,
url: res.data
});
this.setData({
[name]:fileList,
});
},
});
console.log(this.data)
},
我们通过name变量,获取到组件的名称。然后将图片数据,保存在这个组件名称命名的字段内。
这样看起来成功了。因为经过测试,数据保存完全正常。
但是,更大的问题来了。
数据取不出来
数据是存储在mark
字段里的。
但是这个mark
叫什么,在wxml里面是无法直接知道的。需要通过{{item.mark}}
获取。如下代码:
<view wx:if="{{item.type == 'uploader'}}">
<view class="fromsec_title"><text>{{item.title}}</text></view>
<view class="uploader">
<van-uploader name="{{item.mark}}" upload-text="{{item.tip}}" file-list="{{item.mark}}"
max-count="1" bind:after-read="afterRead" deletable="true" />
</view>
</view>
我需要将图片上传的数据,放在上面的file-list的参数内。我在这里放的是{{item.mark}}
.
但很显然,这是错的。
因为{{item.mark}}
获取出来的数据,是这个字段的mark名称。比如是ying_ye_zhi_zhao
.
但事实上,数据需要通过{{ying_ye_zhi_zhao}}才能获取到。
问题就卡在了这里。
我试过的几种解决方案
1,把图片数据,保存在对象内。如果name
的名称是ying_ye_zhi_zhao
,那数据的存储格式就是这样的。
from:{ying_ye_zhi_zhao:数据}
这种方式最终失败的原因是,不知道如何取,因为在wxml内,依然需要先获取mark字段的名称,才能获取最终数据。
大概意思就是在wxml里面需要这么写:
{{from.{{item.mark}} }} 但这么写肯定有问题的。
2,第二种办法,是小程序允许的获取方式。比如这么写:{{from[item.mark]}}
但这种方式,需要把数据存储进 from[name],但小程序没办法这么存储,会提示
VM489 WAService.js:2 Error: Only digits (0-9) can be put inside [] in the path string: from[ying_ye_zhi_zhao]
也就是,小程序只允许数组下标为0-9。
OK。这就是这个问题的全部描述
困扰了1天2夜,最终失败。
后来曲线救国,换了另一种解决方式,比较笨的办法,放在2楼。