# Table 表格

对 `antv` 的 table 组件进行封装

> 如果文档内没有，可以尝试在在线示例内寻找

## 代码演示

### 基础表格

<CodePreview src="/doc-comp/table/basic" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable
      title="基础示例"
      titleHelpMessage="温馨提醒"
      :columns="columns"
      :dataSource="data"
      :canResize="canResize"
      :loading="loading"
      :striped="striped"
      :bordered="border"
      showTableSetting
      :pagination="pagination"
      @columns-change="handleColumnChange"
    >
      <template #toolbar>
        <a-button type="primary" @click="toggleCanResize">
          {{ !canResize ? '自适应高度' : '取消自适应' }}
        </a-button>
        <a-button type="primary" @click="toggleBorder">
          {{ !border ? '显示边框' : '隐藏边框' }}
        </a-button>
        <a-button type="primary" @click="toggleLoading"> 开启loading </a-button>
        <a-button type="primary" @click="toggleStriped">
          {{ !striped ? '显示斑马纹' : '隐藏斑马纹' }}
        </a-button>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, ColumnChangeParam } from '@eciol/ant-ui';

  import { getBasicColumns, getBasicData } from './tableData';

  const canResize = ref(false);
  const loading = ref(false);
  const striped = ref(true);
  const border = ref(true);
  const pagination = ref<any>(false);
  function toggleCanResize() {
    canResize.value = !canResize.value;
  }
  function toggleStriped() {
    striped.value = !striped.value;
  }
  function toggleLoading() {
    loading.value = true;
    setTimeout(() => {
      loading.value = false;
      pagination.value = { pageSize: 20 };
    }, 3000);
  }
  function toggleBorder() {
    border.value = !border.value;
  }

  function handleColumnChange(data: ColumnChangeParam[]) {
    console.log('ColumnChanged', data);
  }
  const columns = getBasicColumns();
  const data = getBasicData();
</script>
```

</details>
</CodePreview>

### 树形表格

<CodePreview src="/doc-comp/table/treeTable" :height="900">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="register">
      <template #toolbar>
        <a-button type="primary" @click="expandAll">展开全部</a-button>
        <a-button type="primary" @click="collapseAll">折叠全部</a-button>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { getBasicColumns, getTreeTableData } from './tableData';

  const [register, { expandAll, collapseAll }] = useTable({
    title: '树形表格',
    isTreeTable: true,
    rowSelection: {
      type: 'checkbox',
      getCheckboxProps(record: Recordable) {
        // Demo: 第一行（id为0）的选择框禁用
        if (record.id === '0') {
          return { disabled: true };
        } else {
          return { disabled: false };
        }
      },
    },
    titleHelpMessage: '树形组件不能和序列号列同时存在',
    columns: getBasicColumns(),
    dataSource: getTreeTableData(),
    rowKey: 'id',
  });
</script>
```

</details>
</CodePreview>

### 远程加载示例

<CodePreview src="/doc-comp/table/fetchTable" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper contentBackground contentClass="flex" dense contentFullHeight fixedHeight>
    <BasicTable @register="registerTable">
      <template #toolbar>
        <a-button type="primary" @click="handleReloadCurrent"> 刷新当前页 </a-button>
        <a-button type="primary" @click="handleReload"> 刷新并返回第一页 </a-button>
      </template>
    </BasicTable>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicTable, PageWrapper, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '/@/api/demo/table';

  import { getBasicColumns } from './tableData';

  const [registerTable, { reload }] = useTable({
    title: '远程加载示例',
    api: demoListApi,
    columns: getBasicColumns(),
    pagination: { pageSize: 10 },
  });
  function handleReloadCurrent() {
    reload();
  }

  function handleReload() {
    reload({
      page: 1,
    });
  }
</script>
```

</details>
</CodePreview>

### 固定列

<CodePreview src="/doc-comp/table/fixedColumn" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable">
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'action'">
          <TableAction
            :actions="[
              {
                label: '删除',
                icon: 'ic:outline-delete-outline',
                onClick: handleDelete.bind(null, record),
              },
            ]"
            :dropDownActions="[
              {
                label: '启用',
                popConfirm: {
                  title: '是否启用？',
                  confirm: handleOpen.bind(null, record),
                },
              },
            ]"
          />
        </template>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicColumn, BasicTable, TableAction, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  const columns: BasicColumn[] = [
    {
      title: 'ID',
      dataIndex: 'id',
      fixed: 'left',
      width: 280,
    },
    {
      title: '姓名',
      dataIndex: 'name',
      width: 260,
    },
    {
      title: '地址',
      dataIndex: 'address',
    },
    {
      title: '编号',
      dataIndex: 'no',
      width: 300,
    },
    {
      title: '开始时间',
      width: 200,
      dataIndex: 'beginTime',
    },
    {
      title: '结束时间',
      dataIndex: 'endTime',
      width: 200,
    },
  ];
  const [registerTable] = useTable({
    title: 'TableAction组件及固定列示例',
    api: demoListApi,
    columns,
    rowSelection: { type: 'radio' },
    bordered: true,
    actionColumn: {
      width: 160,
      title: 'Action',
      dataIndex: 'action',
      // slots: { customRender: 'action' },
    },
  });
  function handleDelete(record: Recordable) {
    console.log('点击了删除', record);
  }
  function handleOpen(record: Recordable) {
    console.log('点击了启用', record);
  }
</script>
```

</details>
</CodePreview>

### 自定义列

<CodePreview src="/doc-comp/table/customerCell" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable">
      <template #bodyCell="{ column, record, text }">
        <template v-if="column.key === 'id'"> ID: {{ record.id }} </template>
        <template v-else-if="column.key === 'no'">
          <ATag color="green">
            {{ record.no }}
          </ATag>
        </template>
        <template v-else-if="column.key === 'avatar'">
          <AAvatar :size="60" :src="record.avatar" />
        </template>
        <template v-else-if="column.key === 'imgArr'">
          <TableImg :size="60" :simpleShow="true" :imgList="text" />
        </template>
        <template v-else-if="column.key === 'imgs'">
          <TableImg :size="60" :imgList="text" />
        </template>

        <template v-else-if="column.key === 'category'">
          <ATag color="green">
            {{ record.no }}
          </ATag>
        </template>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicColumn, BasicTable, TableImg, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  const columns: BasicColumn[] = [
    {
      title: 'ID',
      dataIndex: 'id',
      // slots: { customRender: 'id' },
    },
    {
      title: '头像',
      dataIndex: 'avatar',
      width: 100,
      // slots: { customRender: 'avatar' },
    },
    {
      title: '分类',
      dataIndex: 'category',
      width: 80,
      align: 'center',
      defaultHidden: true,
      // slots: { customRender: 'category' },
    },
    {
      title: '姓名',
      dataIndex: 'name',
      width: 120,
    },
    {
      title: '图片列表1',
      dataIndex: 'imgArr',
      helpMessage: ['这是简单模式的图片列表', '只会显示一张在表格中', '但点击可预览多张图片'],
      width: 140,
      // slots: { customRender: 'img' },
    },
    {
      title: '照片列表2',
      dataIndex: 'imgs',
      width: 160,
      // slots: { customRender: 'imgs' },
    },
    {
      title: '地址',
      dataIndex: 'address',
    },
    {
      title: '编号',
      dataIndex: 'no',
      // slots: { customRender: 'no' },
    },
    {
      title: '开始时间',
      dataIndex: 'beginTime',
    },
    {
      title: '结束时间',
      dataIndex: 'endTime',
    },
  ];
  const [registerTable] = useTable({
    title: '自定义列内容',
    titleHelpMessage: '表格中所有头像、图片均为mock生成，仅用于演示图片占位',
    api: demoListApi,
    columns,
    bordered: true,
    showTableSetting: true,
  });
</script>
```

</details>
</CodePreview>

### 开启搜索区域

<CodePreview src="/doc-comp/table/formTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <BasicTable @register="registerTable">
    <template #form-custom> custom-slot </template>
    <template #headerTop>
      <a-alert type="info" show-icon>
        <template #message>
          <template v-if="checkedKeys.length > 0">
            <span>已选中{{ checkedKeys.length }}条记录(可跨页)</span>
            <a-button type="link" size="small" @click="checkedKeys = []">清空</a-button>
          </template>
          <template v-else>
            <span>未选中任何项目</span>
          </template>
        </template>
      </a-alert>
    </template>
    <template #toolbar>
      <a-button type="primary" @click="getFormValues">获取表单数据</a-button>
    </template>
  </BasicTable>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '/@/api/demo/table';

  import { getBasicColumns, getFormConfig } from './tableData';

  const checkedKeys = ref<Array<string | number>>([]);
  const [registerTable, { getForm }] = useTable({
    title: '开启搜索区域',
    api: demoListApi,
    columns: getBasicColumns(),
    useSearchForm: true,
    formConfig: getFormConfig(),
    showTableSetting: true,
    tableSetting: { fullScreen: true },
    showIndexColumn: false,
    rowKey: 'id',
    rowSelection: {
      type: 'checkbox',
      selectedRowKeys: checkedKeys,
      onSelect,
      onSelectAll,
    },
  });

  function getFormValues() {
    console.log(getForm().getFieldsValue());
  }

  function onSelect(record, selected) {
    if (selected) {
      checkedKeys.value = [...checkedKeys.value, record.id];
    } else {
      checkedKeys.value = checkedKeys.value.filter((id) => id !== record.id);
    }
  }
  function onSelectAll(selected, selectedRows, changeRows) {
    const changeIds = changeRows.map((item) => item.id);
    if (selected) {
      checkedKeys.value = [...checkedKeys.value, ...changeIds];
    } else {
      checkedKeys.value = checkedKeys.value.filter((id) => {
        return !changeIds.includes(id);
      });
    }
  }
</script>
```

</details>
</CodePreview>

### UseTable

<CodePreview src="/doc-comp/table/useTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <div class="mb-4">
      <a-button class="mr-2" @click="reloadTable"> 还原 </a-button>
      <a-button class="mr-2" @click="changeLoading"> 开启loading </a-button>
      <a-button class="mr-2" @click="changeColumns"> 更改Columns </a-button>
      <a-button class="mr-2" @click="getColumn"> 获取Columns </a-button>
      <a-button class="mr-2" @click="getTableData"> 获取表格数据 </a-button>
      <a-button class="mr-2" @click="getTableRawData"> 获取接口原始数据 </a-button>
      <a-button class="mr-2" @click="setPaginationInfo"> 跳转到第2页 </a-button>
    </div>
    <div class="mb-4">
      <a-button class="mr-2" @click="getSelectRowList"> 获取选中行 </a-button>
      <a-button class="mr-2" @click="getSelectRowKeyList"> 获取选中行Key </a-button>
      <a-button class="mr-2" @click="setSelectedRowKeyList"> 设置选中行 </a-button>
      <a-button class="mr-2" @click="clearSelect"> 清空选中行 </a-button>
      <a-button class="mr-2" @click="getPagination"> 获取分页信息 </a-button>
    </div>
    <BasicTable @register="registerTable" />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, ColumnChangeParam, useMessage, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getBasicColumns, getBasicShortColumns } from './tableData';

  const { createMessage } = useMessage();
  function onChange() {
    console.log('onChange', arguments);
  }
  const [
    registerTable,
    {
      setLoading,
      setProps,
      getColumns,
      getDataSource,
      getRawDataSource,
      reload,
      getPaginationRef,
      setPagination,
      getSelectRows,
      getSelectRowKeys,
      setSelectedRowKeys,
      clearSelectedRowKeys,
    },
  ] = useTable({
    canResize: true,
    title: 'useTable示例',
    titleHelpMessage: '使用useTable调用表格内方法',
    api: demoListApi,
    columns: getBasicColumns(),
    defSort: {
      field: 'name',
      order: 'ascend',
    },
    rowKey: 'id',
    showTableSetting: true,
    onChange,
    rowSelection: {
      type: 'checkbox',
    },
    onColumnsChange: (data: ColumnChangeParam[]) => {
      console.log('ColumnsChanged', data);
    },
  });

  function changeLoading() {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
    }, 1000);
  }
  function changeColumns() {
    setProps({
      columns: getBasicShortColumns(),
      rowSelection: {
        type: 'checkbox',
      },
      showIndexColumn: true,
    });
  }
  function reloadTable() {
    setProps({
      columns: getBasicColumns(),
      rowSelection: {
        type: 'checkbox',
      },
      showIndexColumn: true,
    });
    reload({
      page: 1,
    });
  }
  function getColumn() {
    createMessage.info('请在控制台查看！');
    console.log(getColumns());
  }

  function getTableData() {
    createMessage.info('请在控制台查看！');
    console.log(getDataSource());
  }

  function getTableRawData() {
    createMessage.info('请在控制台查看！');
    console.log(getRawDataSource());
  }

  function getPagination() {
    createMessage.info('请在控制台查看！');
    console.log(getPaginationRef());
  }

  function setPaginationInfo() {
    setPagination({
      current: 2,
    });
    reload();
  }
  function getSelectRowList() {
    createMessage.info('请在控制台查看！');
    console.log(getSelectRows());
  }
  function getSelectRowKeyList() {
    createMessage.info('请在控制台查看！');
    console.log(getSelectRowKeys());
  }
  function setSelectedRowKeyList() {
    setSelectedRowKeys(['0', '1', '2']);
  }
  function clearSelect() {
    clearSelectedRowKeys();
  }
</script>
```

</details>
</CodePreview>

### RefTable

<CodePreview src="/doc-comp/table/refTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <div class="mb-4">
      <a-button class="mr-2" @click="reloadTable"> 还原 </a-button>
      <a-button class="mr-2" @click="changeLoading"> 开启loading </a-button>
      <a-button class="mr-2" @click="changeColumns"> 更改Columns </a-button>
      <a-button class="mr-2" @click="getColumn"> 获取Columns </a-button>
      <a-button class="mr-2" @click="getTableData"> 获取表格数据 </a-button>
      <a-button class="mr-2" @click="getTableRawData"> 获取接口原始数据 </a-button>
      <a-button class="mr-2" @click="setPaginationInfo"> 跳转到第2页 </a-button>
    </div>
    <div class="mb-4">
      <a-button class="mr-2" @click="getSelectRowList"> 获取选中行 </a-button>
      <a-button class="mr-2" @click="getSelectRowKeyList"> 获取选中行Key </a-button>
      <a-button class="mr-2" @click="setSelectedRowKeyList"> 设置选中行 </a-button>
      <a-button class="mr-2" @click="clearSelect"> 清空选中行 </a-button>
      <a-button class="mr-2" @click="getPagination"> 获取分页信息 </a-button>
    </div>
    <BasicTable
      ref="tableRef"
      :canResize="false"
      title="RefTable示例"
      titleHelpMessage="使用Ref调用表格内方法"
      :api="demoListApi"
      :columns="columns"
      rowKey="id"
      :rowSelection="{ type: 'checkbox' }"
    />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, TableActionType, useMessage } from '@eciol/ant-ui';
  import { type Nullable } from '@eciol/types';

  import { demoListApi } from '@/api/demo/table';

  import { getBasicColumns, getBasicShortColumns } from './tableData';

  const tableRef = ref<Nullable<TableActionType>>(null);
  const { createMessage } = useMessage();

  function getTableAction() {
    const tableAction = unref(tableRef);
    if (!tableAction) {
      throw new Error('tableAction is null');
    }
    return tableAction;
  }
  function changeLoading() {
    getTableAction().setLoading(true);
    setTimeout(() => {
      getTableAction().setLoading(false);
    }, 1000);
  }
  function changeColumns() {
    getTableAction().setProps({
      columns: getBasicShortColumns(),
      rowSelection: {
        type: 'checkbox',
      },
      showIndexColumn: true,
    });
  }
  function reloadTable() {
    getTableAction().setProps({
      columns: getBasicColumns(),
      rowSelection: {
        type: 'checkbox',
      },
      showIndexColumn: true,
    });

    getTableAction().reload({
      page: 1,
    });
  }
  function getColumn() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getColumns());
  }

  function getTableData() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getDataSource());
  }
  function getTableRawData() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getRawDataSource());
  }

  function getPagination() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getPaginationRef());
  }

  function setPaginationInfo() {
    getTableAction().setPagination({
      current: 2,
    });
    getTableAction().reload();
  }
  function getSelectRowList() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getSelectRows());
  }
  function getSelectRowKeyList() {
    createMessage.info('请在控制台查看！');
    console.log(getTableAction().getSelectRowKeys());
  }
  function setSelectedRowKeyList() {
    getTableAction().setSelectedRowKeys(['0', '1', '2']);
  }
  function clearSelect() {
    getTableAction().clearSelectedRowKeys();
  }
  const columns = getBasicColumns();
</script>
```

</details>
</CodePreview>

### 多级表头

<CodePreview src="/doc-comp/table/multipleHeader" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable" />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getMultipleHeaderColumns } from './tableData';

  const [registerTable] = useTable({
    title: '多级表头示例',
    api: demoListApi,
    columns: getMultipleHeaderColumns(),
  });
</script>
```

</details>
</CodePreview>

### 合并单元格

<CodePreview src="/doc-comp/table/mergeHeader" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable" />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getMergeHeaderColumns } from './tableData';

  const [registerTable] = useTable({
    title: '合并单元格',
    bordered: true,
    api: demoListApi,
    columns: getMergeHeaderColumns(),
  });
</script>
```

</details>
</CodePreview>

### 可展开表格

<CodePreview src="/doc-comp/table/expandTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper
    title="可展开表格"
    content="TableAction组件可配置stopButtonPropagation来阻止操作按钮的点击事件冒泡，以便配合Table组件的expandRowByClick"
  >
    <BasicTable @register="registerTable">
      <template #expandedRowRender="{ record }">
        <span>No: {{ record.no }} </span>
      </template>
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'action'">
          <TableAction
            stopButtonPropagation
            :actions="[
              {
                label: '删除',
                icon: 'ic:outline-delete-outline',
                onClick: handleDelete.bind(null, record),
              },
            ]"
            :dropDownActions="[
              {
                label: '启用',
                popConfirm: {
                  title: '是否启用？',
                  confirm: handleOpen.bind(null, record),
                },
              },
            ]"
          />
        </template>
      </template>
    </BasicTable>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { BasicTable, PageWrapper, TableAction, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getBasicColumns } from './tableData';

  const [registerTable] = useTable({
    api: demoListApi,
    title: '可展开表格演示',
    titleHelpMessage: ['已启用expandRowByClick', '已启用stopButtonPropagation'],
    columns: getBasicColumns(),
    rowKey: 'id',
    canResize: false,
    expandRowByClick: true,
    actionColumn: {
      width: 160,
      title: 'Action',
      dataIndex: 'action',
      fixed: 'right',
      // slots: { customRender: 'action' },
    },
  });
  function handleDelete(record: Recordable) {
    console.log('点击了删除', record);
  }
  function handleOpen(record: Recordable) {
    console.log('点击了启用', record);
  }
</script>
```

</details>
</CodePreview>

###

定高/头部自定义

<CodePreview src="/doc-comp/table/fixedHeight" :height="600">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable">
      <template #headerCell="{ column }">
        <template v-if="column.key === 'name'">
          <span>
            姓名
            <BasicHelp class="ml-2" text="headerHelpMessage方式2" />
          </span>
        </template>
        <template v-else-if="column.key === 'address'">
          地址
          <FormOutlined class="ml-2" />
        </template>
        <template v-else>
          <HeaderCell :column="column" />
        </template>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { FormOutlined } from '@ant-design/icons-vue';
  import { BasicTable, HeaderCell, useTable } from '@eciol/ant-ui';
  import { BasicHelp } from '@eciol/share-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getCustomHeaderColumns } from './tableData';

  const [registerTable] = useTable({
    title: '定高/头部自定义',
    api: demoListApi,
    columns: getCustomHeaderColumns(),
    canResize: false,
    scroll: { y: 100 },
  });
</script>
```

</details>
</CodePreview>

### 表尾行合计

<CodePreview src="/doc-comp/table/footerTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable" />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getBasicColumns } from './tableData';

  function handleSummary(tableData: Recordable[]) {
    const totalNo = tableData.reduce((prev, next) => {
      prev += next.no;
      return prev;
    }, 0);
    return [
      {
        _row: '合计',
        _index: '平均值',
        no: totalNo,
      },
      {
        _row: '合计',
        _index: '平均值',
        no: totalNo,
      },
    ];
  }
  const [registerTable] = useTable({
    title: '表尾行合计示例',
    api: demoListApi,
    rowSelection: { type: 'checkbox' },
    columns: getBasicColumns(),
    showSummary: true,
    summaryFunc: handleSummary,
    scroll: { x: 2000 },
    canResize: false,
  });
</script>
```

</details>
</CodePreview>

### 可编辑单元格

<CodePreview src="/doc-comp/table/editCellTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable
      :beforeEditSubmit="beforeEditSubmit"
      @register="registerTable"
      @edit-end="handleEditEnd"
      @edit-cancel="handleEditCancel"
    />
  </div>
</template>
<script lang="ts" setup>
  import { BasicColumn, BasicTable, useMessage, useTable } from '@eciol/ant-ui';
  import { Progress } from 'ant-design-vue';

  import { optionsListApi } from '@/api/demo/select';
  import { demoListApi } from '@/api/demo/table';
  import { treeOptionsListApi } from '@/api/demo/tree';

  const columns: BasicColumn[] = [
    {
      title: '输入框',
      dataIndex: 'name',
      edit: true,
      editComponentProps: {
        prefix: '$',
      },
      width: 200,
    },
    {
      title: '默认输入状态',
      dataIndex: 'name7',
      edit: true,
      editable: true,
      width: 200,
    },
    {
      title: '输入框校验',
      dataIndex: 'name1',
      edit: true,
      // 默认必填校验
      editRule: true,
      width: 200,
    },
    {
      title: '输入框函数校验',
      dataIndex: 'name2',
      edit: true,
      editRule: async (text) => {
        if (text === '2') {
          return '不能输入该值';
        }
        return '';
      },
      width: 200,
    },
    {
      title: '数字输入框',
      dataIndex: 'id',
      edit: true,
      editRule: true,
      editComponent: 'InputNumber',
      width: 200,
      editComponentProps: () => {
        return {
          max: 100,
          min: 0,
        };
      },
      editRender: ({ text }) => {
        return h(Progress, { percent: Number(text) });
      },
    },
    {
      title: '下拉框',
      dataIndex: 'name3',
      edit: true,
      editComponent: 'Select',
      editComponentProps: {
        options: [
          {
            label: 'Option1',
            value: '1',
          },
          {
            label: 'Option2',
            value: '2',
          },
        ],
      },
      width: 200,
    },
    {
      title: '远程下拉',
      dataIndex: 'name4',
      edit: true,
      editComponent: 'ApiSelect',
      editComponentProps: {
        api: optionsListApi,
        resultField: 'list',
        labelField: 'name',
        valueField: 'id',
      },
      width: 200,
    },
    {
      title: '远程下拉树',
      dataIndex: 'name8',
      edit: true,
      editComponent: 'ApiTreeSelect',
      editRule: false,
      editComponentProps: {
        api: treeOptionsListApi,
        resultField: 'list',
      },
      width: 200,
    },
    {
      title: '日期选择',
      dataIndex: 'date',
      edit: true,
      editComponent: 'DatePicker',
      editComponentProps: {
        valueFormat: 'YYYY-MM-DD',
        format: 'YYYY-MM-DD',
      },
      width: 200,
    },
    {
      title: '时间选择',
      dataIndex: 'time',
      edit: true,
      editComponent: 'TimePicker',
      editComponentProps: {
        valueFormat: 'HH:mm',
        format: 'HH:mm',
      },
      width: 200,
    },
    {
      title: '勾选框',
      dataIndex: 'name5',
      edit: true,
      editComponent: 'Checkbox',
      editValueMap: (value) => {
        return value ? '是' : '否';
      },
      width: 200,
    },
    {
      title: '开关',
      dataIndex: 'name6',
      edit: true,
      editComponent: 'Switch',
      editValueMap: (value) => {
        return value ? '开' : '关';
      },
      width: 200,
    },
    {
      title: '单选框',
      dataIndex: 'radio1',
      edit: true,
      editComponent: 'RadioGroup',
      editComponentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      width: 200,
    },
    {
      title: '单选按钮框',
      dataIndex: 'radio2',
      edit: true,
      editComponent: 'RadioButtonGroup',
      editComponentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      width: 200,
    },
    {
      title: '远程单选框',
      dataIndex: 'radio3',
      edit: true,
      editComponent: 'ApiRadioGroup',
      editComponentProps: {
        api: optionsListApi,
        resultField: 'list',
        labelField: 'name',
        valueField: 'id',
      },
      width: 200,
    },
  ];
  const [registerTable] = useTable({
    title: '可编辑单元格示例',
    api: demoListApi,
    columns,
    showIndexColumn: false,
    bordered: true,
  });

  const { createMessage } = useMessage();

  function handleEditEnd({ record, index, key, value }: Recordable) {
    console.log(record, index, key, value);
    return false;
  }

  // 模拟将指定数据保存
  function feakSave({ value, key, id }) {
    createMessage.loading({
      content: `正在模拟保存${key}`,
      key: '_save_fake_data',
      duration: 0,
    });
    return new Promise((resolve) => {
      setTimeout(() => {
        if (value === '') {
          createMessage.error({
            content: '保存失败：不能为空',
            key: '_save_fake_data',
            duration: 2,
          });
          resolve(false);
        } else {
          createMessage.success({
            content: `记录${id}的${key}已保存`,
            key: '_save_fake_data',
            duration: 2,
          });
          resolve(true);
        }
      }, 2000);
    });
  }

  async function beforeEditSubmit({ record, index, key, value }) {
    console.log('单元格数据正在准备提交', { record, index, key, value });
    return await feakSave({ id: record.id, key, value });
  }

  function handleEditCancel() {
    console.log('cancel');
  }
</script>
```

</details>
</CodePreview>

### 可编辑行

<CodePreview src="/doc-comp/table/editRowTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable" @edit-change="onEditChange">
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'action'">
          <TableAction :actions="createActions(record, column)" />
        </template>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import {
    ActionItem,
    BasicColumn,
    BasicTable,
    EditRecordRow,
    TableAction,
    useMessage,
    useTable,
  } from '@eciol/ant-ui';
  import { cloneDeep } from 'lodash-es';

  import { optionsListApi } from '@/api/demo/select';
  import { demoListApi } from '@/api/demo/table';
  import { treeOptionsListApi } from '@/api/demo/tree';

  const columns: BasicColumn[] = [
    {
      title: '输入框',
      dataIndex: 'name-group',
      editRow: true,
      children: [
        {
          title: '输入框',
          dataIndex: 'name',
          editRow: true,
          editComponentProps: {
            prefix: '$',
          },
          width: 150,
        },
        {
          title: '默认输入状态',
          dataIndex: 'name7',
          editRow: true,
          width: 150,
        },
        {
          title: '输入框校验',
          dataIndex: 'name1',
          editRow: true,
          align: 'left',
          // 默认必填校验
          editRule: true,
          width: 150,
        },
        {
          title: '输入框函数校验',
          dataIndex: 'name2',
          editRow: true,
          align: 'right',
          editRule: async (text) => {
            if (text === '2') {
              return '不能输入该值';
            }
            return '';
          },
        },
        {
          title: '数字输入框',
          dataIndex: 'id',
          editRow: true,
          editRule: true,
          editComponent: 'InputNumber',
          width: 150,
        },
      ],
    },
    {
      title: '下拉框',
      dataIndex: 'name3',
      editRow: true,
      editComponent: 'Select',
      editComponentProps: {
        options: [
          {
            label: 'Option1',
            value: '1',
          },
          {
            label: 'Option2',
            value: '2',
          },
          {
            label: 'Option3',
            value: '3',
          },
        ],
      },
      width: 200,
    },
    {
      title: '远程下拉',
      dataIndex: 'name4',
      editRow: true,
      editComponent: 'ApiSelect',
      editComponentProps: {
        api: optionsListApi,
        resultField: 'list',
        labelField: 'name',
        valueField: 'id',
      },
      width: 200,
    },
    {
      title: '远程下拉树',
      dataIndex: 'name8',
      editRow: true,
      editComponent: 'ApiTreeSelect',
      editRule: false,
      editComponentProps: {
        api: treeOptionsListApi,
        resultField: 'list',
      },
      width: 200,
    },
    {
      title: '日期选择',
      dataIndex: 'date',
      editRow: true,
      editComponent: 'DatePicker',
      editComponentProps: {
        valueFormat: 'YYYY-MM-DD',
        format: 'YYYY-MM-DD',
      },
      width: 150,
    },
    {
      title: '时间选择',
      dataIndex: 'time',
      editRow: true,
      editComponent: 'TimePicker',
      editComponentProps: {
        valueFormat: 'HH:mm',
        format: 'HH:mm',
      },
      width: 100,
    },
    {
      title: '勾选框',
      dataIndex: 'name5',
      editRow: true,

      editComponent: 'Checkbox',
      editValueMap: (value) => {
        return value ? '是' : '否';
      },
      width: 100,
    },
    {
      title: '开关',
      dataIndex: 'name6',
      editRow: true,
      editComponent: 'Switch',
      editValueMap: (value) => {
        return value ? '开' : '关';
      },
      width: 100,
    },
    {
      title: '单选框',
      dataIndex: 'radio1',
      editRow: true,
      editComponent: 'RadioGroup',
      editComponentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      width: 200,
    },
    {
      title: '单选按钮框',
      dataIndex: 'radio2',
      editRow: true,
      editComponent: 'RadioButtonGroup',
      editComponentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
      width: 200,
    },
    {
      title: '远程单选框',
      dataIndex: 'radio3',
      editRow: true,
      editComponent: 'ApiRadioGroup',
      editComponentProps: {
        api: optionsListApi,
        resultField: 'list',
        labelField: 'name',
        valueField: 'id',
      },
      width: 200,
    },
  ];
  const { createMessage: msg } = useMessage();
  const currentEditKeyRef = ref('');
  const [registerTable] = useTable({
    title: '可编辑行示例',
    titleHelpMessage: [
      '本例中修改[数字输入框]这一列时，同一行的[远程下拉]列的当前编辑数据也会同步发生改变',
    ],
    api: demoListApi,
    columns,
    showIndexColumn: false,
    showTableSetting: true,
    tableSetting: { fullScreen: true },
    actionColumn: {
      width: 160,
      title: 'Action',
      dataIndex: 'action',
      // slots: { customRender: 'action' },
    },
  });

  function handleEdit(record: EditRecordRow) {
    currentEditKeyRef.value = record.key;
    record.onEdit?.(true);
  }

  function handleCancel(record: EditRecordRow) {
    currentEditKeyRef.value = '';
    record.onEdit?.(false, false);
  }

  async function handleSave(record: EditRecordRow) {
    // 校验
    msg.loading({ content: '正在保存...', duration: 0, key: 'saving' });
    const valid = await record.onValid?.();
    if (valid) {
      try {
        const data = cloneDeep(record.editValueRefs);
        console.log(data);
        //TODO 此处将数据提交给服务器保存
        // ...
        // 保存之后提交编辑状态
        const pass = await record.onEdit?.(false, true);
        if (pass) {
          currentEditKeyRef.value = '';
        }
        msg.success({ content: '数据已保存', key: 'saving' });
      } catch (error) {
        msg.error({ content: '保存失败', key: 'saving' });
      }
    } else {
      msg.error({ content: '请填写正确的数据', key: 'saving' });
    }
  }

  function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
    if (!record.editable) {
      return [
        {
          label: '编辑',
          disabled: currentEditKeyRef.value ? currentEditKeyRef.value !== record.key : false,
          onClick: handleEdit.bind(null, record),
        },
      ];
    }
    return [
      {
        label: '保存',
        onClick: handleSave.bind(null, record, column),
      },
      {
        label: '取消',
        popConfirm: {
          title: '是否取消编辑',
          confirm: handleCancel.bind(null, record, column),
        },
      },
    ];
  }

  function onEditChange({ column, value, record }) {
    // 本例
    if (column.dataIndex === 'id') {
      record.editValueRefs.name4.value = `${value}`;
    }
    console.log(column, value, record);
  }
</script>
```

</details>
</CodePreview>

### 权限列

<CodePreview src="/doc-comp/table/authColumn" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable">
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'action'">
          <TableAction
            :actions="[
              {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
                auth: 'other', // 根据权限控制是否显示: 无权限，不显示
              },
              {
                label: '删除',
                icon: 'ic:outline-delete-outline',
                onClick: handleDelete.bind(null, record),
                auth: 'super', // 根据权限控制是否显示: 有权限，会显示
              },
            ]"
            :dropDownActions="[
              {
                label: '启用',
                popConfirm: {
                  title: '是否启用？',
                  confirm: handleOpen.bind(null, record),
                },
                ifShow: (_action) => {
                  return record.status !== 'enable'; // 根据业务控制是否显示: 非enable状态的不显示启用按钮
                },
              },
              {
                label: '禁用',
                popConfirm: {
                  title: '是否禁用？',
                  confirm: handleOpen.bind(null, record),
                },
                ifShow: () => {
                  return record.status === 'enable'; // 根据业务控制是否显示: enable状态的显示禁用按钮
                },
              },
              {
                label: '同时控制',
                popConfirm: {
                  title: '是否动态显示？',
                  confirm: handleOpen.bind(null, record),
                },
                auth: 'super', // 同时根据权限和业务控制是否显示
                ifShow: () => {
                  return true;
                },
              },
            ]"
          />
        </template>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicColumn, BasicTable, TableAction, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  const columns: BasicColumn[] = [
    {
      title: '编号',
      dataIndex: 'no',
      width: 100,
    },
    {
      title: '姓名',
      dataIndex: 'name',
      width: 200,
      auth: 'test', // 根据权限控制是否显示: 无权限，不显示
    },
    {
      title: '状态',
      dataIndex: 'status',
    },
    {
      title: '状态1',
      dataIndex: 'status1',
    },
    {
      title: '状态2',
      dataIndex: 'status2',
    },
    {
      title: '状态3',
      dataIndex: 'status3',
    },
    {
      title: '状态4',
      dataIndex: 'status4',
    },
    {
      title: '状态5',
      dataIndex: 'status5',
    },
    {
      title: '地址',
      dataIndex: 'address',
      auth: 'super', // 同时根据权限和业务控制是否显示
      ifShow: (_column) => {
        return true;
      },
    },
    {
      title: '开始时间',
      dataIndex: 'beginTime',
    },
    {
      title: '结束时间',
      dataIndex: 'endTime',
      width: 200,
    },
  ];
  const [registerTable] = useTable({
    title: 'TableAction组件及固定列示例',
    api: demoListApi,
    columns,
    bordered: true,
    rowKey: 'id',
    rowSelection: {
      type: 'checkbox',
    },
    actionColumn: {
      width: 250,
      title: 'Action',
      dataIndex: 'action',
      // slots: { customRender: 'action' },
    },
  });
  function handleEdit(record: Recordable) {
    console.log('点击了编辑', record);
  }
  function handleDelete(record: Recordable) {
    console.log('点击了删除', record);
  }
  function handleOpen(record: Recordable) {
    console.log('点击了启用', record);
  }
</script>
```

</details>
</CodePreview>

### 继承父元素高度

<CodePreview src="/doc-comp/table/resizeParentHeightTable" :height="900">
<details>
<summary>展开查看</summary>

```vue
<template>
  <div class="h-full flex p-4">
    <div class="flex flex-col pr-4 w-1/2">
      <div class="flex-1">
        <BasicTable @register="registerTable" />
      </div>
      <div class="h-4"></div>
      <div class="flex-1">
        <BasicTable @register="registerTable" />
      </div>
    </div>
    <div class="flex-1 flex flex-col w-1/2 h-full">
      <div class="h-1/3 mb-4">
        <BasicTable @register="registerTable" />
      </div>
      <div class="h-1/3 mb-4">
        <BasicTable @register="registerTable2" />
      </div>
      <div class="h-1/3">
        <BasicTable @register="registerTable1" />
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { getBasicColumns, getFormConfig } from './tableData';

  const [registerTable] = useTable({
    api: demoListApi,
    columns: getBasicColumns(),
    useSearchForm: false,
    formConfig: getFormConfig(),
    showTableSetting: false,
    tableSetting: { fullScreen: true },
    showIndexColumn: false,
    isCanResizeParent: true,
    rowKey: 'id',
  });

  const [registerTable1] = useTable({
    api: demoListApi,
    columns: getBasicColumns(),
    formConfig: getFormConfig(),
    showTableSetting: false,
    tableSetting: { fullScreen: true },
    showIndexColumn: false,
    isCanResizeParent: true,
    useSearchForm: false,
    rowKey: 'id',
  });

  const [registerTable2] = useTable({
    api: demoListApi,
    columns: getBasicColumns(),
    formConfig: getFormConfig(),
    showTableSetting: false,
    tableSetting: { fullScreen: true },
    showIndexColumn: false,
    isCanResizeParent: true,
    useSearchForm: false,
    pagination: false,
    rowKey: 'id',
  });
</script>
```

</details>
</CodePreview>

### VxeTable 表格

<CodePreview src="/doc-comp/table/vxeTable" :height="800">
<details>
<summary>展开查看</summary>

```vue
<template>
  <PageWrapper
    title="VxeTable表格"
    content="只展示部分操作，详细功能请查看VxeTable官网事例"
    contentFullHeight
    fixedHeight
  >
    <VxeBasicTable ref="tableRef" v-bind="gridOptions">
      <template #action="{ row }">
        <TableAction outside :actions="createActions(row)" />
      </template>
    </VxeBasicTable>
  </PageWrapper>
</template>
<script lang="ts" setup>
  import {
    ActionItem,
    BasicTableProps,
    PageWrapper,
    TableAction,
    useMessage,
    VxeBasicTable,
    VxeGridInstance,
  } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';

  import { vxeTableColumns, vxeTableFormSchema } from './tableData';

  const { createMessage } = useMessage();

  const tableRef = ref<VxeGridInstance>();

  const gridOptions = reactive<BasicTableProps>({
    id: 'VxeTable',
    keepSource: true,
    editConfig: { trigger: 'click', mode: 'cell', showStatus: true },
    columns: vxeTableColumns,
    toolbarConfig: {
      buttons: [
        {
          content: '在第一行新增',
          buttonRender: {
            name: 'AButton',
            props: {
              type: 'primary',
              preIcon: 'mdi:page-next-outline',
            },
            events: {
              click: () => {
                tableRef.value?.insert({ name: '新增的' });
                createMessage.success('新增成功');
              },
            },
          },
        },
        {
          content: '在最后一行新增',
          buttonRender: {
            name: 'AButton',
            props: {
              type: 'warning',
            },
            events: {
              click: () => {
                tableRef.value?.insertAt({ name: '新增的' }, -1);
              },
            },
          },
        },
      ],
    },
    formConfig: {
      enabled: true,
      items: vxeTableFormSchema,
    },
    height: 'auto',
    proxyConfig: {
      ajax: {
        query: async ({ page, form }) => {
          return demoListApi({
            page: page.currentPage,
            pageSize: page.pageSize,
            ...form,
          });
        },
        queryAll: async ({ form }) => {
          return await demoListApi(form);
        },
      },
    },
  });

  // 操作按钮（权限控制）
  const createActions = (record) => {
    const actions: ActionItem[] = [
      {
        label: '详情',
        onClick: () => {
          console.log(record);
        },
      },
      {
        label: '编辑',
        onClick: () => {},
      },
      {
        label: '删除',
        color: 'error',
        popConfirm: {
          title: '是否确认删除',
          confirm: () => {
            tableRef.value?.remove(record);
          },
        },
      },
    ];

    return actions;
  };
</script>
```

</details>
</CodePreview>

## Usage

### 示例

```vue
<template>
  <div class="p-4">
    <BasicTable
      title="基础示例"
      titleHelpMessage="温馨提醒"
      :columns="columns"
      :dataSource="data"
      :canResize="canResize"
      :loading="loading"
      :striped="striped"
      :bordered="border"
      :pagination="{ pageSize: 20 }"
    >
      <template #toolbar>
        <a-button type="primary"> 操作按钮 </a-button>
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable } from '@eciol/ant-ui';
  import { getBasicColumns, getBasicData } from './tableData';

  const columns = getBasicColumns();
  const data = getBasicData();
</script>
```

### template 示例

所有可调用函数见下方 `Methods` 说明

```vue
<template>
  <div class="p-4">
    <BasicTable
      :canResize="false"
      title="RefTable示例"
      titleHelpMessage="使用Ref调用表格内方法"
      ref="tableRef"
      :api="api"
      :columns="columns"
      rowKey="id"
      :rowSelection="{ type: 'checkbox' }"
    />
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, TableActionType } from '@eciol/ant-ui';
  import { getBasicColumns, getBasicShortColumns } from './tableData';
  import { demoListApi } from '@/api/demo/table';
  const tableRef = ref<Nullable<TableActionType>>(null);

  function getTableAction() {
    const tableAction = unref(tableRef);
    if (!tableAction) {
      throw new Error('tableAction is null');
    }
    return tableAction;
  }
  function changeLoading() {
    getTableAction().setLoading(true);
    setTimeout(() => {
      getTableAction().setLoading(false);
    }, 1000);
  }
  const columns = getBasicColumns();
</script>
```

### BasicColumn 和 tableAction 通过权限和业务控制显示隐藏的示例

```vue
<template>
  <div class="p-4">
    <BasicTable @register="registerTable">
      <template #action="{ record }">
        <TableAction
          :actions="[
            {
              label: '编辑',
              onClick: handleEdit.bind(null, record),
              auth: 'other', // 根据权限控制是否显示: 无权限，不显示
            },
            {
              label: '删除',
              icon: 'ic:outline-delete-outline',
              onClick: handleDelete.bind(null, record),
              auth: 'super', // 根据权限控制是否显示: 有权限，会显示
            },
          ]"
          :dropDownActions="[
            {
              label: '启用',
              popConfirm: {
                title: '是否启用？',
                confirm: handleOpen.bind(null, record),
              },
              ifShow: (_action) => {
                return record.status !== 'enable'; // 根据业务控制是否显示: 非enable状态的不显示启用按钮
              },
            },
            {
              label: '禁用',
              popConfirm: {
                title: '是否禁用？',
                confirm: handleOpen.bind(null, record),
              },
              ifShow: () => {
                return record.status === 'enable'; // 根据业务控制是否显示: enable状态的显示禁用按钮
              },
            },
            {
              label: '同时控制',
              popConfirm: {
                title: '是否动态显示？',
                confirm: handleOpen.bind(null, record),
              },
              auth: 'super', // 同时根据权限和业务控制是否显示
              ifShow: () => {
                return true; // 根据业务控制是否显示
              },
            },
          ]"
        />
      </template>
    </BasicTable>
  </div>
</template>
<script lang="ts" setup>
  import { BasicTable, useTable, BasicColumn, TableAction } from '@eciol/ant-ui';

  import { demoListApi } from '@/api/demo/table';
  const columns: BasicColumn[] = [
    {
      title: '姓名',
      dataIndex: 'name',
      auth: 'test', // 根据权限控制是否显示: 无权限，不显示
    },
    {
      title: '地址',
      dataIndex: 'address',
      auth: 'super', // 同时根据权限控制是否显示
      ifShow: (_column) => {
        return true; // 根据业务控制是否显示
      },
    },
  ];
  const [registerTable] = useTable({
    title: 'TableAction组件及固定列示例',
    api: demoListApi,
    columns: columns,
    bordered: true,
    actionColumn: {
      width: 250,
      title: 'Action',
      dataIndex: 'action',
      slots: { customRender: 'action' },
    },
  });
  function handleEdit(record: Recordable) {
    console.log('点击了编辑', record);
  }
  function handleDelete(record: Recordable) {
    console.log('点击了删除', record);
  }
  function handleOpen(record: Recordable) {
    console.log('点击了启用', record);
  }
</script>
```

## useTable

使用组件自带的 **useTable** 可以方便使用表单

下面是一个使用简单表格的示例，

```vue
<template>
  <BasicTable @register="registerTable" />
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';
  import { getBasicColumns, getBasicShortColumns } from './tableData';
  import { demoListApi } from '@/api/demo/table';
  const [registerTable, { setLoading }] = useTable({
    api: demoListApi,
    columns: getBasicColumns(),
  });

  function changeLoading() {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
    }, 1000);
  }
</script>
```

### Usage

用于调用 Table 内部方法及 table 参数配置

```ts
// 表格的props也可以直接注册到useTable内部
const [register, methods, refData] = useTable(props);
```

**register**

register 用于注册 useTable，如果需要使用`useTable`提供的 api，必须将 register 传入组件的 onRegister

```vue
<template>
  <BasicTable @register="register" />
</template>
<script lang="ts" setup>
  import { BasicTable, useTable } from '@eciol/ant-ui';
  const [register] = useTable();
</script>
```

### Methods

**setProps**

类型：`(props: Partial<BasicTableProps>) => void`

说明: 用于设置表格参数

**reload**

类型：`(opt?: FetchParams) => Promise<void>`

说明: 刷新表格

**redoHeight**

类型：`() => void`

说明: 重新计算表格高度

**setLoading**

类型：`(loading: boolean) => void`

说明: 设置表格 loading 状态

**getDataSource**

获取表格数据

类型：`<T = Recordable>() => T[]`

说明: 获取表格数据

**getRawDataSource**

获取后端接口原始数据

类型：`<T = Recordable>() => T`

说明: 获取后端接口原始数据

**getColumns**

类型：`(opt?: GetColumnsParams) => BasicColumn[]`

说明: 获取表格数据

**setColumns**

类型：`(columns: BasicColumn[] | string[]) => void`

说明: 设置表头数据

**setTableData**

类型：`<T = Recordable>(values: T[]) => void`

说明: 设置表格数据

**setPagination**

类型：`(info: Partial<PaginationProps>) => void`

说明: 设置分页信息

**deleteSelectRowByKey**

类型：`(key: string) => void`

说明: 根据 key 删除取消选中行

**getSelectRowKeys**

类型：`() => string[]`

说明: 获取选中行的 keys

**getSelectRows**

类型：`<T = Recordable>() => T[]`

说明: 获取选中行的 rows

**clearSelectedRowKeys**

类型：`() => void`

说明: 清空选中行

**setSelectedRowKeys**

类型：`(rowKeys: string[] | number[]) => void`

说明: 设置选中行

**getPaginationRef**

类型：`() => PaginationProps | boolean`

说明: 获取当前分页信息

**getShowPagination**

类型：`() => boolean`

说明: 获取当前是否显示分页

**setShowPagination**

类型：`(show: boolean) => Promise<void>`

说明: 设置当前是否显示分页

**getRowSelection**

类型：`() => TableRowSelection<Recordable>`

说明: 获取勾选框信息

**updateTableData**

类型：`(index: number, key: string, value: any)=>void`

说明: 更新表格数据

**updateTableDataRecord**

类型： `(rowKey: string | number, record: Recordable) => Recordable | void`

说明： 根据唯一的 `rowKey` 更新指定行的数据.可用于不刷新整个表格而局部更新数据

**deleteTableDataRecord**

类型： `(rowKey: string | number | string[] | number[]) => void`

说明： 根据唯一的`rowKey` 动态删除指定行的数据.可用于不刷新整个表格而局部更新数据

**insertTableDataRecord**

类型： `(record: Recordable, index?: number) => Recordable | void`

说明： 可根据传入的 `index` 值决定插入数据行的位置，不传则是顺序插入，可用于不刷新整个表格而局部更新数据

**getForm**

类型：`() => FormActionType`

说明: 如果开启了搜索区域。可以通过该函数获取表单对象函数进行操作

**expandAll**

类型：`() => void`

说明: 展开树形表格

**collapseAll**

类型：`() => void`

说明: 折叠树形表格

### refData

**selectedRowRef**

类型：`Ref<Recordable[]>`

说明: 选中行 rows，响应式数据

**selectedRowKeysRef**

类型：`Ref<string[]>`

说明: 选中行 keys，响应式数据

**getDataSourceRef**

类型：`Ref<Recordable[]>`

说明: 表格数据，响应式数据

## Props

::: tip 温馨提醒

- 除以下参数外，官方文档内的 props 也都支持，具体可以参考 [antv table](https://www.antdv.com/components/table-cn/#API)
- 注意：`defaultExpandAllRows`、`defaultExpandedRowKeys` 属性在 basicTable 中不受支持，并且在`antv table` v2.2.0 之后也被移除。

:::

| 属性 | 类型 | 默认值 | 可选值 | 说明 | 版本 |
| --- | --- | --- | --- | --- | --- |
| clickToRowSelect | `boolean` | `true` | - | 点击行是否选中 checkbox 或者 radio。需要开启 |  |
| sortFn | `(sortInfo: SorterResult<any>) => any` | - | - | 自定义排序方法。见下方全局配置说明 |  |
| filterFn | `(sortInfo: Partial<Recordable<string[]>>) => any` | - | - | 自定义过滤方法。见下方全局配置说明 |  |
| showTableSetting | `boolean` | `false` | - | 显示表格设置工具 |  |
| tableSetting | `TableSetting` | - | - | 表格设置工具配置，见下方 TableSetting |  |
| striped | `boolean` | `true` | - | 斑马纹 |  |
| inset | `boolean` | `false` | - | 取消表格的默认 padding |  |
| autoCreateKey | `boolean` | `true` | - | 是否自动生成 key |  |
| showSummary | `boolean` | `false` | - | 是否显示合计行 |  |
| summaryData | `any[]` | - | - | 自定义合计数据。如果有则显示该数据 |  |
| emptyDataIsShowTable | `boolean` | `true` | - | 在启用搜索表单的前提下，是否在表格没有数据的时候显示表格 |  |
| summaryFunc | `(...arg) => any[]` | - | - | 计算合计行的方法 |  |
| ~~canRowDrag~~ | ~~`boolean`~~ | ~~`false`~~ | - | ~~是否可拖拽行排序~~ |  |
| ~~canColDrag~~ | ~~`boolean`~~ | ~~`false`~~ | - | ~~是否可拖拽列~~ |  |
| isTreeTable | `boolean` | `false` | - | 是否树表 |  |
| api | `(...arg: any) => Promise<any>` | - | - | 请求接口，可以直接将`src/api内的函数直接传入` |  |
| beforeFetch | `(T)=>T` | - | - | 请求之前对参数进行处理 |  |
| afterFetch | `(T)=>T` | - | - | 请求之后对返回值进行处理 |  |
| handleSearchInfoFn | `(T)=>T` | - | - | 开启表单后，在请求之前处理搜索条件参数 |  |
| fetchSetting | `FetchSetting` | - | - | 接口请求配置，可以配置请求的字段和响应的字段名，见下方全局配置说明 |  |
| immediate | `boolean` | `true` | - | 组件加载后是否立即请求接口，在 api 有传的情况下，如果为 false，需要自行使用 reload 加载表格数据 |  |
| searchInfo | `any` | - | - | 额外的请求参数 |
| useSearchForm | `boolean` | false | - | 使用搜索表单 |  |
| formConfig | `any` | - | - | 表单配置，参考表单组件的 Props |  |
| columns | `any` | - | - | 表单列信息 BasicColumn[] |  |
| showIndexColumn | `boolean` | ture | - | 是否显示序号列 |  |
| indexColumnProps | `any` | - | - | 序号列配置 BasicColumn |  |
| actionColumn | `any` | - | - | 表格右侧操作列配置 BasicColumn |  |
| ellipsis | `boolean` | `true` | - | 文本超过宽度是否显示... |  |
| canResize | `boolean` | `true` | - | 是否可以自适应高度(如果置于 PageWrapper 组件内，请勿启用 PageWrapper 的 fixedHeight 属性，二者不可同时使用) |  |
| isCanResizeParent | `boolean` | `false` | - | 是否继承父元素高度(如果置于 PageWrapper 组件内，请勿启用 PageWrapper 的 fixedHeight 属性，二者不可同时使用) |  |
| colResizable | `boolean` | `false` | - | 是否可拖动调整宽度，此时 width 必须是 number 类型，设置后全部列均可拖动调整宽度 |  |
| showSelectionBar | `boolean` | `false` | - | 是否显示多选状态栏 |  |
| clearSelectOnPageChange | `boolean` | false | - | 切换页码是否重置勾选状态 |  |
| resizeHeightOffset | `number` | 0 | - | 表格自适应高度计算结果会减去这个值 |  |
| rowSelection | `any` | - | - | 选择列配置 |  |
| title | `string` | - | - | 表格标题 |  |
| titleHelpMessage | `string ｜ string[]` | - | - | 表格标题右侧温馨提醒 |  |
| maxHeight | `number` | - | - | 表格最大高度，超出会显示滚动条 |  |
| dataSource | `any[]` | - | - | 表格数据，非 api 加载情况 |  |
| bordered | `boolean` | `false` | - | 是否显示表格边框 |  |
| pagination | `any` | - | - | 分页信息配置，为 `false` 不显示分页 |  |
| loading | `boolean` | `false` | - | 表格 loading 状态 |  |
| scroll | `any` | - | - | 参考官方文档 scroll |  |
| beforeEditSubmit | `({record: Recordable,index: number,key: string \| number,value: any}) => Promise<any>` | - | - | 单元格编辑状态提交回调，返回 false 将阻止单元格提交数据到 table。该回调在行编辑模式下无效。 | 2.7.2 |

### TableSetting

```ts
{
  // 是否显示刷新按钮
  redo?: boolean;
  // 是否显示尺寸调整按钮
  size?: boolean;
  // 是否显示字段调整按钮
  setting?: boolean;
  // 是否显示全屏按钮
  fullScreen?: boolean;
}
```

## BasicColumn

除 参考官方 [Column 配置](https://www.antdv.com/components/table-cn/#Column)外，扩展以下参数

| 属性 | 类型 | 默认值 | 可选值 | 说明 |
| --- | --- | --- | --- | --- |
| defaultHidden | `boolean` | false | - | 默认隐藏，可在列配置显示 |
| helpMessage | `string｜string[]` | - | - | 列头右侧帮助文本 |
| edit | `boolean` | - | - | 是否开启单元格编辑 |
| editRow | `boolean` | - | - | 是否开启行编辑 |
| editable | `boolean` | false | - | 是否处于编辑状态 |
| editComponent | `ComponentType` | `Input` | - | 编辑组件 |
| editComponentProps | `any` | - | - | 对应编辑组件的 props |
| editDynamicDisabled | `boolean ｜ ((record: Recordable) => boolean)` | - | - | 动态判断编辑组件是否禁用 |
| editRule | `((text: string, record: Recordable) => Promise<string>)` | - | - | 对应编辑组件的表单校验 |
| editValueMap | `(value: any) => string` | - | - | 对应单元格值枚举 |
| onEditRow | `（）=>void` | - | - | 触发行编辑 |
| format | `CellFormat` | - | - | 单元格格式化 |
| auth | `RoleEnum` ｜ `RoleEnum[]` ｜ `string` ｜ `string[]` | - | - | 根据权限编码来控制当前列是否显示 |
| ifShow | `boolean ｜ ((action: ActionItem) => boolean)` | - | - | 根据业务状态来控制当前列是否显示 |

### EditComponentType

```ts
export type ComponentType =
  | 'Input'
  | 'InputNumber'
  | 'Select'
  | 'ApiSelect'
  | 'Checkbox'
  | 'Switch'
  | 'DatePicker' // v2.5.0 以上
  | 'TimePicker'; // v2.5.0 以上
```

### CellFormat

```ts
export type CellFormat =
  | string
  | ((text: string, record: Recordable, index: number) => string | number)
  | Map<string | number, any>;
```

## 事件

::: tip 温馨提醒

除以下事件外，官方文档内的 event 也都支持，具体可以参考 [antv table](https://www.antdv.com/components/table-cn/#API)

:::

| 事件 | 回调参数 | 说明 |
| --- | --- | --- |
| fetch-success | `Function({items,total})` | 接口请求成功后触发 |
| fetch-error | `Function(error)` | 错误信息 |
| selection-change | `Function({keys，rows})` | 勾选事件触发 |
| row-click | `Function(record, index, event)` | 行点击触发 |
| row-dbClick | `Function(record, index, event)` | 行双击触发 |
| row-contextmenu | `Function(record, index, event)` | 行右键触发 |
| row-mouseenter | `Function(record, index, event)` | 行移入触发 |
| row-mouseleave | `Function(record, index, event)` | 行移出触发 |
| edit-end | `Function({record, index, key, value})` | 单元格编辑完成触发 |
| edit-cancel | `Function({record, index, key, value})` | 单元格取消编辑触发 |
| edit-row-end | `Function()` | 行编辑结束触发 |
| edit-change | `Function({column,value,record})` | 单元格编辑组件的 value 发生变化时触发 |

::: tip edit-change 说明

从版本 `2.4.2` 起，对于 `edit-change` 事件，`record` 中的 `editValueRefs` 装载了当前行的所有编辑组件（如果有的话）的值的 `ref` 对象，可用于处理同一行中的编辑组件的联动。请看下面的例子

:::

```javascript
function onEditChange({ column, record }) {
  // 当同一行的单价或者数量发生变化时，更新合计金额（三个数据均为当前行编辑组件的值）
  if (column.dataIndex === 'qty' || column.dataIndex === 'price') {
    const {
      editValueRefs: { total, qty, price },
    } = record;
    total.value = unref(qty) * unref(price);
  }
}
```

## Slots

::: tip 温馨提醒

除以下参数外，官方文档内的 slot 也都支持，具体可以参考 [antv table](https://www.antdv.com/components/table-cn/#API)

:::

| 名称              | 说明                     | 版本  |
| ----------------- | ------------------------ | ----- |
| tableTitle        | 表格顶部左侧区域         |       |
| toolbar           | 表格顶部右侧区域         |       |
| expandedRowRender | 展开行区域               |       |
| headerTop         | 表格顶部区域（标题上方） | 2.6.1 |

## Form-Slots

当开启 form 表单后。以`form-xxxx`为前缀的 slot 会被视为 form 的 slot

xxxx 为 form 组件的 slot。具体参考[form 组件文档](./form.md#Slots)

e.g

```
form-submitBefore
```

## ColumnSetting 组件

> 字段调整组件

提供了可视化操作表格每一列的是否展示、位置、固定；包括序号列、勾选列。会响应`tableMethods`中`setColumns`和`setProps`方法的更改内容。

:::warning 值得注意的是

`序号列`和`勾选列`是在 table 的 props 中定义的，对应的字段分别是`showIndexColumn`、`rowSelection`。因此在**动态改变表格列配置**的时候，建议使用**setProps**方法，并显式地设置这两个字段的值来保证达到预期效果

:::

```ts
// ...
const [registerTable, { setProps }] = useTable({...})

setProps({
  columns: [], // 表格的列配置 BasicColumn[]
  showIndexColumn: false, // 是否展示序号列
  rowSelection: false // 勾选列配置
})
```

## 内置组件（只能用于表格内部）

### TableAction

用于表格右侧操作列渲染

#### Props

| 属性 | 类型 | 默认值 | 可选值 | 说明 | 版本 |
| --- | --- | --- | --- | --- | --- |
| actions | `ActionItem[]` | - | - | 右侧操作列按钮列表 |  |
| dropDownActions | `ActionItem[]` | - | - | 右侧操作列更多下拉按钮列表 |  |
| stopButtonPropagation | `boolean` | `false` | `true/false` | 是否阻止操作按钮的 click 事件冒泡 | 2.5.0 |

**ActionItem**

```ts
export interface ActionItem {
  // 按钮文本
  label: string;
  // 是否禁用
  disabled?: boolean;
  // 按钮颜色
  color?: 'success' | 'error' | 'warning';
  // 按钮类型
  type?: string;
  // button组件props
  props?: any;
  // 按钮图标
  icon?: string;
  // 气泡确认框
  popConfirm?: PopConfirm;
  // 是否显示分隔线，v2.0.0+
  divider?: boolean;
  // 根据权限编码来控制当前列是否显示，v2.4.0+
  auth?: RoleEnum | RoleEnum[] | string | string[];
  // 根据业务状态来控制当前列是否显示，v2.4.0+
  ifShow?: boolean | ((action: ActionItem) => boolean);
  // 点击回调
  onClick?: Fn;
  // Tooltip配置，2.5.3以上版本支持，可以配置为string，或者完整的tooltip属性
  tooltip?: string | TooltipProps;
}
```

有关 TooltipProps 的说明，请参考[tooltip](https://www.antdv.com/components/tooltip-cn#API)

**PopConfirm**

```ts
export interface PopConfirm {
  title: string;
  okText?: string;
  cancelText?: string;
  confirm: Fn;
  cancel?: Fn;
  icon?: string;
}
```

### TableImg

用于渲染单元格图片,支持图片预览

#### Props

| 属性       | 类型       | 默认值  | 可选值       | 说明                             | 版本  |
| ---------- | ---------- | ------- | ------------ | -------------------------------- | ----- |
| imgList    | `string[]` | -       | -            | 图片地址列表                     |       |
| size       | `number`   | -       | -            | 图片大小                         |       |
| simpleShow | `boolean`  | `false` | `true/false` | 简单显示模式（只显示第一张图片） | 2.5.0 |
| showBadge  | `boolean`  | `true`  | `true/false` | 简单模式下是否显示计数 Badge     | 2.5.0 |
| margin     | `number`   | 4       | -            | 常规模式下的图片间距             | 2.5.0 |
| srcPrefix  | `string`   | -       | -            | 在每一个图片 src 前插入的内容    | 2.5.0 |

## 全局配置

在[componentsSettings](http://192.168.9.192/eci-frontend/base-framework/-/tree/main/src/settings/componentSetting.ts) 可以配置全局参数。用于统一整个项目的风格。可以通过 props 传值覆盖
