tm-table
v1.2.14
Published
A table component for Vue
Downloads
510
Readme
TM Table
一个功能强大的 Vue 表格组件,支持 Vue2 和 Vue3,基于 Element UI 二次封装。
✨ 特性
- 🚀 双版本支持 - 同一套代码,同时支持 Vue2 和 Vue3
- 🔍 智能搜索 - 内置多种搜索组件,支持高级搜索功能
- 📊 灵活数据源 - 支持静态数据和异步函数加载
- 🔧 列管理 - 可自定义列显示/隐藏、排序、冻结
- 📄 分页器 - 内置完整分页功能
- 🌈 国际化 - 支持中英文切换
- 🎨 高度定制 - 丰富的插槽和配置选项
- 📐 布局控制 - 支持搜索表单的布局参数配置(labelWidth、inputWidth、direction)
- 🧩 插槽扩展 - 提供多个位置插槽,支持在表格各个区域插入自定义内容
📦 安装
依赖
Element UI/Element Plus、sortable.js
安装包
npm install tm-table🚀 快速开始
引入方式
完整引入
// Vue 2
import Vue from "vue";
import TmTable from "tm-table";
import "tm-table/style";
Vue.use(TmTable);
// Vue 3
import { createApp } from "vue";
import TmTable from "tm-table/v3";
import "tm-table/v3/style";
const app = createApp();
app.use(TmTable);按需引入
// Vue 2
import { Table, Column } from "tm-table";
// Vue 3
import { Table, Column } from "tm-table/v3";基础用法
静态数据表格
<template>
<tm-table :data="tableData">
<tm-table-column prop="id" label="ID" width="80"></tm-table-column>
<tm-table-column prop="name" label="姓名" width="120"></tm-table-column>
<tm-table-column prop="email" label="邮箱"></tm-table-column>
</tm-table>
</template>
<script>
export default {
data() {
return {
tableData: [
{ id: 1, name: "张三", email: "[email protected]" },
{ id: 2, name: "李四", email: "[email protected]" },
],
};
},
};
</script>异步数据表格
<template>
<tm-table :data="loadData">
<tm-table-column prop="id" label="ID" width="80"></tm-table-column>
<tm-table-column prop="name" label="姓名" width="100"></tm-table-column>
<tm-table-column prop="email" label="邮箱"></tm-table-column>
</tm-table>
</template>
<script>
export default {
methods: {
async loadData({ paginationInfo, params }) {
// 模拟API调用
const response = await fetch(
`/api/users?page=${paginationInfo.current}&size=${paginationInfo.pageSize}`
);
const data = await response.json();
return {
list: data.list,
total: data.total,
current: data.current,
pageSize: data.pageSize,
};
},
},
};
</script>插槽使用示例
<tm-table :data="tableData" :search-columns="['custom']">
<!-- 自定义搜索组件 -->
<template #search-custom="{ searchForm }">
<el-input v-model="searchForm.custom" placeholder="自定义搜索" />
</template>
<!-- 菜单栏左侧按钮 -->
<template #menu-left>
<el-button type="primary">新增</el-button>
<el-button>导入</el-button>
</template>
<!-- 菜单栏右侧按钮 -->
<template #menu-right>
<el-button>导出</el-button>
</template>
<!-- 查询区域和菜单栏之间 -->
<template #between-search-menu>
<div style="padding: 10px; background: #f0f9ff;">
<el-alert message="数据统计:共 1000 条记录" type="info" :closable="false" />
</div>
</template>
<!-- 菜单栏和表格之间 -->
<template #between-menu-table>
<el-tabs v-model="activeTab">
<el-tab-pane label="全部商品" name="all" />
<el-tab-pane label="上架商品" name="online" />
</el-tabs>
</template>
<!-- 表格和分页器之间 -->
<template #between-table-pagination>
<div style="padding: 10px; text-align: right;">
<span>已选择 {{ selectedRows.length }} 项</span>
</div>
</template>
<!-- 分页器左侧 -->
<template #pagination-left>
<div style="padding-right: 10px;">
<el-button size="small" @click="handleExport">导出</el-button>
</div>
</template>
<!-- 分页器之后 -->
<template #after-pagination>
<div style="text-align: center; color: #999; font-size: 12px;">
© 2024 数据管理系统
</div>
</template>
<!-- 表格列 -->
<tm-table-column prop="name" label="姓名"></tm-table-column>
</tm-table>
<script>
export default {
data() {
return {
searchForm: {},
searchColumns: ["custom"],
activeTab: 'all',
selectedRows: []
};
},
};
</script>🔥 完整功能示例
包含搜索、列配置、自适应高度等完整功能:
<template>
<tm-table
:data="loadData"
:search-columns="searchColumns"
v-model:search-form="searchForm"
height="auto"
:calc-height="60"
table-key="user-list"
:column-setting-button="true"
:refresh-button="true"
:label-width="120"
:input-width="250"
direction="horizontal"
@selection-change="handleSelectionChange"
>
<!-- 自定义搜索组件 -->
<template #search-custom="{ searchForm }">
<el-input v-model="searchForm.custom" placeholder="自定义搜索" />
</template>
<!-- 菜单栏按钮 -->
<template #menu-left>
<el-button type="primary" @click="handleAdd">新增</el-button>
</template>
<!-- 表格列定义 -->
<tm-table-column type="selection" width="55"></tm-table-column>
<tm-table-column prop="id" label="ID" width="80"></tm-table-column>
<tm-table-column prop="name" label="姓名" width="120"></tm-table-column>
<tm-table-column
prop="email"
label="邮箱"
min-width="150"
></tm-table-column>
<tm-table-column
prop="department"
label="部门"
width="100"
></tm-table-column>
<tm-table-column prop="status" label="状态" width="80">
<template #default="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'info'">
{{ row.status === 'active' ? '启用' : '禁用' }}
</el-tag>
</template>
</tm-table-column>
<tm-table-column
prop="createTime"
label="创建时间"
width="160"
></tm-table-column>
<tm-table-column label="操作" width="150" fixed="right" :visible="false">
<template #default="{ row }">
<el-button size="mini">编辑</el-button>
<el-button size="mini">删除</el-button>
</template>
</tm-table-column>
</tm-table>
</template>
<script>
export default {
data() {
return {
searchForm: {},
selectedRows: [],
searchColumns: [
{
type: "input",
label: "用户搜索",
fields: { value: "keyword", label: "keywordType" },
fieldOptions: [
{ label: "姓名", value: "name" },
{ label: "邮箱", value: "email" },
],
placeholder: "请输入搜索关键词",
multiple: true,
},
"custom",
{
type: "select",
label: "部门",
fields: { value: "department" },
options: this.getDepartmentOptions,
placeholder: "请选择部门",
},
{
type: "select",
label: "状态",
fields: { value: "status" },
options: [
{ label: "启用", value: "active" },
{ label: "禁用", value: "inactive" },
],
},
{
type: "date",
label: "创建时间",
fields: { start: "startTime", end: "endTime" },
},
],
};
},
methods: {
// 异步加载数据
async loadData({ paginationInfo, params }) {
try {
const response = await this.$http.get("/api/users", {
params: {
page: paginationInfo.current,
size: paginationInfo.pageSize,
...params,
},
});
return {
list: response.data.list,
total: response.data.total,
current: response.data.current,
pageSize: response.data.pageSize,
};
} catch (error) {
this.$message.error("数据加载失败");
return { list: [], total: 0 };
}
},
// 获取部门选项(异步)
async getDepartmentOptions() {
const response = await this.$http.get("/api/departments");
return response.data.map((item) => ({
label: item.name,
value: item.id,
}));
},
// 选择变化回调
handleSelectionChange(selection) {
this.selectedRows = selection;
},
// 新增用户
handleAdd() {
// 处理新增逻辑
},
},
};
</script>📋 API 文档
Table 属性
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | --------------------- | ---------------------- | ---------------- | ------------------ | -------- | | data | 表格数据,可以是数组或异步函数 | Array / Function | — | [] | | show-search | 是否显示搜索区域 | Boolean | — | true | | show-menu | 是否显示菜单栏 | Boolean | — | true | | show-pagination | 是否显示分页器 | Boolean | — | true | | height | 表格高度,支持 'auto' 自适应 | String / Number | — | — | | calc-height | 自适应高度时需要减去的高度 | Number | — | 0 | | column-setting-button | 是否显示列配置按钮 | Boolean | — | true | | refresh-button | 是否显示刷新按钮 | Boolean | — | true | | table-key | 表格的唯一标识,作为缓存表格列配置的Key | String | — | 当前路由路径 | | first-load | 初始化时是否请求数据 | Boolean | — | true | | border | 是否显示边框 | Boolean | — | true | | search-columns | 搜索列配置,详见 SearchColumns | Array | — | [] | | search-form | 搜索表单数据(支持 v-model) | Object | — | {} | | label-width | 搜索表单标签宽度(全局配置) | String / Number | — | 100 | | input-width | 搜索表单输入框宽度(全局配置) | String / Number | — | 200 | | direction | 搜索表单布局方向(全局配置) | String | horizontal/vertical | horizontal | | label-align | 搜索表单标签对齐方式(全局配置) | String | left/right/center | left |
Table 事件
| 事件名 | 说明 | 参数 | | ------------------ | ---------- | ---------- | | selection-change | 选择项发生变化时触发 | selection | | size-change | 每页条数改变时触发 | size | | current-change | 当前页改变时触发 | current | | update:search-form | 搜索表单更新时触发 | searchForm |
Table 方法
| 方法名 | 说明 | 参数 | | ------------ | --------------- | --- | | loadData | 手动触发数据加载 | — | | refreshTable | 刷新表格布局 | — | | reloadTable | 重新加载表格(会触发重新渲染) | — |
Table 插槽
| 插槽名 | 说明 | 参数 | | ------------------------ | -------------------------------------- | ------------------- | | search | 自定义整个搜索区域 | — | | menu-left | 菜单栏左侧内容 | — | | menu-right | 菜单栏右侧内容 | — | | search-{prop} | 自定义列搜索框内容,prop 为 searchColumns 中配置的插槽名 | { searchForm, col } | | between-search-menu | 查询区域和菜单栏之间的内容 | — | | between-menu-table | 菜单栏和表格之间的内容 | — | | between-table-pagination | 表格和分页器之间的内容 | — | | pagination-left | 分页器左侧内容 | — | | after-pagination | 分页器之后的内容 | — |
Column 属性
TmColumn 基于 el-table-column 封装,支持所有 el-table-column 的属性,额外增加:
| 参数 | 说明 | 类型 | 默认值 | | ----------- | ------------------------------- | ------- | ---- | | prop | 必填,列配置基于该属性实现,同一table中,该值不可重复定义 | String | — | | visible | 默认显示当前列 | Boolean | true | | cancellable | 在列配置中,是否可取消 | Boolean | true |
其他属性参考 Element UI Table Column
📐 布局配置
搜索表单布局控制
TmTable 支持灵活的搜索表单布局配置,包括标签宽度、输入框宽度和布局方向。
全局配置
在 tm-table 组件上设置全局布局参数,影响所有搜索表单项:
<tm-table
:data="tableData"
:search-columns="searchColumns"
:label-width="120"
:input-width="250"
direction="vertical"
>
<!-- 表格列定义 -->
</tm-table>单独配置
在 searchColumns 中为特定表单项设置布局参数,优先级高于全局配置:
searchColumns: [
{
type: 'input',
label: '商品名称',
fields: { value: 'name' },
// 单独配置,会覆盖全局配置
labelWidth: 150,
inputWidth: 300,
direction: 'horizontal'
},
{
type: 'select',
label: '商品状态',
fields: { value: 'status' },
// 不设置布局参数,使用全局配置
options: [...]
}
]配置优先级
参数优先级:单独配置 > 全局配置 > 默认值
- 单独配置:在 searchColumns 中为特定项设置的参数
- 全局配置:在 tm-table 组件上设置的参数
- 默认值:labelWidth: 100px, inputWidth: 200px, direction: 'horizontal', labelAlign: 'left'
布局模式
横向布局(horizontal):
- 标签和输入框在同一行
- 适合标签文字较短的场景
- 默认模式
纵向布局(vertical):
- 标签和输入框分两行显示
- 标签和输入框之间间距 2px
- 适合标签文字较长或需要更多垂直空间的场景
完整示例
<template>
<tm-table
:data="loadData"
:search-columns="searchColumns"
:label-width="layoutConfig.labelWidth"
:input-width="layoutConfig.inputWidth"
:direction="layoutConfig.direction"
>
<tm-table-column prop="name" label="商品名称" />
<tm-table-column prop="status" label="状态" />
</tm-table>
</template>
<script>
export default {
data() {
return {
layoutConfig: {
labelWidth: 120, // 全局标签宽度
inputWidth: 250, // 全局输入框宽度
direction: 'horizontal' // 全局布局方向
},
searchColumns: [
{
type: 'input',
label: '商品名称',
fields: { value: 'name' },
// 使用全局配置
},
{
type: 'input',
label: '详细描述信息',
fields: { value: 'description' },
// 单独配置,适合长标签
labelWidth: 150,
direction: 'vertical'
},
{
type: 'select',
label: '状态',
fields: { value: 'status' },
// 单独配置更宽的输入框
inputWidth: 300,
options: [...]
}
]
}
}
}
</script>🔍 搜索功能详解
SearchColumns 配置
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------ | ---------- |
| type | 搜索项表单类型 | String | input|select|cascader|autocomplete|date|radio|checkbox|switch|textarea | input |
| label | 表单左侧文案 | String | — | — |
| placeholder | 输入框占位符 | String | — | — |
| fields | 字段映射配置,当表单值发生改变时同步修改 search-form 绑定字段的值无默认值:{ value: "valueField" }有默认值1:{ value: ["valueField", "默认值"] }有默认值2:{ value: { name: "value1", default: "默认值" } }注:默认值在表单初始化和重置时生效 | Object | — | — |
| fieldOptions | 字段类型选项,格式:[{label:"用户名", value:"userName"}] | Array | — | [] |
| options | 选项配置,type 为 select|cascader 时可用 | Array | — | [] |
| multiple | type 为 input 时表示精确搜索,为 select 或 cascader 时表示多选 | Boolean | — | false |
| checkAll | 全选功能,type 为 select 时有效;type 为 cascader 且 props.multiple: true 时也有效 | Boolean | — | false |
| labelWidth | 单独配置该项的标签宽度(优先级高于全局配置) | String/Number | — | 继承全局配置 |
| inputWidth | 单独配置该项的输入框宽度(优先级高于全局配置) | String/Number | — | 继承全局配置 |
| direction | 单独配置该项的布局方向(优先级高于全局配置) | String | horizontal/vertical | 继承全局配置 |
各类型搜索组件配置
Input 输入框搜索
{
type: 'input',
label: '名称搜索',
fields: {
value: 'name', // 必需:值字段
label: 'nameType' // 可选:字段类型
},
fieldOptions: [ // 可选:字段选择器选项
{ label: '姓名', value: 'name' },
{ label: '邮箱', value: 'email' }
],
placeholder: '请输入搜索内容',
multiple: true // 可选:是否支持批量精确搜索
}Select 下拉选择
{
type: 'select',
label: '状态',
fields: { value: 'status' },
options: [ // 静态选项
{ label: '启用', value: 'active' },
{ label: '禁用', value: 'inactive' }
],
// 或者异步选项
options: async () => {
const res = await fetch('/api/options');
return res.json();
},
multiple: true, // 可选:是否多选
checkAll: true, // 可选:是否显示全选
placeholder: '请选择状态'
}Date 日期选择
{
type: 'date',
label: '日期范围',
fields: {
start: 'startDate', // 开始日期字段
end: 'endDate' // 结束日期字段
},
fieldOptions: [ // 可选:日期类型选择
{ label: '创建时间', value: 'createTime' },
{ label: '更新时间', value: 'updateTime' }
],
startPlaceholder: '开始日期',
endPlaceholder: '结束日期'
}Cascader 级联选择
{
type: 'cascader',
label: '地区',
fields: { value: 'region' },
options: [
{
value: 'beijing',
label: '北京',
children: [
{ value: 'chaoyang', label: '朝阳区' },
{ value: 'haidian', label: '海淀区' }
]
}
],
props: { // Element UI cascader 属性
multiple: true,
checkStrictly: true
},
checkAll: true // 多选模式下在下拉面板顶部显示“全选”复选框
}
checkAll仅在props.multiple: true时生效;会遍历所有叶子节点并尊重emitPath,禁用节点自动跳过。
Autocomplete 自动补全
基于 el-autocomplete,支持异步取数(fetchSuggestions)或静态本地过滤(options)。
// 异步:模拟接口
{
type: 'autocomplete',
label: '供应商搜索',
fields: { value: 'supplier_keyword' },
placeholder: '输入关键字搜索供应商',
valueKey: 'name', // 候选项中作为显示/输入的字段
fetchSuggestions: (query, cb) => {
// 签名同 el-autocomplete:拿到结果后调用 cb(items)
api.searchSupplier(query).then(cb)
}
}
// 静态:本地模糊过滤
{
type: 'autocomplete',
label: '城市',
fields: { value: 'city_keyword' },
options: [{ value: '北京' }, { value: '上海' }, { value: '广州' }]
}| 字段 | 说明 | 类型 | 默认值 |
| ----------------- | --------------------------------------------------- | -------- | ------- |
| valueKey | 候选项中用于显示和输入的字段 | String | value |
| fetchSuggestions | 异步取数函数,签名 (query, cb) => cb(items);未传则用 options | Function | — |
| options | 静态候选项数组(fetchSuggestions 优先级更高) | Array | [] |
| triggerOnFocus | 聚焦时是否触发一次取数 | Boolean | true |
| clearable | 是否显示清除按钮 | Boolean | true |
其他类型
// 单选框
{ type: 'radio', label: '性别', fields: { value: 'gender' }, options: [...] }
// 多选框
{ type: 'checkbox', label: '兴趣', fields: { value: 'interests' }, options: [...] }
// 开关
{
type: 'switch',
label: '是否启用',
fields: { value: 'enabled' },
activeText: '启用',
inactiveText: '禁用'
}
// 文本域
{
type: 'textarea',
label: '备注',
fields: { value: 'remark' },
placeholder: '请输入备注信息',
rows: 3, // 行数,默认2
maxlength: 500, // 最大长度
showWordLimit: true, // 显示字数限制
autosize: { minRows: 2, maxRows: 6 }, // 自适应高度
resize: 'vertical', // 调整大小,默认vertical
clearable: true // 可清除,默认true
}自定义搜索组件
使用字符串形式配置,然后通过插槽自定义:
<tm-table :search-columns="['customSearch']">
<template #search-customSearch="{ searchForm }">
<el-input v-model="searchForm.custom" placeholder="自定义搜索" />
</template>
</tm-table>带搜索的完整示例
<template>
<tm-table
:data="loadData"
:search-columns="searchColumns"
v-model:search-form="searchForm"
>
<tm-table-column prop="name" label="姓名"></tm-table-column>
<tm-table-column prop="department" label="部门"></tm-table-column>
<tm-table-column prop="createTime" label="创建时间"></tm-table-column>
</tm-table>
</template>
<script>
export default {
data() {
return {
searchForm: {},
searchColumns: [
{
type: "input",
label: "姓名搜索",
fields: { value: "name" },
placeholder: "请输入姓名",
},
{
type: "select",
label: "部门",
fields: { value: "department" },
options: [
{ label: "技术部", value: "tech" },
{ label: "产品部", value: "product" },
],
},
],
};
},
};
</script>🧩 其他组件
EllipsisText 省略文本
用于显示可能过长的文本,支持省略和 tooltip。
基础用法
<template>
<ellipsis-text
text="这是一段很长的文本,超出指定宽度时会显示省略号"
:width="200"
:copyable="true"
/>
</template>EllipsisText 属性
| 参数 | 说明 | 类型 | 默认值 | | ------------ | ------------ | ------------- | ----- | | text | 要显示的文本 | String | — | | width | 文本容器宽度 | String/Number | auto | | max-width | 文本最大宽度 | String/Number | 100% | | lines | 显示的行数 | Number | 1 | | show-tooltip | 是否显示 tooltip | Boolean | true | | copyable | 是否显示复制按钮 | Boolean | false |
🌍 国际化
使用方法
import { setLang } from "tm-table";
// 设置语言
setLang("en-US"); // 英文
setLang("zh-CN"); // 中文(默认)支持的语言
- 中文(zh-CN)
- 英文(en-US)
📊 数据格式规范
异步数据函数
当 data 属性传入函数时,函数接收参数并返回指定格式:
async function loadData({ paginationInfo, params }) {
// paginationInfo: { current: 1, pageSize: 20 }
// params: 搜索表单的数据
return {
list: [], // 数据列表(必需)
total: 0, // 总条数(必需)
current: 1, // 当前页(可选)
pageSize: 20, // 每页条数(可选)
};
}选项数据格式
所有选择类组件的选项支持:
// 静态数组
options: [{ label: "显示文本", value: "值", disabled: false }];
// 异步函数
options: async () => {
const response = await fetch("/api/options");
return response.json();
};📝 更新日志
1.2.14
- 🎯 新增
autocomplete搜索组件,基于el-autocomplete,支持异步fetchSuggestions与静态options
1.2.11
- 🎯 Cascader 多选支持
checkAll全选 - 🐛 修复 Vue3 下 Cascader v-model 不生效
1.2.10
- 🎯 新增方法
- 新增
getTableRef()方法,用于获取内部 el-table 实例
- 新增
1.2.8
- 🐛 修复热更新场景下的性能问题
- 修复
EllipsisText组件在热更新时可能导致页面卡死的问题 - 为溢出检测添加递归深度限制和并发保护机制
- 修复
1.2.7
- 🐛 修复列显示控制问题
- 修复
tm-table-column组件上v-if指令不生效的问题 - 支持
v-show指令动态控制列的显示和隐藏 - 优化列配置功能,确保在
v-if变化时正确保持列配置(排序、可见性等) - 兼容 Vue2 和 Vue3,自动检测插槽变化并更新列定义
- 修复
- 🧩 新增插槽
- 新增
pagination-left插槽,支持在分页器左侧添加自定义内容(如导出按钮、批量操作等)
- 新增
1.2.3
- 🎯 新增布局配置功能
- 支持搜索表单的
labelAlign参数配置 - 支持全局配置和单独配置,单独配置优先级更高
- 支持搜索表单的
- 🎯 去除部分配置
textarea组件,不再支持rows配置,使用autosize代替
1.2.1
- 📝 新增 textarea 搜索组件
- 支持多行文本输入搜索
- 提供清除功能
1.2.0
- 🎯 新增布局配置功能
- 支持搜索表单的
labelWidth、inputWidth、direction参数配置 - 支持全局配置和单独配置,单独配置优先级更高
- 新增横向和纵向两种布局模式
- 支持搜索表单的
- 🧩 新增插槽扩展
- 新增
between-search-menu插槽(查询区域和菜单栏之间) - 新增
between-menu-table插槽(菜单栏和表格之间) - 新增
between-table-pagination插槽(表格和分页器之间) - 新增
after-pagination插槽(分页器之后)
- 新增
- 🔧 组件架构优化
- 重构过滤器组件,提取通用的
FormItemLayout组件 - 统一搜索表单的布局和样式处理
- 优化参数传递链路,支持层级配置覆盖
- 重构过滤器组件,提取通用的
1.0.82
- 新增 Vue3 支持
- 优化搜索组件
- 修复已知问题
📄 许可证
MIT
