config-form-generator
v1.2.0
Published
A configuration-based form generator for Vue 3 + Element Plus
Readme
ConfigForm Generator
ConfigForm Generator 是一个逻辑与 UI 分离的配置化表单生成方案。目前的默认实现基于 Vue 3 + Element Plus,但其核心引擎(状态管理、校验、联动逻辑)是完全独立的,可以轻松适配 Ant Design Vue、Naive UI 或其他 UI 库。
💡 核心理念:通过 JSON Schema 描述表单结构、验证规则和联动逻辑,实现“一次配置,多端渲染”或“后端驱动前端表单”。
✨ 特性 (Features)
- 🛡️ UI 无关核心 (UI Agnostic Core): 核心逻辑层不依赖任何特定 UI 库,仅通过适配器层渲染。
- 📝 配置驱动: 完全通过 JSON Schema 描述表单,支持无限嵌套布局。
- 🦾 类型安全: 完整的 TypeScript 类型定义,开发体验友好。
- 🔗 智能联动: 内置强大的表达式引擎 (
{{ model.xxx }}),支持字段间的复杂依赖(显示/隐藏、禁用/启用、值联动等)。 - 🔌 扩展性强: 所有的组件(输入框、布局容器)均通过注册表管理,可自由替换或扩展自定义组件。
- ⚡️ 高性能: 细粒度的依赖追踪,只有相关字段才会触发更新。
🧩 架构说明 (Architecture)
项目采用 Core + Adapter 的架构设计:
Core Layer (
src/core):- Registry: 组件注册中心,管理字符串 (
'input') 到具体 Vue 组件的映射。 - State Management: 维护表单数据模型 (Model) 和 UI 状态 (Shadow State)。
- Linkage Engine: 处理字段间的依赖关系和动态计算。
- 此层完全不包含 Element Plus 代码。
- Registry: 组件注册中心,管理字符串 (
Adapter Layer (
src/components/ConfigForm):- 目前的实现是 Element Plus Adapter。
ConfigForm.vue: 封装了<el-form>。ConfigFormItem.vue: 封装了<el-form-item>并处理通用属性映射。- 如果你想使用 Ant Design,只需重写这一层即可。
🚀 快速开始 (Quick Start)
1. 安装依赖
本项目需要 Vue 3 和 Element Plus(作为默认 UI 库)。
# 安装核心库
npm install config-form-generator
# 安装 UI 库 (如果尚未安装)
npm install element-plus2. 引入组件与样式
在你的入口文件(如 main.ts)或组件中引入。
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
// 引入 ConfigForm Generator 样式
import 'config-form-generator/dist/style.css';
const app = createApp(App);
app.use(ElementPlus);
app.mount('#app');3. 组件注册
ConfigForm 组件在挂载时会默认注册 Element Plus 的常用组件预设(通过 useElementPlusPresets)。
如果你需要注册自定义组件或覆盖默认组件,可以使用 registerComponent:
import { registerComponent } from 'config-form-generator';
import MyCustomInput from './MyCustomInput.vue';
// 注册自定义组件
registerComponent('my-input', MyCustomInput);4. 基础使用示例
<script setup lang="ts">
import { ref } from 'vue';
import { ConfigForm } from 'config-form-generator';
import type { FormConfig } from 'config-form-generator/dist/core/types';
const formData = ref({});
const formConfig: FormConfig = {
labelWidth: '100px',
schemas: [
{
category: 'field',
type: 'input',
field: 'username',
label: '用户名',
props: { placeholder: '请输入用户名' },
rules: [{ required: true, message: '必填项', trigger: 'blur' }]
},
{
category: 'field',
type: 'select',
field: 'role',
label: '角色',
props: {
options: [
{ label: '管理员', value: 'admin' },
{ label: '用户', value: 'user' }
]
}
}
]
};
const handleSubmit = (data: any) => {
console.log('表单提交:', data);
};
</script>
<template>
<ConfigForm
:config="formConfig"
v-model="formData"
@submit="handleSubmit"
/>
</template>📖 配置手册 (Configuration Guide)
1. FormConfig (表单根配置)
| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| schemas | FormSchema[] | [] | 核心。表单项的配置数组。 |
| labelWidth | string | number | '100px' | 全局标签宽度。 |
| labelPosition | 'left' | 'right' | 'top' | 'right' | 标签对齐方式。 |
| size | 'large' | 'default' | 'small' | 'default' | 表单组件尺寸。 |
| disabled | boolean | false | 全局禁用表单。 |
| hideRequiredAsterisk | boolean | false | 隐藏必填字段的星号。 |
| context | Record<string, any> | {} | 外部上下文数据,可用于联动表达式 {{ context.xxx }}。 |
| hiddenFields | string[] | [] | 强制隐藏的组件列表(黑名单)。⚠️ 注意:仅支持通过 Key 匹配(必须确保 Schema 中定义了 key),不支持 field。 |
| editableFields | string[] | [] | 查看模式 (mode='view') 下的编辑白名单。⚠️ 注意:仅支持通过 Key 匹配,不支持 field。 |
2. FormSchema (表单项配置)
表单项主要分为两类:字段 (Field) 和 容器 (Container)。
A. FieldSchema (字段配置)
用于产生值的输入控件。
| 属性 | 类型 | 说明 |
| :--- | :--- | :--- |
| category | 'field' | 必填。标识为字段。 |
| type | string | 必填。组件类型(需先通过 registerComponent 注册),如 'input', 'select'。 |
| field | string | 必填。绑定的数据字段路径,支持嵌套如 'user.name'。 |
| label | string | 标签文本。 |
| defaultValue | any | 默认值。 |
| props | Record<string, any> | 透传给组件的 props,如 placeholder, clearable 等。支持表达式。 |
| rules | RuleItem[] | 校验规则(同 Element Plus / Async-Validator)。 |
| visible | boolean | string | 是否显示。支持表达式 {{ model.age > 18 }}。 |
| disabled | boolean | string | 是否禁用。支持表达式。 |
| required | boolean | string | 是否必填。支持表达式。 |
| col | ColLayout | 栅格布局配置,如 { span: 12 }。 |
| dependencies | DependencyConfig[] | 高级联动配置。 |
B. ContainerSchema (容器配置)
用于布局,不产生数据值。
| 属性 | 类型 | 说明 |
| :--- | :--- | :--- |
| category | 'container' | 必填。标识为容器。 |
| type | string | 必填。容器组件类型,如 'card', 'grid', 'tabs'。 |
| children | FormSchema[] | 子节点 Schema 数组。 |
| props | Record<string, any> | 透传给容器组件的 props。 |
| visible | boolean | string | 是否显示容器。 |
| scope | string | 数据作用域。若设置,子字段将基于此路径绑定。例如 scope 为 'user',子字段 'name' 实际绑定 'user.name'。 |
🔗 智能联动系统 (Smart Linkage)
这是本库最核心的功能,支持两种方式定义联动:
1. 表达式联动 (Expression)
最简单直观的方式,直接在属性中使用 {{ }} 语法。
- 支持的属性:
visible,disabled,required,label,props内的任意属性。 - 可用变量:
model: 当前表单的完整数据对象。context: 外部传入的上下文。
示例:
{
"type": "input",
"field": "reason",
"label": "拒绝原因",
"visible": "{{ model.status === 'reject' }}",
"props": {
"placeholder": "{{ model.type === 'personal' ? '请输入个人原因' : '请输入公事原因' }}"
}
}2. 依赖配置 (Dependencies)
当表达式无法满足需求(如需要修改值、修改选项、触发异步请求)时,使用 dependencies。
interface DependencyConfig {
source: string | string[]; // 监听的字段,如 'role'
trigger?: 'change' | 'blur' | 'init'; // 触发时机
type: 'value' | 'options' | 'visible' | 'disabled' | ...; // 联动类型
target?: string; // 目标字段(默认为当前字段)
expression?: string; // 逻辑表达式
}示例:角色决定权限选项
{
"type": "select",
"field": "permissions",
"dependencies": [
{
"source": "role",
"type": "options",
"expression": "model.role === 'admin' ? [{label:'全部',value:'all'}] : [{label:'部分',value:'part'}]"
}
]
}🧩 自定义组件开发
你可以轻松编写自定义组件并集成到表单中。
1. 编写组件 (MyCustomInput.vue)
组件只需满足:
- 接收
modelValueprop。 - 触发
update:modelValueevent。
<script setup lang="ts">
defineProps(['modelValue', 'placeholder']);
defineEmits(['update:modelValue']);
</script>
<template>
<div class="my-custom-input">
<input
:value="modelValue"
@input="$emit('update:modelValue', ($event.target as any).value)"
:placeholder="placeholder"
/>
</div>
</template>2. 注册组件
import { registerComponent } from 'config-form-generator';
import MyCustomInput from './MyCustomInput.vue';
registerComponent('my-input', MyCustomInput);3. 在 Schema 中使用
{
"category": "field",
"type": "my-input",
"field": "customField",
"label": "自定义组件",
"props": {
"placeholder": "这是透传的属性"
}
}❓ 常见问题
Q: 为什么默认不内置 Input 等组件? A: 为了保持核心包体积最小,且不强制依赖特定版本的 UI 库。你可以按需注册,甚至混用 Element Plus 和 Ant Design 的组件。
Q: 支持 TypeScript 吗?
A: 完全支持。请使用 FormConfig, FormSchema 等类型定义来获得完整的代码提示。
Q: 如何进行表单校验?
A: 通过 ref 获取组件实例,调用 validate 方法:
const formRef = ref();
// ...
await formRef.value.validate();License
MIT
