@shaonq/vue
v1.6.1
Published
shaonq Vue Library
Readme
@shaonq/vue
Vue 3 组件库:OrcTable(vxe-table 封装)、OrcForm(配置驱动动态表单)、OrcSearch(搜索栏)。
ColBuilder 配置构建器
ColBuilder 用于以最少的代码构建 orc-table 列配置(OrcTableItem)与 orc-form 表单项配置(OrcFormItem),消除 type/model/name 与深层嵌套(OrcFormTable / OrcFormLayer)的样板代码。
import { ColBuilder } from '@shaonq/vue';表格列(orc-table / orc-search)
| 方法 | 签名 | 说明 |
| ----------------- | ----------------------------------------------- | ------------------------------------------------------------ |
| tableItem | (dataIndex, title?, overrides?) | 通用列。overrides.dataType 自动附带默认列宽 |
| tableItemStatus | (dataIndex, title, texts?, size?, overrides?) | 状态标签列(Tag 渲染),texts 为 { value, label, color }[] |
ColBuilder.tableItem('name', '姓名', { dataType: 'string' });
ColBuilder.tableItem('amount', '金额', { dataType: 'number', align: 'right' });
ColBuilder.tableItem('other'); // 占位操作列
ColBuilder.tableItemStatus('status', '状态', [
{ value: '1', label: '启用', color: 'green' },
{ value: (v) => !v, label: '空', color: 'gray' }, // value 支持函数
]);表单项(orc-form config)
| 方法 | 签名 | 说明 |
| ----------- | ------------------------------------ | --------------------------------------------------------------------------- |
| formItem | (model, name?, overrides?) | 通用表单项。overrides.props 与 componentAttr 合并透传组件属性 |
| formTable | (model, name, columns, opts?) | 子表格(OrcFormTable)。columns 为列配置(继续用 formItem) |
| formLayer | (model, name, tableOptions, opts?) | 弹窗选行(OrcFormLayer)。tableOptions 为表格配置,columns 用 tableItem |
// 基础表单项
ColBuilder.formItem('name', '姓名', { type: 'a-input', options: { required: true } });
ColBuilder.formItem('role', '角色', {
type: 'a-select',
options: { required: true },
defaultValue: '1',
props: { allowClear: true },
events: { onMounted: () => [{ label: '管理员', value: '1' }] }, // 异步选项
});
// 子表格(OrcFormTable):列配置用 formItem
ColBuilder.formTable(
'list',
'不合格明细',
[
ColBuilder.formItem('F_BadId', '不合格原因', { type: 'a-select' }),
ColBuilder.formItem('F_Qty', '数量', { type: 'a-input-number' }),
ColBuilder.formItem('other'),
],
{
span: 24, // 栅格宽度
required: true, // 表单项必填
defaultValue: [], // 缺省 []
height: 200, // 表格高度
hideTools: false,
tableOptions: { rules: { F_Qty: [{ type: 'number', min: 10, message: '数量必须≥10' }] } }, // 行编辑校验
}
);
// 弹窗选行(OrcFormLayer):表格配置用 OrcTableOption,列用 tableItem
ColBuilder.formLayer(
'holiday',
'选择假期',
{
selectedType: 'radio',
scrollX: { enabled: true },
scrollY: { enabled: true },
columns: [ColBuilder.tableItem('date', '日期', { dataType: 'date' }), ColBuilder.tableItem('name', '假日名称', { dataType: 'string' })],
},
{
span: 24,
required: true,
pagination: true,
nameKey: 'name', // 回显字段,缺省 'name'
tableData: [], // 静态数据(不走接口时)
}
);输出结构(等价手写配置)
formTable→{ type: OrcFormTable, model, name, options: { span, required }, defaultValue: [] , componentAttr: { ...剩余项, options: columns } }formLayer→{ type: OrcFormLayer, model, name, options: { span, required }, componentAttr: { ...剩余项, options: tableOptions } }formItem→ 字段分发:options/defaultValue/events保持原结构,props与componentAttr合并到componentAttr
注意(formTable / formLayer):
- 字段级
events.onChange对 OrcFormTable 不生效(子表格不 emit change 事件)。行内联动请写在列配置的events.onChange上(参数为[value, { rowIndex, row }])- OrcFormLayer 的
events.onChange参数是{ name, data }对象而非新值(与普通字段不同),联动建议直接用util.getModel(...)componentAttr中的函数属性(如disabled)每次渲染执行两次,请保持为纯函数(无请求/计数副作用)
OrcTable 树形表格
orc-table 继承 vxe-table,通过 option.treeConfig 与列的 treeNode 开启树形结构(treeConfig 即 vxe 的 tree-config)。
| 配置 | 类型 | 说明 |
| ---------------------- | --------- | ------------------------------------------------------------------------- |
| option.treeConfig | object | { transform: true, rowField: 'id', parentField: 'parentId' } 等原生配置 |
| option.checkStrictly | boolean | 复选框关闭父子联动 |
| columns[].treeNode | boolean | 标记展开图标所在列 |
<orc-table
:data="list"
:option="{
rowKey: 'id',
treeConfig: { transform: true, rowField: 'id', parentField: 'parentId', expandAll: true },
columns: [
{ dataIndex: 'name', title: '名称', treeNode: true, width: 260 },
{ dataIndex: 'amount', title: '金额', width: 120, align: 'right' },
],
}"
/>transform: true时传扁平数据(含parentId),由 vxe 自动转成树;根节点parentId置空transform: false时传嵌套数据(children)rowKey需与treeConfig.rowField一致option.tableProps优先级最高,会覆盖treeConfig
OrcForm 替换 a-form 使用指南
OrcForm 内部就是 a-form + a-row + a-col + a-form-item 的封装,把「模板 + v-model + rules」改写成「一个 config 数组」。数据绑定、校验、提交、禁用、联动全部由配置驱动,模板只剩一行。
1. 对比示例
a-form 写法(模板 20 行)
<a-form ref="formRef" :model="formData" :rules="rules" :label-col-props="{ flex: '110px' }" label-align="right" size="small">
<a-row :gutter="8">
<a-col :span="12">
<a-form-item field="name" label="姓名" :rules="[{ required: true, message: '请输入姓名' }]">
<a-input v-model="formData.name" placeholder="请输入姓名" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="role" label="角色">
<a-select v-model="formData.role" :options="roleOptions" placeholder="请选择角色" allow-search />
</a-form-item>
</a-col>
</a-row>
<a-form-item>
<a-button type="primary" html-type="submit">提交</a-button>
</a-form-item>
</a-form>orc-form 等价写法(config 一份 + 模板一行)
import { OrcForm, ColBuilder } from '@shaonq/vue';
import type { OrcFormItem } from '@shaonq/vue';
const roleOptions = [
{ label: '管理员', value: '1' },
{ label: '用户', value: '2' },
];
const config: OrcFormItem[] = [
ColBuilder.formItem('name', '姓名', {
type: 'a-input',
options: { required: true }, // 自动生成必填规则 + 红星
props: { allowClear: true }, // 组件属性透传(等价 v-bind)
}),
ColBuilder.formItem('role', '角色', {
type: 'a-select',
defaultValue: '1',
props: { options: roleOptions, allowSearch: true }, // 组件属性(含数据源)
}),
];<orc-form v-model="formData" :config="config" @submit="onSubmit" />2. 属性映射表
| a-form 写法 | orc-form 写法 |
| ---------------------------------------- | ------------------------------------------------------------------------- |
| :model="formData" + 每个控件 v-model | v-model="formData" 整体绑定,控件 v-model 自动完成 |
| 整表 :rules="rules" | 每个字段 options.required 或字段级 rules |
| :label-col-props="{ flex: '110px' }" | label-width="110px" |
| label-align="right" | label-align="right"(默认 right) |
| size="small" | 固定 small,不需要写 |
| <a-row :gutter="8"> | :gutter="8"(默认 8) |
| <a-col :span="12"> | 字段 options.span: 12(或组件级 :span 全局默认) |
| <a-form-item field="xxx"> | 字段 model: 'xxx' |
| <a-form-item label="xxx"> | 字段 name: 'xxx' |
| <a-form-item :disabled> | 字段 options.disabled 或组件 :disabled |
| <a-form-item :help> | 字段 options.help |
| <a-form-item :tooltip> | 字段 options.tooltip |
| <a-form-item :validate-trigger> | 字段 validateTrigger |
| <a-form-item :label-col-flex> | 字段 itemAttr: { labelColFlex: ... } |
| 控件 placeholder | 字段 options.placeholder,不写自动「请输入 / 请选择」 |
| 控件其余 props | 字段 componentAttr(ColBuilder 中写 props 同义) |
| 自定义提交按钮 | 组件自带「保存」按钮(hide-submit 可隐藏,#row 插槽可替换) |
| @submit(需 ev.preventDefault()) | @submit="({ data, utils }) => ...",校验通过才触发,无需 preventDefault |
3. 校验
// 必填:options.required 自动生成 [{ required: true, message: '请输入/请选择xxx' }]
{ type: 'a-input', model: 'name', name: '姓名', options: { required: true } }
// 自定义规则:字段级 rules(支持 Arco 全部规则,或返回规则数组的函数)
{
type: 'a-input-number', model: 'qty', name: '数量',
rules: [{ type: 'number', min: 10, max: 100, message: '数量必须在 10 ~ 100 之间' }],
}
// 触发时机
{ type: 'a-input', model: 'code', name: '编码', validateTrigger: 'blur' }提交时自动校验全部字段;未通过则触发 @error 且不触发 @submit。也可用 ref 手动校验:
const formRef = ref(); // <orc-form ref="formRef" />
const [err, data] = await formRef.value.onSubmit(); // err 为空表示通过4. 提交与重置
function onSubmit({ data, utils }: OrcFormSubmit) {
utils.setLoading(true); // 保存按钮转圈
try {
await saveApi(data); // data 为校验通过后的表单数据
utils.resetFields(); // 重置字段(或调 ref.onReset())
Message.success('保存成功');
} finally {
utils.setLoading(false);
}
}utils.setLoading(true/false)— 保存按钮 loadingutils.resetFields()— 清空字段- ref 方法:
formRef.value.onSubmit()(校验+提交)、onReset()(恢复 defaultValue)、resetFields()(清空) - 弹窗模式(OrcFormButton)额外有
utils.setVisible(true/false)
5. 动态交互(替换原来的 watch / 手动赋值)
| 需求 | a-form 做法 | orc-form 做法 |
| ------------------ | ------------------------- | ----------------------------------------------------------------------------- |
| 下拉选项异步加载 | onMounted 请求后赋值 | 字段 events.onMounted: () => options数组 |
| 字段联动显示/隐藏 | v-if + watch 手动控制 | events.onChange 里 util.setShow(model, bool) |
| 字段联动赋值 | 手动 formData.xxx = ... | util.setModel('xxx', val) 或 util.setModel({a: 1}) |
| 读取字段值 | formData.xxx | util.getModel('xxx') / util.getModels() |
| 动态切换下拉选项 | 手动改 roleOptions | util.setModelOption('role', newOptions) |
| 控件属性随状态变化 | computed props | componentAttr: { disabled: (util) => util.getModel('type') === 'readonly' } |
| 值变化监听 | @change 逐个绑定 | 字段 events.onChange: (val, util) => ... |
// 联动示例:切换「类型」后,动态显示/隐藏字段并重新加载选项
ColBuilder.formItem('type', '类型', {
type: 'a-select',
options: { required: true },
events: {
onMounted: () => [{ label: 'A', value: 'a' }, { label: 'B', value: 'b' }],
onChange(val, util) {
util.setShow('code', val === 'a'); // 显示/隐藏
util.setModelOption('role', val === 'a' ? [{ label: 'x', value: '1' }] : []); // 切换选项
},
},
}),6. 常用字段速查
ColBuilder.formItem('name', '姓名', { type: 'a-input' }); // 文本框
ColBuilder.formItem('remark', '备注', { type: 'a-textarea', props: { maxLength: 200, showWordLimit: true } });
ColBuilder.formItem('qty', '数量', { type: 'a-input-number', props: { min: 0, max: 100 } });
ColBuilder.formItem('date', '日期', { type: 'a-date-picker' });
ColBuilder.formItem('datetime', '时间', { type: 'a-date-picker', props: { showTime: true } });
ColBuilder.formItem('switch', '启用', { type: 'a-switch', defaultValue: true });
ColBuilder.formItem('role', '角色', { type: 'a-select', props: { options: roleOptions, allowSearch: true } });
ColBuilder.formItem('sex', '性别', {
type: 'a-radio-group',
defaultValue: 'male',
props: {
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
],
},
});
ColBuilder.formItem('dept', '部门', { type: 'a-tree-select', props: { treeData: [], allowSearch: true } });7. 复杂字段(替代自定义封装)
| 场景 | 用法 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| 子表格(行内编辑) | ColBuilder.formTable(model, name, 列config, opts) — 见上文 ColBuilder 章节 |
| 弹窗选行(TablePicker 类) | ColBuilder.formLayer(model, name, tableOptions, opts) — 见上文 ColBuilder 章节 |
| 自定义组件 | type: 组件对象(如 defineAsyncComponent),自动注入 modelValue / update:modelValue / utils 实现 v-model |
| 覆盖默认控件渲染 | 插槽 <template #model名="{ value, setValue, options, utils }"> |
字典类下拉(字典数据经 events.onMounted 异步加载选项,配合 util.setModelOption 可动态刷新):
// config 中:挂载时加载字典选项
{
type: 'a-select', model: 'status', name: '状态',
events: {
onMounted: async (util) => {
const list = await fetchDict('order-status'); // 自行实现字典请求
util.setModelOption('status', list);
},
},
}8. 弹窗表单(替换 a-modal + a-form + 按钮)
<orc-form-button title="新增用户" :config="config" @submit="onSubmit">
<a-button>新增</a-button>
</orc-form-button>// @submit 回调:校验通过后触发,utils 额外支持 setVisible
function onSubmit({ data, utils }: OrcFormSubmit) {
utils.setLoading(true);
await saveApi(data);
utils.setLoading(false);
utils.setVisible(false); // 关闭弹窗
}常用 props:title(弹窗标题)、config、modelValue(回显数据)、labelWidth、disabled、isView(只读展示模式)、buttonAttrs(按钮属性)、modalAttrs(弹窗属性)、span/gutter。
9. 常见坑
- 字段
options.span缺省继承组件:span(默认 24 即整行);想一行放多个字段记得配 span componentAttr中函数属性(如disabled)在渲染时执行,入参util,可做跨字段联动;保持纯函数(每次渲染执行两次,勿放请求/计数副作用)- 非字符串
type(自定义组件)会自动绑定 v-model,不要重复传modelValue - 不要给
type传'a-form'/'a-form-item'这类容器组件,容器由 OrcForm 统一管理 hide-submit隐藏保存按钮后,仍可用ref.onSubmit()触发校验提交- 子表格行内联动:在列配置的
events.onChange中setModel修改整行数据时,组件随后会基于旧数据回写当前列,可能覆盖联动结果。推荐写法:
// 行内联动(子表格列配置):先拿到当前行,改完后整体 setModel 刷新
ColBuilder.formItem('adjust', '调整金额', {
type: 'a-input-number',
events: {
onChange([val, { rowIndex, row }], util) {
const list = util.getModel('tableData'); // 从表单模型取整表
list[rowIndex] = { ...row, adjust: val, status: val > 3000 ? '待审核' : '已确认' };
util.setModel('tableData', [...list]); // 整体覆盖,避免组件旧数据回写覆盖
},
},
}),本地命令
lerna clean # 从所有包中删除 node_modules
lerna publish # 发布
lerna link # 关联包,模拟发布后的效果
lerna ls # 查看本地包列表