动态form表单

102 阅读2分钟

动态表单的场景还是比较多的,其中两个用途比较常见第一个是方便多处form表单复用,第二个就是动态表单,点击按钮新增一个input或者select框,代码在下面

form组件封装

<template>
  <!-- <el-form v-bind="$attrs" :model="model" ref="formRef"> -->
  <el-form v-bind="$attrs" :model="model" ref="formRef">
    <el-row>
      <el-col v-for="item in schema" :key="item.key" :span="item.span">
        <el-form-item :label="item.label" :prop="item.model">
          <template v-if="item.component === 'input'">
            <el-input
              v-model="model[item.model]"
              v-bind="item.props"
            ></el-input>
          </template>
          <template v-else-if="item.component === 'select'">
            <el-select v-model="model[item.model]" v-bind="item.props">
              <el-option
                v-for="option in item.selectOptions"
                :key="option.value"
                :label="option.label"
                :value="option.value"
              />
            </el-select>
          </template>
          <template v-else-if="item.component === 'submit-button'">
            <el-button v-bind="item.props" @click="submit(item.handleSubmit)">{{
              item.btnText
            }}</el-button>
          </template>
          <template v-else-if="item.component === 'button'">
            <el-button v-bind="item.props || {}" v-on="item.listeners || {}">{{
              item.btnText
            }}</el-button>
          </template>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>

<script lang="ts">
export default {
  name: "XForm",
};
</script>

<script lang="ts" setup>
import type { FormInstance } from "element-plus";
import { ref } from "vue";

defineProps<{
  schema: any[];
  model: any;
}>();

const formRef = ref<FormInstance>();

const submit = async (callback: () => {}) => {
  // 进行表单校验
  await formRef.value?.validate();
  // 如果通过了, 调用传入的回调来通知父组件
  callback();
};
</script>

<style scoped></style>

组件使用

<template>
  <h2>表单封装测试</h2>
  <FormTest />
  <hr />
  <button @click="add">添加一项</button>
  <XForm :schema="schema" :model="model" :rules="rules" label-width="100px" />
</template>

<script lang="ts">
export default {
  name: "Test1",
};
</script>

<script lang="ts" setup>
import { reactive, ref } from "vue";
import XForm from "../components/XForm/index.vue";
import FormTest from "./FormTest.vue";
/*
key: 唯一标识
span: 栅格数
component: 组件名, 对应不同的element表单项
  input ==> el-input
  select ==> el-select
  cancel-button ==> 取消el-button
  submit-button ==> 提交el-button
label: 左侧标题
model: 收集输入数据的属性名
props: 直接传递给表单项的属性
selectOptions: 用于显示表单项列表的数组
btnText: 按钮文本
handleSubmit: 点击提交按钮的回调
listeners: 包含事件监听的对象
*/
const schema = ref([
  {
    key: "username",
    span: 12,
    component: "input", // el-input
    label: "用户名",
    model: "username",
    props: {
      placeholder: "请输入用户名",
    },
  },

  {
    key: "pwd",
    span: 12,
    component: "input",
    label: "密码",
    model: "password",
    props: {
      placeholder: "请输入密码",
      type: "password",
    },
  },

  {
    key: "city",
    component: "select",
    label: "选择城市",
    span: 8,
    model: "city",
    props: {
      placeholder: "请选择城市",
    },
    selectOptions: [
      {
        label: "武汉",
        value: "wh",
      },
      {
        label: "北京",
        value: "bj",
      },
      {
        label: "深圳",
        value: "sz",
      },
    ],
  },

  {
    key: "desc",
    component: "input",
    label: "描述",
    model: "description",
    props: {
      type: "textarea",
      rows: 2,
      placeholder: "请输入描述",
    },
  },

  {
    key: "submit-btn",
    component: "submit-button",
    span: 2,
    props: {
      type: "primary",
    },
    btnText: "提交",
    handleSubmit: () => {
      alert("校验通过后发请求" + JSON.stringify(model));
    },
  },

  {
    key: "cancel-button",
    component: "button",
    span: 2,
    btnText: "返回2",
    listeners: {
      click: () => alert("返回上一个页面"),
      // xxx: () => {}
    },
  },
]);

const model = reactive({
  username: "",
  password: "",
  city: "",
  description: "",
});

const rules = {
  // 表单校验名称必须和数据名称一致
  username: [{ required: true, message: "用户名是必须的", trigger: "blur" }],
  password: [{ required: true, message: "密码是必须的", trigger: "blur" }],
};
const add = () => {
  schema.value.push({
    key: "pwd1",
    span: 12,
    component: "input",
    label: "密码",
    model: "password",
    props: {
      placeholder: "请输入密码",
      type: "password",
    },
  });
};

// 封装一个
function getList() {
  return new Promise((res) => {
    res([{ name: "中岛瑞" }]);
  });
}
function test111(cb) {
  getList().then((res) => {
    cb(res);
  });
}
test111((aaa) => {
  console.log(aaa);
});
</script>

<style scoped></style>