Skip to content

表格与列表体系

列表页通常是中后台项目数量最多、维护时间最长的一类页面。Art Design Pro X 把请求、分页、搜索、列配置和展示组件拆开,目的是让业务页面专注字段和操作,而不是重复编写加载状态与分页逻辑。

组件关系

text
业务页面 index.vue
├── *-search.vue
│   └── ArtSearchBar
├── ArtTableHeader
├── ArtTable
└── useTable
    ├── apiFn: src/mock 或 src/api
    ├── 搜索参数
    ├── 分页状态
    ├── 响应适配
    ├── 列配置 useTableColumns
    └── 刷新与可选缓存

主要入口:

能力文件
列表状态src/hooks/core/useTable.ts
列配置src/hooks/core/useTableColumns.ts
搜索栏src/components/core/forms/art-search-bar
表格主体src/components/core/tables/art-table
表格工具栏src/components/core/tables/art-table-header
全局表格映射src/utils/table
列偏好状态src/store/modules/table.ts
标准参考页面src/views/system/user

标准页面结构

vue
<template>
  <div class="art-full-height">
    <UserSearch
      v-model="searchForm"
      :role-list="roleList"
      @search="handleSearch"
      @reset="handleReset"
    />

    <ElCard class="art-table-card" shadow="never">
      <ArtTableHeader
        v-model:columns="columnChecks"
        :loading="loading"
        @refresh="refreshData"
      >
        <template #left>
          <ElButton v-auth="'add'" type="primary" @click="showDialog('add')">
            新增用户
          </ElButton>
        </template>
      </ArtTableHeader>

      <ArtTable
        :loading="loading"
        :data="data"
        :columns="columns"
        :pagination="pagination"
        @pagination:size-change="handleSizeChange"
        @pagination:current-change="handleCurrentChange"
      />
    </ElCard>
  </div>
</template>

art-full-heightart-table-card 负责让搜索区与表格区形成稳定的全高布局。不要再给表格外层嵌套多层卡片,否则容易出现高度计算和滚动条问题。

配置 useTable

ts
const {
  columns,
  columnChecks,
  data,
  loading,
  pagination,
  replaceSearchParams,
  resetSearchParams,
  handleSizeChange,
  handleCurrentChange,
  refreshData
} = useTable({
  core: {
    apiFn: getDemoUserList,
    apiParams: {
      current: 1,
      size: 20,
      ...searchForm.value
    },
    columnsFactory: () => [
      { type: 'selection' },
      { type: 'index', width: 60, label: '序号' },
      { prop: 'username', label: '用户名' },
      { prop: 'createdAt', label: '创建时间' },
      {
        prop: 'operation',
        label: '操作',
        width: 120,
        fixed: 'right'
      }
    ]
  }
})

核心配置:

  • apiFn:返回 Promise 的列表函数,可以来自 src/mocksrc/api
  • apiParams:初始分页和搜索参数。
  • columnsFactory:返回列配置,适合依赖权限、语言或响应式状态的列。
  • paginationKey:单页覆盖请求分页字段。
  • responseAdapter:单页覆盖响应结构解析。
  • 数据转换与缓存等高级能力按页面需要开启,不建议默认堆叠配置。

搜索参数

搜索组件通过 v-model 维护表单值,并向页面发出 searchreset

ts
const handleSearch = async (params: UserSearchFormParams) => {
  const { daterange, ...filters } = params
  const [startTime, endTime] = Array.isArray(daterange)
    ? daterange
    : [undefined, undefined]

  await replaceSearchParams({
    ...filters,
    startTime,
    endTime
  })
}

使用 replaceSearchParams 而不是直接修改内部参数,可以保证搜索后页码回到正确位置并触发统一查询。

重置时同步清空页面表单:

ts
const handleReset = () => {
  Object.assign(searchForm.value, {
    username: undefined,
    role: undefined,
    daterange: undefined
  })

  resetSearchParams()
}

分页字段

项目默认请求字段为:

json
{
  "current": 1,
  "size": 20
}

常见响应字段可以映射为:

语义常见字段
列表recordslistdataitemsrows
总数totalcount
当前页currentpagepageNum
每页数量sizepageSizelimit

只有一个接口格式特殊时,在页面使用 paginationKeyresponseAdapter。当客户全部列表都遵循同一格式时,再修改 src/utils/table 中的全局配置。

列配置

列可以定义:

  • 普通字段与标题。
  • 选择列、序号列。
  • 固定宽度和左右固定。
  • 排序。
  • formatter 自定义内容。
  • 表头插槽和表头搜索。
  • 操作列中的权限按钮。
ts
{
  prop: 'roles',
  label: '关联角色',
  formatter: (row) =>
    row.roles?.length
      ? row.roles.map((item) => item.name).join(' / ')
      : '-'
}

formatter 应保持无副作用,不要在格式化过程中修改行数据或发起请求。

表头搜索与历史

表头搜索适合高频、单字段过滤,但不要与顶部搜索栏重复堆叠所有条件。搜索历史默认关闭,只有满足以下条件时才开启:

  • 字段不包含手机号、证件号、邮箱、账号、订单号等敏感或可识别信息。
  • 配置稳定的 historyKey
  • 明确使用标准历史策略。
  • 已验证账号切换和登出时不会串数据。

从预置数据迁移到 API

迁移前:

ts
import { getDemoUserList } from '@/mock/system/organization'

迁移后:

ts
import { fetchUserList } from '@/api/customer/user'

然后只修改:

ts
apiFn: fetchUserList

为了做到这一点,API 应尽量保持:

  • 相同的查询参数类型。
  • 相同的分页语义。
  • 相同的列表项类型。
  • 错误通过 Promise 抛出。

写操作与刷新

接口接入后,新增或编辑成功通常执行:

ts
await saveUser(payload)
dialogVisible.value = false
await refreshData()

注意:

  • 提交期间禁用确认按钮,避免重复请求。
  • 接口失败时不要关闭弹窗。
  • 删除最后一条数据时处理当前页回退。
  • 批量操作完成后清理选择状态。
  • 本地数据阶段继续使用边界提示,不要伪造刷新后的持久化结果。

常见问题

接口成功但表格为空

检查统一请求层是否已经返回 data,以及列表字段能否被当前响应适配器识别。不要把完整 Axios Response 直接交给 useTable

搜索后仍停留在后面的页码

使用 replaceSearchParams,不要只修改查询对象后调用普通刷新。

表格出现双滚动条

检查页面是否使用 art-full-height,表格卡片是否处于正确的弹性布局,以及外层是否额外设置固定高度或多层卡片。

列设置在不同用户之间串用

检查表格偏好键和用户隔离逻辑。包含敏感字段的列和搜索历史不要默认持久化。

useTable、列配置和搜索历史的组合边界见Hooks 与组合式能力,表格相关公共视图见通用组件

根据 MIT 许可证发布