我用NavieUI封装了一个动态表单

461 阅读4分钟

之前用naive-ui做了一个管理后台的系统,但是使用过程中发现Form表单组件,用起来比较麻烦,代码也不够优雅,而且我特别讨厌在template里面写一堆内容,于是想着通过JSON配置的方式,代码更佳清爽优雅简便,于是自己搞了一个对Naive-UI二次封装的组件hyb-naive

快速使用
npm install hyb-naive
pnpm install hyb-naive
yarn add hyb-naive
HForm(表单组件)

表单组件,有两种使用模式一个是基础的表单录入(FORM)和页面筛选器(FILTER

  • 筛选器 image.png
<template>
  <HForm 
      :title="`结果:${JSON.stringify(filterParams)}`" 
      type="FILTER" 
      card 
      :rules="filterRules" 
      @search="search" 
      @reset="reset" /> 
</template>

<script setup lang='ts'> 
import { HForm ,HFormRule} from 'hyb-naive' // 确保HForm组件已正确引入
import { ref } from 'vue'

const filterRules = [
  {
    field: 'category',
    compType: 'Select',
    label: '项目类别',
    options: [
      { label: '项目1', value: '1' },
      { label: '项目2', value: '2' },
    ],
  },
  {
    field: 'projectName',
    compType: 'Input',
    label: '项目名称',
  },
  {
    label: '操作时间',
    field: 'time',
    compType: 'DatePicker',
    fullWidth: true,
    props: {
      type: 'date',
    },
  },
]
const filterParams = ref({})
// 搜索
function search(params) {
  console.log('filterParams', params)
  filterParams.value=params
}
function reset(params) {
   filterParams.value=params
}

</script>


  • 表单录入

image.png

<template>
  <HForm :title="`表单结果:${JSON.stringify(formData)}`" type="FORM" card :rules="HFormRules" :card="false" @confirm="commit" :cols="2" />
</template>
 
<script lang="ts" setup>
import { HFormRule, HForm } from 'hyb-naive'
import { ref } from 'vue'

const HFormRules:HFormRule[] = [
  {
    field: 'name',
    compType: 'Input',
    label: '姓名',
    required: true,
  },
  {
    field: 'sex',
    compType: 'RadioGroup',
    label: '性别',
    required: true,
    options: [
      { label: '男', value: '1' },
      { label: '女', value: '2' },
    ],
  },
  {
    field: 'age',
    compType: 'InputNumber',
    label: '年龄',
    defaultValue: 18,
    fullWidth: true,
    required: true,
    validateType: 'number',
  },
  {
    field: 'grade',
    compType: 'Select',
    label: '年级',
    required: true,
    options: [
      { label: '1年级', value: '1' },
      { label: '2年级', value: '2' },
    ],
  },
  {
    field: 'address',
    compType: 'Input',
    label: '地址',
    fullWidth: true,
    required: true,
    span: 24,
    props: {
      type: 'textarea',
    },
  },
]

const formData = ref({})

function commit(data: object) {
  console.log('formData=', data)
  formData.value=data
}
</script>

HFormModal 表单弹框

image.png

image.png

<template>
  <div>
    <NSpace>
      <NButton type="primary" @click="showModal=true">表单录入</NButton>
      <NButton type="primary" @click="showFilterModal=true">表单筛选</NButton>
    </NSpace>

    <HFormModal 
      width="50%" 
      :form-type="'FILTER'" 
      title="表单筛选" 
      :show="showFilterModal" 
      :rules="filterRules" 
      @search="search" 
      @reset="reset"
      @close="()=>{showFilterModal=false}">
        <NDataTable
          style="margin-top:15px"
          :bordered="true"
          :single-line="true"
          :columns="columns"
          :data="data"
        />
        <template #footer>
          <div style="text-align:center">footer slot</div>
        </template>
      </HFormModal>
    <HFormModal 
      :width="600" 
      :title="`表单结果:${JSON.stringify(formData)}`" 
      :show="showModal" 
      :rules="formRules" 
      @confirm="commit" 
      :form-cols="2" 
      :confirm-loading="commitLoading"
      @close="()=>{showModal=false}"
      />
  </div>
 
</template>
 
<script lang="ts" setup>
import { HFormRule, HFormModal ,HFormApi} from 'hyb-naive'
import { reactive,ref } from 'vue'
import {NButton,NSpace,NDataTable} from 'naive-ui'

const showModal = ref(false)
const commitLoading=ref(false)
const showFilterModal = ref(false)

const formRules =reactive<HFormRule[]>([
  {
    field: 'name',
    compType: 'Input',
    label: '姓名',
    required: true,
  },
  {
    field: 'sex',
    compType: 'RadioGroup',
    label: '性别',
    required: true,
    options: [
      { label: '男', value: '1' },
      { label: '女', value: '2' },
    ],
  },
  {
    field: 'age',
    compType: 'InputNumber',
    label: '年龄',
    defaultValue: 18,
    fullWidth: true,
    required: true,
    validateType: 'number',
  },
 {
    field: 'grade',
    compType: 'Select',
    label: '年级',
    required: true,
    onLoad(fApi:HFormApi){
      setTimeout(()=>{
        fApi.setOptions('grade',[
          { label: '1年级', value: '1' },
          { label: '2年级', value: '2' },
        ])
      },1000)
    }
  },
  {
    field: 'address',
    compType: 'Input',
    label: '地址',
    fullWidth: true,
    required: true,
    span: 24,
    props: {
      type: 'textarea',
    },
  },
]) 


const filterRules =reactive<HFormRule[]>([
  {
    field: 'name',
    compType: 'Input',
    label: '姓名',
  },
  {
    field: 'age',
    compType: 'InputNumber',
    label: '年龄',
    fullWidth: true,
    props: {
      min: 1,
    }
  },
]) 

const formData = ref({})

const columns = [
  { title: 'Name',  key: 'name' },
  { title: 'Age', key: 'age' },
  { title: 'Address', key: 'address'}
]
const sourceData= [
  {
    key: 0,
    name: 'John Brown',
    age: 18,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: 1,
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: 2,
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park',
  }
]

const data = ref(sourceData)
function commit(data: object) {
  formData.value=data
  commitLoading.value=true
  setTimeout(()=>{
    commitLoading.value=false
  },1000)
}

function search(filterData: object) {
  if (filterData['name'] || filterData['age']){
    data.value=sourceData.filter((item:any)=>{
      if(filterData['age']){
        return item.age==filterData['age']
      }else{
        return true
      }
    }).filter((item:any)=>item.name.includes(filterData['name']))
  }else{
    data.value=sourceData
  }
}

function reset(){
  data.value=sourceData
}
</script>


HFormDrawer表单抽屉

image.png

<template>
  <div>
    <NButton type="primary" @click="show=true">打开抽屉</NButton>
    <HFormDrawer
      title="表单录入"
      :show="show"
      :width="600"
      :formLabelWidth="150"
      :rules="formItems"
      @dataChange="commit"
      @confirm="commit"
      @close="show=false"
    >
      <template #Slot>
       Slot TODO ...
      </template>
      <template #header>
       header slot
      </template>
      <template #footer-btn-prefix>
       prefix slot
      </template>
      <template #footer-btn-suffix>
        suffix slot
      </template>
      <!-- <template #footer>
        footer 整个底部自定义
      </template> -->
      <div>
       todo default slot custom
      </div>

    </HFormDrawer>
  </div>
	
</template>

<script lang="ts" setup>
  import { HFormRule, HForm, HFormApi,HDatePickerProps,HFormDrawer } from 'hyb-naive'
  import {reactive,ref} from 'vue'
  import {NButton} from 'naive-ui'
  import {
    SelectProps,
    TreeSelectProps,
    TreeProps,
    DatePickerProps,
    InputNumberProps,
    CascaderProps,
    TransferProps,
    SliderProps,
    RateProps,
    CheckboxProps,
    RadioProps,
  } from 'naive-ui'

  const show=ref(false)

	const formItems: HFormRule[] = reactive([
		{
			compType: 'Input',
			field: 'name',
			label: 'Input',
			required: true,
      defaultValue: '111',
			trigger: ['blur', 'input'],
		},
		{
			compType: 'Select',
			field: 'sex',
			label: 'select',
			required: true,
			trigger: ['blur', 'input'],
      defaultValue: '1',
			props: {
        clearable: true,
        options: [
          { label: '男', value: '1',},
          { label: '女', value: '0', },
        ],
      } as SelectProps,
		},
		{
			compType: 'TreeSelect',
			field: 'TreeSelect',
			label: 'TreeSelect',
			required: true,
			trigger: ['blur', 'input'],
			props: (): TreeSelectProps => {
				return {
					options: [
						{
							label: '部门',
							key: 'Rubber Soul',
							children: [
								{ label: '运维', key: 'a',},
								{ label: '开发',key: 'b',},
							],
						},
					],
				}
			},
		},
		{
			compType: 'Tree',
			field: 'tree',
			label: 'Tree',
			required: true,
			message: '请选择',
			trigger: ['change'],
			validator: (_item, _data, _callback, source) => {
				return Object.keys(source.tree ?? {}).length ? true : false
			},
			props: (formAPi:HFormApi): TreeProps => {
				return {
					checkable: true,
					onUpdateCheckedKeys(value) {
						formAPi.formData.tree = value
					},
					data: [
						{
							label: 'root',
							key: 'root',
							children: [
								{ label: 'a', key: 'a', },
								{ label: 'b', key: 'b', },
							],
						},
					],
				}
			},
		},
		{
			compType: 'Switch',
			field: 'NSwitch',
			label: '开关',
      required: true,
      defaultValue: false,
      validateType:'boolean'
		},
		{
			compType: 'DatePicker',
			field: 'DatePicker',
			label: 'DatePicker',
			required: true,
			trigger: ['change', 'blur'],
      validateType:'array',
			props: (): HDatePickerProps => {
				return {
					type: 'daterange',
					clearable: true,
          rangeMappingToFields:['startTime','endTime']
				}
			},
		},
		{
			compType: 'InputNumber',
			field: 'number',
			label: 'InputNumber',
			message: '数字必须大于1',
			required: true,
			defaultValue: 1,
			trigger: ['blur', 'change'],
			validator: (_item, _data, _callback, formData) => {
				return formData.number > 1
			},
			props: (): InputNumberProps => {
				return {
					clearable: true,
					min: 1,
				}
			},
		},
		{
			compType: 'Cascader',
			field: 'cascader',
			label: 'Cascader',
			message: '必须选择',
			required: true,
			props: {
        clearable: true,
        options: [
          {
            label: '1',
            value: '1',
            children: [
              { label: '1-1', value: '11', },
            ],
          },
          {
            label: '2',
            value:'2',
            children: [
              { label: '2-1', value: '21' },
            ],
          },
        ],
			},
		} as CascaderProps,
		{
			compType: 'Slider',
			field: 'slider',
			label: 'Slider',
      defaultValue: 50,
      required: true,
      validateType:'number',
			props: {
				step: 1,
			} as SliderProps,
		},
    {
			compType: 'Radio',
			field: 'Radio',
			label: 'Radio',
      required: true,
      validateType:'boolean',
			props: {
				label: 'Radio',
			} as CheckboxProps,
		},
		{
			compType: 'RadioGroup',
			field: 'RadioGroup',
			label: 'RadioGroup',
      defaultValue: '1',
      required: true,
			options: [
				{ label: '男', value: '1', },
				{ label: '女', value: '0', },
			],
		},
    {
			compType: 'RadioButtonGroup',
			field: 'RadioButtonGroup',
			label: 'RadioButtonGroup',
      // required: true,
			options: [
				{ label: '男', value: '1', },
				{ label: '女', value: '0', },
			],
		},
		{
			compType: 'TimePicker',
			field: 'TimePicker',
			label: 'TimePicker',
      required: true,
      validateType:'number'
		},
		{
			compType: 'AutoComplete',
			field: 'AutoComplete',
			label: 'AutoComplete',
      props(formApi:HFormApi) {
        return {
          onUpdateValue(value:any){
            console.log(value)
            const options=['@gmail.com', '@163.com', '@qq.com'].map((suffix) => {
              const prefix = value.split('@')[0]
              return {
                label: prefix + suffix,
                value: prefix + suffix
              }
            })
            formApi.setOptions('AutoComplete',options)
          }
        }
      }
		},
    {
			compType: 'Checkbox',
			field: 'Checkbox',
			label: 'Checkbox',
      required: true,
      validateType:'boolean',
			props: {
				label: 'Checkbox',
			} as CheckboxProps,
		},
		{
			compType: 'CheckboxGroup',
			field: 'checkboxGroup',
			label: 'CheckboxGroup',
      required: true,
			options: [
				{ label: '男', value: '1', },
				{ label: '女', value: '0', },
			],
		},
		{
			compType: 'Rate',
			field: 'rate',
			label: 'rate',
			defaultValue: 3,
      required: true,
      validateType:'number',
			props: {
				count: 5,
        size: 'large',
        color: 'red',
			} as RateProps,
		},
    {
			compType: 'Slot',
			field: 'Slot',
			label: 'Slot',
		},
		{
			compType: 'Transfer',
			field: 'Transfer',
			label: '穿梭框',
			message: '必须选择',
			required: true,
			span: 24,
      validateType:'array',
			trigger: ['blur', 'change'],
			props: {
			  options: [
          {
            label: '1',
            value: 1,
            disabled: true,
          },
          { label: '2', value: 2, },
        ],
			} as TransferProps,
		},
	])

	function commit(params: any) {
		console.log('params', params)
	}
</script>



还有一些高级的玩法,详情请看hyb-naive