Appearance
PcTable 通用表格
PcTable 是基于 Element Plus Table 封装的业务表格组件,用于管理后台列表页。支持接口自动请求、外部数据传入、分页、多选列、序号列、自定义列渲染与操作列。
基础用法
由父组件维护数据时,直接传入 tableList 即可渲染。列配置的 prop 会对应同名插槽,可用于自定义状态列、操作列等。
loading
自动请求
传入 requestApi 后,组件负责请求数据、维护 loading 与分页。接口入参为 { pageNo, pageSize, ...reqParams },返回结构需为 { data: { records, total } }。配合 useTablePage 管理表格引用,可通过 requestTable('reset' | 'default') 主动刷新。
loading
多选与序号列
设置 selection 显示多选列,typeIndex 显示序号列。可通过 selectable 控制某行勾选框是否可用,并监听 selection-change 获取选中项。
loading
列渲染
单元格内容有三种渲染方式:默认显示 row[prop];使用 #[prop] 插槽自定义;或在列配置里用 render 函数返回 VNode。表头可用 #header-[prop] 插槽自定义。
loading
与 PcSearch 联动
搜索区使用 PcSearch,推荐用 useTablePage 统一管理 tableRef / searchRef,在 request 事件里调用 requestTable() 刷新数据并回到第一页。
loading
行拖拽排序
开启 row-draggable 后可拖拽行来调整顺序。该能力基于 sortablejs 实现,但不打进组件包,需使用方自行安装并在 install 阶段注入,避免与宿主应用重复打包:
ts
// main.ts
import Sortable from "sortablejs" // 使用方自行 npm install sortablejs
import PcTable from "@sud-web/vue-ui"
app.use(PcTable, { sortable: Sortable })loading
拖拽把手(推荐)
默认整行都可抓取拖拽。若希望只有某个图标能起手(更贴近后台表格习惯),给该列配置加 drag: true:该列单元格会渲染抓手图标,Sortable 自动切到 handle 模式,只有抓住图标才能拖拽。
ts
const columns = [
{ label: '排序', prop: 'sort', width: 90, drag: true }, // 该列显示抓手
{ label: '名称', prop: 'name', minWidth: 160 }
]抓手图标可用 #drag-handle 插槽整体替换(作用域为 { row, column, $index });不传则用内置默认图标:
vue
<PcTable :colum-list="columns" row-draggable @row-drag-end="onRowDragEnd">
<template #drag-handle>
<el-icon><Rank /></el-icon>
</template>
</PcTable>
drag: true需配合row-draggable才生效(row-draggable仍是总开关,负责注入 Sortable 与抛row-drag-end)。也可用sortable-options.handle自定义 handle 选择器,传入后以你的值为准。
落点提示线
拖拽时组件内置了「落点插入线」样式(行顶一条线 = 插入边界,方向唯一、不再是含糊的整块高亮),无需额外写 CSS。线的颜色用 drop-line-color 配置(默认取主题色 --el-color-primary):
vue
<PcTable :colum-list="columns" row-draggable drop-line-color="#f56c6c" ... />想换回整块或自定义样式,可用
sortable-options.ghostClass覆盖内置的ghostClass。
禁止某行拖拽
传 row-draggable-fn,返回 false 的行会被禁止拖拽:该行不渲染把手图标、也无法起手拖动(其余行照常)。
vue
<PcTable
:colum-list="columns"
row-draggable
:row-draggable-fn="(row) => row.locked !== true"
/>该能力只阻止「被拖动」(作为拖拽源)。若还想让被锁定行的位置在别人拖动时也不被挤动,可再通过
sortable-options.onMove返回false拦截。
交互流程(乐观拖拽 + 后端确认回滚)
组件只负责抛事件、不改数据。拖拽结束抛 row-drag-end,并进入 loading 等待使用方确认:
vue
<template>
<PcTable
:table-list="list"
:colum-list="columns"
row-draggable
hide-pagination
row-key="id"
@row-drag-end="onRowDragEnd"
/>
</template>
<script setup>
async function onRowDragEnd({ oldIndex, newIndex, row, oldRow, position, done }) {
try {
// 推荐「单锚点相对契约」:把 row 插到 oldRow 的前/后,position 组件已算好
// position: 'after'=下拖(落在 oldRow 之后)、'before'=上拖(落在 oldRow 之前)
// 相对意图对并发更稳:别处被人改序也不影响「插到某行前/后」这个语义
await api.reorder({ id: row.id, anchorId: oldRow.id, position })
// 成功:更新数据(本地 splice 或 requestData())让新顺序生效
const next = [...list.value]
const [moved] = next.splice(oldIndex, 1)
next.splice(newIndex, 0, moved)
list.value = next
done(true)
} catch {
done(false) // 失败:组件把行还原回原位,等于拖拽取消
}
}
</script>- 成功:调
done(true),并由使用方更新数据(splice或requestData())驱动渲染出新顺序。 - 失败:调
done(false),组件把被拖动行还原回拖拽前的位置,并弹出失败提示(ElMessage)。提示文案通过locale.dragFail配置,默认「拖拽失败」。 done必须调用,否则 loading 不会结束。
落位精度由组件内部保证:
onEnd里会先还原 sortablejs 造成的 DOM 移动,再交给 Vue 按数据渲染,避免真实 DOM 与虚拟 DOM 错位导致的落位偏移。因此即使不传row-key也不会错位、失败也能回滚。
何时需要传 row-key
row-key 通过 $attrs 透传给内部 el-table。是否需要传取决于是否涉及行身份追踪,而非落位精度:
| 场景 | 是否需要 row-key | 原因 |
|---|---|---|
纯拖拽排序,不含多选/展开,成功后本地 splice 保持同一行对象引用 | 不需要 | 落位由组件的 DOM 还原逻辑保证 |
拖拽 + selection 多选,且希望重排后勾选状态稳定跟随对应行 | 建议传 | el-table 默认按行对象引用记录选中,splice 能保持;但一旦 requestData 重新拉取生成新对象,无 row-key 勾选会丢 |
成功后用 requestData() 从后端重新拉取数据来体现新顺序,并要保持多选/展开 | 必须传 | 新数据是全新对象,el-table 需靠 row-key 把新老行对应起来 |
| 含可展开行(expand)或树形数据,拖拽后要保持展开状态 | 必须传 | 展开状态按 row-key 记录 |
开启跨页保留选中(reserve-selection) | 必须传 | el-table 该特性本身要求 row-key |
一句话:落位靠组件内部的 DOM 还原,不依赖 row-key;row-key 是为“重排/刷新后仍要正确追踪某一行”(多选、展开、跨页保留)而传的。
API
Attributes
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
colum-list | 列配置列表 | IColumItem[] | [] |
request-api | 自动请求接口,入参 { pageNo, pageSize, ...reqParams } | (params: any) => Promise<{ data: { records: T[]; total: number } }> | — |
req-params | 请求时附加的查询参数 | Record<string, any> | — |
request-immediate | 挂载后是否立即请求 | boolean | true |
table-list | 外部传入的数据源(传入后进入手动模式,不再走 request-api) | T[] | — |
total | 外部数据模式下的总条数(不传则取 table-list.length) | number | — |
selection | 是否显示多选列 | boolean | false |
selectable | 多选时控制某行勾选框是否可用 | (row: T, index: number) => boolean | — |
type-index | 是否显示序号列 | boolean | false |
last-modify-colu | 是否显示更新人 / 更新时间快捷列 | boolean | true |
last-modify-name-permission | 更新人列权限标识 | string | — |
last-modify-time-permission | 更新时间列权限标识 | string | — |
show-action-colu | 是否显示操作列 | boolean | true |
action-width | 操作列宽度 | number | 180 |
hide-pagination | 是否隐藏分页 | boolean | false |
row-draggable | 是否开启行拖拽排序(需在 install 时注入 Sortable) | boolean | false |
row-draggable-fn | 控制某行是否可拖拽,返回 false 的行禁止拖拽(不渲染把手、无法起手) | (row: T, index: number) => boolean | — |
drop-line-color | 拖拽落点插入线颜色(内置落点线样式) | string | --el-color-primary |
sortable-options | 透传给 new Sortable 的额外配置(handle、animation、ghostClass 等;组件默认已设 filter 让多选框/展开图标不触发拖拽,并用内置 ghostClass 画落点线) | Record<string, any> | — |
pagination-size | 默认每页条数 | number | 10 |
pagination-sizes | 每页条数选项 | number[] | [10, 20, 50, 100] |
pagination-layout | 分页布局 | string | "prev, pager, next, jumper, total, sizes" |
table-size | 表格尺寸 | "large" | "default" | "small" | "default" |
handle-data | 接口返回后对 records 的处理函数 | (data: T[]) => T[] | — |
locale | 文案覆盖(序号、操作、更新人等) | Partial<ITableLocale> | — |
未在上表列出的属性会通过
$attrs透传给内部的el-table(如border、stripe、row-key等)。
v-model
| 名称 | 说明 | 类型 |
|---|---|---|
v-model:page-index | 当前页码 | number |
v-model:page-size | 每页条数 | number |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
selection-change | 多选项变化时触发 | (rows: T[]) |
update:selectItems | 多选项变化时触发(同上,便于 v-model:selectItems) | (rows: T[]) |
before-request | 自动请求发起前触发 | — |
table-request-end | 自动请求结束(成功或失败)后触发 | — |
sort-change | 列排序变化时触发 | ({ column, prop, order }) |
row-drag-end | 行拖拽结束时触发(需 row-draggable) | ({ oldIndex, newIndex, row, oldRow, position, list, done }):row 为被拖动的行(拖拽前 list[oldIndex])、oldRow 为落点锚点行(拖拽前 list[newIndex])、position 为被拖动行相对 oldRow 的落点(after=下拖、before=上拖,组件已算好,直接用于「插到某行前/后」的后端契约);done(success: boolean) 由使用方在处理完后端后调用:true 保持新位置、false 还原回原位并提示失败 |
Slots
| 插槽名 | 说明 | 作用域参数 |
|---|---|---|
[prop] | 自定义对应列的单元格内容 | { row, column, $index } |
header-[prop] | 自定义对应列的表头内容 | { column, $index } |
drag-handle | 自定义拖拽把手图标(需列配置 drag: true 且开启 row-draggable) | { row, column, $index } |
action | 操作列内容 | { row, column, $index } |
Exposes
通过 ref 获取组件实例后可访问:
| 名称 | 说明 | 类型 |
|---|---|---|
requestData | 重新请求数据,reset 会回到第一页 | (type?: "reset" | "default") => void |
reload | 重新请求当前页 | () => void |
total | 当前数据总条数(只读) | number |
displayTableList | 当前渲染的数据列表(只读) | ComputedRef<T[]> |
类型声明
ts
interface IColumItem {
label: string
prop: string
width?: number
minWidth?: number
type?: string
fixed?: boolean
align?: "left" | "center" | "right"
sortable?: boolean
drag?: boolean // 该列渲染拖拽把手图标(需配合 row-draggable);开启后只有图标能起手拖拽
/** 第一个参数是注入的 h,第二个参数是 { row, index, column } */
render?: (h: typeof import("vue").h, params: { row: T; index: number; column: any }) => VNode
}
interface ITableLocale {
index?: string
action?: string
creator?: string
createTime?: string
updater?: string
updateTime?: string
dragFail?: string // 拖拽失败提示文案,默认「拖拽失败」
}
interface PcTableInstance {
requestData: (type?: "reset" | "default") => void
reload: () => void
displayTableList: any
/** 当前数据总条数 */
total: number
}