Skip to content
Return to top
On this page

# Fat Table #

`FatTable` 用于快速创建一个表格页面。

## 1. 快速创建一个表格页面 #

查看代码
tsx

    import { defineFatTable } from '@wakeadmin/components';
    import { ElButton } from 'element-plus';

    enum ItemStatus {
      Initial,
      Pending,
      Done,
    }

    /**
     * 列表项定义
     */
    interface Item {
      id: number;
      name: string;
      status: ItemStatus;
      createdDate: number;
    }

    /**
     * 表单查询
     */
    interface Query {
      name: string;
      status: ItemStatus;
    }

    export default defineFatTable<Item, Query>(({ column }) => {
      return () => ({
        title: 'Hello',
        rowKey: 'id',
        async request(params) {
          const { pagination, query } = params;

          // 模拟请求
          const mockData: Item[] = new Array(pagination.pageSize).fill(0).map((_, idx) => {
            const r = Math.floor(Math.random() * 1000);
            return {
              id: idx,
              name: `${r}-${pagination.page}-${query?.name ?? ''}`,
              status: r % 3 === 0 ? ItemStatus.Pending : r % 2 === 0 ? ItemStatus.Initial : ItemStatus.Done,
              createdDate: Date.now(),
            };
          });

          return {
            list: mockData,
            total: 100,
          };
        },
        renderNavBar() {
          return (
            <span>
              <ElButton type="primary">创建</ElButton>
            </span>
          );
        },
        columns: [
          column({
            prop: 'name',
            label: '名称',
            queryable: true,
            valueType: 'search',
          }),
          column({
            prop: 'status',
            label: '状态',
            queryable: true,
            valueType: 'select',
            valueProps: {
              options: [
                { label: '未开始', value: ItemStatus.Initial, color: 'blue' },
                { label: '正在进行', value: ItemStatus.Pending, color: 'green' },
                { label: '已结束', value: ItemStatus.Done, color: 'gray' },
              ],
            },
          }),
          column({
            prop: 'createdDate',
            label: '创建时间',
            valueType: 'date-time',
          }),
          column({
            type: 'actions',
            label: '操作',
            actions: (table, row) => {
              const ended = row.status === ItemStatus.Done;
              const pending = row.status === ItemStatus.Pending;

              return [
                {
                  name: pending ? '结束' : '开始',
                  visible: !ended,
                },
                {
                  name: '删除',
                  type: 'danger',
                  onClick: () => table.remove(row),
                },
              ];
            },
          }),
        ],
      });
    });

## 2. defineFatTable（推荐） #

我们推荐使用 `defineFatTable` + `TSX` 来快速定义一个表格组件，使用 defineFatTable 可以获取到更好的智能提示和类型检查。

`defineFatTable` 大致用法如下：

tsx

    interface T {
      // 列表项类型声明
    }

    interface Q {
      // 表单查询类型声明
    }

    export const MyTable = defineFatTable<T, Q>(({ table, column }) => {
      // 和 vue 的 setup 方法一样, 这里可以放置 Vue Composition API
      const someRef = ref(0);
      const someMethod = () => {};

      // 返回 FatTable props
      return () => ({
        // 列表请求
        async request(params) {
          // ...
        },
        // 列定义
        columns: [
          // ...
        ],
        // ... 其他 FatTable props
      });
    });

defineFatTable 类似于 Vue 的 defineComponent, 支持放置 Vue Hooks，只不过要求返回的是 FatTable 的 props 定义。

同样的功能使用 template 来写, 会丢失上下文信息(vue 组件不支持`泛型`)：

vue

    <template>
      <FatTable :request="request" :columns="columns" ref="tableRef"> </FatTable>
    </template>

    <script setup>
      import { ref } from 'vue';
      import { FatTable, useFatTableRef } from '@wakeadmin/components';
      const someRef = ref(0);
      const someMethod = () => {};

      const tableRef = useFatTableRef();

      const request = async () => {
        // ...
      };

      const columns = [
        /* ... */
      ];
    </script>

显然 defineFatTable 可以让你更关注 FatTable 本身的配置。

## 3. 原件 #

原件是 FatTable 的’原子‘组成单位，表格的单元格、查询表单都使用原件进行声明。

我们的组件库针对常用的场景内置了很多原件，如果这些原件不能满足你的需求，我们也支持传入自定义原件。

单元格中默认使用的是原件的`预览形态`, 而查询表单中使用的是`编辑形态`。 以下是部分原件的使用示例:

查看代码
tsx

    import { defineFatTable } from '@wakeadmin/components';

    export default defineFatTable(({ column }) => {
      return () => ({
        title: '原件示例',
        async request() {
          return {
            total: 1,
            list: [
              {
                text: '文本, 默认原件',
                status: 0,
                date: Date.now(),
                progress: 50,
                currency: 1024,
                image:
                  'https://images.unsplash.com/photo-1663774026607-6836c94f93e6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=927&q=80',
              },
            ],
          };
        },
        enablePagination: false,
        columns: [
          column({
            prop: 'text',
            label: '文本',
            // 同时作为查询表单，
            queryable: true,
            valueProps: { placeholder: '搜索关键字' },
          }),
          column({
            prop: 'status',
            label: '下拉列表',
            queryable: true,
            valueType: 'select',
            valueProps: {
              colorMode: 'dot',
              options: [
                { label: '选项1', value: 0, color: 'red' },
                { label: '选项2', value: 1, color: 'blue' },
              ],
            },
          }),
          column({
            prop: 'date',
            label: '时间',
            queryable: true,
            valueType: 'date',
          }),
          column({
            prop: 'progress',
            label: '进度',
            valueType: 'progress',
          }),
          column({ prop: 'currency', label: '货币', valueType: 'currency' }),
          column({ prop: 'image', label: '图片', valueType: 'image' }),
        ],
      });
    });

## 4. 表单查询 #

大部分场景下，`查询表单字段`和`表格列`是匹配的，换句话说，表单筛选是针对表格的列进行的：

基于这个前提，我们可以利用原件的`预览态`和`编辑态` 来快速开发表单页面。比如上图，columns 代码如下：

tsx

    [
      column({ prop: 'name', name: '旅程名称', queryable: true }), // 🔴 queryable 表示该列同时作为查询字段
      column({ prop: 'enterUserNumber', name: '进入人数' }),
      column({ prop: 'enterNumber', name: '进入次数' }),
      column({
        prop: 'type',
        name: '旅程类型',
        valueType: 'select',
        valueProps: { options: [{ label: '实时触发', value: 0 } /*...*/] },
        order: 100, // 🔴  可以使用 order 调整查询表单的顺序，默认为 1000， 值越小，越靠前
      }),
      column({
        prop: 'status',
        name: '旅程状态',
        valueType: 'select',
        valueProps: { options: [{ label: '草稿', value: 0, color: 'red' } /*...*/] },
      }),
      column({
        prop: 'updateTime',
        name: '更新时间',
      }),
      column({
        prop: 'createTime',
        name: '创建时间',
        valueType: 'date-time-range',
        valueProps: {
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
        },
      }),
      column({
        type: 'actions',
        actions: [
          /*...*/
        ],
      }),
      column({
        type: 'query', // 🔴  只作为查询表单，不作为表格列
        valueType: 'checkbox',
        valueProps: {
          label: '预警旅程',
        },
      }),
    ];

## 5. 操作按钮 #

FatTable 中通过 actions 来定义表格的操作:

查看代码
tsx

    import { defineFatTable } from '@wakeadmin/components';
    import { reactive } from 'vue';

    import ActionsForm from './ActionsForm';

    export default defineFatTable(() => {
      const initialValue = reactive({
        disableDelete: false,
        editable: true,
      });

      return () => ({
        title: '表格操作',
        async request() {
          return { list: [{ id: 0 }], total: 1 };
        },
        renderAfterForm() {
          return <ActionsForm initialValue={initialValue} />;
        },
        enablePagination: false,
        rowKey: 'id',
        async remove(list) {
          // 在这里进行表格删除数据请求
        },
        columns: [
          {
            type: 'actions',
            label: '操作',
            // 支持传入一个函数，常用于一些需要动态计算的场景
            actions: () => {
              return [
                {
                  name: '标题',
                  title: '提示信息',
                },
                {
                  name: '危险',
                  type: 'danger',
                },
                {
                  name: '警告',
                  type: 'warning',
                  confirm: '可以设置确认信息',
                  onClick: () => {
                    console.log('点击了警告操作');
                  },
                },
                {
                  name: '删除',
                  type: 'danger',
                  // 禁用
                  disabled: initialValue.disableDelete,
                  // FatTable 内置了删除功能
                  onClick: (table, row) => {
                    table.remove(row);
                  },
                },
                {
                  name: '编辑',
                  // 支持 vue router
                  visible: initialValue.editable,
                  link: { name: 'someRoute', query: { id: 'someId' } },
                },
              ];
            },
          },
          {
            type: 'actions',
            label: '按钮操作',
            actionsType: 'button',
            actions: () => {
              return [{ name: '快乐' }, { name: '星球' }];
            },
          },
        ],
      });
    });

## 6. 批量操作按钮 #

和操作按钮类似， FatTable 也支持快速创建批量操作按钮:

查看代码
tsx

    import { defineFatTable } from '@wakeadmin/components';
    import { reactive } from 'vue';

    import ActionsForm from './ActionsForm';

    export default defineFatTable(() => {
      const initialValue = reactive({
        disableDelete: false,
        editable: true,
      });

      return () => ({
        title: '表格操作',
        async request() {
          return {
            list: new Array(10).fill(0).map((_, idx) => {
              return { id: idx };
            }),
            total: 10,
          };
        },
        rowKey: 'id',
        async remove(list) {
          // 在这里进行表格删除数据请求
        },
        enableSelect: true,
        batchActions: table => [
          {
            name: '删除',
            onClick: table.removeSelected,
          },
          {
            name: '导出',
            confirm: '确认导出',
            onClick: () => console.log('导出操作'),
          },
        ],
        columns: [
          {
            label: '快乐',
            prop: 'id',
          },
        ],
      });
    });

## 7. 插槽 #

FatTable 提供了丰富的插槽，用于满足复杂的自定义需求：

上图，红色矩形部分为 FatTable 提供的插槽。 插槽渲染有两种使用方式：

1. 在 Vue template 里面使用, 例如

vue

    <template>
      <FatTable>
        <template #toolbar>
          <el-button>删除</el-button>
        </template>
      </FatTable>
    </template>

2. 使用 `render*` 方法。使用 defineFatTable 时，用这种方式比较合适：

tsx

    defineFatTable({
      renderToolbar() {
        return <ElButton>删除</ElButton>;
      },
    });

示例：

查看代码
tsx

    import { ElButton } from 'element-plus';
    import { defineFatTable } from '@wakeadmin/components';

    export default defineFatTable(() => {
      return () => ({
        title: '表格插槽',
        async request() {
          return {
            list: new Array(10).fill(0).map((_, idx) => {
              return { id: idx };
            }),
            total: 10,
          };
        },
        renderToolbar() {
          return (
            <div>
              <ElButton>批量操作</ElButton>
              <ElButton>批量操作</ElButton>
            </div>
          );
        },
        renderBottomToolbar() {
          return (
            <div>
              <ElButton>批量操作</ElButton>
              <ElButton>批量操作</ElButton>
            </div>
          );
        },
        columns: [
          {
            label: '快乐',
            prop: 'id',
          },
        ],
      });
    });

## 8. 自定义布局 #

FatTable 默认使用惟客云 UI 规范的布局。你也可以自定义布局，布局协议如下：

ts

    export type FatTableLayout = (slots: {
      /**
       * 根节点属性
       */
      rootProps: { class?: ClassValue; style?: StyleValue; [key: string]: unknown };

      /**
       * 自定义布局参数
       */
      layoutProps: any;

      /**
       * 渲染标题栏
       */
      renderTitle?: () => any;

      /**
       * 渲染导航栏
       */
      renderNavBar?: () => any;

      /**
       * 渲染查询表单
       */
      renderQuery?: () => any;

      /**
       * 渲染错误提示
       */
      renderError?: () => any;

      /**
       * 渲染工具栏
       */
      renderToolbar?: () => any;

      /**
       * 渲染表格
       */
      renderTable?: () => any;

      /**
       * 渲染底部工具栏
       */
      renderBottomToolbar?: () => any;

      /**
       * 渲染分页
       */
      renderPagination?: () => any;
    }) => VNodeChild;

默认实现：

tsx

    const DefaultLayout: FatTableLayout = props => {
      return (
        <FatContainer
          {...props.rootProps}
          class={normalizeClassName(props.rootProps.class, 'fat-table', 'fat-table--default')}
          v-slots={{ title: props.renderTitle, extra: props.renderNavBar, query: props.renderQuery }}
        >
          <div class="fat-table__body">
            {!!props.renderError && <div class="fat-table__error">{props.renderError()}</div>}
            {!!props.renderToolbar && <div class="fat-table__toolbar">{props.renderToolbar()}</div>}

            <div class="fat-table__table">{props.renderTable?.()}</div>
          </div>

          <div class="fat-table__footer">
            {!!props.renderBottomToolbar && <div class="fat-table__bottom-toolbar">{props.renderBottomToolbar()}</div>}
            {!!props.renderPagination && <div class="fat-table__pagination">{props.renderPagination()}</div>}
          </div>
        </FatContainer>
      );
    };

TIP

当插槽不存在时会传入 `undefined`，你可以根据这个决定要不要渲染包裹器

TIP

可以配合 `FatConfigurableProvider` 实现全局配置。

## 9. API #

### 9.1 FatTable 属性 #

### 9.2 FatTable 事件 #

### 9.3 FatTable 实例方法 #

`FatTable` 实例方式获取有两种方式：

1. `defineFatTable` 函数参数 table 属性中获取：

   ts

       defineFatTable(({ table }) => {});

2. `<template>` 中，使用 `useFatTableRef`:

   vue

       <template>
         <FatTable ref="tableRef">...</FatTable>
       </template>

       <script setup lang="tsx">
         import { FatTable, useFatTableRef } from '@wakeadmin/components';

         const tableRef = useFatTableRef();
       </script>

### 9.4 FatTable 插槽 #

### 9.5 列定义 #