动态表单实现的一个思路【前端代码】

560 阅读3分钟

基于jq + mui 实现, 和框架没有关系. 分享一个思路

0. 后端返回的json数据

在开始看代码之前, 先看看一下后端返回的json数据

[
	{
      apiParamsName: "",		//input的name属性
      disabled: false,	
      id: "",	
      properties: {	
          id: "",	
          subType: "TEXT",					//和外层的type拼接确认类型
          placeholder: "请输入单行文本",		  //输入提示
          defaultValue: "测试"			      //默认值
      },
      readonly: false,
      required: false,	//是否必填
      title: "单行文本",	 //元素的名称
      type: "TEXT",
  },
  ....
]

1. 创建一个InputBase基类

import { toast } from "./../../../plugIn/toast/toast.js";

class InputBase {
    input;
	
    //返回input名称的方法
    getKey(){
        let thah = this;
        return thah.input.apiParamsName;
    }
	
    //获取值得方法, 有些input的值需要特殊处理
    getValue(){
        let thah = this;
        return $(`input[name='${thah.input.apiParamsName}']`).val();
    }
	
    //返回dom元素的方法
    returnHtml() { };
	
    //注册dom元素的点击事件的方法
    formEvents() { };
	
    //dom元素的执行的方法
    formEventsAction() { };
	
    //验证方法
    verification() {
        let input = this.input;
        return this.verificationAction({ input: input });
    };
	
    //验证的执行方法
    verificationAction({ bool = true, input }) {
        if (!input.required) {
            return true;
        }

        let value = $(`input[name='${input.apiParamsName}']`).val();
        if (!value) {
            toast(`${bool ? '请选择' : '请输入'}${input.title}`);
            return false;
        }

        return true;
    };
	
    //接受值和返回dom元素
    createInput(inputPojo) {
        this.input = inputPojo;
        let html = this.returnHtml(inputPojo);

        return html;
    };
}

export default InputBase;

2. 根据一些input的特征, 在抽象出几个基类

2.1 CheckboxBase, 多选和单选的基类

import InputBase from "./../formModeBase/inputBase.js";

class CheckboxBase extends InputBase {
	//重写的返回值的方法, 更具业务要求改写
    getValue(){
        let value =  $(`input[name=${this.input.apiParamsName}]`).val();
        if (value.length == 0){
            return JSON.stringify([]);
        }

        var values = value.split(',');
        return JSON.stringify(values);
    }
	//两个Input ,一个显示选中的中文值并绑定点击事件. 一个保存选中的值. 
    returnHtml() {
        let input = this.input;
        
        return `<div class="mui-input-row">
                    <label>${input.title}</label>
                    <input type="text" readonly
                    name="${input.apiParamsName}Text"
                    id="${input.apiParamsName}Text"
                    class="mui-input-clear" 
                    placeholder="${input.placeholder ?? "请选择"}">
                    
                    <input type="hidden" readonly
                    id="${input.apiParamsName}"
                    name="${input.apiParamsName}">
                </div>`;
    };
	//单选和多选的事件绑定方法
    formEvents() {
        let thah = this;
        $(`.mui-input-row`).on("click", `input[name=${this.input.apiParamsName}Text]`, function (e) {
            thah.formEventsAction();//调用的执行方法
        })
    }
}

export default CheckboxBase;
还有几个基类, 篇幅原因, 就不一一列出来了

3. 创建具体的实现类

3.1 单选的具体实现类

import CheckboxBase from "./inputBase/CheckboxBase.js"

class SingChoice extends CheckboxBase {
	getValue(){
        return $(`input[name=${this.input.apiParamsName}]`).val();
    }
	
    //单选的点击事件需要初始化PopPicker, 不能用父类的formEvents
	formEvents() {
        let input = this.input;
		var userPicker = new mui.PopPicker();
		userPicker.setData(input.data);

		var showUserPickerButton = document.getElementById(`${input.apiParamsName}Text`);
		var userResult = document.getElementById(`${input.apiParamsName}`);
		showUserPickerButton.addEventListener('tap', function (event) {
			userPicker.show(function (items) {
				$(userResult).val(items[0].value);
				$(showUserPickerButton).val(items[0].text);
			});
		}, false);
	}
	
	createInput(input) {
    	//data需要特殊处理
		let data = input.data;
		let options = JSON.parse(data);

		input.data = [];
		options.forEach((item, index) => {
			input.data.push({
				text: item.label,
				value: item.value
			});
		})

		//调用父类的方法, 返回dom
		return super.createInput(input);
	}
}


export { SingChoice };

4. 使用

4.1 将实现类都放到map

import { Text as createText } from "./formMode/text.js";
import { SingChoice as createRadio } from "./formMode/singleChoice.js";
import { Date as createDate } from "./formMode/date.js";
import { Money as createMoney } from "./formMode/money.js";
import { UserList as createUserList } from "./formMode/userList.js";
import { Checkbox as createCheckbox } from "./formMode/checkbox.js";
import { Department as createDepartment } from "./formMode/department.js";
import { Textarea as createTextarea } from "./formMode/textarea.js";
import { Quoteform as createQuoteform } from "./formMode/quoteform.js";
import { UpFile as createUpFile } from "./formMode/upFile.js";
import { Timerange as createTimerange } from "./formMode/timerange.js";


var modelMap = new Map();
modelMap.set("TEXT.TEXT", new createText());
modelMap.set("NUMBER.COMMON", new createText());
modelMap.set("SELECT.RADIO", new createRadio());//单选
modelMap.set("DATEPICKER.DATE", new createDate());//时间选择
modelMap.set("DATEPICKER.DATETIME", new createDate());//日期+时间
modelMap.set("NUMBER.MONEY", new createMoney());//钱
modelMap.set("QUOTE.QUOTEUSER", new createUserList());//用户
modelMap.set("SELECT.MULTIPLE", new createCheckbox());//多选
modelMap.set("QUOTE.QUOTEDEPARTMENT", new createDepartment());//选择部门
modelMap.set("TEXT.TEXTAREA", new createTextarea());
modelMap.set("TEXT.EXPLAIN", new createTextarea());
modelMap.set("QUOTE.QUOTEFORM", new createQuoteform());//选择部门
modelMap.set("FILE.", new createUpFile());
modelMap.set("DATEPICKER.TIMERANGE", new createTimerange());//时长


export const map = modelMap; 

4.2 循环创建dom

inputList.forEach((item, index) => {
		let inputType = `${item.type}.${item.properties.subType || ""}`;
		let createDom = map.get(inputType);

		if (createDom) {
			let html = createDom.createInput({
				maxSize: item.properties.maxSize,
				number: item.properties.number,
				defaultValue: inputType == 'QUOTE.QUOTEFORM' ? item.properties.quoteFormId : item.properties.defaultValue,
				title: item.title,
				placeholder: item.properties.placeholder || item.properties.buttonText,
				required: item.required,
				apiParamsName: item.apiParamsName,
				type: item.type == "DATEPICKER" || item.properties.subType == "EXPLAIN" ? item.properties.subType.toLowerCase() : item.type,
				data: item.properties.options
			});
			
            //添加到提交按钮前面
			$("#submitFrom").before(html);
            //绑定点击事件
			createDom.formEvents();
			//添加到集合里, 提交时调用验证方法
			verifications.push(createDom);
		}
	});

inputPojo参数的解释, 代码里没有用到(我也是才发现的)

class InputPojo{
	maxSize;	//文件的最大size
	number;	//文件的格数
	defaultValue;	//默认值
    title;	//元素的名称
	placeholder;	//input的输入提示
	apiParamsName = "";	//input的name元素
	required;	//是否必填
	type = "";	//input的type
	data;	//多选框的数据
}

export default InputPojo;