Skip to content
此页目录
本文总阅读量

vue3 组件-表单组件

该组件依赖element-plus

典型表单

查看代码
vue
<template>
  <KForm
    :options="options"
    @submit="submit"
    @submit-check-fail="submitCheckFail"
  />
</template>
<script setup lang="ts">
import { ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components"
import { KForm } from "@tomiaa/vue3-components"

// 活动标签列表,模拟异步数据
const tag = ref("")
const selectTagData = ref<FormProps["options"]>([])
setTimeout(() => {
  selectTagData.value = [
    {
      el: "el-option",
      label: "标签一",
      value: "one",
    },
    {
      el: "el-option",
      label: "标签二",
      value: "two",
    },
  ]
  tag.value = selectTagData.value[0].value
}, 5000)

// 表单配置,FormProps["options"] 声明以获取更好的 TS 类型提示
const options = ref<FormProps["options"]>([
  {
    el: "el-input",
    label: "活动名称",
    prop: "name",
    modelValue: "这是双向绑定的值,也是默认值", // 存在 modelValue 时 prop 字段必填,否者值不会同步
  },
  {
    el: "el-select",
    label: "活动地点",
    prop: "zone",
    children: [
      {
        el: "el-option",
        label: "上海",
        value: "shanghai",
      },
      {
        el: "el-option",
        label: "北京",
        value: "beijing",
      },
    ],
    rules: [
      {
        required: true,
        message: "必填",
      },
    ],
  },
  {
    el: "el-select",
    label: "活动标签",
    prop: "tag",
    // 可以是 ref 对象
    modelValue: tag,
    children: selectTagData,
  },
  {
    el: "el-date-picker",
    prop: "startDate",
    label: "开始时间",
    span: 12,
  },
  {
    el: "el-date-picker",
    prop: "endDate",
    label: "结束时间",
    span: 12,
  },
  {
    el: "el-switch",
    prop: "instantDelivery",
    label: "状态",
    span: 4,
  },
  {
    el: "el-checkbox-group",
    prop: "type",
    label: "活动类型",
    span: 10,
    children: [
      {
        el: "el-checkbox",
        label: "online",
        children: "在线",
      },
      {
        el: "el-checkbox",
        label: "offline",
        children: "离线",
      },
    ],
    rules: {
      required: true,
      message: "必填",
    },
  },
  {
    el: "el-radio-group",
    label: "资源",
    prop: "resources",
    span: 10,
    children: [
      {
        el: "el-radio",
        label: "sponsor",
        children: "赞助",
      },
      {
        el: "el-radio",
        label: "venue",
        children: "场地",
      },
    ],
    rules: {
      required: true,
      message: "必填",
    },
  },
  {
    el: "el-upload",
    label: "活动文件",
    autoUpload: false,
    prop: "files",
    listType: "picture-card",
    fileList: [
      {
        name: "food.jpeg",
        url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
      },
    ],
    children: [
      {
        el: "span", // 可以是 dom 标签
        style: {
          color: "var(--c-brand)",
        },
        children: "点击选择文件",
      },
    ],
  },
  {
    el: "el-input",
    type: "textarea",
    label: "活动描述",
    prop: "desc",
    rules: { required: true, message: "必填" },
  },
  {
    el: "editor",
    prop: "editor",
    label: "活动计划",
    modelValue:
      '<p><span style="color: rgb(212, 56, 13); font-size: 29px;">这是富文本😄</span></p>',
  },
])

const submit = ({ /* validate, */ rawFormModel }: any) => {
  // validate 为校验方法,传入 autoValidateOnSubmit=false 会关闭组件的自动校验
  console.log("校验通过:", rawFormModel)
}

const submitCheckFail = () => {
  console.log("校验失败")
}
</script>

弹窗回显

  1. v-model存在即视为弹窗
  2. 通过ref拿到组件的方法setFormModel设置回显的字段
查看代码
vue
<template>
  <div>
    <KForm
      ref="kFormRef"
      v-model="show"
      :options="options"
      @submit="submit"
      @submit-check-fail="submitCheckFail"
    />
    <el-button @click="open">打开弹窗</el-button>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components"
import { KForm } from "@tomiaa/vue3-components"

const show = ref(false)
const kFormRef = ref()

// 活动标签列表,模拟异步数据
const selectTagData = ref<FormProps["options"]>([])
setTimeout(() => {
  selectTagData.value = [
    {
      el: "el-option",
      label: "标签一",
      value: "one",
    },
    {
      el: "el-option",
      label: "标签二",
      value: "two",
    },
  ]
}, 5000)

// 表单配置,FormProps["options"] 声明以获取更好的 TS 类型提示
const options = ref<FormProps["options"]>([
  {
    el: "el-input",
    label: "活动名称",
    prop: "name",
    modelValue: "这是双向绑定的值,也是默认值", // 存在 modelValue 时 prop 字段必填,否者值不会同步
  },
  {
    el: "el-select",
    label: "活动地点",
    prop: "zone",
    children: [
      {
        el: "el-option",
        label: "上海",
        value: "shanghai",
      },
      {
        el: "el-option",
        label: "北京",
        value: "beijing",
      },
    ],
    rules: [
      {
        required: true,
        message: "必填",
      },
    ],
  },
  {
    el: "el-select",
    label: "活动标签",
    prop: "tag",
    children: selectTagData,
  },
  {
    el: "el-date-picker",
    prop: "startDate",
    label: "开始时间",
    span: 12,
  },
  {
    el: "el-date-picker",
    prop: "endDate",
    label: "结束时间",
    span: 12,
  },
  {
    el: "el-switch",
    prop: "instantDelivery",
    label: "状态",
    span: 4,
  },
  {
    el: "el-checkbox-group",
    prop: "type",
    label: "活动类型",
    span: 10,
    children: [
      {
        el: "el-checkbox",
        label: "online",
        children: "在线",
      },
      {
        el: "el-checkbox",
        label: "offline",
        children: "离线",
      },
    ],
    rules: {
      required: true,
      message: "必填",
    },
  },
  {
    el: "el-radio-group",
    label: "资源",
    prop: "resources",
    span: 10,
    children: [
      {
        el: "el-radio",
        label: "sponsor",
        children: "赞助",
      },
      {
        el: "el-radio",
        label: "venue",
        children: "场地",
      },
    ],
    rules: {
      required: true,
      message: "必填",
    },
  },
  {
    el: "el-upload",
    label: "活动文件",
    autoUpload: false,
    prop: "files",
    listType: "picture-card",
    children: [
      {
        el: "span", // 可以是 dom 标签
        style: {
          color: "var(--c-brand)",
        },
        children: "点击选择文件",
      },
    ],
  },
  {
    el: "el-input",
    type: "textarea",
    label: "活动描述",
    prop: "desc",
    rules: { required: true, message: "必填" },
  },
  {
    el: "editor",
    prop: "editor",
    label: "活动计划",
  },
])

// 模拟弹窗数据回显
const open = () => {
  show.value = true
  // data 是分页的数据
  const data = {
    id: "219087307",
    test: "这是多余的字段",
    name: "活动名",
    zone: "beijing",
    tag: "two",
    startDate: Date.now(),
    endDate: Date.now(),
    instantDelivery: true,
    type: ["offline"],
    resources: "sponsor",
    files: [
      {
        name: "food.jpeg",
        url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
      },
    ],
    desc: "活动描述",
    editor:
      '<p><span style="color: rgb(212, 56, 13); font-size: 29px;">这是富文本😄</span></p>',
  }
  // 设置表单数据回显
  kFormRef.value.setFormModel(data, {
    // includes: [], // 可以规定要回显哪些字段
    exclude: ["test"], // 可以排除不需要的字段,这里 test 字段是多余的
  })
}

const submit = ({ /* validate, */ rawFormModel }: any) => {
  console.log("校验通过:", rawFormModel)
}

const submitCheckFail = () => {
  console.log("校验失败")
}
</script>

插槽

查看代码
vue
<template>
  <KForm
    :options="options"
    :show-actions="false"
  >
    <template #my-slot> 这是slot插槽 </template>
    <template #inputFix>这是input插槽</template>
    <template #my-file="scope">
      这是文件的插槽,文件名:{{ scope.file.name }}
    </template>
  </KForm>
</template>
<script setup lang="ts">
import { ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components"
import { KForm } from "@tomiaa/vue3-components"

const options = ref<FormProps["options"]>([
  {
    slot: "my-slot",
  },
  {
    el: "el-input",
    label: "活动名称",
    prop: "name",
    children: {
      slot: "inputFix", // 传给 KForm 的插槽
      deepSlot: "prefix", // KForm 传给更深层的插槽名
    },
  },
  {
    el: "el-upload",
    label: "活动文件",
    autoUpload: false,
    prop: "files",
    listType: "picture-card",
    fileList: [
      {
        name: "figure-2.png",
        url: "/images/figure-2.png",
      },
    ],
    children: [
      "点击选择文件", // 这里默认是 default 插槽
      {
        slot: "my-file", // 当前传递给 KForm 的插槽名
        deepSlot: "file", // KFrom 传递给 el-upload 的插槽名
      },
    ],
  },
])
</script>

动态表单

查看代码
vue
<template>
  <KForm
    ref="dialogForm"
    :model="formModel"
    :gutter="15"
    :options="options"
    :actions-config="actionsConfig"
  >
  </KForm>
</template>
<script setup lang="ts">
import { computed, ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components/src"
import { KForm } from "@tomiaa/vue3-components"

const formModel = ref({ list: [] as string[] })

const inputList = computed<FormProps["options"]>(() =>
  formModel.value.list.map((modelValue: string, i: number) => ({
    el: "el-input",
    modelValue,
    prop: `list.${i}`,
    label: `${i + 1}`,
    rules: {
      required: true,
      message: `${i + 1}行必填`,
    },
  }))
)

const actionsConfig: FormProps["actionsConfig"] = {
  showCancel: false,
  showReset: false,
  submitText: "校验表单",
}

const options = ref<FormProps["options"]>([
  {
    el: "el-button",
    type: "primary",
    children: "新增",
    span: 8,
    onClick: () => {
      formModel.value.list.push("")
    },
  },
  {
    el: "el-button",
    type: "primary",
    children: "设置第一行",
    span: 8,
    onClick: () => {
      formModel.value.list[0] = "设置第一行的内容" + Date.now()
    },
  },
  {
    el: "el-button",
    type: "danger",
    children: "删除第一行",
    span: 8,
    onClick: () => {
      formModel.value.list.shift()
    },
  },
  // 二维数组会递归当做平级处理(外层会添加 el-col,el-form-item)
  // 否则需要自己添加 { el: "el-form-item" },相应的属性也会失效
  inputList,
  {
    el: "el-divider",
    children: "分隔线",
  },
])
</script>

复杂动态表单

查看代码
vue
<template>
  <KForm
    ref="dialogForm"
    :model="formModel"
    :gutter="15"
    :options="options"
    :actions-config="actionsConfig"
  >
  </KForm>
</template>
<script setup lang="ts">
import { computed, ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components/src"
import { KForm } from "@tomiaa/vue3-components"

const formModel = ref({ list: [] as any[] })

const inputList = computed<FormProps["options"]>(() =>
  formModel.value.list.map((modelValue: string, i: number) => [
    {
      el: "el-input",
      modelValue,
      prop: `list.${i}.name`,
      span: 8,
      rules: {
        required: true,
        message: `${i + 1}行姓名必填`,
      },
    },
    {
      el: "el-input",
      modelValue,
      prop: `list.${i}.age`,
      span: 8,
      rules: {
        required: true,
        message: `${i + 1}行年龄必填`,
      },
    },
    {
      el: "el-button",
      children: "删除",
      type: "danger",
      span: 8,
      onClick() {
        formModel.value.list.splice(i, 1)
      },
    },
  ])
)

const actionsConfig: FormProps["actionsConfig"] = {
  showCancel: false,
  showReset: false,
  submitText: "校验表单",
}

const options = ref<FormProps["options"]>([
  {
    el: "span",
    children: "姓名",
    required: true,
    span: 8,
  },
  {
    el: "span",
    children: "年龄",
    required: true,
    span: 8,
  },
  {
    el: "el-button",
    type: "primary",
    children: "新增",
    span: 8,
    onClick: () => {
      formModel.value.list.push({})
    },
  },
  // 二维数组会递归当做平级处理(外层会添加 el-col,el-form-item)
  // 否则需要自己添加 { el: "el-form-item" },相应的属性也会失效
  inputList,
  {
    el: "el-divider",
    children: "分隔线",
  },
])
</script>

自定义控制栏

查看代码
vue
<template>
  <KForm
    :options="options"
    :actions-config="actionsConfig"
  >
  </KForm>
</template>
<script setup lang="ts">
import { ref } from "vue"
import type { FormProps } from "@tomiaa/vue3-components/src"
import { KForm } from "@tomiaa/vue3-components"

const actionsConfig: FormProps["actionsConfig"] = {
  showCancel: true, // 默认值
  showSubmit: true, // 默认值
  showReset: true, // 默认值
  submitText: "提交", // 默认值
  resetText: "重置", // 默认值
  cancelText: "取消", // 默认值
  custom(list) {
    // 修改按钮列表
    list[0].icon = "el-icon-menu"
    // 以返回的列表渲染
    return list
  },
}

const options = ref<FormProps["options"]>([
  {
    el: "span",
    children: "姓名",
    required: true,
    span: 8,
  },
  {
    el: "el-divider",
    children: "分隔线",
  },
])
</script>

i18n 国际化

属性可以是 Ref 对象来动态显示

查看代码
ts
import { computed } from "vue"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const queryAttrs = ref([
  {
    el: "el-input",
    label: computed(() => t("名称")),
    prop: "name",
    placeholder: "请输入",
    clearable: true,
  },
  {
    el: "el-input",
    label: computed(() => t("代码")),
    prop: "code",
    placeholder: "请输入",
    clearable: true,
  },
  {
    el: "el-input",
    label: computed(() => t("域名")),
    prop: "host",
    placeholder: "请输入",
    clearable: true,
  },
])

也可以直接是字符串以$t(t(开头与)结尾即视为i18n,内部会调用$t获取语言配置

按需引入

vue
<script setup lang="ts">
import { KForm } from "@tomiaa/vue3-components"
</script>

属性

属性说明类型默认值必填
modelValue在弹窗中显示,modelValue 存在也会视为弹窗booleanundefined
showActions显示操作栏,为 true 时且弹窗时存在 footer 插槽时不会生效booleantrue
actionsConfig操作栏配置[ActionsConfig](#ActionsConfig 操作栏配置)
autoValidateOnSubmitsubmit 时自动校验,校验通过后触发 @submit 事件booleantrue
options表单配置FormOptions[]
...rest剩余属性为el-form的属性与el-dialog的属性(modelValue存在时)

ActionsConfig 操作栏配置

属性说明类型默认值必填
showCancel是否显示取消按钮booleantrue
cancelText取消按钮文字string取消
showReset是否显示重置按钮booleantrue
resetText重置按钮文字string重置
showSubmit是否显示提交按钮booleantrue
submitText提交按钮文字string提交
custom自定义控制栏方法,参数为控制按钮列表数组,需要返回该数组CustomElementProps["list"]...
...ElCol 的属性

FormOptions 配置

属性说明类型默认值必填
el标签名/组件(存在 render 或 setup 方法视为组件)element-plus 组件名 | dom 标签名 | 'editor' | string
ref该属性赋值 ref 对象可以同步拿到当前 el 的 vue ref 对象,如果该字段是函数,则会传入当前的 ref 对象Ref<any> | (ref: Ref<any>) => void当前 el 的 ref 对象
editorAttrswangEditor富文本的编辑器配置,el="editor"时生效Object
toolbarAttrswangEditor富文本的工具栏配置,el="editor"时生效Object
slot插槽名称(存在则视为插槽)string
deepSlotslot传递给深层次的插槽名称string
children子元素FormOptions | FormOptions[] | DefineComponent | DefineComponent[] | Ref<any> | string
...rest剩余属性包括element-plus所有组件的属性与事件

注意

默认情况下el-form-item会被el-col包裹一层,FormOptions直接填el-col的属性可以进行表达布局,但在组件传入属性:inline="true"时外层不会包裹el-col,因为inline属性开启了el-form的行内表单。

事件

事件名说明回调参数
submit提交按钮{
formRef,// el-form 的 ref
dialogRef, // el-dialog 的 ref
formModel, // 表单的数据(双向绑定的)
validate, // el-form 的表单校验方法
rawFormModel, // 接触绑定表单数据
}
reset重置按钮submit一致
cancel取消按钮,取消时会默认发送事件emit("update:modelValue", false)
submitCheckFailautoValidateOnSubmit 为 true 时,submit 校验 form 失败
update:model同步 model,model 改变时
...rest剩余事件为el-form事件与el-dialog事件(modelValue 属性存在时)

评论

交流群