之前用naive-ui做了一个管理后台的系统,但是使用过程中发现Form表单组件,用起来比较麻烦,代码也不够优雅,而且我特别讨厌在template里面写一堆内容,于是想着通过JSON配置的方式,代码更佳清爽优雅简便,于是自己搞了一个对Naive-UI二次封装的组件hyb-naive
快速使用
npm install hyb-naive
pnpm install hyb-naive
yarn add hyb-naive
HForm(表单组件)
表单组件,有两种使用模式一个是基础的表单录入(FORM)和页面筛选器(FILTER)
- 筛选器
<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>
- 表单录入
<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 表单弹框
<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表单抽屉
<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