Skip to content
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: '预警旅程',
    },
  }),
];


TIP

FatTable 的表单底层使用 FatForm 渲染,你可以通过 formProps 深入定义它的行为,比如修改提交按钮文案等等.





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: '星球' }];
        },
      },
      {
        type: 'actions',
        label: '无操作',
        actions: [],
      },
    ],
  });
});



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',
      },
    ],
  });
});



表格设置

查看代码
tsx
import { defineFatTable } from '@wakeadmin/components';
import { reactive } from 'vue';

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

  return () => ({
    title: '表格设置',
    async request() {
      return {
        list: new Array(10).fill(0).map((_, idx) => {
          return { id: idx, name: `name-${idx}` };
        }),
        total: 10,
      };
    },
    rowKey: 'id',
    async remove(list) {
      // 在这里进行表格删除数据请求
    },
    enableSelect: true,
    enableSetting: true,
    settingProps: {
      persistentKey: 'hello-world',
    },
    renderToolbar() {
      return <div style={{ marginRight: '10px' }}>custom toolbar</div>;
    },
    batchActions: table => [
      {
        name: '删除',
        onClick: table.removeSelected,
      },
      {
        name: '导出',
        confirm: '确认导出',
        onClick: () => console.log('导出操作'),
      },
    ],
    columns: [
      column({
        queryable: true,
        label: 'ID',
        prop: 'id',
      }),
      column({
        label: '名称',
        prop: 'name',
      }),
      column({
        label: '创建时间',
        prop: 'createdAt',
      }),
      column({
        type: 'query',
        prop: 'start',
        // 关联到 createdAt 字段的设置
        columnKey: 'createdAt',
        label: '开始时间',
        valueType: 'date',
      }),
      column({
        type: 'query',
        prop: 'end',
        // 关联到 createdAt 字段的设置
        columnKey: 'createdAt',
        label: '结束时间',
        valueType: 'date',
      }),
      // 不受控制
      column({
        type: 'query',
        prop: 'fake',
        label: '额外请求条件',
      }),
      column({
        type: 'actions',
        label: '操作',
        actions: [
          {
            name: '编辑',
          },
        ],
      }),
    ],
  });
});




7. 插槽


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


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

  1. 在 Vue template 里面使用, 例如
vue
<template>
  <FatTable>
    <template #toolbar>
      <el-button>删除</el-button>
    </template>
  </FatTable>
</template>

  1. 使用 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 规范的布局。你也可以 layout 属性自定义布局,布局协议如下:

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}
      {...props.layoutProps}
      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,你可以根据这个决定要不要渲染包裹器


WARNING

默认布局使用的是 FatContainer, 在惟客云微前端底座下,FatContainer 默认复用基座提供的 wkc-header。如果要关闭这个行为,可以通过 layoutProps#reuseBayIfNeed 关闭掉


TIP

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





9. API

9.1 FatTable 属性





9.2 FatTable 事件




9.3 FatTable 实例方法


FatTable 实例方式获取有两种方式:

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

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

  1. <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 列定义