动态表单的实现和校验

需求

在用户下单过程中,需要填写用户信息,包括:姓名,手机号,身份证号,邮箱等。这部分需要填写的内容由后端进行配置,可能只需要填写姓名+手机号。可能需要填写姓名+手机号+身份证号。后端配置的内容包含输入框标题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;
          }
	}

呼~经过上面这些代码,包括初始化,包括选择联系人信息的回填,都要于后端配置的信息进行挂钩。提交表单的时候还需要对配置的表单内容进行校验。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3 中的动态表单校验可以通过使用内置的校验器和自定义校验规则来实现。我们可以在表单输入的时候对输入的内容进行校验,然后给出相应的提示信息。 首先,我们可以利用 Vue 3 的响应式特性来实时监听表单输入的变化。可以使用 `v-model` 指令将表单的值与 Vue 实例中的数据进行绑定。 其次,Vue 3 提供了内置的校验器,可以用于检查常见的表单规则,例如必填、长度范围等。可以通过在表单元素上使用 `v-bind` 指令来绑定校验规则。在编写校验规则时,我们可以利用正则表达式来实现更复杂的校验逻辑。 除了内置的校验器外,我们还可以自定义校验规则。可以使用 `watch` 监听表单值的变化,并根据需要编写校验逻辑。在校验过程中,可以根据校验结果来给出相应的提示信息。 在表单提交时,可以通过调用校验方法来进行整体校验。可以遍历表单中的所有字段,逐一进行校验,并将校验结果保存在一个对象中。校验结果可以用于在页面上显示相应的提示信息,或者阻止表单的提交。 综上所述,Vue 3 中的动态表单校验可以通过使用响应式特性、内置的校验器和自定义校验规则来实现。这样可以方便地对表单进行实时的校验,并给出相应的提示信息。在实际应用中,可以根据具体的场景和需求,选择合适的校验方式和规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值