@complex-suite/data 快速使用
@complex-suite/data 是 complex-suite 的核心数据层。它不负责渲染 UI,而是负责描述业务页面的数据结构、搜索条件、字典字段、分页、选择、编辑、详情、异步加载和业务操作。
在当前 monorepo 中,它位于依赖链中间:utils → plugin → data → component → component-antd。实际业务页面通常通过 data 创建无头数据模型,再交给 component-antd 的 ComplexQuickList、ComplexQuickCascade 等组件渲染。
安装
pnpm add @complex-suite/data
架构概览
packages/data/
├── index.ts # 包入口,按 7 组导出
├── dataConfig.ts # 全局配置(颜色/empty Symbol/工具函数)
├── src/
│ ├── data/ # 数据类继承链
│ │ ├── Data.ts # 基类:_id/_buffer/_syncData 钩子
│ │ ├── SimpleData.ts # $extra 额外数据
│ │ ├── DefaultData.ts # $prop/$life 生命周期/Storage
│ │ ├── BaseData.ts # $status/$promise/$depend/$module/active/loadData
│ │ └── ComplexData.ts # 业务回调(buildData/changeData/...)+ 模块代理方法
│ ├── core/ # 业务数据类
│ │ ├── ComplexList.ts # 列表页主模型
│ │ ├── ComplexInfo.ts # 详情页模型
│ │ ├── SelectData.ts # 选择器数据模型
│ │ └── TrackData.ts # 轨迹回放模型(独立模块)
│ ├── module/ # 可复用功能模块
│ │ ├── ModuleData.ts # 模块容器
│ │ ├── DictionaryData.ts # 字典模块(必填)
│ │ ├── SearchData.ts # 检索模块
│ │ ├── PaginationData.ts# 分页模块
│ │ ├── ChoiceData.ts # 多选模块
│ │ ├── SortData.ts # 排序模块
│ │ ├── UpdateData.ts # 定时刷新模块
│ │ ├── StatusData.ts # 状态机模块
│ │ ├── DependData.ts # 依赖加载模块
│ │ ├── PromiseData.ts # Promise 缓存模块
│ │ └── ResetData.ts # reset/destroy 基类
│ ├── lib/ # 值对象与工具类
│ │ ├── DictionaryValue.ts# 字典项容器
│ │ ├── ArrayValue.ts # 可观测数组
│ │ ├── SelectValue.ts # 选择器数据源
│ │ ├── CascaderValue.ts # 级联选择器
│ │ ├── FormValue.ts # 表单校验桥接
│ │ ├── StorageValue.ts # 持久化缓存
│ │ ├── ForceValue.ts # 强制加载配置
│ │ ├── InterfaceValue.ts # 多场景接口值
│ │ ├── AttrsValue.ts # 属性合并
│ │ ├── TipValue.ts # 提示文本
│ │ ├── FileValue.ts # 文件数据
│ │ ├── GridParse.ts # 栅格解析
│ │ └── LayoutParse.ts # 布局解析
│ ├── dictionary/ # 字典项实现(24 个)
│ │ ├── DefaultMod.ts # 字典项基类
│ │ ├── DefaultList.ts, DefaultInfo.ts, DefaultEdit.ts
│ │ ├── InputEdit.ts, TextAreaEdit.ts, InputNumberEdit.ts
│ │ ├── SelectEdit.ts, DefaultSelectEdit.ts, SwitchEdit.ts
│ │ ├── DateEdit.ts, DateRangeEdit.ts, SimpleDateEdit.ts
│ │ ├── FileEdit.ts, ButtonEdit.ts, ButtonGroupEdit.ts
│ │ ├── ContentEdit.ts, CustomEdit.ts, CustomLoadEdit.ts
│ │ ├── FormEdit.ts, ListEdit.ts
│ │ ├── DefaultSimpleEdit.ts, DefaultLoadEdit.ts
│ │ └── ObserveList.ts
│ ├── locale/ # 国际化(zh/en)
│ └── types/ # 类型定义
└── README.md
核心概念
ComplexList:列表页主数据模型,维护 $list、加载状态、搜索、分页、选择、字典和增删改查方法。
ComplexInfo:详情页数据模型,维护 $info,常用于详情弹窗或详情页。
module.dictionary:字段字典,统一描述表格列、详情字段、编辑表单、创建表单和搜索字段。
module.search:搜索区配置,包含搜索字段和顶部菜单按钮。
module.pagination:分页模块,提供 getPage()、getPageSize()、setPageCount() 等能力。
SelectValue / SelectEdit:选择器数据源与选择器字典项,支持静态选项和远程加载。
ButtonEdit:按钮字典项,常用于搜索区菜单、自定义操作和上传入口。
数据类继承链
Data (utils._Data)
└─ SimpleData # $extra
└─ DefaultData # $prop / $life / $storage
└─ BaseData # $status / $promise / $depend / $module / loadData
└─ ComplexData # 业务回调(getData/buildData/...)+ 模块代理方法
├─ ComplexList # $list
└─ ComplexInfo # $info
每一层只关心自己的关注点,子类通过 _triggerCreateLife('ClassName', false/true, initOption) 触发对应的 beforeCreate/created 生命周期。
模块系统
ComplexList 通过 module 挂载功能模块,每个模块独立管理一类能力:
dictionary | DictionaryData | 字段字典管理 | ✅ |
search | SearchData | 检索表单与菜单 | - |
pagination | PaginationData | 分页参数与总数 | - |
choice | ChoiceData | 多选状态 | - |
sort | SortData | 排序参数 | - |
update | UpdateData | 定时刷新 | - |
depend | DependData | 依赖加载 | - |
status | StatusData | 状态机 | 内置 |
promise | PromiseData | Promise 缓存 | 内置 |
模块通过 installData / uninstallData 安装卸载,触发 _install / _uninstall 钩子。模块间通过 ModuleData.getData(modName) 互相查询。
生命周期
ComplexList 内置 Life 系统,常用生命周期:
beforeCreate / created | 实例构造前后 |
beforeLoad | loadData 开始 |
loaded | 加载成功 |
loadFail | 加载失败 |
beforeReload / reloaded / reloadFail | reloadData 各阶段 |
beforeSearch | setSearch 校验前 |
searched / searchFail | 检索成功/失败 |
beforeUpdate / updated / updateFail | 更新数据各阶段 |
beforeReset / reseted | reset 前后 |
beforeDestroy / destroyed | destroy 前后 |
dataChange | 业务方法成功后(buildData/changeData/deleteData/...) |
parentChange | $setParent 时 |
监听方式:
listData.onLife('loaded', {
id: 'myHandler',
handler: (lifeValue, res) => {
console.log('加载完成', res)
}
})
listData.offLife('loaded', 'myHandler')
onLoaded(handler) 是 loaded 的快捷监听,若当前已加载完成会立即触发。
基础列表用法
下面示例提炼自真实业务页面 src/pages/index/views/list/base/index.vue,展示标准模式:页面创建 ComplexList,配置搜索、字典、分页和业务接口,然后传给 ComplexQuickList 渲染。
<template>
<div class="local-page">
<ComplexQuickList
:list-data="mainData"
:components="['spin', 'search', 'table', 'edit', 'info']"
:simple-table="false"
:components-props="componentsProps"
@search="onSearch"
@table="onTable"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ButtonEdit, ComplexList, SelectEdit, SelectValue, type fileDataType } from '@complex-suite/data'
import { ComplexQuickList, icon, type componentsProps, type tablePayload } from '@complex-suite/component-antd'
import listApi from '@/api/listApi'
export default defineComponent({
name: 'RecordList',
components: { ComplexQuickList },
setup() {
// 1) 静态选项数据源
const select = new SelectValue({
list: [
{ value: 0, label: '选项1' },
{ value: 1, label: '选项2' },
{ value: 2, label: '选项3' }
]
})
// 2) 创建列表数据模型
const mainData = new ComplexList({
prop: 'mainData',
module: {
// 搜索区
search: {
menu: {
list: [
'build', // 内置:新增按钮
'delete', // 内置:批量删除按钮
new ButtonEdit({ // 自定义按钮
prop: 'choice2',
type: 'button',
option: {
name: () => '已选择2条时可用',
disabled(payload: any) {
return payload.choice !== 2
},
click() {
return Promise.resolve({})
}
}
}),
new ButtonEdit({ // 文件上传按钮
prop: '$import',
type: 'button',
option: {
name: () => '导入',
icon: () => icon.local('emptyImage', { size: 16 }),
upload: () => Promise.resolve({})
}
})
]
},
list: [
{
prop: 'input',
name: '输入框',
mod: {
search: {
$format: 'edit',
type: 'input',
collapse: 2,
option: { size: 20 }
}
}
},
{
prop: 'search',
name: '检索框',
mod: {
search: {
$format: 'edit',
type: 'select',
cascader: undefined,
width: 100,
reload: true,
search: {},
// 远程加载选项
getData(this: SelectEdit) {
return new Promise((resolve) => {
this.$select.setList([
{ value: this.$search?.value, label: this.$search?.value || '' },
{ value: 1, label: '1' },
{ value: 2, label: '2' }
])
resolve({})
})
}
}
}
}
]
},
// 字典:字段定义(同时服务 list/info/edit/build/change)
dictionary: {
propData: { id: 'id' },
list: [
{ prop: 'menu', name: '操作', originFrom: 'local', mod: { list: { width: 140 } } },
{ prop: '$index', name: 'No', originFrom: 'local', mod: { list: { width: 60 } } },
{
prop: 'id',
name: 'ID',
mod: { list: { width: 80 }, info: {} }
},
{
prop: 'input',
name: '输入框',
mod: {
list: { width: 100 },
info: {},
edit: { type: 'input', required: true, option: { size: 20 } },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
},
{
prop: 'switch',
name: '开关',
assign(value) { return value === 1 ? true : false },
collect(value) { return value ? 1 : 0 },
mod: {
list: { width: 70 },
edit: { type: 'switch', required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
},
{
prop: 'select',
name: '选择器',
showProp: { default: 'value', list: 'label' },
assign(value) { return select.getItem(value) },
mod: {
list: { width: 100 },
edit: {
type: 'select',
cascader: undefined,
required: true,
select: select,
pagination: {},
getData(this: any) {
return new Promise((resolve) => {
setTimeout(() => {
this.$select.setList(select.getList())
this.$pagination.setCount(100)
resolve({})
}, 200)
})
}
},
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
},
{
prop: 'multipleFile',
name: '多文件',
assign(value) { return value ? (value as string).split(',') : [] },
collect(value) { return value ? (value as string[]).join(',') : '' },
mod: {
edit: { type: 'file', multiple: true, required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
},
{
prop: 'timeRange',
name: '时间范围',
assign(value) { return (value as string).split(',') },
collect(value) { return (value as string[]).join(',') },
mod: {
list: { width: 300 },
edit: {
type: 'dateRange',
required: true,
option: {
time: {},
rangeLimit: { value: 5 * 24 * 60 * 60 },
disabledDate: {
start: { value: 'today', eq: true },
end: { value: 'tomorrow', eq: true }
}
}
},
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
}
]
},
pagination: true
},
// 业务回调:均要求返回 Promise
getData(this: ComplexList) {
return new Promise((resolve, reject) => {
const postData = {
...this.getSearch(),
page: this.getPage(),
size: this.getPageSize()
} as any
listApi.baseList.require(postData).then(res => {
this.formatList(res.data.data.list, res.data.data.num)
resolve(res)
}).catch(reject)
})
},
buildData(this: ComplexList, targetData) {
return new Promise((resolve, reject) => {
listApi.baseBuild.require(targetData).then(res => {
this.reloadData(true)
resolve(res)
}).catch(reject)
})
},
changeData(this: ComplexList, targetData, originData) {
return new Promise((resolve, reject) => {
targetData.id = originData.id
listApi.baseChange.require(targetData).then(res => {
this.reloadData(true)
resolve(res)
}).catch(reject)
})
},
deleteData(this: ComplexList, targetData) {
return new Promise((resolve) => {
const index = this.$list.indexOf(targetData)
if (index > -1) {
this.$list.splice(index, 1)
}
resolve({})
})
},
multipleDeleteData(this: ComplexList, choiceList) {
return new Promise((resolve) => {
choiceList.forEach(item => {
const index = this.$list.indexOf(item)
if (index > -1) {
this.$list.splice(index, 1)
}
})
resolve({})
})
}
})
const onSearch = function(_prop: string) {}
const onTable = function(_prop: string, _payload: tablePayload) {}
return {
mainData: mainData,
onSearch: onSearch,
onTable: onTable,
componentsProps: {
table: {
menu: {
menu: [
{ prop: '$change', name: () => '编辑', color: 'link' },
{ prop: '$delete', name: () => '删除', color: 'danger' },
{ prop: '$info', name: () => '详情', color: 'link' }
]
}
},
edit: {}
} as componentsProps
}
},
mounted() {
this.mainData.loadData(true)
}
})
</script>
字典字段配置
module.dictionary.list 是列表页的核心协议。一个字段可同时服务多个场景:
{
prop: 'name',
name: '名称',
originFrom: 'local',
showProp: {
default: 'value',
list: 'label'
},
assign(value) { ... },
collect(value) { ... },
dictionary: {
list: [...]
},
mod: {
list: { width: 100 },
info: {},
edit: { type: 'input', required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' },
search: { $format: 'edit', type: 'input' }
}
}
编辑类型对照
input | InputEdit | 文本输入 |
textArea | TextAreaEdit | 多行文本 |
inputNumber | InputNumberEdit | 数字输入 |
select | SelectEdit / DefaultSelectEdit | 选择器 |
switch | SwitchEdit | 开关 |
date | DateEdit / SimpleDateEdit | 日期 |
dateRange | DateRangeEdit | 日期范围 |
file | FileEdit | 文件上传 |
button | ButtonEdit | 按钮(搜索区/自定义操作) |
form | FormEdit | 内嵌表单 |
list | ListEdit | 内嵌列表 |
content | ContentEdit | 纯展示 |
custom | CustomEdit / CustomLoadEdit | 自定义渲染 |
搜索区配置
module.search 分为菜单和搜索字段两部分:
search: {
menu: {
list: [
'build',
'delete',
new ButtonEdit({ ... })
]
},
list: [
{
prop: 'keyword',
name: '关键字',
mod: {
search: {
$format: 'edit',
type: 'input',
collapse: 2
}
}
}
]
}
ComplexQuickList 已内置处理这些菜单:
$search | 调用 listData.setSearch(),更新搜索条件并触发列表加载 |
$reset | 调用 listData.resetSearch(),重置搜索条件 |
$refresh | 调用 listData.reloadData({ data: true, sync: true }) |
$build | 打开新增弹窗 |
$delete | 触发批量删除(基于 choice) |
$info | 打开详情弹窗 |
$export | 调用 listData.triggerMethod('exportData') |
$import / $image | 文件上传按钮(通过 upload 回调) |
ButtonEdit 区分两种语义:
click():普通点击回调,返回 Promise
upload(file: File):文件上传专用回调,由组件触发文件选择 → 拿到 File → 调用 upload
远程选择器
选择器可以使用静态 SelectValue,也可以在 SelectEdit.getData 中远程加载选项:
{
prop: 'search',
name: '检索框',
mod: {
search: {
$format: 'edit',
type: 'select',
cascader: undefined,
width: 100,
reload: true,
search: {
limit: 3,
limitContent: '限制3个'
},
getData(this: SelectEdit) {
return new Promise((resolve) => {
this.$select.setList([
{ value: this.$search?.value, label: this.$search?.value || '' },
{ value: 1, label: '1' },
{ value: 2, label: '2' }
])
resolve({})
})
}
}
}
}
如果远程接口有分页结果,可在 getData 中同时调用 this.$pagination.setCount(total)。
数据加载与提交
ComplexList 的业务方法都要求返回 Promise,这样状态模块才能正确维护 load、operate 等状态。
getData(this: ComplexList) {
return new Promise((resolve, reject) => {
const postData = {
...this.getSearch(),
page: this.getPage(),
size: this.getPageSize()
}
api.list(postData).then(res => {
this.formatList(res.list, res.total)
resolve(res)
}).catch(reject)
})
}
常用方法:
loadData(force?, ...args) | 加载数据,force 控制是否强制 |
reloadData(force?, ...args) | 重新加载,触发 beforeReload/reloaded/reloadFail |
formatList(list, total) | 写入 $list,同步分页总数 |
getSearch() | 获取搜索区当前值 |
getPage() / getPageSize() | 获取当前分页参数 |
setPage(page) / setPageSize(size) | 修改分页参数 |
triggerMethod(method, args, option) | 通过状态机触发业务方法 |
triggerMethodWithOperateAndStatus(method, args, statusOption, operateOption) | 触发并联动目标 status + operate |
setSort(prop, order) | 设置排序 |
setChoice(data) / resetChoice() | 多选操作 |
force 参数支持多种形式:
listData.loadData(true)
listData.loadData(false)
listData.loadData({ data: true, ing: true })
listData.loadData({
data: true,
ing: true,
promise: true,
sync: true,
trigger: { from: 'search', action: 'set' },
module: { pagination: true }
})
业务回调
ComplexList 支持在初始化配置中定义业务回调,this 绑定到当前实例:
interface ComplexDataInitOption {
module: ModuleDataInitOption
getData?: (this: ComplexList) => Promise<any>
buildData?: (this: ComplexList, targetData) => Promise<any>
changeData?: (this: ComplexList, targetData, originData) => Promise<any>
editData?: (this: ComplexList, targetData, originData) => Promise<any>
deleteData?: (this: ComplexList, targetData) => Promise<any>
multipleDeleteData?: (this: ComplexList, choiceList) => Promise<any>
refreshData?: (this: ComplexList, targetData) => Promise<any>
updateData?: (this: ComplexList) => Promise<any>
exportData?: (this: ComplexList) => Promise<any>
importData?: (this: ComplexList, file: File) => Promise<any>
}
回调内可直接访问 this.$list、this.reloadData()、this.getSearch() 等。buildData/changeData/deleteData 等修改类方法成功后会自动触发 dataChange 生命周期。
表格菜单
业务页面通常通过 componentsProps.table.menu.menu 配置表格行操作:
componentsProps: {
table: {
menu: {
menu: [
{ prop: '$change', name: () => '编辑', color: 'link' },
{ prop: '$delete', name: () => '删除', color: 'danger' },
{ prop: '$info', name: () => '详情', color: 'link' }
]
}
}
}
ComplexQuickList 默认行为:
$change | 打开编辑弹窗,使用当前行作为原始数据 |
$delete | 确认后调用 listData.triggerMethod('deleteData', [targetData]) |
$info | 打开详情弹窗 |
字段转换:assign 与 collect
接口值与表单值往往存在差异,字典字段提供了两个转换点:
{
prop: 'switch',
assign(value) {
return value === 1 ? true : false
},
collect(value) {
return value ? 1 : 0
}
}
assign:在 getData 成功后、数据进入 $list/$info/表单前调用
collect:在 buildData/changeData 提交前调用
对于多文件等场景,可结合 isFile 判断:
{
prop: 'multipleFile',
assign(value) { return value ? (value as string).split(',') : [] },
collect(value) {
return (value as any[])
.map(item => isFile(item) ? item.name : item)
.join(',')
}
}
嵌套字典(FormEdit / ListEdit)
字段可嵌套完整字典,用于复杂场景(如内嵌表单/列表):
{
prop: 'form',
name: '内嵌表单',
assign(value) { return value || { name: 'a', date: '2019-05-18' } },
collect(value) { return value },
dictionary: {
list: [
{
prop: 'name',
name: '名称',
mod: {
list: { width: 120 },
info: { $redirect: 'edit' },
edit: { type: 'input', required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
}
]
},
mod: {
list: { width: 300 },
info: { $redirect: 'edit' },
edit: {
grid: {
line: 1,
custom(data, position, _gridParse) {
if (position === 'label') return { span: 0 }
if (position === 'content') return { span: 24 }
return data
}
},
type: 'form',
required: true,
option: {}
},
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
}
级联列表
级联列表通常使用 ComplexQuickCascade。主列表通过表格菜单暴露 $sublist,点击后打开子列表弹窗,并由子列表自己的 ComplexList 加载数据。
推荐模式:
const parentData = new ComplexList({
prop: 'parentData',
module: {
dictionary: { list: [...] },
pagination: true
},
getData(this: ComplexList) {
return new Promise((resolve, reject) => {
listApi.parentList.require({
...this.getSearch(),
page: this.getPage(),
size: this.getPageSize()
}).then(res => {
this.formatList(res.data.data.list, res.data.data.num)
resolve(res)
}).catch(reject)
})
}
})
const childData = new ComplexList({
prop: 'childData',
module: {
dictionary: { list: [...] },
pagination: true
},
getData(this: ComplexList) {
const parentId = this.getExtra('parentId')
return new Promise((resolve, reject) => {
listApi.childList.require({
parentId,
page: this.getPage(),
size: this.getPageSize()
}).then(res => {
this.formatList(res.data.data.list, res.data.data.num)
resolve(res)
}).catch(reject)
})
}
})
const showSubList = function(targetData: any) {
childData.setExtra('parentId', targetData.id)
childData.reloadData(true)
}
$extra 是不参与字典/搜索/分页的额外数据容器,通过 setExtra / getExtra / clearExtra 操作。
全局配置与国际化
import { dataConfig, dataLocale } from '@complex-suite/data'
const primary = dataConfig.style.color.primary
dataLocale.use('en')
dataLocale.t('baseData.methodNotExist', { method: 'foo' })
可注入自定义消息:
import { dataLocale, type DataMessages } from '@complex-suite/data'
const customMessages: Partial<DataMessages> = {
zh: { baseData: { methodNotExist: '方法 {method} 不存在' } }
}
dataLocale.inject(customMessages)
重置与销毁
listData.reset()
listData.reset({ status: false, pagination: true })
listData.destroy()
listData.destroy({ life: true, depend: true })
resetOption 通过 parseResetOption(option, prop) 解析:true / false / undefined / { [prop]: boolean }。
TypeScript 泛型
ComplexList 支持泛型约束列表项类型:
interface UserItem { id: number; name: string }
interface UserVO { id: number; name: string; deptName: string }
const listData = new ComplexList<UserItem, UserVO>({
prop: 'userList',
module: { dictionary: { list: [] }, pagination: true },
getData(this: ComplexList<UserItem, UserVO>) {
return fetch('/api/users').then(r => r.json()).then(res => {
this.formatList(res.list, res.total)
return res
})
}
})
listData.$list
ComplexInfo<D> 类似,用于详情页:
const infoData = new ComplexInfo<UserVO>({
prop: 'userInfo',
module: { dictionary: { list: [...] } },
getData(this: ComplexInfo<UserVO>) {
return fetch(`/api/users/${this.$extra.id}`).then(r => r.json()).then(res => {
this.formatInfo(res.data)
return res
})
}
})
infoData.$info
SelectValue 与级联
import { SelectValue, CascaderValue } from '@complex-suite/data'
const select = new SelectValue({
list: [{ value: 1, label: 'A' }, { value: 2, label: 'B' }],
equal: (a, b) => a.value === b.value,
hidden: true
})
const cascader = new CascaderValue({
list: [
{
value: 'zhejiang', label: '浙江',
children: [
{ value: 'hangzhou', label: '杭州' },
{ value: 'ningbo', label: '宁波' }
]
}
],
cascader: 'children'
})
推荐实践
- 列表页优先用
ComplexList 作为唯一业务状态入口,UI 组件只消费它,不重复维护列表、分页和搜索状态。
- 字段配置优先集中在
module.dictionary.list,通过 list、info、edit、build、change 复用同一字段语义。
- 所有业务回调都返回
Promise,包括 getData、buildData、changeData、deleteData、自定义按钮 click 和上传 upload。
- 接口返回值进入表单前使用
assign,提交前使用 collect,避免在 UI 组件中散落转换逻辑。
- 新增和修改表单大多可通过
$redirect: 'edit' 复用配置,只在字段行为确实不同的时候单独覆盖。
- 远程加载选择器时,在
getData 中同步调用 this.$pagination.setCount(total) 让分页模块拿到总数。
- 使用
onLife / offLife 监听关键生命周期(loaded、dataChange、beforeReset),避免在组件内用 setTimeout 等待异步。
force 参数优先用对象形式({ data: true, ing: true }),可读性优于布尔简写。
- 使用
component-antd 时,data 只负责无头数据协议,渲染细节交给 ComplexQuickList、TableView、EditArea、InfoArea。
依赖
@complex-suite/utils:核心工具函数和基类(Life、Watcher、setProp 等)。
@complex-suite/plugin:布局和通知系统。