@web_qiang/aform
@web_qiang/aform
基于 Vue3 + Element Plus 的声明式表单 / 表格组件库(aform)。用一份 config 即可渲染表单、表格、搜索区,内置字典联动、异步请求、表单校验与自定义组件自动注册,开箱即用且完整支持 TypeScript。
- 包名:
@web_qiang/aform - 仓库内演示项目:
demo/(独立可运行)
目录
- 特性
- 安装
- 快速使用
- 暗黑模式(默认跟随系统)
- 语言(locale)
- 核心概念
- 配置项完整字段(RawConfig)
- 函数型配置(响应式)
- 组件 API
- 每种控件 type 用法
- 字典
- 选项存模型:options.toString
- 布局与响应式
- 校验
- 事件
- 自定义组件
- 命令式 API
- Composables
- 工具函数 AFormUtils
- 导出总表
- 开发 & 构建 & 发布
- License
特性
- 配置驱动:一份
config描述表单项,自动渲染对应控件,无需手写大量模板。 - type 驱动:内置常用 Element Plus 控件,自定义组件放入
src/aform/components/custom/*.tsx即自动注册为新type(文件名即类型名)。 - 状态标签
r/w/a:同一字段在只读态展示、写态编辑之间自动切换;可用type?w强制指定。 - 字典联动:内置
dict体系,支持本地字典与远程字典。 - 异步一体化:搜索区、表格请求、表单校验共享同一套配置。
- 表格复用配置:表格列与表单表单项共用同一配置结构。
- 完整类型:随包发布
.d.ts(含RawConfig、NormalizedConfig等)。
安装
pnpm add @web_qiang/aform element-plus @element-plus/icons-vue
vue、element-plus、@element-plus/icons-vue为 peerDependencies,需由你的项目自行安装。 库在构建时已通过registerElementPlus()把用到的 Element Plus 组件登记进私有注册表(按需引入,可 tree-shaking)。
快速使用
全局注册(推荐)
import { createApp } from "vue";
import ElementPlus from "element-plus";
import "@web_qiang/aform/dist/style.css";
import AForm from "@web_qiang/aform";
const app = createApp(App);
app.use(ElementPlus);
app.use(AForm); // 注册 AForm / AFormItem / AContent / ATable / ATableColumn / ASearch
全局注册后,模板里可直接用 <AForm> <ATable> 等标签。
局部引入
import { AForm, AFormItem } from "@web_qiang/aform";
import "@web_qiang/aform/dist/style.css";
<template>
<AForm :config="config" v-model="model" />
</template>
样式必须单独引入:
import '@web_qiang/aform/dist/style.css'。库入口不会自动注入样式(否则.d.ts会带一个不存在的 scss 路径),故需手动在应用入口引入一次。 该文件已包含 Element Plus 全量样式(构建时合并),无需再额外引入element-plus/dist/index.css。
暗黑模式(默认跟随系统)
库已内置 Element Plus 暗黑主题变量,并默认自动跟随系统深色 / 浅色偏好(prefers-color-scheme):系统切换时页面实时响应,零配置。
如需手动固定色调(或彻底关闭暗黑),调用 setThemeMode:
import { setThemeMode } from "@web_qiang/aform";
setThemeMode("auto"); // 跟随系统(默认,库导入时已自动生效)
setThemeMode("light"); // 始终亮色(停止监听系统变化)
setThemeMode("dark"); // 始终暗黑(停止监听系统变化)
辅助 API:getThemeMode() 读取当前模式;isDark() 判断当前是否暗黑。
<template>
<ElRadioGroup v-model="mode" @change="setThemeMode">
<ElRadioButton value="auto">跟随系统</ElRadioButton>
<ElRadioButton value="light">亮色</ElRadioButton>
<ElRadioButton value="dark">暗黑</ElRadioButton>
</ElRadioGroup>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { setThemeMode, type ThemeMode } from "@web_qiang/aform";
const mode = ref<ThemeMode>("auto");
</script>
说明:
- 暗黑变量随
@web_qiang/aform/dist/style.css一起打包,无需额外引入element-plus/theme-chalk/dark/css-vars.css。 - 变量全部限定在
html.dark作用域内(约 3KB),对亮色模式零副作用,不使用暗黑也照常。 - 库自身样式不写死颜色,统一走
--el-*CSS 变量(如var(--el-color-danger)、var(--el-text-color-placeholder)),因此会跟随主题自动切换。 - 自动模式靠 JS 检测系统偏好,首帧可能有一瞬闪白;介意的话在
index.html加一段内联脚本提前设好类名:
<script>
if (matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.classList.add("dark");
}
</script>
语言(locale)
库内 Element Plus 组件默认使用中文(zh-cn),已通过 ElConfigProvider 自动注入到 AForm / ATable 根节点,日期面板、分页、空状态等文案开箱即用,无需配置。
如需切换语言,调用 setLocale 全局覆盖(必须在组件挂载前调用):
import { setLocale } from "@web_qiang/aform";
import en from "element-plus/es/locale/lang/en";
setLocale(en);
核心概念
配置驱动(config → 控件)
每个表单项是一个配置对象 RawConfig,至少包含 field(绑定模型字段)与 type(控件类型)。库据此渲染对应的 Element Plus 控件或自定义控件,并自动双向绑定到 v-model。
状态(state)与状态标签(tag)
- 表单级 state:
AForm的state决定整张表单的交互态,下发给每个表单项。'write'(默认):可编辑。'readonly':只读展示(写组件自动回退为common展示组件)。'disabled':禁用(等同于写态但整体置灰)。
- 状态标签 tag:每个组件声明自己「在哪些 state 下生效」:
w/W/write→ 仅在写态 / 禁用态生效(编辑控件)。r/R/readonly→ 仅在只读态生效(展示控件)。a/A→ 任何状态都生效(读写双态组件,如price、dict、upload)。
同一字段在写态用编辑控件、只读态自动显示文案,无需手写两套模板。
归一化
配置对象里的任意字段都可写成 函数(见下节),运行时会结合当前模型值(row)求值并归一化为 NormalizedConfig(formItem + content[])。只有真正读到的响应式数据变化时才重算对应字段,性能友好。
配置项完整字段(RawConfig)
顶层配置数组 configs: RawConfig[] 的每一项即为一个表单项。所有字段均可写成函数 () => 值(函数收到 { row, config, ...scope }),详见函数型配置。
| 字段 | 类型 | 说明 |
|---|---|---|
field / props |
string | string[] | (ctx)=>... |
绑定 v-model 的路径。支持 'a'、['a','b']、'a,b'(多字段用 options.toString 合并)。 |
label |
string | { text } | { html } |
标签文本。text 为字符串;html 可为字符串、VNode 或返回 VNode 的函数(自定义标签渲染)。 |
type |
string | Component | (ctx)=>... |
控件类型,见每种控件 type 用法。可写 type?w 强制用写组件。 |
dict |
string | object | (ctx)=>... |
字典名(字符串 → 调 getDict)或字典配置对象 { list, label, value, children, disabled }。下拉 / 单选 / 多选 / 级联 / 字典展示类控件从此读选项。 |
state |
'write' | 'readonly' | 'disabled' |
单字段覆盖表单级 state(优先级高于 AForm.state)。 |
show |
boolean | (ctx)=>boolean |
是否渲染该项。false / 返回 false 则整项隐藏(含校验一并跳过)。 |
span |
number | string |
在 CSS Grid 中跨几列(1 ~ column)。省略则占 1 格。 |
layout |
object |
布局控制:{ flex } 控件撑满剩余空间(flex:1 占满整行;flex:true 占剩余);{ right } 右对齐;{ wrap } 换行;{ gap } 子项间距。 |
gap |
string |
行/列间距(主要经 AForm.gap 控制,单字段也可用)。 |
content |
RawContentConfig[] |
在一个表单项里横向放多个控件(共享同一 label)。 |
rules |
RuleObject[] | (ctx)=>... |
校验规则(async-validator 语法)。 |
on |
Record<string, Function> |
事件:watch(val,old)=>... 值变化监听;其它键如 change/visible-change 等会被规范成 onXxx 透传到底层组件,回调收到 eventArgs(见事件)。 |
attrs |
object |
透传给底层组件的属性(Element Plus 原生 props + 自定义控件专属 props)。 |
format |
any | (args)=>any |
只读态格式化:字符串 / VNode 直接渲染;函数 ({ val, row, config })=>... 返回字符串或 VNode。 |
class |
object | string |
挂在控件外层的类名。 |
slots |
Record<string, any> |
插槽声明。'slot-xxx' 取宿主插槽;函数原样用;其它值包一层 <span>。支持 label / header / default 等键。 |
options |
{ toString } |
多选类控件把数组拼成字符串存回模型的分隔符(见options.toString)。 |
placeholder / clearable / disabled |
— | 自定义写组件通用属性(来自 commonProps),也可用 attrs 透传。 |
单字段配置(顶层直接写
type/field,不带content数组)会自动归一化为content[0],与带content数组的写法行为一致。
函数型配置(响应式)
任意字段写成函数时,运行时会在求值阶段调用它,并传入回调参数:
type HandlerCallbackArgs = {
row: any; // 当前模型(表格中即当前行)
config: () => NormalizedConfig; // 当前归一化快照(非响应式)
};
// 依赖 row.type 的响应式显隐
{ field: 'extra', label: '补充说明', type: 'input', show: ({ row }) => row.type === '2' }
// 依赖当前值的占位提示
{ field: 'name', label: '姓名', type: 'input',
attrs: ({ row }) => ({ placeholder: row.id ? '修改姓名' : '请输入姓名' }) }
当函数内部读到的响应式数据变化,该字段会精确重算(只重算这一个 handler,不会整棵树重算)。
组件 API
AForm 表单
<AForm
v-model="model"
:configs="configs"
:column="3"
state="write"
@validate="onValidate"
/>
Props
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
modelValue |
object | array |
{} |
表单模型(v-model)。 |
configs |
RawConfig[] |
[] |
表单项配置数组。 |
column |
number | string |
1 |
一行几列(CSS Grid,列数)。 |
state |
'write' | 'readonly' | 'disabled' |
'write' |
表单级状态,下发给每个表单项。 |
gap |
string |
'0px 16px' |
Grid 列间距 / 行间距。 |
labelWidth |
string | number |
'auto' |
标签宽度,透传 ElForm。 |
labelPosition |
'left' | 'right' | 'top' |
'right' |
标签位置。 |
size |
'large' | 'default' | 'small' |
'default' |
控件尺寸。 |
group |
boolean |
false |
作为分组根:开启后 validate() 会连带校验内部所有嵌套 AForm。 |
scope |
object |
{} |
额外作用域,合并进配置函数回调参数。 |
overrideConfig |
{ formItem?, content? } |
{} |
全局覆盖配置,合并进每个表单项(表单级 state 也经此下发)。 |
方法(通过 ref 调用)
| 方法 | 签名 | 说明 |
|---|---|---|
validate |
(cb?) => Promise<{valid, fields}> |
校验整表(含 group 内子表单)。成功 valid:true,失败含 fields。 |
validateField |
(fields) => Promise |
校验指定字段(透传 ElForm.validateField)。 |
resetFields |
(fields?) => void |
重置字段(含子表单)。 |
clearValidate |
(fields?) => void |
清除校验状态(含子表单)。 |
scrollToField |
(field) => void |
滚动到指定字段。 |
patch |
(index, path, value, mode?) => void |
命令式给第 index 个配置项打补丁(见命令式 API)。 |
事件:update:modelValue、change、validate。
<script setup>
import { ref } from "vue";
const formRef = ref();
async function submit() {
const { valid, fields } = await formRef.value.validate();
if (!valid) return;
// ...
}
</script>
AFormItem 表单项
通常不用手动写,由 AForm / ATableColumn 内部渲染。可独立挂在原生 el-form 下。
Props:config(RawConfig)、model(v-model 目标)、scope、from('formItem' | 'tableColumn')、bare(不渲染 ElFormItem 外壳,仅内容)、prop(自定义校验定位字段)。
暴露:config(归一化结果)、patch(path, value, mode)、validate(cb)、itemRef、contentRefs。
ATable 表格
<ATable
ref="tableRef"
v-model="rows"
:configs="columns"
:request="loadRows"
:query="query"
/>
Props
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
modelValue |
any[] |
— | 受控数据(不传 request 时使用)。v-model 返回当前页数据。 |
configs |
RawConfig[] |
[] |
列配置数组。 |
request |
(params)=>Promise |
— | 数据请求函数,传了即接管数据与分页(自带竞态处理)。见 TableRequest。 |
query |
object |
— | 额外查询条件,变化时自动回到第一页重载。 |
pager |
boolean | object |
true |
是否显示分页;传对象则合并进 ElPagination 的 props。 |
pageSize |
number |
10 |
每页条数。 |
state |
'write' | 'readonly' | 'disabled' |
'readonly' |
表格级状态(默认只读展示;'write' 开可编辑单元格)。 |
fit |
boolean |
false |
列宽自适应(基于 canvas 文本量算)。 |
group |
boolean |
true |
作为校验分组根(可编辑表格用)。 |
height |
string | number |
— | 表格高度(固定表头)。 |
border |
boolean |
true |
是否显示边框。 |
stripe |
boolean |
false |
斑马纹。 |
rowKey |
string | function |
'id' |
行唯一键。 |
overrideConfig |
{ formItem?, content? } |
{} |
列级全局覆盖配置(如统一 state)。 |
方法(ref 调用):reload()、refresh()、setPage(pageNum, size?)、validate()、clearValidate()、getSelection()、clearSelection()、toggleRowSelection(row, selected?)、doLayout();属性 page、total、loading、rows。
事件:update:modelValue、selection-change、row-click、loaded({ rows, total })。插槽:toolbar(收到 { reload, refresh })、empty、append、columns(自定义列)、actions(搜索区附加按钮)。
TableRequest 签名:
type TableRequest = (params: {
pageNum: number;
pageSize: number;
}) => Promise<
{ rows?: any[]; list?: any[]; data?: any[]; total?: number } | any[]
>;
返回结构兼容
rows/list/data三种键;数组则视为整页数据。query自动并入params。
ATableColumn 表格列
<ATable :configs="columns" v-model="rows" :request="loadRows">
<ATableColumn :config="{ field: 'name', label: '姓名' }" />
<ATableColumn :config="{ field: 'age', label: '年龄', type: 'input-number' }" />
<ATableColumn :config="{ type: 'selection' }" />
</ATable>
config:列配置(与表单项同构)。省略type时,单元格按field展示值(回退common展示组件)。data:表格数据(用于表头归一化占位,一般不用手填)。- 列上的
type: 'selection' | 'index' | 'expand'走el-table-column原生渲染;其余由AFormItem(bare 模式)按行归一化,支持state: ({ row }) => ...行级状态。
列级 attrs:width、minWidth、align、fixed、sortable、filters、resizable 等直接透传 ElTableColumn(列渲染时会自动剔除 prop/rules/placeholder/clearable/filterable/showWordLimit/loading 等仅控件用的键)。
ASearch 搜索区
<ASearch
v-model="query"
:configs="searchConfig"
@search="onSearch"
@reset="onReset"
/>
基于 AForm,顶部自带「查询 / 重置 / 展开收起」按钮,默认折叠。
Props
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
modelValue |
object |
{} |
查询模型(v-model)。 |
configs |
RawConfig[] |
[] |
搜索项配置。 |
column |
number | string |
4 |
一行几个。 |
collapseCount |
number |
column-1 |
折叠时显示的条数(默认留一格给按钮)。 |
collapsible |
boolean |
true |
是否可折叠。 |
defaultCollapsed |
boolean |
true |
初始是否折叠。 |
labelWidth |
string | number |
'auto' |
标签宽度。 |
loading |
boolean |
false |
查询按钮 loading。 |
方法(ref):search()、reset()、collapsed。事件:search、reset、update:modelValue。
重置回到初始快照(首次传入
modelValue的深拷贝),而非清空成undefined,保留默认值。
每种控件 type 用法
写态用对应编辑控件;只读态自动回退为
common展示组件(把值直接展示出来,带字典翻译 / 溢出 tooltip)。
写态控件(编辑用)
| type | 底层 | 说明 | 关键 attrs / 专属 props |
|---|---|---|---|
input |
ElInput |
文本输入 | type:'text'|'textarea'|'password'、maxlength、show-word-limit、clearable、prefix/suffix 插槽 |
input-number |
ElInputNumber |
数字输入 | min、max、step、precision、controls |
select |
内置 ASelect |
下拉选择 | dict 或 attrs.options;multiple、filterable(默认 true)、clearable、multiple-limit、collapse-tags;字典项带 children 自动分组成 ElOptionGroup |
cascader |
内置 cascader |
级联选择 | dict(含 children 的树);emitPath(默认 false,只存末级值)、checkStrictly、multiple、cascaderProps、clearable |
radio-group |
内置 radioGroup |
单选组 | dict;button:true 按钮样式 |
checkbox-group |
内置 checkboxGroup |
多选组 | dict;button:true;配 options:{ toString:',' } 把数组存成 'a,b' |
switch |
ElSwitch |
开关 | active-value、inactive-value、active-text、inactive-text |
slider |
ElSlider |
滑块 | range、show-input、step、show-stops |
rate |
ElRate |
评分 | max、allow-half、show-score、colors |
color-picker |
ElColorPicker |
取色器 | show-alpha、predefine |
date |
内置 date |
日期/时间 | type:'date'|'datetime'|'month'|'year'|'daterange'|'datetimerange'|'monthrange'(默认 date);valueFormat 自动推导;format;placeholder 自动 |
time-picker |
ElTimePicker |
时间选择 | format、value-format、clearable |
time-select |
ElTimeSelect |
时间下拉 | start、end、step、clearable |
autocomplete |
ElAutocomplete |
自动补全 | attrs.fetchSuggestions(返回建议列表的回调)、trigger-on-focus |
upload |
内置 upload |
上传 | 写态 ElUpload,只读态文件链接列表;action、resolve(res,file)=>({name,url})、limit、accept、tipText;模型存 [{name,url}] |
展示 / 读写双态控件
| type | 底层 | tag | 说明 | 关键配置 |
|---|---|---|---|---|
dict |
内置 dict |
a |
字典标签展示:把值翻译成字典文案 | dict;attrs.tagType: { '0':'success', '1':'danger' }(或 true 用默认色)渲染带色 ElTag;separator(默认 ,) |
number |
内置 number |
w |
数字输入(带千分位/负数控制) | precision、min、max、thousands(失焦千分位)、negative(允许负数)、unit(单位后缀) |
price |
内置 price |
a |
金额:写态复用 number(2 位+千分位),只读态右对齐金额文本 |
precision(默认 2)、symbol(默认 ¥)、showSymbol(默认 true) |
format |
内置 format |
r |
只读格式化展示 | format: ({ val, row, config }) => string|VNode,或字符串/VNode 直接渲染 |
tooltip |
内置 tooltip |
a |
带 hover 提示的只读文本 | attrs.tip(字符串或 ({val,row,config})=>...)、placement(默认 top) |
common |
内置 common |
a |
默认展示组件(兜底) | 字典翻译 → format → 布尔转「是/否」→ 数组 join → JSON;文本溢出自动 ElTooltip;showOverflowTooltip |
empty |
内置 empty |
a |
占位 | 什么都不渲染(用于「这一格留空」) |
列类型与内置列
ATableColumn 的 config.type 还可为:
selection→ 多选列(配合ATable的selection-change事件)index→ 序号列expand→ 展开行(用#default插槽放展开内容)
省略 type 时,单元格按 field 展示值(回退 common)。
type 的多种写法
type 支持多种形式,解析顺序:
- 组件对象:
type: MyComponent(直接传defineComponent结果或函数式组件)。 - 字符串别名:
- 内置写组件:
'el-input'、'ElInput'、'input'都能命中(自动尝试 小写 / kebab / 首字母大写 camel / camel 四种变体)。 - 自定义组件:文件名即类型名,如
components/custom/select.tsx→type:'select'。 - 任意全局注册的
el-xxx(app.use(ElementPlus)注册但私有表未登记)也会被兜底解析。
- 内置写组件:
- 未知类型:回退为
common展示组件(开发环境console.warn提示)。
状态标签 type?w
在 type 后用 ? 追加标签,强制该字段使用某类组件:
// 只读态也用写组件(不回退展示)
{ field: 'remark', label: '备注', type: 'input?w' }
// 强制只读展示组件
{ field: 'x', label: 'X', type: 'dict?r' }
标签含义:w 写态生效、r 只读态生效、a 任意态。
字典
字典提供「值 ↔ 文案」映射,下拉 / 单选 / 多选 / 级联 / dict 展示都从字典读选项。
取值器 Dict(config)
import { Dict } from "@web_qiang/aform";
const d = Dict({
list,
label: "dictLabel",
value: "dictValue",
children: "children",
disabled: "dictDisabled",
});
d.getDictLabel(item); // 取文案
d.getDictValue(item); // 取值
d.getDictDisabled(item);
d.getDictChildren(item);
缺省字段名回落顺序:dictLabel/label → dictValue/value/id。
配置远程字典 configureDict
import { configureDict } from "@web_qiang/aform";
configureDict({
fetcher: (code: string) =>
fetch(`/api/dict?code=${code}`).then((r) => r.json()),
});
本地静态字典 setLocalDict
import { setLocalDict } from "@web_qiang/aform";
setLocalDict("STATUS", [
{ dictLabel: "启用", dictValue: "1" },
{ dictLabel: "停用", dictValue: "0" },
]);
按需加载 getDict
import { getDict, clearDictCache } from '@web_qiang/aform'
const list = await getDict('STATUS') // 字符串编码:先查本地,再走 fetcher
const list2 = await getDict({ list: [...] }) // 直接传选项对象
await getDict('STATUS', { cacheTime: 5 * 60_000 }) // 自定义缓存时长(默认 2 分钟)
clearDictCache('STATUS') // 清单个
clearDictCache() // 清全部
配置项里写
dict: 'STATUS'即自动调getDict拉选项。
选项存模型:options.toString
多选类控件(如 checkbox-group、select multiple)默认把数组存回模型;用 options.toString 可改为字符串:
{ field: 'roles', label: '角色', type: 'checkbox-group', dict: 'ROLE',
options: { toString: ',' } } // 模型里存 'a,b,c';缺省 ',' 用 ',',可给任意分隔符或函数动态返回
toString: false/ 不写 → 存数组。toString: true→ 存,连接的字符串。toString: '|'→ 存|连接。- 仅在
attrs.multiple === true时生效(单值控件始终存标量)。
布局与响应式
const configs = [
// 一行多个控件(共享 label)
{ field: 'row1', label: '组合', type: 'input', content: [
{ field: 'a', type: 'input', label: 'A' },
{ field: 'b', type: 'input', label: 'B' },
] },
// 跨整行
{ field: 'name', label: '姓名', type: 'input', span: 2 },
// 撑满剩余空间
{ field: 'desc', label: '描述', type: 'input', layout: { flex: 1 } },
// 响应式显隐
{ field: 'x', label: 'X', type: 'input', show: ({ row }) => row.type === '2' },
],
}
AForm.column:一行几列(CSS Grid)。span:跨列数(1 ~ column),用于「占满整行」或「占半行」。layout.flex:true→ 占剩余空间;1/'1 1 0%'→ 占满整行。show:函数式响应式显隐,返回false连校验一起跳过。label.html:自定义标签渲染(字符串 / VNode / 函数)。
校验
规则沿用 async-validator 语法,写在 rules:
const config = {
items: [
{
field: "phone",
label: "手机号",
type: "input",
rules: [
{ required: true, message: "必填" },
{ pattern: /^1\d{10}$/, message: "格式错误" },
],
},
{
field: "age",
label: "年龄",
type: "input-number",
rules: ({ row }) => [{ required: row.needAge, message: "需填年龄" }],
}, // 函数式
],
};
- trigger:写组件默认
blur;select/cascader/radio-group/date/switch/upload/dict等默认change。可用rules[].trigger覆盖,支持'change,blur'或数组。 - 只读态跳过:
state:'readonly'的表单项不挂prop/rules,不显示必填星号、不拦截校验。 - 整表校验:
formRef.value.validate()返回{ valid, fields };可编辑ATable用tableRef.value.validate()。 - 分组校验:父
AForm设:group="true",其子AForm/ATable一并被校验。
事件
配置项 on 里:
watch(val, oldVal)(实际回调收到eventArgs,见下)→ 值变化监听。- 其它键(
change、visible-change、node-click…)→ 规范成onXxx透传底层组件,如on: { change: fn }会绑到onChange。
{ field: 'name', label: '姓名', type: 'input',
on: {
watch: ({ val, oldVal, row }) => console.log('值变了', val, oldVal),
change: ({ val, selected, row, config }) => save(val),
} }
事件回调参数 eventArgs:
{
...scope, // 表格 scope / AForm.scope
row, // 当前模型(表格为当前行)
val, // 当前控件值
selected, // 带字典控件的选中项 { labels, values, items }(cascader 无)
config, // 归一化后的 content 配置
raw, // 底层组件原始事件参数
}
自定义组件
在 src/aform/components/custom/ 新建 xxx.tsx,文件名即类型名,无需改注册代码(index.ts 已用 import.meta.glob 扫描)。
// components/custom/myType.tsx
import { defineComponent } from "vue";
import { commonProps, commonEmits } from "./shared";
export const tag = "w"; // 该组件在哪些 state 生效:w / r / a
export default defineComponent({
name: "AMyType",
props: { ...commonProps }, // modelValue / config / state / placeholder / disabled / clearable
emits: commonEmits, // update:modelValue / change
setup(props, { emit }) {
return () => (
<el-input
modelValue={props.modelValue}
onInput={(v: string) => emit("update:modelValue", v)}
/>
);
},
});
使用时 { field: 'x', type: 'myType' }。
commonProps(来自./shared):modelValue、config(NormalizedContent)、state、placeholder、disabled、clearable——自定义写组件直接展开即可拿到归一化配置(字典、状态、格式化都在config里)。commonEmits:['update:modelValue', 'change']。useOptions(config):把config.dict转成统一的{ label, value, disabled, children }[],下拉/单选/多选类组件直接用它渲染。tag:决定组件在哪种state生效;不写默认'a'(任意态)。- 展示类组件建议
tag:'r'(只在只读态生效),避免写态被误用。
注意:自定义组件放在
src/aform/components/custom/,属于库源码。如果消费方要扩展类型,应 fork 本库或在自己项目里复制该目录结构后重新构建;本包不直接提供「运行时注册业务组件」的 API(可通过register()在运行时补登组件,见下)。
命令式 API
从包导出的命令式能力:
| 导出 | 签名 | 说明 |
|---|---|---|
useFormItem |
(getRaw, getModel, options?) => NormalizedConfig |
归一化单个表单项。返回稳定的响应式归一化对象,可直接在模板 / computed 读。 |
useForm |
(getConfigs, getModel, options?) => NormalizedConfig[] |
批量归一化(API 兼容)。 |
patchConfig |
(ref, path, value, mode?) => void |
命令式给某个归一化结果打补丁。ref 为 useFormItem 返回值或 AFormItem 暴露的 config。 |
getFormItemImpl |
(ref) => impl | undefined |
取到归一化对象背后的实现(高级:直接 patch)。 |
register |
(entry) => void |
注册组件({ name, component, tag }),登记原名/小写/kebab 三种别名。 |
registerComponents |
(entries[]) => void |
批量注册。 |
registerElementPlus |
(list?) => void |
注册 Element Plus 组件(默认已注册内置清单)。 |
registerCustomComponents |
(modules) => void |
注册 import.meta.glob 扫描到的自定义模块。 |
setDefaultComponent |
(component, name?) => void |
设置默认回退展示组件(默认 common)。 |
getComponent / listComponents / resolveType / getDefaultComponent |
— | 注册表查询与类型解析(resolveType(rawType, { state, kind }))。 |
UseFormItemOptions:
interface UseFormItemOptions {
handlerCallBackArgs?: Record<string, any>; // 配置函数额外回调参数(表格里是 scope)
overrideConfig?: { formItem?: any; content?: any }; // 全局覆盖配置
callback?: (config: any) => void; // 首次归一化完成回调
}
示例:外部联动打补丁
import { useFormItem, patchConfig } from "@web_qiang/aform";
const cfg = useFormItem(
() => rawConfig,
() => model,
);
// 外部把某字段改为禁用
patchConfig(cfg, "formItem.attrs.disabled", true);
patchConfig(cfg, "content.0.attrs.loading", true, "merge");
Composables
| 导出 | 说明 |
|---|---|
useTableRequest |
表格数据请求:竞态防护、兼容 rows/list/data、分页、query 变化自动回第一页。返回 { page, rows, total, loading, error, reload, refresh, setPage }。 |
useTableFit |
列宽自适应(canvas 文本量算 + ResizeObserver),fit 开启时生效。 |
useVModel |
v-model 桥接 computed(get 读 props.modelValue,set 发 update:modelValue)。 |
useResize |
ResizeObserver 封装:useResize({ el, change, wait })。 |
useDoLayout |
MutationObserver 封装(DOM/样式变化回调)。 |
useFormGroup / provideFormGroup / createFormGroup |
表单分组注册表(provide/inject),用于整页一次性校验所有 AForm。 |
工具函数 AFormUtils
import AFormUtils from '@web_qiang/aform'(export { default as AFormUtils } from './utils'),包含:
- 类型判断:
isStringisFunctionisArrayisObjectisPlainObjectisEmptyisComponentgetType - 克隆/比较:
cloneDeepdeepEqual - 对象:
assignimmerAssignomitpick - 路径读写:
splitPropKey_get_setimmerSet - 插槽:
createSlotgetNodeTypegetContentText - 函数:
debouncethrottleprocessValuewithResolvers - 其它:
kebabCasenamingVariantsGlobalConfig
导出总表
import AForm, {
// 组件(install 时全局注册同名标签)
AForm,
AFormItem,
AContent,
ATable,
ATableColumn,
ASearch,
// 核心 / 命令式
useFormItem,
useForm,
patchConfig,
getFormItemImpl,
// composables
useTableRequest,
useTableFit,
useVModel,
useResize,
useDoLayout,
// 注册表
register,
registerComponents,
registerElementPlus,
registerCustomComponents,
getComponent,
listComponents,
resolveType,
setDefaultComponent,
getDefaultComponent,
// 字典 / 语言 / 主题 / 枚举 / 工具
configureDict,
setLocalDict,
clearDictCache,
getDict,
Dict,
setLocale,
getLocale,
setThemeMode,
getThemeMode,
isDark,
ThemeMode,
State,
Tag,
tagMatch,
normalizeTrigger,
AFormUtils, // default export
} from "@web_qiang/aform";
开发 & 构建 & 发布
pnpm dev # 本地演示(应用模式,使用 vite.config.ts)
pnpm build:lib # 构建库:产出 dist/aform.js、aform.umd.cjs、style.css 与 index.d.ts
pnpm build # 构建演示应用
仓库内含一个独立可运行的示例项目 demo/:
cd demo
pnpm install
pnpm dev
发布
pnpm build:lib
npm publish # scoped 包默认 restricted(私有);如需公开请加 --access public
package.json 关键字段:"main": "dist/aform.umd.cjs"、"module": "dist/aform.js"、"types": "dist/index.d.ts"、"style": "dist/style.css"、"exports" 同时指向 ESM/UMD/类型/样式,"sideEffects": ["dist/style.css"](CSS 不被 tree-shake 误删)。
License
MIT
Related Packages
A directive what used sortablejs to add drag support for element-plus's table co...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.0.5 | ^3.5.0 | MIT | |
| 0.0.4 | ^3.5.0 | MIT | |
| 0.0.3 | ^3.5.0 | MIT | |
| 0.0.2 | ^3.5.0 | MIT | |
| 0.0.1 | ^3.5.0 | MIT |