Skip to content

Form 配置表单

该组件是基于el-form封装的组件,提供配置表单功能

注意

注意:0.0.27版本之后,如果是用reactive定义,使用@update:model-value="Object.assign(form, $event)"配合来监听数据变化,如果是ref定义,可以直接使用v-model

基础用法

配置表单示例
姓名
指标单位
手机号
+86
国内手机号
性别
数量
启动
滑块
评分
日期
日期时间
复选框
单选框
树形选择
请选择树形选择
这里是树形选择提示语
标签输入
音乐电影
选择输入
颜色选择
级联
插槽
false-----------customSlot
备注
0 / 200
提交按钮
查看代码
vue
<template>
  <div>
    <gi-card title="配置表单示例" bordered>
      <template #extra>
        <el-switch v-model="disabled" active-text="禁用"></el-switch>
        <el-radio-group v-model="labelPosition" size="small">
          <el-radio-button label="左侧" value="left" />
          <el-radio-button label="右侧" value="right" />
          <el-radio-button label="顶部" value="top" />
        </el-radio-group>
      </template>

      <gi-form ref="GiFormRef" :model-value="form" :columns="columns" :disabled="disabled" label-width="auto"
        :label-position="labelPosition" :grid-item-props="{
          span: { xs: 24, sm: 24, md: 24, lg: 12, xl: 12, xxl: 12 },
        }" @update:model-value="Object.assign(form, $event)">
        <template #customSlot="{ item }">
          {{ form.status }}-----------{{ item.field }}
        </template>
        <template #customSlot2>
          <el-alert title="Success alert" type="success" effect="dark" />
          <el-alert title="Info alert" type="info" effect="dark" />
          <el-alert title="Warning alert" type="warning" effect="dark" />
          <el-alert title="Error alert" type="error" effect="dark" />
        </template>
        <template #btns>
          <el-space>
            <el-button @click="reset">重置</el-button>
            <el-button type="primary" @click="submit"> 提交 </el-button>
          </el-space>
        </template>
      </gi-form>
    </gi-card>
  </div>
</template>

<script lang="ts" setup>
import type { FormColumnItem, FormInstance } from 'gi-component'
import { ElMessage, ElTag } from 'element-plus'
import { computed, h, reactive, ref } from 'vue'
import { treeData } from './data'

const GiFormRef = ref<FormInstance | null>()
const disabled = ref(false)
const labelPosition = ref<any>('right')

const form = reactive({
  name: '',
  age: 18,
  sex: '1',
  address: '北京',
  desc: '我是一个描述',
  date: '2021-01-01',
  datetime: '',
  time: '12:00:00',
  status: false,
  star: 3,
  radio: '吃饭',
  remark: '',
  inputTag: ['音乐', '电影'],
  color: '#0077F7',
  startTime: '',
  endTime: '',
  inputSearchId: '',
  inputSearchName: ''
})

const columns = computed(() => {
  return [
    {
      type: 'title',
      label: '基本信息',
      field: 'title-base'
    },
    {
      type: 'input',
      label: '姓名',
      field: 'name',
      required: true,
      extra: '指标单位',
      props: {
        // 如果需要用到组件事件,直接在事件名前面拼接上on即可,例如input事件就写成onInput
        // 其他事件同理,例如focus事件就写成onFocus,blur事件就写成onBlur,
        onInput: () => {
          ElMessage.success('输入了内容')
        }
      }
    },
    {
      type: 'input',
      label: '手机号',
      field: 'phone',
      required: true,
      tip: '国内手机号',
      formItemProps: { style: { alignItems: 'baseline' } },
      slots: {
        prepend: '+86'
      }
    },
    {
      type: 'select',
      label: '性别',
      field: 'sex',
      props: {
        options: [
          { label: '男', value: '1' },
          { label: '女', value: '2' }
        ]
      }
    },
    {
      type: 'input-number',
      label: '数量',
      field: 'num',
      extra: () => h(ElTag, { type: 'danger' }, () => '个')
    },
    {
      type: 'switch',
      label: '启动',
      labelRender: () => h('span', { style: { color: 'red' } }, '启动'),
      field: 'status'
    },
    {
      type: 'slider',
      label: '滑块',
      field: 'slider',
      hide: () => form.status === true
    },
    {
      type: 'title',
      label: '其他信息',
      field: 'title-other'
    },
    {
      type: 'rate',
      label: '评分',
      field: 'star'
    },
    {
      type: 'date-picker',
      label: '日期',
      field: 'date'
    },
    {
      type: 'date-picker',
      label: '日期时间',
      field: 'datetime',
      props: {
        type: 'datetime'
      }
    },
    {
      type: 'checkbox-group',
      label: '复选框',
      field: 'checkbox',
      props: {
        options: [
          { label: '吃饭', value: '吃饭' },
          { label: '睡觉', value: '睡觉' },
          { label: '打豆豆', value: '打豆豆' }
        ]
      }
    },
    {
      type: 'radio-group',
      label: '单选框',
      field: 'radio',
      props: {
        options: [
          { label: '吃饭', value: '吃饭' },
          { label: '睡觉', value: '睡觉' },
          { label: '打豆豆', value: '打豆豆' }
        ]
      }
    },
    {
      type: 'tree-select',
      label: '树形选择',
      field: 'treeSelect',
      tip: '这里是树形选择提示语',
      props: {
        data: treeData
      },
      formItemProps: { style: { alignItems: 'baseline' } }
    },
    {
      type: 'input-tag',
      label: '标签输入',
      field: 'inputTag'
    },
    {
      type: 'input-search',
      label: '选择输入',
      field: 'inputSearchId',
      fieldName: 'inputSearchName',
      props: {
        onSearch: () => {
          ElMessage.success('点击了搜索')
          form.inputSearchId = '111,222,333'
          form.inputSearchName = 'aaa,bbb,ccc'
        },
        onClear: () => {
          ElMessage.success('点击了清除')
          form.inputSearchId = ''
          form.inputSearchName = ''
        }
      }
    },
    {
      type: 'color-picker',
      label: '颜色选择',
      field: 'color'
    },
    {
      type: 'cascader',
      label: '级联',
      field: 'cascader',
      props: {
        options: treeData
      }
    },
    {
      type: 'slot',
      label: '插槽',
      field: 'customSlot',
      tip: '这里使用了自定义插槽'
    },
    {
      type: 'slot',
      label: '',
      field: 'customSlot2',
      span: 24,
      formItemProps: { labelWidth: 0, class: 'hide-label' },
      tip: '暂满宽度的插槽示例'
    },
    {
      type: 'textarea',
      label: '备注',
      field: 'remark',
      span: 24
    },
    {
      type: 'slot',
      label: '提交按钮',
      field: 'btns',
      span: 24,
      formItemProps: {
        labelWidth: 0,
        class: 'hide-label',
        style: { marginBottom: 0 }
      }
    }
  ] as FormColumnItem[]
})

async function submit() {
  await GiFormRef.value?.formRef?.validate()
}
function reset() {
  GiFormRef.value?.formRef?.resetFields()
}
</script>

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

搜索表单

查看代码
vue
<template>
  <gi-form ref="GiFormRef" :model-value="form" :columns="columns" search :grid-item-props="{
    span: { xs: 24, sm: 12, md: 12, lg: 12, xl: 8, xxl: 8 },
  }" @search="search" @reset="reset" @update:model-value="Object.assign(form, $event)">
  </gi-form>
</template>

<script lang="ts" setup>
import type { FormColumnItem, FormInstance } from 'gi-component'
import { ElMessage } from 'element-plus'
import { computed, reactive, ref } from 'vue'

const GiFormRef = ref<FormInstance | null>()

function search() {
  ElMessage.success('点击了搜索')
}

function reset() {
  ElMessage.info('点击了重置')
}

const form = reactive({})

const columns = computed(() => {
  return [
    {
      type: 'input',
      label: '姓名',
      field: 'name'
    },
    {
      type: 'input',
      label: '手机号',
      field: 'phone'
    },
    {
      type: 'input',
      label: '数量',
      field: 'num'
    },
    {
      type: 'input',
      label: '评分',
      field: 'star'
    },
    {
      type: 'select',
      label: '兴趣',
      field: 'hobby',
      props: {
        options: [
          { label: '吃饭', value: '吃饭' },
          { label: '睡觉', value: '睡觉' },
          { label: '打豆豆', value: '打豆豆' }
        ]
      }
    },
    {
      type: 'date-picker',
      label: '日期',
      field: 'date'
    },
    {
      type: 'date-picker',
      label: '时间',
      field: 'datetime',
      props: {
        type: 'datetime'
      }
    }
  ] as FormColumnItem[]
})
</script>

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

结合字典

性别
请选择性别
状态
兴趣
查看代码
vue
<template>
  <gi-form ref="GiFormRef" :model-value="form" :columns="columns" :grid-item-props="{
    span: { xs: 24, sm: 12, md: 12, lg: 12, xl: 12, xxl: 12 },
  }" @update:model-value="Object.assign(form, $event)">
  </gi-form>
</template>

<script lang="ts" setup>
import type { FormColumnItem, FormInstance } from 'gi-component'
import { computed, reactive, ref } from 'vue'
import { useDictStore } from './useDictStore'

const GiFormRef = ref<FormInstance | null>()

const form = reactive({
  status: '1'
})
const { dict } = useDictStore(['HOBBY', 'STATUS'])

const columns = computed(() => {
  return [
    {
      type: 'select',
      label: '性别',
      field: 'sex',
      dictCode: 'SEX' // 需要配置dictRequest
    },
    {
      type: 'radio-group',
      label: '状态',
      field: 'status',
      dictCode: 'STATUS' // 需要配置dictRequest
    },
    {
      type: 'checkbox-group',
      label: '兴趣',
      field: 'hobby',
      props: {
        options: dict.HOBBY // 通过hooks获取字典数据
      }
    }
  ] as FormColumnItem[]
})
</script>

表单控制

姓名
手机号
{
  "name": {
    "required": true,
    "disabled": false,
    "hidden": false
  },
  "phone": {
    "required": true,
    "disabled": false,
    "hidden": false
  }
}
查看代码
vue
<template>
  <div>
    <gi-edit-table :columns="tableColumns" :data="data" size="small" class="gi-mb"></gi-edit-table>
    <gi-form ref="formRef" :model-value="formData" :columns="columns" :fc="fc" :scroll-to-error="false"
      @update:model-value="Object.assign(formData, $event)" />
    <el-row justify="end">
      <el-space>
        <el-button @click="handleReset">重置</el-button>
        <el-button type="primary" @click="handleSubmit">提交</el-button>
      </el-space>
    </el-row>
    <pre class="doc-pre">{{ fc }}</pre>
  </div>
</template>

<script setup lang="ts">
import type { EditTableColumnItem, FormColumnItem } from 'gi-component'
import { ElMessage } from 'element-plus'
import { computed, reactive, ref, useTemplateRef } from 'vue'

const tableColumns: EditTableColumnItem[] = [
  { label: '字段', prop: 'field' },
  { type: 'checkbox', label: '必填', prop: 'required' },
  { type: 'checkbox', label: '禁用', prop: 'disabled' },
  { type: 'checkbox', label: '隐藏', prop: 'hidden' }
]
const data = ref([
  {
    field: 'name',
    required: true,
    disabled: false,
    hidden: false
  },
  {
    field: 'phone',
    required: true,
    disabled: false,
    hidden: false
  }
])

const fc = computed(() => {
  const obj: Record<string, { required: boolean, disabled: boolean, hidden: boolean }> = {}
  data.value.forEach((item) => {
    obj[item.field] = {
      required: item.required,
      disabled: item.disabled,
      hidden: item.hidden
    }
  })
  return obj
})

// 表单数据
const formData = reactive({
  name: '',
  phone: ''
})

// 表单列配置
const columns = [
  {
    type: 'input',
    label: '姓名',
    field: 'name'
  },
  {
    type: 'input',
    label: '手机号',
    field: 'phone'
  }
] as FormColumnItem[]

const formRef = useTemplateRef('formRef')
async function handleSubmit() {
  try {
    await formRef.value?.formRef?.validate?.()
    ElMessage.success('提交成功')
  } catch (error) {
    ElMessage.error('提交失败')
  }
}

function handleReset() {
  formRef.value?.formRef?.resetFields?.()
}
</script>

<style lang="scss" scoped>
.header {
  font-size: 12px;
}
</style>

API 说明

Props

参数类型说明默认值
modelValue / v-modelany表单数据对象-
columnsFormColumnItem[]表单项配置数组[]
gridPropsGrid.Props网格配置-
gridItemPropsGridItem.Props网格项默认配置{ span: { xs: 24, sm: 24, md: 12 } }
searchboolean是否为搜索表单false
searchTextstring搜索按钮文本'查询'
hideFoldBtnboolean是否隐藏折叠按钮false
defaultCollapsedboolean默认是否折叠-
suffixboolean搜索表单时,搜索/重置/折叠按钮所在行是否作为 Grid 的 suffix 布局(靠右或单独一行)true
fcRecord<string, { required: boolean; disabled: boolean; hidden: boolean }>表单控制属性-

TIP

继承 el-form 的所有属性

Events

事件名说明参数
update:modelValue表单数据更新事件value: any
search搜索事件-
reset重置事件-

ColumnItem 配置

FormColumnItem 是表单项的配置接口,支持以下属性:

属性类型说明
typeColumnType表单项类型
labelstring标签文本
labelRender() => VNode标签渲染函数
fieldstring字段名,用于绑定表单数据
fieldNamestring字段名称
spannumber | GridItemProps['span']占用列数
propsColumnProps组件属性,根据不同的 type 有不同的配置
formItemPropsFormItemPropsElFormItem 的属性配置
gridItemPropsGridItem.PropsGridItem 的属性配置
requiredboolean是否必填
rulesFormItemRule[]验证规则
hideboolean | ((form: any) => boolean)是否隐藏,支持函数动态控制
tipstring提示信息
slotNamestring插槽名称
slotsColumnSlots插槽配置
extrastring | VNode右侧额外内容