@web_qiang/aform

基于 Vue3 + Element Plus 的声明式表单/表格组件库(aform):配置驱动渲染、字典联动、异步请求与自定义组件自动注册。
Install
npm install @web_qiang/aform
Latest Version:0.0.5
Vue:3
License:MIT
Last Updated:Sep 2, 2026
Links: npm
Maintainer: web_qiang

@web_qiang/aform

基于 Vue3 + Element Plus 的声明式表单 / 表格组件库(aform)。用一份 config 即可渲染表单、表格、搜索区,内置字典联动、异步请求、表单校验与自定义组件自动注册,开箱即用且完整支持 TypeScript。

  • 包名:@web_qiang/aform
  • 仓库内演示项目:demo/(独立可运行)

目录


特性

  • 配置驱动:一份 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 支持多种形式,解析顺序:

  1. 组件对象:type: MyComponent(直接传 defineComponent 结果或函数式组件)。
  2. 字符串别名:
    • 内置写组件:'el-input'、'ElInput'、'input' 都能命中(自动尝试 小写 / kebab / 首字母大写 camel / camel 四种变体)。
    • 自定义组件:文件名即类型名,如 components/custom/select.tsx → type:'select'。
    • 任意全局注册的 el-xxx(app.use(ElementPlus) 注册但私有表未登记)也会被兜底解析。
  3. 未知类型:回退为 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'),包含:

  • 类型判断:isString isFunction isArray isObject isPlainObject isEmpty isComponent getType
  • 克隆/比较:cloneDeep deepEqual
  • 对象:assign immerAssign omit pick
  • 路径读写:splitPropKey _get _set immerSet
  • 插槽:createSlot getNodeType getContentText
  • 函数:debounce throttle processValue withResolvers
  • 其它:kebabCase namingVariants GlobalConfig

导出总表

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

lk-component-element

一个基于Element Plus封装的Vue3功能组件库

3,095
element-plus-generator

基于element-plus封装的表格、表单生成器

2,054
zxt-table

一个基于 Vue 3 和 Element Plus 的高级表格、表单组件库

5,778
element-plus-table-dragable

A directive what used sortablejs to add drag support for element-plus's table co...

19,522 14
advanced-ele-ui

Advanced Component Library Based on Vue3 + Element Plus

6,248 4

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