useSelection
表格选择管理组合函数,专为 Element Plus el-table 设计的选择解决方案,统一处理单选和多选模式。
📋 功能特性
- 统一接口: 单选和多选使用相同的API,组件无需关心内部实现
- 跨页选择: 多选模式下自动保持选中状态,切换页面不丢失选择
- 智能同步: 自动同步内部状态与表格UI,确保显示一致性
- 灵活移除: 支持标签移除、批量移除等多种移除方式
- 类型安全: 完整的 TypeScript 支持,泛型确保类型安全
- 模式切换: 支持运行时动态切换单选/多选模式
🎯 基础用法
多选模式
vue
<template>
<div>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="userList"
@selection-change="selectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="email" label="邮箱" />
</el-table>
<!-- 选中项标签 -->
<div v-if="selectionItems.length > 0">
<h4>已选中 {{ selectionItems.length }} 项:</h4>
<el-tag
v-for="item in selectionItems"
:key="item.userId"
closable
@close="selectionRemove(item.userId)"
>
{{ item.name }}
</el-tag>
</div>
<!-- 操作按钮 -->
<div>
<el-button @click="selectionClear">清空选择</el-button>
<el-button
@click="handleBatchDelete"
:disabled="selectionItems.length === 0"
type="danger"
>
批量删除
</el-button>
</div>
</div>
</template>
<script setup>
import { useSelection } from '@/composables/useSelection'
const tableRef = ref()
const userList = ref([
{ userId: 1, name: '张三', email: 'zhang@example.com' },
{ userId: 2, name: '李四', email: 'li@example.com' }
])
const {
selectionItems,
selectionChange,
selectionSync,
selectionRemove,
selectionClear
} = useSelection('userId', tableRef, userList, ref(true)) // true = 多选模式
// 处理批量删除
const handleBatchDelete = () => {
const selectedIds = selectionItems.value.map(item => item.userId)
console.log('要删除的ID:', selectedIds)
}
// 页面切换后恢复选中状态
watch(userList, () => {
nextTick(() => {
selectionSync() // 同步表格选中状态
})
})
</script>
单选模式
vue
<template>
<div>
<!-- 表格 -->
<el-table ref="tableRef" :data="userList">
<el-table-column width="55">
<template #default="{ row }">
<el-radio
:model-value="selectionItems[0]?.userId || ''"
:label="row.userId"
@change="selectionChange(row)"
>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" />
<el-table-column prop="email" label="邮箱" />
</el-table>
<!-- 当前选中项 -->
<div v-if="selectionItems.length > 0">
<h4>当前选中:{{ selectionItems[0].name }}</h4>
<el-button @click="selectionClear">清空选择</el-button>
</div>
</div>
</template>
<script setup>
import { useSelection } from '@/composables/useSelection'
const tableRef = ref()
const userList = ref([...]) // 用户数据
const {
selectionItems,
selectionChange,
selectionClear
} = useSelection('userId', tableRef, userList, ref(false)) // false = 单选模式
</script>
🔧 API 参考
参数
typescript
useSelection<T>(
keyField: keyof T, // 数据对象的唯一标识字段名
tableRef: Ref<any>, // Element Plus 表格实例的引用
dataListRef: Ref<T[]>, // 当前页面数据列表的引用
multiple: Ref<boolean> // 是否为多选模式 (可选,默认 true)
)
返回值
typescript
interface SelectionReturn<T> {
// 状态
selectionItems: Ref<T[]> // 选中的完整对象数组
selectionIsRestoring: Ref<boolean> // 是否正在恢复选中状态
// 方法
selectionChange: (selection: T[] | T) => void // 处理选择变化
selectionSync: () => Promise<void> // 同步表格选中状态
selectionRemove: (key: any, item?: T) => Promise<void> // 移除选中项
selectionClear: () => void // 清空所有选中项
selectionInit: (data: any[] | (() => Promise<T[]>)) => Promise<void> // 初始化选中项
}
🌟 高级用法
初始化预选数据
vue
<script setup>
const { selectionInit } = useSelection(...)
// 方式1:使用ID数组初始化
onMounted(async () => {
await selectionInit([1, 2, 3]) // 预选ID为1,2,3的项
})
// 方式2:使用对象数组初始化
onMounted(async () => {
const preselectedData = [
{ userId: 1, name: '张三' },
{ userId: 2, name: '李四' }
]
await selectionInit(preselectedData)
})
// 方式3:使用异步函数初始化
onMounted(async () => {
await selectionInit(async () => {
const [err, data] = await getUsersByIds([1, 2, 3])
return err ? [] : data
})
})
</script>
分页场景处理
vue
<script setup>
const currentPage = ref(1)
const userList = ref([])
// 切换页面时同步选中状态
const handlePageChange = async (page) => {
currentPage.value = page
await loadUserList() // 加载新页面数据
await nextTick()
await selectionSync() // 恢复选中状态
}
// 监听数据变化自动同步
watch(userList, async () => {
await nextTick()
await selectionSync()
})
</script>
动态切换选择模式
vue
<template>
<div>
<el-switch
v-model="isMultiple"
active-text="多选"
inactive-text="单选"
/>
<el-table ref="tableRef" :data="userList">
<!-- 多选 -->
<el-table-column
v-if="isMultiple"
type="selection"
width="55"
/>
<!-- 单选 -->
<el-table-column v-else width="55">
<template #default="{ row }">
<el-radio
:model-value="selectionItems[0]?.userId || ''"
:label="row.userId"
@change="selectionChange(row)"
>
</el-radio>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
const isMultiple = ref(true)
const { selectionItems, selectionChange, selectionClear } =
useSelection('userId', tableRef, userList, isMultiple)
// 切换模式时清空选择
watch(isMultiple, () => {
selectionClear()
})
</script>
📝 使用场景
用户选择器
vue
<!-- UserSelector.vue -->
<template>
<el-dialog v-model="visible" title="选择用户">
<el-table ref="tableRef" :data="userList" @selection-change="selectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="department" label="部门" />
</el-table>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">
确定 ({{ selectionItems.length }})
</el-button>
</template>
</el-dialog>
</template>
<script setup>
const emit = defineEmits(['confirm'])
const { selectionItems, selectionChange, selectionInit } =
useSelection('userId', tableRef, userList)
// 打开时初始化预选数据
const openDialog = (preselectedUsers = []) => {
visible.value = true
nextTick(() => {
selectionInit(preselectedUsers)
})
}
const handleConfirm = () => {
emit('confirm', selectionItems.value)
visible.value = false
}
defineExpose({ openDialog })
</script>
批量操作
vue
<template>
<div>
<!-- 批量操作工具栏 -->
<div class="batch-toolbar" v-show="selectionItems.length > 0">
<span>已选择 {{ selectionItems.length }} 项</span>
<el-button @click="batchDelete" type="danger">批量删除</el-button>
<el-button @click="batchExport">批量导出</el-button>
<el-button @click="selectionClear">取消选择</el-button>
</div>
<!-- 表格 -->
<el-table ref="tableRef" :data="dataList" @selection-change="selectionChange">
<el-table-column type="selection" width="55" />
<!-- 其他列 -->
</el-table>
</div>
</template>
<script setup>
const { selectionItems, selectionChange, selectionClear } =
useSelection('id', tableRef, dataList)
const batchDelete = async () => {
const ids = selectionItems.value.map(item => item.id)
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 项吗?`)
// 执行删除操作...
selectionClear()
}
</script>
⚠️ 注意事项
- keyField 必须唯一: 确保用作唯一标识的字段在数据中是唯一的
- 表格引用: tableRef 必须正确引用到 el-table 实例
- 事件绑定: 多选模式需要绑定
@selection-change
,单选模式需要在 radio 上绑定@change
- 状态同步: 数据更新后调用
selectionSync()
恢复选中状态 - 内存清理: 组件销毁时会自动清理,无需手动处理