Skip to content

BaseForm 表单组件

来源:@jhlc/common-core 远程组件

📝 作者
朱祥
谢飞
朱祥高级开发工程师谢飞高级开发工程师
👤 工号:025877 · 026789研发部

BaseForm 是一个功能强大的表单组件,支持多列布局、数据验证、逻辑数据类型、自定义组件等功能。适用于新增、编辑场景。

📦 导入方式

typescript
// 全局注册(已在项目中配置)
// 直接使用 <BaseForm /> 即可

// 类型导入
import type { BaseFormItemDesc } from "@jhlc/common-core/src/components/form/common/type";

🚀 基本用法

vue
<template>
  <BaseForm
    ref="formRef"
    :form="form"
    :items="formItems"
    :columns="2"
    label-width="100px"
  />
</template>

<script setup lang="ts">
import { ref, reactive, computed } from "vue";

const formRef = ref();
const form = reactive({
  userName: "",
  phone: "",
  userType: "",
});

const formItems = computed(() => [
  {
    name: "userName",
    label: "用户名",
    required: true,
    placeholder: "请输入用户名",
  },
  {
    name: "phone",
    label: "手机号",
    placeholder: "请输入手机号",
  },
  {
    name: "userType",
    label: "用户类型",
    logicType: "dict",
    logicValue: "USER_TYPE",
  },
]);
</script>

📋 Props 属性

属性名类型默认值说明
formObject-表单数据对象
itemsBaseFormItemDesc[][]表单项配置数组
columnsnumber-列数
labelWidthstring-标签宽度
size'default' | 'large' | 'small'-表单尺寸
disabledbooleanfalse是否禁用
isViewerbooleanfalse是否查看模式
noFormbooleanfalse不使用 form 包裹
stylestring | Object-自定义样式
classnamesArray | string-自定义类名

📋 Expose 方法 (通过 ref 调用)

方法名参数返回值说明
validatecb: (valid: boolean) => void-验证表单
setDefaultValue--设置默认值
reset--重置表单
resetFields--重置表单字段

📋 表单项配置 BaseFormItemDesc

基础属性

typescript
interface BaseFormItemDesc<T = any> {
  // 字段名(绑定 form 对象的属性名)
  name?: string;
  // 标签文本
  label?: string;
  // 标签宽度
  labelWidth?: string;
  // 显示冒号
  showColon?: boolean;
  // 占位提示
  placeholder?: string;
  // 栅格数(24 格布局)
  span?: number;
  // 是否禁用
  disabled?: boolean | ((form: T) => boolean);
  // 是否可清空
  clearable?: boolean;
  // 是否显示
  show?: boolean | ((form: T) => boolean);
}

校验规则

typescript
{
  name: "userName",
  label: "用户名",
  // 必填
  required: true,
  // 或使用函数动态判断
  required: (form) => form.userType === "admin",
  // 校验提示
  message: "请输入用户名",
  // 自定义校验规则
  rules: [
    { required: true, message: "请输入用户名", trigger: "blur" },
    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
    {
      pattern: /^[a-zA-Z0-9_]+$/,
      message: "只能包含字母、数字和下划线",
      trigger: "blur"
    },
    {
      validator: (rule, value, callback) => {
        if (value === "admin") {
          callback(new Error("不能使用 admin 作为用户名"));
        } else {
          callback();
        }
      },
      trigger: "blur"
    }
  ]
}

逻辑数据类型

组件会根据 logicType 自动选择合适的表单控件:

typescript
{
  name: "status",
  label: "状态",
  // 字典类型 - 自动渲染为下拉选择
  logicType: "dict",
  logicValue: "ORDER_STATUS"
}

{
  name: "createTime",
  label: "创建时间",
  // 日期类型 - 自动渲染为日期选择器
  logicType: "date"
}

{
  name: "createDatetime",
  label: "创建时间",
  // 日期时间类型
  logicType: "datetime"
}

{
  name: "amount",
  label: "金额",
  // 数字类型 - 自动渲染为数字输入框
  logicType: "number"
}

{
  name: "isActive",
  label: "是否启用",
  // 布尔类型 - 自动渲染为开关
  logicType: "boolean"
}

{
  name: "userId",
  label: "负责人",
  // 用户类型 - 自动渲染为用户选择器
  logicType: "user"
}

{
  name: "deptId",
  label: "部门",
  // 部门类型 - 自动渲染为部门选择器
  logicType: "dept"
}

{
  name: "remark",
  label: "备注",
  // 多行文本
  logicType: "textarea"
}

默认值配置

typescript
{
  name: "createTime",
  label: "创建时间",
  logicType: "date",
  // 支持多种默认值类型
  defaultValue: "currentDay"     // 当天
  // defaultValue: "currentMonth"  // 当月
  // defaultValue: "currentYear"   // 当年
}

// 日期范围默认值
{
  type: "range",
  name: "dateRange",
  startName: "startDate",
  endName: "endDate",
  logicType: "date",
  defaultValue: "recentDay7"      // 最近 7 天
  // defaultValue: "recentDay30"   // 最近 30 天
  // defaultValue: "rangeDayCurrentMonth1ToToday"  // 当月1号到今天
}

支持的默认值类型:

说明
currentDay当天 (2024-09-22)
currentMonth当月 (2024-09)
currentYear当年 (2024)
currentDept当前部门
recentDay3最近 3 天
recentDay7最近 7 天
recentDay30最近 30 天
rangeDatetimeToday当天 0 点到 23 点
rangeDayCurrentMonth1ToToday当月 1 号到今天
rangeDayCurrentMonth1ToLastDay当月 1 号到月末

范围输入

typescript
{
  type: "range",
  label: "日期范围",
  startName: "startDate",
  endName: "endDate",
  logicType: "date",
  rangeSeparator: "至",
  // 自定义起止表单项
  startFormItem: {
    placeholder: "开始日期"
  },
  endFormItem: {
    placeholder: "结束日期"
  }
}

自定义组件

typescript
import { h } from "vue";

// 方式一:使用 component
{
  name: "color",
  label: "颜色",
  component: (form) => ({
    tag: "el-color-picker",
    props: { showAlpha: true }
  })
}

// 方式二:使用 componentVNode
{
  name: "custom",
  label: "自定义",
  componentVNode: (form) => {
    return h("div", { class: "custom-component" }, [
      h("span", form.custom),
      h("el-button", { onClick: () => {} }, "选择")
    ]);
  }
}

// 方式三:自定义属性
{
  name: "amount",
  label: "金额",
  logicType: "number",
  customProps: (form) => ({
    min: 0,
    max: 10000,
    precision: 2,
    step: 100
  })
}

💡 完整示例

基础表单

vue
<template>
  <el-card>
    <BaseForm
      ref="formRef"
      :form="form"
      :items="formItems"
      :columns="2"
      label-width="100px"
    />
    <div class="form-footer">
      <el-button type="primary" @click="handleSubmit">提交</el-button>
      <el-button @click="handleReset">重置</el-button>
    </div>
  </el-card>
</template>

<script setup lang="ts">
import { ref, reactive, computed } from "vue";
import type { BaseFormItemDesc } from "@jhlc/common-core/src/components/form/common/type";

const formRef = ref();

const form = reactive({
  orderNo: "",
  customerName: "",
  amount: 0,
  orderDate: "",
  status: "",
  remark: "",
});

const formItems = computed<BaseFormItemDesc[]>(() => [
  {
    name: "orderNo",
    label: "订单号",
    required: true,
    placeholder: "请输入订单号",
  },
  {
    name: "customerName",
    label: "客户名称",
    required: true,
    placeholder: "请输入客户名称",
  },
  {
    name: "amount",
    label: "订单金额",
    logicType: "number",
    required: true,
    customProps: () => ({
      min: 0,
      precision: 2,
    }),
  },
  {
    name: "orderDate",
    label: "订单日期",
    logicType: "date",
    required: true,
  },
  {
    name: "status",
    label: "订单状态",
    logicType: "dict",
    logicValue: "ORDER_STATUS",
  },
  {
    name: "remark",
    label: "备注",
    logicType: "textarea",
    span: 24,
  },
]);

const handleSubmit = () => {
  formRef.value?.validate((valid) => {
    if (valid) {
      console.log("表单数据:", form);
      // 提交逻辑
    }
  });
};

const handleReset = () => {
  formRef.value?.resetFields();
};
</script>

动态表单

vue
<template>
  <BaseForm
    ref="formRef"
    :form="form"
    :items="dynamicItems"
    :columns="2"
    label-width="100px"
  />
</template>

<script setup lang="ts">
import { ref, reactive, computed } from "vue";

const form = reactive({
  type: "person",
  name: "",
  idCard: "",
  companyName: "",
  businessLicense: "",
});

const dynamicItems = computed(() => {
  const baseItems = [
    {
      name: "type",
      label: "类型",
      logicType: "dict",
      logicValue: "CUSTOMER_TYPE",
    },
  ];

  // 根据类型动态显示不同字段
  if (form.type === "person") {
    return [
      ...baseItems,
      { name: "name", label: "姓名", required: true },
      { name: "idCard", label: "身份证号", required: true },
    ];
  } else {
    return [
      ...baseItems,
      { name: "companyName", label: "公司名称", required: true },
      { name: "businessLicense", label: "营业执照", required: true },
    ];
  }
});
</script>

⚠️ 注意事项

  1. 字段名使用 name 而非 prop

    typescript
    // ✅ 正确
    { name: "userName", label: "用户名" }
    
    // ❌ 错误
    { prop: "userName", label: "用户名" }
  2. logicType 会自动选择组件

    • 不需要手动指定 type: "select"
    • 设置 logicType: "dict" 即可
  3. 范围类型需要设置 startName 和 endName

    typescript
    {
      type: "range",
      startName: "startDate",
      endName: "endDate",
      logicType: "date"
    }
  4. 使用 computed 包装 items 实现响应式

    typescript
    const formItems = computed(() => [...]);

📚 相关文档

You may not distribute, modify, or sell this software without permission.