Skip to content

默认增加了 输入框(input)、选择器(select)、日期选择器(date)、数字输入框(inputNumber)、自定义组件(custom)组件。

动态表单截图

vue
<template>
  <DynamicForm
    ref="dynamicFormRef"
    :form-items="formItems"
    :show-action-btn="true"
    @submit="handleFormSubmit"
    @reset="handleFormReset">
  </DynamicForm>

  <el-button type="primary" @click="submit">提交(可传递额外参数)</el-button>
</template>

<script setup lang="tsx">
import { ref } from "vue";
import type { DynamicFormExpose, FormItem } from "h-business-ui";

const dynamicFormRef = ref<DynamicFormExpose>();

const formItems = ref<FormItem[]>([
  // 输入框
  {
    label: "用户名",
    prop: "username",
    type: "input",
    required: true,
    props: {
      placeholder: "请输入用户名",
      maxlength: 20,
      showWordLimit: true,
    },
  },
  {
    label: "真实姓名",
    prop: "realName",
    type: "input",
    required: true,
    props: {
      placeholder: "请输入真实姓名",
      maxlength: 20,
      showWordLimit: true,
    },
  },

  // 自定义组件
  {
    label: "密码",
    prop: "password",
    type: "custom",
    required: true,
    props: {
      placeholder: "请输入密码",
      maxlength: 20,
      showWordLimit: true,
    },
    render: (formData: Record<string, unknown>, prop: string, commonAttrs) => {
      return <el-input v-model={formData[prop]} {...commonAttrs} />;
    },
  },

  {
    label: "性别",
    prop: "gender",
    type: "select",
    options: [
      { label: "男", value: "male" },
      { label: "女", value: "female" },
    ],
  },

  {
    label: "年龄",
    prop: "age",
    type: "inputNumber",
    required: true,
    // 只有当性别为男时才显示
    hidden: (formData) => formData.gender !== "male",
  },

  {
    label: "用户类型",
    prop: "userType",
    type: "select",
    options: [
      { label: "普通用户", value: "normal" },
      { label: "VIP用户", value: "vip" },
    ],
    required: true,
  },

  {
    label: "手机号",
    prop: "phone",
    type: "input",
    required: true,
    props: {
      placeholder: "请输入手机号",
      maxlength: 11,
      showWordLimit: true,
    },
    rules: [
      {
        pattern: /^1[3456789]\d{9}$/,
        message: "请输入正确的手机号",
        trigger: "blur",
      },
    ],
    // 当用户类型为VIP时,禁用手机号输入
    disabled: (formData) => {
      return formData.userType === "vip";
    },
  },
]);

// 表单提交
const handleFormSubmit = (
  formData: Record<string, unknown>,
  ...args: unknown[]
) => {
  console.log("表单提交数据:", formData);
  console.log("额外参数:", ...args);
  // 这里可以写提交逻辑
};

// 表单重置
const handleFormReset = () => {
  console.log("表单已重置");
};

const submit = () => {
  dynamicFormRef.value?.handleSubmit(close, "extra2");
};
</script>

<style lang="less" scoped></style>

属性名类型默认值说明
formItemsFormItem[][]表单项配置,查看formItem说明
labelWidthstring"120px"表单标签宽度
inlinebooleanfalse是否为行内表单
showActionBtnbooleantrue是否显示操作按钮
rulesFormRules{}表单校验规则

formItem说明

属性名类型默认值说明
labelstring""表单项标签
propstring""表单项属性名
typestringinput表单项类型,可选值:input、select、date、inputNumber、custom
propsobject{}表单项组件属性
labelWidthstring"120px"表单项标签宽度
requiredbooleanfalse是否必填
optionsobject[{label: string, value: string}][][]下拉选择器选项
placeholderstring""占位符
render(formData: Record<string, unknown>, prop: string, commonAttrs: object,h:typeof createVNode) => VNodeundefined自定义渲染函数
eventsobjectundefined自定义事件,记得加on,如:{onBlur: (e: Event) => {console.log(e)}}
hiddenfunctionundefined表单项是否隐藏
disabledfunctionundefined表单项是否禁用
rulesFormRules{}表单项校验规则,同element-plus中表单的校验规则。这里的值与表单上的rules合并。所以可以单独在表单项上去写,也可以在表单上写。也可以混合写。

###

事件名参数说明
submit(formData: Record<string, unknown>, args: unknown[]) => void提交事件,参数为表单数据,args为额外的参数
reset重置事件

方法名参数返回值描述
formRef表单组件实例
formDataObject表单数据
getFormDataObject获取表单数据(获取参数)
handleSubmitargs: unknown[]void提交表单
handleResetvoid重置表单
setFormDataformData: Record<string, unknown>void设置表单数据
validateBoolean校验表单,同步
clearValidateprop: string | string[]void清除表单校验状态

typescript
import type { VNode, createVNode, Ref } from "vue";
import type { FormItemRule, FormInstance } from "element-plus";

export type FormItemType =
  | "input"
  | "select"
  | "date"
  | "daterange"
  | "inputNumber"
  | "custom";

export interface FormItem {
  // 标签文本
  label: string;
  // 绑定字段
  prop: string;
  // 组件类型('input' | 'select' | 'date' | 'daterange' | 'inputNumber' | 'custom')
  type: FormItemType;
  // 组件属性
  props?: Record<string, unknown>;
  // 标签宽度
  labelWidth?: string;
  // 是否必填
  required?: boolean;
  //  下拉选项(仅当type为select时有效)
  options?: {
    label: string;
    value: unknown;
  }[];
  // 占位符
  placeholder?: string;
  // 自定义渲染函数
  render?: (
    formData: Record<string, unknown>,
    prop: string,
    commonAttrs?: Record<string, unknown>,
    h?: typeof createVNode,
  ) => VNode;
  // 事件
  events?: Record<string, (e: unknown) => void>;
  // 可选,根据 formData 动态判断是否隐藏
  hidden?: (formData: Record<string, unknown>) => boolean;
  // 可选,根据 formData 动态判断是否禁用
  disabled?: (formData: Record<string, unknown>) => boolean;
  rules?: FormItemRule | FormItemRule[];
}

export interface Props {
  formItems: FormItem[];
  labelWidth?: string;
  inline?: boolean;
  showActionBtn?: boolean;
  rules?: Record<string, FormItemRule | FormItemRule[]>;
}

export interface DynamicFormExpose {
  formRef: Ref<FormInstance | undefined>;
  formData: Ref<Record<string, unknown>>;
  getFormData: () => Record<string, unknown>;
  handleSubmit: (...args: unknown[]) => Promise<void>;
  handleReset: () => void;
  setFormData: (formData: Record<string, unknown>) => void;
  validate: () => Promise<boolean>;
  clearValidate: (props?: string | string[]) => void;
}