🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@complex-suite/data

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@complex-suite/data

a complex data

Source
npmnpm
Version
5.0.11
Version published
Weekly downloads
686
-2.28%
Maintainers
1
Weekly downloads
 
Created
Source

@complex-suite/data 快速使用

@complex-suite/data 是 complex-suite 的核心数据层。它不负责渲染 UI,而是负责描述业务页面的数据结构、搜索条件、字典字段、分页、选择、编辑、详情、异步加载和业务操作。

在当前 monorepo 中,它位于依赖链中间:utils → plugin → data → component → component-antd。实际业务页面通常通过 data 创建无头数据模型,再交给 component-antdComplexQuickListComplexQuickCascade 等组件渲染。

安装

pnpm add @complex-suite/data

核心概念

  • ComplexList:列表页主数据模型,维护 $list、加载状态、搜索、分页、选择、字典和增删改查方法。
  • ComplexInfo:详情页数据模型,维护 $info,常用于详情弹窗或详情页。
  • module.dictionary:字段字典,统一描述表格列、详情字段、编辑表单、创建表单和搜索字段。
  • module.search:搜索区配置,包含搜索字段和顶部菜单按钮。
  • module.pagination:分页模块,提供 getPage()getPageSize()setPageCount() 等能力。
  • SelectValue / SelectEdit:选择器数据源与选择器字典项,支持静态选项和远程加载。
  • ButtonEdit:按钮字典项,常用于搜索区菜单、自定义操作和上传入口。

模块系统

ComplexList 通过 module 挂载功能模块,每个模块独立管理一类能力:

模块作用
dictionaryDictionaryData字典管理(必填)
searchSearchData检索表单与菜单
paginationPaginationData分页参数与总数
choiceChoiceData多选状态
sortSortData排序参数
updateUpdateData定时刷新

模块通过 installData / uninstallData 安装卸载,触发 _install / _uninstall 钩子。

生命周期

ComplexList 内置 Life 系统,常用生命周期:

名称触发时机
beforeLoadloadData 开始
loaded加载成功
loadFail加载失败
beforeSearchsetSearch 校验前
searched检索成功
beforeUpdate字典更新前
resetedreset

监听方式:

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,这样状态模块才能正确维护 loadoperate 等状态。

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.$listthis.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,通过 listinfoeditbuildchange 复用同一字段语义。
  • 所有业务回调都返回 Promise,包括 getDatabuildDatachangeDatadeleteData、自定义按钮 click 和上传 upload
  • 接口返回值进入表单前使用 assign / parse,提交前使用 collect,避免在 UI 组件中散落转换逻辑。
  • 新增和修改表单大多可通过 $redirect: 'edit' 复用配置,只在字段行为确实不同的时候单独覆盖。
  • 使用 component-antd 时,data 只负责无头数据协议,渲染细节交给 ComplexQuickListTableViewEditAreaInfoArea

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  // 类型为 UserItem[]

依赖

  • @complex-suite/utils:核心工具函数和基类(Life、Watcher)。
  • @complex-suite/plugin:布局和通知系统。

Keywords

complex

FAQs

Package last updated on 03 Jul 2026

Did you know?

Socket

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.

Install

Related posts