# TableLayout

根据 UI 统一设计，封装的表格常规布局组件。

# Feature

## 产品，UI 诉求

- 取消操作弹窗，仅保留提示类弹窗（提示类的定义包括删除，异常等需要确认的提示，以及不需要确认的弱提示）

- 在数据列表内，对当前页列表内数据编辑或新增时，统一从右侧拉出抽屉式子页面，左侧保留 3 列默认字段，右侧保存或返回时，关闭抽屉返回列表页

- 抽屉态下，如果是对某一确认数据行操作，则左侧对应行高亮，如果是全局，则左侧无可操作区域。

- 集团平台组织结构树保留，抽屉状态下 也展开，只是宽度缩小

# Usage

`TableLayout` 相关组件（由于 name 过长，这里采用缩写 `Tl`）。

- Tl
- TlMain
- TlDrawer
- TlFilter
- TlFilterLeft
- TlFilterRight
- TlFilterSecond

## 注册

一般采用全局注册的方式

```ts
import TableLayout from '@kt/unity-table-layout';
app.use(TableLayout);
```

## 基本布局

```html
<tl>
  <tl-filter>
    <a-form-item>
      <a-input placeholder="姓名" v-model:value="name"></a-input>
    </a-form-item>
    <a-form-item>...</a-form-item>

    <a-button type="primary" @click="handleSearch">查询</a-button>
    <a-button>...</a-button>
  </tl-filter>

  <tl-filter-second>
    <a-input placeholder="其他" size="small"></a-input>
  </tl-filter-second>

  <tl-table>
    <a-table ...>...</a-table>
  </tl-table>
</tl>
```

> tips：tl-filter-second 内部的所有表单元素都使用 size="small"

## tl-filter 左右布局

`tl-filter` 子级被分成 `tl-filter-left` 和 `tl-filter-right` 两部分

```html
...
<tl-filter>
  <tl-filter-left>
    <a-form-item>
      <a-input placeholder="姓名" v-model:value="name"></a-input>
    </a-form-item>
    <a-form-item>...</a-form-item>
    <a-button type="primary" @click="handleSearch">查询</a-button>
    <a-button>...</a-button>
  </tl-filter-left>
  <tl-filter-right>
    <a-button type="link">导出</a-button>
  </tl-filter-right>
</tl-filter>
...
```

## 带有 drawer 的布局

`tl` 子级被分成 `tl-main` 和 `tl-drawer` 两部分

```html
<tl v-model:drawer="drawer.visible">
  <tl-main>
    <tl-filter>
      <a-form-item>
        <a-input placeholder="姓名" v-model:value="name"></a-input>
      </a-form-item>
      <a-form-item>...</a-form-item>

      <a-button type="primary" @click="handleSearch">查询</a-button>
      <a-button>...</a-button>
    </tl-filter>

    <tl-filter-second>
      <a-input placeholder="其他" size="small"></a-input>
    </tl-filter-second>

    <tl-table>
      <a-table ...>...</a-table>
    </tl-table>
  </tl-main>
  <tl-drawer :title="drawer.title" :spin="{ spinning: drawer.spinning }">
    <template #buttons>
      <a-button type="primary" size="small" @click="handleConfirm">
        修改
      </a-button>
      <a-button size="small" @click="drawer.visible = false">返回</a-button>
    </template>
  </tl-drawer>
</tl>
```

> tips：tl-drawer 的 buttons 插槽，内部的所有按钮都使用 size="small"

## tl 嵌套 tl 的布局

```html
<tl v-model:drawer="drawer.visible">
  <tl-main>
    ...
    这里可以是一个表格，也可以是一个表单，或者其他。
    ...
  </tl-main>
  <tl-drawer>
    <tl>
      <tl-filter>...</tl-filt>
      <tl-table>...</tl-table>
    </tl>
  </tl-drawer>
</tl>
```

# API

## TableLayout

| 参数            | 说明                                 | 类型    | 默认值 |
| --------------- | ------------------------------------ | ------- | ------ |
| fixed           | 开启固定模式，<br>表格 body 开启滚动 | boolean | true   |
| drawer(v-model) | 是否显示右侧抽屉                     | boolean | false  |

## TableLayoutTable

| 参数           | 说明                                                             | 类型           | 默认值                    |
| -------------- | ---------------------------------------------------------------- | -------------- | ------------------------- |
| visibleColumns | 抽屉显示时，需要显示的列索引                                     | number[]       | [0,1,2]<br>默认表格前三列 |
| visibleAction  | 抽屉显示时，需要显示的操作列索引<br>(可以配置'last'表示最后一列) | number\|string | -                         |
| visibleButton  | 抽屉显示时，需要显示的操作列里的 button 索引                     | number         | -                         |

## TableLayoutDrawer

| 参数  | 说明                                                             | 类型           | 默认值 |
| ----- | ---------------------------------------------------------------- | -------------- | ------ |
| title | drawer 标题                                                      | string \| slot | ''     |
| spin  | 参考 [antd 官网 spin](https://next.antdv.com/components/spin-cn) | object         | -      |

## TableLayoutDrawer Slots

| 插槽名称 | 说明                            | 参数 |
| -------- | ------------------------------- | ---- |
| title    | 抽屉标题                        | -    |
| buttons  | 抽屉顶部按钮组                  | -    |
| default  | 默认插槽，一般放置表单/详情展示 | -    |

# 蓝湖参考

- [表格左右布局形式](https://lanhuapp.com/web/#/item/project/detailDetach?pid=ff997f2c-0ea3-4901-8f3a-49f76a11e747&project_id=ff997f2c-0ea3-4901-8f3a-49f76a11e747&image_id=c1241130-6f6c-406a-ad20-8861d361852b&fromEditor=true)

- [表格左右布局形式 2](https://lanhuapp.com/web/#/item/project/detailDetach?pid=ff997f2c-0ea3-4901-8f3a-49f76a11e747&project_id=ff997f2c-0ea3-4901-8f3a-49f76a11e747&image_id=058a7aef-1a4f-482f-84f3-94fb327bb435&fromEditor=true)

# 注意

- TableLayout 可以配置打开 drawer 时，左边还可以有操作按钮进行操作。如果开启了操作列，就意味着这可能是一个异步请求展示右边 drawer 的内容。这个行为可能会很频繁（我们不能保证用户的网络情况都良好），所以这里需要做接口的取消行为，保证请求数据的准确性

  参考 axios 取消请求：https://axios-http.com/zh/docs/cancellation

  ✨ demo

  ```js
  let controller: AbortController;
  const handleFoo = async (record: any) => {
    // 请求前取消上一次还在 pending 的接口
    controller?.abort();
    controller = new AbortController();

    // ...

    drawer.spinning = true;
    const { code, data, error } = await request.get(
      'foo/bar',
      {
        foo: 'bar'
      },
      {
        signal: controller.signal
      }
    );
    // 当前如果是被取消的话，不要关闭 loading
    if (code === 4400 && error?.message === 'canceled') {
      drawer.spinning = true;
    } else if (code === 2000) {
      drawer.spinning = false;

      // ...
    }
  };
  ```

# FAQ

- 什么时候使用 TableLayout 组件？

  TableLayout 组件包含了顶部的条件过滤以及灵活的 flex 布局会自动铺满 table body 的高度，溢出滚动，符合目前 UI 的规范。包含 drawer 抽屉功能：增、改、查都可以在抽屉体现。

- 点击表格的某个单元格，查看详情，详情是一个表格，之前是通过弹框，现在要用 TableLayout 的 drawer 吗？[蓝湖例子](https://lanhuapp.com/web/#/item/project/detailDetach?pid=ff997f2c-0ea3-4901-8f3a-49f76a11e747&image_id=a4ebde23-757f-480f-9b0d-ddf9a4cc3acd&tid=3e189f00-e766-420d-a5d1-e963c303b08c&project_id=ff997f2c-0ea3-4901-8f3a-49f76a11e747&fromEditor=true)

  是的。目前 TableLayout 有做了处理，可以实现 TableLayout 嵌套 TableLayout 来处理这种场景，也就是类似这样子的嵌套：

  ```html
  <tl>
    <tl-main>...</tl-main>
    <tl-drawer>
      <tl>
        <tl-filter>...</tl-filter>
        <tl-table>
          <a-table />
        </tl-table>
      </tl>
    </tl-drawer>
  </tl>
  ```

- 使用 TableLayout 打开抽屉，如果在抽屉这块还需要有交互，比如说修改表格的某一行，那怎么处理？

  这个时候就需要使用弹框来处理了。总结一下有两级交互，第一级是在 drawer 处理，如果 drawer 还有交互才在 modal（第二级） 处理。
