行列形式展示一组结构化的数据或内容。
适用:用于大量数据展示或需要横纵对比的场景。
不适用:非结构化数据的展示或无横纵对比诉求的场景,此时可用列表。
通用原则
- 表格中允许展示静态数据或操作;允许单列折叠展开;允许批量操作
- 高度根据内容自适应
- 表格中信息重要层级从左到右依次递减
- 左侧第一栏通常为标题,操作栏放于最右侧;标题栏和操作栏可固定,只滚动内容区
- 表头的标题保证简洁可读,不可折行,尽量避免「…」
表头可在批量操作时变为操作栏
构成
- 操作:当有滚动条的时候,表头可吸顶。
- 左侧为表格操作,默认操作数不超过 3 个,第 4 个操作收到「 更多 」中,可搭配图标,未勾选时批量操作状态为 Disable
- 右侧为简易搜索框、高级搜索开关、表格刷新按钮及展示列设置按钮
- 表头:当有滚动条的时候,表头可吸顶。
- 批量勾选(可选):默认一直出现,用户随时勾选后,批量操作按钮由 Disable 状态变为 Normal 状态
- 列标题:
- 言简意赅,不允许折行,可带单位,如「 数量(个) 」,超过可「 … 」;建议不超过 6 个汉字
- 有左右滚动条的时候,首列允许固定
- 操作列:有左右滚动条的时候,允许固定
- 内容区:允许上下左右滚动。
- 序列 / 多选(可选)
- 展开/收起(有树形结构的时候使用)
- 单行操作:操作数默认不超过 4 个,第 5 个操作收起到「 更多 」中
- 分页
行为
批量操作
- 当表格支持批量操作时,应出现 Checkbox 列
- 批量操作区出现在表格之上
- 操作按钮允许搭配图标
方式1:点击表头的全选 Checkbox
- 列表进入批量操作状态
- 所有行全部被选中
- 再次点击反选所有列,退出批量操作状态,回到正常状态
方式2:选择单列
- 选中列表最左侧的 checkbox,进入批量操作状态
- 表头的全选 Checkbox 为半选状态
- 此时点击表头全选 Checkbox 为选择所有列,再次点击表头全选 Checkbox 为清除所有选择,退出批量操作状态,回到正常状态
单列操作
- 列操作通常包括排序、筛选、拖拽列宽度等常规操作
- 表格数据为空时,批量操作均不可用
- 支持的操作有解释说明、排序、筛选
- 解释说明:移入「解释说明 icon」出现
Tooltip
,对标题进行解释
- 排序:
- 点击列标题或「排序图标」即生效
- 可反复点击在正排、倒排、默认排序中切换
- 生效的时候,「排序图标」须展示在列标题右侧且高亮
- 点击列标题或「排序图标」即生效
- 筛选:
- 若当前列支持筛选,鼠标 hover 列标题出现「筛选 icon」
- 点击「筛选 icon」唤起进一步操作
- 生效的时候,「筛选 icon」须展示在列标题右侧且高亮
- 当筛选规则失效的时候,「筛选 icon」消失,并回到默认状态
- 若当前列支持筛选,鼠标 hover 列标题出现「筛选 icon」
调整列宽度
- 鼠标移入列标题栏的分割线时,可直接拖拽分隔线,调整列宽度
- 单列可根据业务需要设定最小/大宽度
单行操作
- 操作采用文字按钮
- 操作数默认不超过 4 个,第 5 个操作收起到「 更多 」中
展开/收起
- 展开收起表格取消表格斑马线
- 当列收起时,点击「 + 」展开下一级
- 当列展开时,点击「 - 」收起所有下一级
容器相关
- 三大状态:单列有 Normal、Hover、Selected 3 种状态
- 加载:内容区加载
- 空列表
样式
布局
- 可在独立页面、弹窗、滑块面板(SlidePanel)中使用
- 表格标题的对齐应与内容对齐
- 内容行:文字和图标首选左对齐,若数字内容中不会出现小数点则首选右对齐。有小数点则小数点对齐
开发
代码演示
表格不建议封装成组件,所以提供以下模板样式,更多API,请参考https://www.antdv.com/components/table-cn/
.y-common-table-contanier{
.y-common-table-operator-group{
margin-bottom: 16px;
.ant-btn{
&+.ant-btn{
margin-left: 14px;
}
}
}
.y-table-column-action-button{
display: flex;
align-items: center;
.spacing{
width: 1px;
height: 8px;
background-color: #ebeef5;
margin: 0 8px;
}
}
}
<template>
<div class="y-common-table-contanier">
<div class="y-common-table-operator-group">
<a-button type="primary" icon="play-circle"> 操作 </a-button>
<a-button icon="poweroff"> 关机 </a-button>
<a-button icon="loading"> 重启 </a-button>
<a-dropdown>
<a-menu slot="overlay" @click="handleMenuClick">
<a-menu-item key="1"> 1st item </a-menu-item>
<a-menu-item key="2"> 2nd item </a-menu-item>
<a-menu-item key="3"> 3rd item </a-menu-item>
</a-menu>
<a-button>
更多
<a-icon type="down" />
</a-button>
</a-dropdown>
</div>
<a-table bordered :components="components" :columns="columns" :data-source="data" :pagination="false">
<a slot="name" slot-scope="text" class="y-button-link">{{ text }}</a>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" :key="tag" :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'">
{{ tag.toUpperCase() }}
</a-tag>
</span>
<template v-slot:action>
<div class="y-table-column-action-button">
<y-button-action text="新增" />
<span class="spacing"></span>
<y-button-action text="修改" />
<span class="spacing"></span>
<y-button-action text="复制" />
<span class="spacing"></span>
<y-button-action text="删除" />
<span class="spacing"></span>
<a-dropdown :trigger="['click']">
<y-button-action text="更多" />
<a-menu>
<a-menu-item>
<a href="javascript:;">a</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</div>
</template>
</a-table>
</div>
</template>
<script>
import Vue from 'vue';
import VueDraggableResizable from 'vue-draggable-resizable';
Vue.component('vue-draggable-resizable', VueDraggableResizable);
const columns = [
{
title: '标题',
dataIndex: 'name',
key: 'name',
scopedSlots: { customRender: 'name' },
width: 200,
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'Jim',
value: 'Jim',
},
],
// specify the condition of filtering result
// here is that finding the name started with `value`
onFilter: (value, record) => record.name.indexOf(value) === 0,
sorter: (a, b) => a.name.length - b.name.length,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 200,
},
{
title: 'AddressAddressAddressAddress',
dataIndex: 'address',
key: 'address',
ellipsis: true,
width: 200,
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
scopedSlots: { customRender: 'tags' },
width: 200,
},
{
title: '操作',
key: 'action',
width: 300,
scopedSlots: { customRender: 'action' },
},
];
const data = [
{
key: '1',
name: '内容文本,点击可进入详情',
age: 32,
address: 'New York No. 1 Lake Park New York NoNew York NoNew York NoNew York NoNew York NoNew York NoNew York No',
tags: ['nice', 'developer'],
},
{
key: '2',
name: '内容文本,点击可进入详情',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: '内容文本,点击可进入详情',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
const draggingMap = {};
columns.forEach((col) => {
draggingMap[col.key] = col.width;
});
const draggingState = Vue.observable(draggingMap);
const ResizeableTitle = ({ props, children }) => {
let thDom = null;
const { key, ...restProps } = props;
const col = columns.find((col) => {
const k = col.dataIndex || col.key;
return k === key;
});
if (!col.width) {
return <th {...restProps}>{children}</th>;
}
const onDrag = (x) => {
draggingState[key] = 0;
col.width = Math.max(x, 1);
};
const onDragstop = () => {
draggingState[key] = thDom.getBoundingClientRect().width;
};
return (
<th {...restProps} v-ant-ref={(r) => (thDom = r)} width={col.width} class="resize-table-th">
{children}
<vue-draggable-resizable
key={col.key}
class="table-draggable-handle"
w={10}
x={draggingState[key] || col.width}
z={1}
axis="x"
draggable={true}
resizable={false}
onDragging={onDrag}
onDragstop={onDragstop}
></vue-draggable-resizable>
</th>
);
};
export default {
data() {
this.components = {
header: {
cell: ResizeableTitle,
},
};
return {
data,
columns,
selectedRowKeys: [], // Check here to configure the default column
};
},
methods: {
onSelectChange(selectedRowKeys) {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.selectedRowKeys = selectedRowKeys;
},
handleMenuClick(e) {
console.log('click', e);
},
},
};
</script>
<style lang="less">
.resize-table-th {
position: relative;
.table-draggable-handle {
height: 100% !important;
bottom: 0;
left: auto !important;
right: -5px;
cursor: col-resize;
touch-action: none;
}
}
.table-draggable-handle {
transform: none !important;
position: absolute;
}
</style>