@zy-frontend/form-core
v2.0.11
Published
- 👍简洁的配置规则 - 📦与UI库解耦,兼容多端,可自定义样式 - ⚙️支持主动/被动联动,可实现复杂的字段/表单间的联动关系 - ⚔️支持单表单、多表单两种模式 - 🛡️支持分级字段校验 - ☁️支持远程加载选项列表
Readme
配置化表单
特点
- 👍简洁的配置规则
- 📦与UI库解耦,兼容多端,可自定义样式
- ⚙️支持主动/被动联动,可实现复杂的字段/表单间的联动关系
- ⚔️支持单表单、多表单两种模式
- 🛡️支持分级字段校验
- ☁️支持远程加载选项列表
使用
安装
npm i -S @zy-frontend/form-core使用示例
单表单场景:
<script setup lang="ts">
import { ZYForm } from '@zy-frontend/form-core'
import formConfig from './form-config.json'
import { ElForm, ElFormItem, ElInput } from 'element-plus'
// 表单数据
const formData = ref({})
// 表单字段组件
const components = {
form: ElForm,
formItem: ElFormItem,
input: ElInput,
}
// 请求方法
const requestFn: RequestFunction = async (
keyPath,
value,
url,
extraParams,
componentParams = {},
) => {
const res: Option[] = await getSelectOptions(url, extraParams, componentParams.query)
return res
}
</script>
<template>
<ZYForm
ref="formRef"
v-model="formData"
:config="formConfig"
:components="components"
:requestFn="requestFn"
/>
</template>多表单场景:
<script setup lang="ts">
import { ZYFormGroup } from '@zy-frontend/form-core'
import formConfig from './form-config.json'
import { ElForm, ElFormItem, ElInput } from 'element-plus'
// 表单数据
const formData = ref({})
// 表单字段组件
const components = {
form: ElForm,
formItem: ElFormItem,
input: ElInput,
}
// 请求方法
const requestFn: RequestFunction = async (
keyPath,
value,
url,
extraParams,
componentParams = {},
) => {
const res: Option[] = await getSelectOptions(url, extraParams, componentParams.query)
return res
}
</script>
<template>
<ZYFormGroup
ref="formRef"
v-model="formData"
:config="formConfig"
:components="components"
:requestFn="requestFn"
/>
</template>API
Form Attributes
| 属性名 | 说明 | 类型 | 是否必填 | 默认值 | | ----------------- | --------------------------------------------------------------------------- | --------------- | -------- | ------ | | v-model | 表单数据 | object | 是 | | | config | 表单配置 | Form[] | 是 | | | formKey | 表单唯一key | string | 是 | | | components | 表单字段组件集合 | UIComponents | 是 | | | requestFn | 请求方法,使用场景包括远程选项获取、文件上传等 | RequestFunction | 否 | | | scenario | 场景:用带指定前缀的属性覆盖原属性,如scenario为pc时,会用pc-props覆盖props | string | 否 | | | requiredFieldOnly | 是否只显示必填字段 | boolean | 否 | false | | disabled | 禁用 | boolean | 否 | false | | readonly | 只读 | boolean | 否 | false |
Form Methods
| 方法名 | 说明 | 类型 | | -------------- | ---------------------- | ---------------------------------------------- | | initFormData | 更新表单数据 | (newData?: Record<string, any>) => void | | resetFormValue | 重置表单数据 | () => void | | validate | 表单校验,支持分级校验 | (maxLevel: number = Infinity, fields: string[] = []) => Promise | | clearValidate | 清空表单校验 | () => void | | getFormData | 获取表单数据 | () => Record<string, any> |
Form Events
| 事件名 | 说明 | 回调参数 | | ---------- | -------------------------- | ---------------------------------------------------- | | change | 当表单字段值发生改变时触发 | (keyPath: string, fieldValue: any) | | blur | 当表单字段失去焦点时触发 | (e: FocusEvent, keyPath: string, fieldValue: any) | | keyupEnter | 当enter键按下时触发 | (e: KeyboardEvent, keyPath: string, fieldValue: any) | | mounted | 表单加载后触发 | |
FormGroup Attributes
| 属性名 | 说明 | 类型 | 是否必填 | 默认值 | | ----------------- | --------------------------------------------------------------------------- | --------------- | -------- | ------ | | v-model | 表单数据 | object | 是 | | | config | 表单配置 | Form[] | 是 | | | components | 表单字段组件集合 | UIComponents | 是 | | | requestFn | 请求方法,使用场景包括远程选项获取、文件上传等 | RequestFunction | 否 | | | scenario | 场景:用带指定前缀的属性覆盖原属性,如scenario为pc时,会用pc-props覆盖props | string | 否 | | | requiredFieldOnly | 是否只显示必填字段 | boolean | 否 | false | | disabled | 禁用 | boolean | 否 | false | | readonly | 只读 | boolean | 否 | false | | defaultTabKey | 默认激活的tab的key | string | 否 | |
FormGroup Methods
| 方法名 | 说明 | 类型 | | -------------- | ---------------------- | ---------------------------------------------- | | initFormData | 更新表单数据 | (newData?: Record<string, any>) => void | | resetFormValue | 重置表单数据 | (formKey?: string | string[]) => void | | validate | 表单校验,支持分级校验 | (formKey?: string | string[], maxLevel?: number) => Promise | | clearValidate | 清空表单校验 | (formKey?: string | string[]) => void | | getFormData | 获取表单数据 | () => Record<string, any> |
FormGroup Events
| 事件名 | 说明 | 回调参数 | | ---------- | -------------------------- | ---------------------------------------------------- | | change | 当表单字段值发生改变时触发 | (keyPath: string, fieldValue: any) | | tabChange | 切换tab时触发 | (...args: any) 传递Tab组件emit的所有参数 | | blur | 当表单字段失去焦点时触发 | (e: FocusEvent, keyPath: string, fieldValue: any) | | keyupEnter | 当enter键按下时触发 | (e: KeyboardEvent, keyPath: string, fieldValue: any) | | mounted | 表单加载后触发 | |
表单配置
类型声明
export interface Form {
key: string // 表单唯一键
title?: string // 表单标题,多表单模式下必填
props?: FormProps // 表单组件props
show?: boolean // 显示/隐藏
gutter?: FormGutter // 表单项间距
fields: Field[] // 表单项数组
preScript?: string | PreScriptFunction // 表单数据前置处理脚本
postScript?: string | PostScriptFunction // 表单数据后置处理脚本
[key: string]: any
}
export type PreScriptFunction = (
formData: Ref<Record<string, any>>, // 当前表单数据
formConfig: UnwrapNestedRefs<Form>, // 当前表单配置
) => void
export type PostScriptFunction = (
formData: Ref<Record<string, any>>, // 当前表单数据
formConfig: UnwrapNestedRefs<Form>, // 当前表单配置
) => Record<string, any>
export type FormGutter = {
horizontal?: number | string // 表单项横向间距(默认单位px)
vertical?: number | string // 表单项纵向间距(默认单位px)
[key: string]: any
}
export interface FormProps {
labelPosition?: FormLabelPosition // 表单项标签位置
labelWidth?: string | number // 表单项标签宽度
hideRequiredAsterisk?: boolean // 是否隐藏必填星号
disabled?: boolean // 表单禁用
[key: string]: any
}
export type FormLabelPosition = 'left' | 'right' | 'top'
export interface Field {
key: string // 字段键,在当前表单中需唯一
formKey?: string // 所属表单键,自动赋值,无需填写
span?: number // 表单项占据的栅格列数,最小1,最大24(默认24)
label?: Label | string // 标签
description?: string // 字段描述
props?: Record<string, any> // 表单项组件props
type: FieldType // 字段类型
defaultValue?: any // 默认值
show?: boolean // 显示/隐藏
disabled?: boolean | IsFieldDisabled | string // 禁用/启用,支持传入动态计算方法
rules?: Rule[] // 校验逻辑集合
component?: Component // 组件
actions?: Array<Action | ActionHandler | string> // 主动联动配置
subscribers?: Subscriber[] // 被动联动配置
datetimeFormatPattern?: string // 日期格式,仅当表单为只读且字段type为datetime时生效
className?: string // 自定义类名
advancedConfig?: AdvancedConfig // 高级配置项
[key: string]: any
}
export interface Label {
text?: string // 标签文本
width?: string // 标签宽度,会覆盖表单的labelWidth
[key: string]: any
}
// 校验逻辑,继承自async-validator的RuleItem类型
export interface Rule extends Omit<RuleItem, 'validator'> {
level?: number // 规则级别,数值越小优先级越高,默认0
validator?: string | ValidatorFunction | Validator // 表单验证函数/对象
trigger?: string | string[] // 触发方式
}
export type ValidatorFunction = (
rule: any,
value: any,
callback: any,
formData: UnwrapNestedRefs<Record<string, any>>, // 当前表单数据
formConfig: UnwrapNestedRefs<Form>, // 当前表单配置
validatorParams: { rule: any; value: any; callback: any },
formGroupData: UnwrapNestedRefs<Record<string, any>>,
depValues?: any[], // 依赖项值的集合,作为Validator的校验函数时有值
) => void
export interface Validator {
name?: string // 名称
deps: string[] // 依赖项,格式为formKey.fieldKey
handler: string | ValidatorFunction // 校验函数
}
export interface Component {
type: ComponentType // 组件类型
props?: Record<string, any> // 组件props
options?: Option[] // 组件选项集合
remote?: ComponentRemote | boolean // 远程请求配置
[key: string]: any
}
export interface ComponentRemote {
url?: string // 远程请求URL,执行requestFn时会作为参数传入
extraParams?: Record<string, string | number | boolean> // 远程请求参数,执行requestFn时会作为参数传入
[key: string]: any
}
export type Option = {
value: string | number | boolean | Record<string, any>
label: string
disabled?: boolean
[key: string]: any
}
export type LabelPosition = 'top' | 'right' | 'left'
export type FieldType =
| 'boolean'
| 'string'
| 'number'
| 'array'
| 'object'
| 'datetime'
| 'images'
| 'null'
| 'container'
| 'invisible'
export type IsFieldDisabled = (fieldValue: any, formData: any, fieldConfig: Field) => boolean
// 组件类型
export enum ComponentTypeEnum {
Input = 'input',
InputNumber = 'inputNumber',
Select = 'select',
MultiSelect = 'multiSelect',
Cascader = 'cascader',
Radio = 'radio',
Checkbox = 'checkbox',
Switch = 'switch',
DatePicker = 'datePicker',
DateTimePicker = 'dateTimePicker',
Upload = 'upload',
DynamicTable = 'dynamicTable',
DynamicForm = 'dynamicForm',
DynamicTabs = 'dynamicTabs',
Title = 'title',
Collapse = 'collapse',
ImageList = 'imageList',
}
export type ComponentType = ComponentTypeEnum | string
// 被动联动配置
export interface Subscriber {
name?: string // 名称
immediate?: boolean // 是否在数据初始化后立即触发
deps: string[] // 依赖项,格式为formKey.fieldKey
handler: SubscriberHandler | string // 联动函数
}
export type SubscriberHandler = (
depValues: any[], // 依赖项值的集合
formKey: string, // 当前表单key
formData: Record<string, any>, // 当前所有表单数据
fieldConfig: Field, // 当前字段配置
) => void
export type Action = {
name?: string // 名称
immediate?: boolean // 是否在数据初始化后立即触发
handler: string | ActionHandler // 联动函数
}
export type ActionHandler = (
fieldValue: any, // 当前字段的值
formKey: string, // 当前表单key
formData: Record<string, any>, // 当前所有表单数据
formConfig: Record<string, FormWithFieldMap>, // 所有表单配置的Map
oldFieldValue: any, // 当前字段的旧值
) => void
export type FormWithFieldMap = { fieldMap: Record<string, Field> } & Form
// 高级配置项
export type AdvancedConfig = {
skipAutoDataReset?: boolean // 是否跳过自动数据重置,默认false
}
// UI组件
export interface UIComponents extends Record<ComponentType, any> {
form: any // 表单组件
formItem: any // 表单项组件
tabs?: any // 标签页组件,用于多表单模式
collapse?: any // 折叠面板根组件
collapseItem?: any // 折叠面板子组件
[key: string]: any
}
export type RemoteResponse = Option[] | Promise<Option[]> | any
export interface RequestContext {
requestId: number // 当前组件内递增的请求标识,用于识别过期响应
signal?: AbortSignal // 新请求替换旧请求或组件卸载时触发
}
// 远程请求方法
export type RequestFunction = (
keyPath: string, // 字段键路径,格式为formKey.fieldKey
value: any, // 字段值
url?: string, // 字段remote里配置的远程请求URL
extraParams?: Record<string, string | number | boolean>, // 字段remote里配置的远程请求参数
componentParams?: Record<string, any>, // 由UI组件传入的参数,如Select组件的query、Upload组件的file等
context?: RequestContext, // 请求上下文,用于取消请求及识别过期响应
) => RemoteResponse
export type RemoteMethod = (
componentParams?: Record<string, any>,
context?: RequestContext,
) => Option[] | Promise<Option[]> // 远程选项获取方法
export type UploadResponse = any | Promise<any>
export type UploadMethod = (
componentParams?: Record<string, any>,
context?: RequestContext,
) => UploadResponse // 上传文件方法
export type FileRemoveMethod = (
componentParams?: Record<string, any>,
context?: RequestContext,
) => Promise<void> | void // 文件删除方法远程查询和上传应将 context.signal 传给 fetch、Axios 等请求库。组件会在新查询替换旧查询、文件删除或组件卸载时触发取消;不支持取消的实现也必须避免使用过期响应回写状态。
Axios 示例:
const requestFn: RequestFunction = async (
keyPath,
value,
url,
extraParams,
componentParams,
context,
) => {
const { data } = await axios.get(url || '', {
params: {
...extraParams,
...componentParams,
},
signal: context?.signal,
})
return data
}自定义组件
提供一个对象,通过键名匹配自定义组件
{
// ...
components: {
// 自定义组件(示例)
// ref: https://elemefe.github.io/element-react/#/zh-CN/component/form
'ZYInput': Input,
}
}