
Research
/Security News
jscrambler npm Package Compromised in Supply Chain Attack
A compromised jscrambler npm release added a malicious preinstall hook that runs hidden native binaries on Linux, macOS, and Windows.
@complex-suite/data
Advanced tools
@complex-suite/data 是 complex-suite 的核心数据层。它不负责渲染 UI,而是负责描述业务页面的数据结构、搜索条件、字典字段、分页、选择、编辑、详情、异步加载和业务操作。
在当前 monorepo 中,它位于依赖链中间:utils → plugin → data → component → component-antd。实际业务页面通常通过 data 创建无头数据模型,再交给 component-antd 的 ComplexQuickList、ComplexQuickCascade 等组件渲染。
pnpm add @complex-suite/data
ComplexList:列表页主数据模型,维护 $list、加载状态、搜索、分页、选择、字典和增删改查方法。ComplexInfo:详情页数据模型,维护 $info,常用于详情弹窗或详情页。module.dictionary:字段字典,统一描述表格列、详情字段、编辑表单、创建表单和搜索字段。module.search:搜索区配置,包含搜索字段和顶部菜单按钮。module.pagination:分页模块,提供 getPage()、getPageSize()、setPageCount() 等能力。SelectValue / SelectEdit:选择器数据源与选择器字典项,支持静态选项和远程加载。ButtonEdit:按钮字典项,常用于搜索区菜单、自定义操作和上传入口。ComplexList 通过 module 挂载功能模块,每个模块独立管理一类能力:
| 模块 | 类 | 作用 |
|---|---|---|
dictionary | DictionaryData | 字典管理(必填) |
search | SearchData | 检索表单与菜单 |
pagination | PaginationData | 分页参数与总数 |
choice | ChoiceData | 多选状态 |
sort | SortData | 排序参数 |
update | UpdateData | 定时刷新 |
模块通过 installData / uninstallData 安装卸载,触发 _install / _uninstall 钩子。
ComplexList 内置 Life 系统,常用生命周期:
| 名称 | 触发时机 |
|---|---|
beforeLoad | loadData 开始 |
loaded | 加载成功 |
loadFail | 加载失败 |
beforeSearch | setSearch 校验前 |
searched | 检索成功 |
beforeUpdate | 字典更新前 |
reseted | reset 后 |
监听方式:
listData.onLife('loaded', {
id: 'myHandler',
handler: (lifeValue, res) => {
console.log('加载完成', res)
}
})
listData.offLife('loaded', 'myHandler')
下面示例来自真实业务页面 src/pages/index/views/list/base/index.vue 的标准模式:页面创建 ComplexList,配置搜索、字典、分页和业务接口,然后传给 ComplexQuickList 渲染。
<template>
<ComplexQuickList
:list-data="mainData"
:components="['spin', 'search', 'table', 'edit', 'info']"
:simple-table="false"
:components-props="componentsProps"
@search="onSearch"
@table="onTable"
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ButtonEdit, ComplexList, SelectEdit, SelectValue } from '@complex-suite/data'
import { ComplexQuickList, type componentsProps, type tablePayload } from '@complex-suite/component-antd'
import listApi from '@/api/listApi'
export default defineComponent({
name: 'RecordList',
components: {
ComplexQuickList
},
setup() {
const select = new SelectValue({
list: [
{ value: 0, label: '选项1' },
{ value: 1, label: '选项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({})
}
}
})
]
},
list: [
{
prop: 'keyword',
name: '关键字',
mod: {
search: {
$format: 'edit',
type: 'input'
}
}
},
{
prop: 'status',
name: '状态',
mod: {
search: {
$format: 'edit',
type: 'select',
select: select
}
}
}
]
},
dictionary: {
list: [
{
prop: 'id',
name: 'ID',
mod: {
list: { width: 80 },
info: {}
}
},
{
prop: 'name',
name: '名称',
mod: {
list: {},
info: {},
edit: { type: 'input', required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
},
{
prop: 'status',
name: '状态',
mod: {
list: {},
info: {},
edit: { type: 'select', select: select },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
}
]
},
pagination: true
},
getData(this: ComplexList) {
return new Promise((resolve, reject) => {
const postData = {
...this.getSearch(),
page: this.getPage(),
size: this.getPageSize()
}
listApi.baseList.require(postData).then(res => {
this.formatList(res.data.data.list, res.data.data.num)
resolve(res)
}).catch(err => {
reject(err)
})
})
},
buildData(this: ComplexList, targetData) {
return new Promise((resolve, reject) => {
listApi.baseBuild.require(targetData).then(res => {
this.reloadData(true)
resolve(res)
}).catch(err => {
reject(err)
})
})
},
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(err => {
reject(err)
})
})
},
deleteData(this: ComplexList, targetData) {
return new Promise((resolve) => {
const index = this.$list.indexOf(targetData)
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: '名称',
mod: {
list: {},
info: {},
edit: { type: 'input', required: true },
build: { $redirect: 'edit' },
change: { $redirect: 'edit' }
}
}
list:表格列配置,供 TableView 渲染列。info:详情字段配置,供详情区域展示。edit:通用编辑配置。build:新增表单配置,可通过 $redirect: 'edit' 复用编辑配置。change:修改表单配置,可通过 $redirect: 'edit' 复用编辑配置。assign:后端值进入展示/表单前的转换函数。collect:表单值提交接口前的转换函数。module.search 分为菜单和搜索字段两部分。
search: {
menu: {
list: [
'build',
'delete',
new ButtonEdit({
prop: 'custom',
type: 'button',
option: {
name: '自定义按钮',
click() {
return Promise.resolve({})
}
}
})
]
},
list: [
{
prop: 'keyword',
name: '关键字',
mod: {
search: {
$format: 'edit',
type: 'input'
}
}
}
]
}
ComplexQuickList 已内置处理这些菜单:
$search:调用 listData.setSearch(),更新搜索条件并触发列表加载。$reset:调用 listData.resetSearch(),重置搜索条件。$refresh:调用 listData.reloadData({ data: true, sync: true })。$build:打开新增弹窗。$delete:触发批量删除。$info:打开详情弹窗。$export:调用 listData.triggerMethod('exportData')。选择器可以使用静态 SelectValue,也可以在 SelectEdit.getData 中远程加载选项。
{
prop: 'search',
name: '检索框',
mod: {
search: {
$format: 'edit',
type: 'select',
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({})
})
}
}
}
}
如果远程接口有分页结果,可在 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(err => {
reject(err)
})
})
}
常用方法:
loadData(true):首次或强制加载数据。reloadData(true):重新加载数据,通常在新增、修改后调用。formatList(list, total):把接口列表写入 $list,并同步分页总数。getSearch():获取搜索区当前值。getPage() / getPageSize():获取当前分页参数。triggerMethod(method, args, option):通过状态机触发业务方法。ComplexList 支持在初始化配置中定义业务回调,this 绑定到当前实例:
interface ComplexDataInitOption {
module: ModuleDataInitOption
getData?: (this: ComplexList) => Promise<any>
buildData?: (this: ComplexList, targetData) => Promise<any>
changeData?: (this: ComplexList, targetData, originData) => Promise<any>
deleteData?: (this: ComplexList, targetData) => Promise<any>
multipleDeleteData?: (this: ComplexList, choiceList) => Promise<any>
exportData?: (this: ComplexList) => Promise<any>
importData?: (this: ComplexList, file: File) => Promise<any>
}
回调内可直接访问 this.$list、this.reloadData()、this.getSearch() 等。
业务页面通常通过 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:打开详情弹窗。级联列表通常使用 ComplexQuickCascade。主列表通过表格菜单暴露 $sublist,点击后打开子列表弹窗,并由子列表自己的 ComplexList 加载数据。
推荐模式:
const parentData = new ComplexList({
prop: 'parentData',
module: {
dictionary: { list: [] },
pagination: true
},
getData(this: ComplexList) {
return Promise.resolve({})
}
})
const childData = new ComplexList({
prop: 'childData',
module: {
dictionary: { list: [] },
pagination: true
},
getData(this: ComplexList) {
const parentId = this.getExtra('parentId')
return Promise.resolve(parentId)
}
})
const showSubList = function(targetData: any) {
childData.setExtra('parentId', targetData.id)
childData.reloadData(true)
}
ComplexList 作为唯一业务状态入口,UI 组件只消费它,不重复维护列表、分页和搜索状态。module.dictionary.list,通过 list、info、edit、build、change 复用同一字段语义。Promise,包括 getData、buildData、changeData、deleteData、自定义按钮 click 和上传 upload。assign / parse,提交前使用 collect,避免在 UI 组件中散落转换逻辑。$redirect: 'edit' 复用配置,只在字段行为确实不同的时候单独覆盖。component-antd 时,data 只负责无头数据协议,渲染细节交给 ComplexQuickList、TableView、EditArea、InfoArea。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 // 类型为 UserItem[]
@complex-suite/utils:核心工具函数和基类(Life、Watcher)。@complex-suite/plugin:布局和通知系统。FAQs
a complex data
The npm package @complex-suite/data receives a total of 653 weekly downloads. As such, @complex-suite/data popularity was classified as not popular.
We found that @complex-suite/data demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A compromised jscrambler npm release added a malicious preinstall hook that runs hidden native binaries on Linux, macOS, and Windows.

Research
/Security News
A malicious .NET package is typosquatting the Braintree SDK to steal live payment card data, merchant API keys, and host secrets from production apps.

Security News
/Research
Compromised Injective SDK npm version 1.20.21 exfiltrates wallet private keys and mnemonics through fake telemetry functionality.