需求
在用户下单过程中,需要填写用户信息,包括:姓名,手机号,身份证号,邮箱等。这部分需要填写的内容由后端进行配置,可能只需要填写姓名+手机号。可能需要填写姓名+手机号+身份证号。后端配置的内容包含输入框标题text和输入框的key,用户个数可以添加,新增的用户同样满足后端配置的填写表单要求。
思考逻辑
我们面对这个需求,可以想到的是首先把表单写成固定的表单,然后根据后端配置进行判断展示,但是这样对于后端配置的text和key就不能很好适配。
那我们考虑把text和key需要变成一个变量运用起来。
现在还有一个问题就是在于表单内容类型,包含普通输入框text,数字输入框number,身份证号输入框idcard带图片识别身份证号功能。
有这么多种类型的输入框,我们首先需要考虑将所有的输入框类型集合到一个输入框组件中。外部传递一个类型type,我们就返回对应的输入框。
然后对于后端配置的内容,我们根据它们返回的格式为data:[{text:“XXX”,key:“XXX”}]。我们需要对data进行循环再展示。
代码展示
输入框组件
components/PersonInput.vue
<template>
<view class="conter">
<view class="label">
{{ label }}
</view>
<view>
<input
v-if="type === 'text'"
:type="type"
:value="inputVal"
:placeholder="placeholder"
class="input"
placeholder-class="placeholder"
@input="fieldInput"
@blur="fiedBlur"
/>
<input
v-if="type === 'number'"
:type="type"
:value="inputVal"
:placeholder="placeholder"
class="input"
placeholder-class="placeholder"
@input="fieldInput"
@blur="fiedBlur"
/>
<input
v-if="type === 'idcard'"
:type="type"
:value="inputVal"
:placeholder="placeholder"
class="input"
placeholder-class="placeholder"
@input="fieldInput"
@blur="fiedBlur"
/>
<!-- 包含 -->
<view
v-if="label.includes('身份证') || type === 'idcard'"
class="upload-box"
>
<view class="upload-item" @click="uploadIdCard">
<image
class="img"
:src="cardImg ? cardImg : imgDefault"
mode="scaleToFill"
/>
<u-loading class="loading" mode="circle" color="#5fc970" :show="showLoading"></u-loading>
</view>
</view>
</view>
</view>
</template>
export default {
name: "PersonInput",
props: {
// 输入框类型
type: {
type: String,
default: "text",
},
// 标题文本
label: {
type: String,
default: "",
},
// 输入框提示
placeholder: {
type: String,
default: "",
},
// 输入框的值
inputVal: {
type: String,
default: "",
},
//验证码
core: {
type: String,
default: "",
},
boxType:{
type:[String,Number],
default: "",
},
cardImg:{
type:String,
default: "",
}
},
data() {
return {
locationImg: "XXXXXX/",
url: fileUrl, // 上传图片地址
file: null, //文件
isUploadRe: false, //
imgDefault: "/u001.png",//默认的展示图片
showLoading:false,//是否展示加载动画
};
},
methods: {
fieldInput(e) {
this.$emit("input", e.detail.value);
},
fiedBlur(e) {
this.$emit("blur", e.detail.value);
},
// 图片上传成功回调主要是用于身份证这里
uploadIdCard() {
uni.chooseImage({
count: 1,
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
// sourceType: ['camera'], //从相册选择
success: (chooseImageRes) => {
this.showLoading = true;
const tempFilePaths2 = chooseImageRes.tempFilePaths;
uni.uploadFile({
url: `XXXXXXX`,
filePath: tempFilePaths2[0],
formData: {
parentKey: "miniApp",
rentId: rentId,
},
name: "file",
success: (uploadFileRes) => {
this.showLoading = false;
let res = JSON.parse(uploadFileRes.data);
if(res.state){
// 识别成功之后,调用回调函数
// "type": "Front", "name": "姓名","id": "身份证号","addr": "地址","gender": "性别"
let param = {
contact:res.result.name,
idCard:res.result.id,
}
this.$emit('recognize',param,this.boxType,tempFilePaths2[0])
}else{
this.showLoading = false;
uni.showToast({
title: `识别身份证号出错`,
duration: 2000,
icon: "none",
});
}
},
fail: (err) => {
uni.showToast({
title: `${err}`,
duration: 2000,
icon: "none",
});
},
});
},
});
},
},
};
下单页面使用 order.vue
<template>
//用户模块,用户只有一份信息
<view
class="order-input"
v-for="(item, index) in contacts"
:key="index"
>
<PersonInput
type="text"
box-type="linkaddress"
:cardImg="linkaddress.cardImg"
:label="item.text"
:inputVal="linkaddress[item.key]"
v-model="linkaddress[item.key]"
:placeholder="'请输入' + item.text"
@recognize="recognize"
@input="
e => {
fieldInput(e, item.key);
}
"
></PersonInput>
</view>
// 游客信息 可以填写多个游客信息,选择人数的
<view
class="order-input"
v-for="(item, index) in formDate"
:key="item.id"
>
<template
v-for="(orderItem, itemIndex) in passenger"
>
<PersonInput
type="text"
:box-type="index"
:cardImg="formDate[index].cardImg"
:key="itemIndex"
:label="orderItem.text"
:inputVal="formDate[index][orderItem.key]"
v-model="formDate[index][orderItem.key]"
:placeholder="'请输入' + orderItem.text"
@recognize="recognize"
@input="
e => {
fieldInput(e, orderItem.key);
}
"
@blur="
e => {
orderItem.key == 'idCard' ? fiedBlur(e, index) : '';
}
"
></PersonInput>
</template>
<view class="chooseaddress" @tap="chooseaddress(index)"
>选择</view
>
<view class="delbox" @tap.stop="DelRoom(index)" v-if="value > 1">
<text class="ddm-wc"></text>
</view>
</view>
</template>
data() {
return {
// 用户表单
linkaddress: {},
//游客数据,多条
formDate: [
{
id: Date.now(),
userName: "",
idCard: "",
singleRoomNum: 0
}
],
}
}
// 手动选择联系人之后的数据回填
setNowItem(itemObj, index, cardImg) {
if (index == "linkaddress") {
let item = Object.assign(this.linkaddress, itemObj);
this.contacts.forEach(obj => {
if (Object.keys(item).includes(obj.key)) {
this.$set(this.linkaddress, obj.key, item[obj.key]);
}
obj.key === "phoneNum" &&
this.$set(this.linkaddress, obj.key, item.telno);
obj.key === "personAddress" &&
this.$set(this.linkaddress, obj.key, item.detailaddress);
});
this.$set(this.linkaddress, "cardImg", cardImg);
} else {
let nowIndex = parseInt(index);
let item = Object.assign(this.formDate[nowIndex], itemObj);
this.passenger.forEach(obj => {
if (Object.keys(item).includes(obj.key)) {
this.$set(this.formDate[nowIndex], obj.key, item[obj.key]);
}
obj.key === "phone" &&
this.$set(this.formDate[nowIndex], obj.key, item.telno);
obj.key === "userName" &&
this.$set(this.formDate[nowIndex], obj.key, item.contact);
obj.key === "personAddress" &&
this.$set(this.formDate[nowIndex], obj.key, item.detailaddress);
});
this.$set(this.formDate[nowIndex], "cardImg", cardImg);
}
},
/**
* @description 身份证信息图片识别
* @param {res} Object 身份证识别结果数据
* */
recognize(res, index, cardImg) {
this.setNowItem(res, index, cardImg); //
},
/**
* @description 用于处理新增客户信息时的动态添加对象
* @param {type} 'fictitious'为虚拟实名信息,即生成得客户信息需要有数据
* @param {index} 循环时的循环下标,用于处理一些循环事件
*/
getPersonObj(type, index) {
let newPersonObj = {};
newPersonObj = this.passenger.reduce(
(acc, cur) => {
acc[cur.key] = "";
return acc;
},
{}
);
newPersonObj = {
...newPersonObj,
...{ id: Date.now(), singleRoomNum: 0 }
};
return newPersonObj;
},
/**
* @description 用于联系人过校验的方法
*
*/
validatePerson() {
// 联系人过校验方法
for (let i = 0; i < this.contacts.length; i++) {
let item = this.contacts[i];
if (
this.linkaddress.hasOwnProperty(item.key) &&
this.linkaddress[item.key] == ""
) {
this.myShowToast(`请填写${item.text}!`);
return false;
}
if (item.key == "phoneNum") {
if (
this.linkaddress.hasOwnProperty("phoneNum") &&
!this.myreg.test(this.linkaddress.phoneNum)
) {
uni.showToast({
title: "请输入正确的手机号码",
icon: "none",
duration: 2000
});
return false;
}
} else if (
this.linkaddress.hasOwnProperty("idCard") &&
!this.validateIdCard(this.linkaddress.idCard)
) {
this.myShowToast(`请输入正确的身份证号`);
return false;
}
}
return true;
},
/**
* @description 用于游客信息过校验的方法
*
*/
validateFormDate(index) {
// 联系人过校验方法
for (
let i = 0;
i < this.passenger.length;
i++
) {
let item = this.passenger[i];
if (
this.formDate[index].hasOwnProperty(item.key) &&
this.formDate[index][item.key] == ""
) {
this.myShowToast(`请填写游客${index + 1}的${item.text}!`);
return false;
}
if (item.key == "phone") {
if (
this.formDate[index].hasOwnProperty("phone") &&
!this.myreg.test(this.formDate[index].phone)
) {
uni.showToast({
title: `请正确填写游客${index + 1}手机号码`,
icon: "none",
duration: 2000
});
return false;
}
}
if (item.key == "idCard") {
if (
this.formDate[index].hasOwnProperty("idCard") &&
!this.validateIdCard(this.formDate[index].idCard)
) {
this.myShowToast(`请正确填写游客${index + 1}身份证号`);
return false;
}
}
}
return true;
},
// 提交表单时的校验
submit(){
// 校验联系人输入信息格式
if (!this.validatePerson()) return false;
for (let i = 0; i < this.formDate.length; i++) {
if (!this.validateFormDate(i)) return false;
}
}
呼~经过上面这些代码,包括初始化,包括选择联系人信息的回填,都要于后端配置的信息进行挂钩。提交表单的时候还需要对配置的表单内容进行校验。