Skip to content

AdvancedTable

基于FTable封装的表格组件。

  • 统一UI样式,未来若样式变更只需升级组件即可,无需侵入业务
  • 统一交互行为,如表格头部标题处理,按钮处理,分页查询处理
  • 统一编码风格,便于长期维护

FTable区别?

此组件适用于页面表格查询场景,仅简化大量通用配置,比如头部标题和按钮组的处理,底部分页信息的维护等,剥离变量维护与相关交互逻辑关注。
非此场景可直接使用FTable

说明,
后续会根据通用的业务场景继续扩展,如处理ellipsis+tooltip,status等。
排序,过滤等规则也应该固化,以进一步提效,类pagination固化。

提示

鉴于现有业务暂未完全梳理出可固化的场景,因此AdvancedTable也并未进一步扩展更多业务场景,但是未来可能会有一些逐渐固定下来的东西,届时可进一步丰富AdvancedTable的能力。

业务场景

  • 符合UI规范的查询表格场景,多与SearchForm组件组合使用。

如何使用

vue
<script setup lang="ts">
import { h, onMounted } from 'vue'
import { useCommonPage } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  title: '仓库管理列表',
  actions: [
    {
      label: '新增',
      type: 'primary',
      icon: 'icon-tianjia2',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
    {
      label: '导出',
      icon: 'icon-daochu',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
  ],
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
      fixed: 'left',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const query = () => {
  loading.value = true
  api
    .fetchTableList()
    .then((res: any) => {
      advTable.value.setDataSource(res?.list ?? [], res?.totalCount ?? 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleAction = (action: any) => {
  console.log(action)
}
const handleCreate = (id: string) => {
  console.log(id)
}
const handleDelete = (id: string) => {
  console.log(id)
}

onMounted(() => {
  query()
})
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    @query="query"
    @action="handleAction"
  >
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <f-button v-permission="'WMS.Function.Edit'" type="link" @click="handleCreate(record.id)"> 编辑 </f-button>
          <f-button v-permission="['WMS.Function.Delete']" type="link" @click="handleDelete(record.id)">
            删除
          </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

代码示例

基本使用

适用于绝大部分常规业务场景。

<script setup lang="ts">
import { onMounted } from 'vue'
import { message } from '@fs/smart-design'
import { useCommonPage, mockApi } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  title: '仓库管理列表',
  actions: [
    {
      label: '新增',
      type: 'primary',
      icon: 'icon-tianjia2',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
    {
      label: '导出',
      icon: 'icon-daochu',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
  ],
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const query = () => {
  loading.value = true
  mockApi
    .fetchTableList({
      currPage: tableModel.value.pagination.current,
      pageSize: tableModel.value.pagination.pageSize,
    })
    .then((res: any) => {
      advTable.value?.setDataSource(res?.list || [], res?.totalCount || 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleAction = (action: any) => {
  console.log(action)
  message.info(action.label)
}
const handleRowAction = (id: string) => {
  console.log(id)
  message.info(id)
}

onMounted(() => {
  query()
})
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    @query="query"
    @action="handleAction"
  >
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

头部按钮

  • 头部按钮支持权限配置permission,适用于通过权限控制的按钮,也可以同具名插槽,自定义显示隐藏逻辑。
  • 支持type,disabled属性配置,也可通过props透传,API详见基础组件FButton
  • 关于按钮icon,推荐通过配置icon属性实现(即组件库icon+具体业务icon),详见FIconAPI设计。当然也可通过设置iconfont属性实现。
  • 也可通过配置具名插槽,实现自定义内容,如UI差异很大的按钮,或者不仅仅受权限码控制,还需要其他业务逻辑。
<script setup lang="ts">
import { onMounted, computed } from 'vue'
import { message, FIcon, FButton } from '@fs/smart-design'
import { useCommonPage, p, mockApi } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  title: '仓库管理列表',
  actions: [
    {
      label: '新增',
      type: 'primary',
      icon: 'icon-tianjia2',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
    {
      slot: 'export',
    },
    // 自定义插槽
    {
      slot: 'fullScreen',
    },
    {
      slot: 'setColumns',
    },
  ],
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const query = () => {
  loading.value = true
  mockApi
    .fetchTableList({
      currPage: tableModel.value.pagination.current,
      pageSize: tableModel.value.pagination.pageSize,
    })
    .then((res: any) => {
      advTable.value?.setDataSource(res?.list ?? [], res?.totalCount ?? 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleAction = (action: any) => {
  console.log(action)
  message.info(action.label)
}
const handleRowAction = (id: string) => {
  console.log(id)
  message.info(id)
}

onMounted(() => {
  query()
})

// 自定义显示逻辑 如权限code+业务相关
const isAllowExport = computed(() => {
  return p('WMS.Function.WarehouseManagementNew') && p('WMS.Function.WarehouseManagementNew')
})
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    @query="query"
    @action="handleAction"
  >
    <template #export>
      <FButton v-if="isAllowExport">
        <template #icon>
          <FIcon type="icon-daochu" />
        </template>
        导出
      </FButton>
    </template>
    <template #setColumns>
      <FButton shape="circle">
        <template #icon>
          <FIcon type="icon-shezhi1" />
        </template>
      </FButton>
    </template>

    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

完全自定义头部

  • 不设置titleactions表头不会出现。
  • 如果需要其他表头呈现,可通过slot:header实现,也可通过组件组合的方式实现具体业务。
<script setup lang="ts">
import { onMounted } from 'vue'
import { message } from '@fs/smart-design'
import { useCommonPage, mockApi } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  pagination: false,
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const query = () => {
  loading.value = true
  mockApi
    .fetchTableList({
      currPage: tableModel.value.pagination.current,
      pageSize: tableModel.value.pagination.pageSize,
    })
    .then((res: any) => {
      advTable.value?.setDataSource(res?.list || [], res?.totalCount || 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleAction = (action: any) => {
  console.log(action)
  message.info(action.label)
}

const handleRowAction = (id: string) => {
  console.log(id)
  message.info(id)
}

onMounted(() => {
  query()
})
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    @query="query"
    @action="handleAction"
  >
    <template #header>
      <div>完全自定义header</div>
    </template>
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

直接传入dataSource

可通过直接设置dataSource,即和FTableAPI保持一致,数据源交由父组件。

<script setup lang="ts">
import { useCommonPage } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  pagination: false,
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const dataSource = [
  {
    id: 0.1435498553846657,
    code: 'CS0',
    localName: {
      zh_CN: '仓库0',
      en: 'warehouse0',
    },
    description: '描述信息~~0~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.47271867926452393,
    code: 'CS1',
    localName: {
      zh_CN: '仓库1',
      en: 'warehouse1',
    },
    description: '描述信息~~1~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.026548785827699595,
    code: 'CS2',
    localName: {
      zh_CN: '仓库2',
      en: 'warehouse2',
    },
    description: '描述信息~~2~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.1810888184874626,
    code: 'CS3',
    localName: {
      zh_CN: '仓库3',
      en: 'warehouse3',
    },
    description: '描述信息~~3~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.8185781857135921,
    code: 'CS4',
    localName: {
      zh_CN: '仓库4',
      en: 'warehouse4',
    },
    description: '描述信息~~4~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.724627050725462,
    code: 'CS5',
    localName: {
      zh_CN: '仓库5',
      en: 'warehouse5',
    },
    description: '描述信息~~5~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.6569273174448551,
    code: 'CS6',
    localName: {
      zh_CN: '仓库6',
      en: 'warehouse6',
    },
    description: '描述信息~~6~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.9928902310845029,
    code: 'CS7',
    localName: {
      zh_CN: '仓库7',
      en: 'warehouse7',
    },
    description: '描述信息~~7~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.028812088717129924,
    code: 'CS8',
    localName: {
      zh_CN: '仓库8',
      en: 'warehouse8',
    },
    description: '描述信息~~8~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
  {
    id: 0.1844870850704896,
    code: 'CS9',
    localName: {
      zh_CN: '仓库9',
      en: 'warehouse9',
    },
    description: '描述信息~~9~~',
    createdBy: '',
    createdAt: null,
    updatedBy: '潘伟(Buck.Pan)',
    updatedAt: '2023-11-28T09:56:18',
  },
]
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    :dataSource="dataSource"
  >
    <template #bodyCell="{ column }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

可勾选

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { message } from '@fs/smart-design'
import { useCommonPage, mockApi } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  title: '仓库管理列表',
  actions: [
    {
      slot: 'export',
    },
    {
      label: '新增',
      type: 'primary',
      icon: 'icon-tianjia2',
      // permission: 'WMS.Function.WarehouseManagementNew',
    },
  ],
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const query = () => {
  loading.value = true
  mockApi
    .fetchTableList({
      currPage: tableModel.value.pagination.current,
      pageSize: tableModel.value.pagination.pageSize,
    })
    .then((res: any) => {
      advTable.value?.setDataSource(res?.list ?? [], res?.totalCount ?? 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleRowAction = (id: string) => {
  console.log(id)
  message.info(id)
}

onMounted(() => {
  query()
})

// 表格勾选配置项
const selectedRowKeys = ref<string[]>([])

// 表格勾选
function onSelectChange(keys: string[], selectedRows: object[]) {
  console.log(keys, selectedRows)
  selectedRowKeys.value = keys
}
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    :row-selection="{ selectedRowKeys, onChange: onSelectChange }"
    @query="query"
  >
    <template #export>
      <f-button :disabled="selectedRowKeys.length <= 0">
        <template #icon>
          <FIcon type="icon-daochu" />
        </template>
        导出
      </f-button>
    </template>
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

可展开

<script setup lang="ts">
import { onMounted, getCurrentInstance } from 'vue'
import { message } from '@fs/smart-design'
import { useCommonPage } from '@fs/lib'

const { loading, advTable, tableModel } = useCommonPage()

// 表格配置
const tabConfig = {
  title: '仓库管理列表',
  columns: [
    {
      title: '编码',
      dataIndex: 'code',
    },
    {
      title: '名称',
      dataIndex: 'localName',
      customRender: ({ text }: any) => {
        return text?.[`zh_CN`]
      },
    },
    {
      title: '邮箱',
      dataIndex: 'contactEmail',
    },
    {
      title: '操作',
      key: 'operation',
      fixed: 'right',
      // permission: ['WMS.Function.Edit', 'WMS.Function.Delete'],
    },
  ],
}

const $api = getCurrentInstance()?.appContext.config.globalProperties.$api
const query = () => {
  loading.value = true
  $api
    .fetchTableList({
      currPage: tableModel.value.pagination.current,
      pageSize: tableModel.value.pagination.pageSize,
    })
    .then((res: any) => {
      advTable.value?.setDataSource(res?.list || [], res?.totalCount || 0)
    })
    .finally(() => {
      loading.value = false
    })
}

const handleAction = (action: any) => {
  console.log(action)
  message.info(action.label)
}

const handleRowAction = (id: string) => {
  console.log(id)
  message.info(id)
}

onMounted(() => {
  query()
})

const handleExpand = (expanded: boolean, record: any) => {
  console.log(expanded, record)
}
</script>

<template>
  <AdvancedTable
    ref="advTable"
    v-model="tableModel"
    :config="tabConfig"
    :loading="loading"
    :scroll="null"
    @query="query"
    @action="handleAction"
    @expand="handleExpand"
  >
    <template #expandedRowRender="{ record }">
      {{ record.description }}
    </template>
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'operation'">
        <f-space :size="12">
          <!-- v-permission="'WMS.Function.Edit'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 编辑 </f-button>
          <!-- v-permission="'WMS.Function.Delete'" -->
          <f-button type="link" @click="handleRowAction(record.id)"> 删除 </f-button>
        </f-space>
      </template>
    </template>
  </AdvancedTable>
</template>

API

参数说明类型默认值版本
value(v-model)表格响应式内容object--
config表格配置IAdvancedTableConfig--

说明,
config类型约束如下,其余属性和事件可透传。

ts
export interface IAdvancedTableConfig {
  title?: string // 标题
  actions?: IAdvancedTableAction[] // 头部操作安妮
  columns: Record<string, any>[] // columns
  pagination?: boolean | Record<string, any> // 分页器 参考分页器配置
  rowKey?: string | ((record: Record<string, any>) => string) // 注意唯一性
}
export interface IAdvancedTableAction {
  label: string
  type?: string
  disabled?: boolean
  icon?: string // icon type fs-icon smart-design基础组件库
  iconfont?: string // iconfont 业务iconfont
  props?: Record<string, any>
  slot?: string | ((action: IAdvancedTableAction) => string) // 动态slot
  permission?: string | string[] // 权限
}
export interface IAdvancedTableProps {
  modelValue?: Record<string, any>
  loading?: boolean
  dataSource?: Record<string, any>[] // 数据
  config: IAdvancedTableConfig
}

说明,

  • 拓展了官方column.ellipsis属性,true为一行省略,number时,最多支持3行省略。

插槽

  • header
  • action.slot

事件

事件名说明回调参数版本
query触发表格查询,如分页操作,排序操作function(model: object)-
change分页、排序、筛选变化时触发function(pagination, filters, sorter, { currentDataSource })-
action表格头部按钮组点击回调function(action: any)-
init表格初始化时回调function(modelValue: any)-

说明:

  • 一般情况下通过query可以接收到表格查询相关的条件,如分页,排序,筛选等,基于AntdTable的change事件处理
  • change事件与AntdTable保持一致,用以降低迁移成本

方法

方法名说明回调参数版本
setDataSource写入datasfunction(datas: Record<string, any>[], total: number)-
getDataSource获取datasfunction(): Record<string, any>[]-