antd-vue Checkbox Group 封装实现多选、全选、搜索过滤组件
'# antd-vue Checkbox Group 封装实现多选、全选、搜索过滤组件
一、背景与问题
在实际开发中,传统的 Checkbox Group 组件存在诸多局限性。例如:
- 无法直接实现全选/取消全选功能
- 缺乏搜索过滤能力
- 无法动态更新选中状态
- 无法处理大规模数据时的性能问题
在用户管理、权限配置等场景中,我们需要一个既能支持多选,又能处理全选和搜索过滤的组件。传统方案需要手动维护大量状态逻辑,容易导致代码冗余和维护困难。
本方案通过封装 antd-vue 的 Checkbox Group 组件,实现以下功能:
- 支持多选
- 支持全选/取消全选
- 支持搜索过滤
- 支持状态同步
- 支持动态数据更新
二、基本原理
核心实现原理分为三个部分:
1. 状态管理
使用 Vue 3 的 ref 和 reactive 创建以下状态:
selectedItems: 当前选中项searchText: 搜索关键字isAllSelected: 全选状态filteredOptions: 过滤后的选项列表
2. 事件处理
@change: 监听 Checkbox 变化事件@update:search: 监听搜索输入事件@update:all: 监听全选状态变化事件
3. 逻辑处理
- 搜索过滤逻辑:根据 searchText 过滤 options
- 全选逻辑:根据 filteredOptions 的长度动态计算全选状态
- 状态同步:处理选中项的增删逻辑
三、环境准备
npm install @ant-design/antd-vue@latest
npm install vue@3四、核心实现
1. 封装组件代码
<template>
<div class="checkbox-group">
<a-input
v-model:value="searchText"
placeholder="请输入搜索内容"
@update:search="handleSearch"
/>
<a-checkbox-group
v-model:value="selectedItems"
:options="filteredOptions"
@change="handleChange"
@update:all="handleAll"
/>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import { CheckboxGroup, Input } from '@ant-design/antd-vue'
const props = defineProps({
options: {
type: Array,
required: true
},
selected: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:selected', 'update:search', 'update:all'])
const searchText = ref('')
const selectedItems = ref(props.selected)
const isAllSelected = ref(false)
const filteredOptions = computed(() => {
if (!searchText.value) return props.options
return props.options.filter(item =>
item.label.toLowerCase().includes(searchText.value.toLowerCase())
)
})
const handleSearch = (value) => {
searchText.value = value
emit('update:search', value)
}
const handleChange = (value) => {
selectedItems.value = value
emit('update:selected', value)
// 判断是否全选
const allSelected = filteredOptions.value.every(item =>
selectedItems.value.includes(item.value)
)
isAllSelected.value = allSelected
emit('update:all', allSelected)
}
const handleAll = (value) => {
isAllSelected.value = value
if (value) {
selectedItems.value = filteredOptions.value.map(item => item.value)
} else {
selectedItems.value = []
}
emit('update:selected', selectedItems.value)
}
</script>
<style scoped>
.checkbox-group {
display: flex;
flex-direction: column;
gap: 12px;
}
</style>2. 使用示例代码
<template>
<div>
<h2>用户权限管理</h2>
<CustomCheckboxGroup
:options="userOptions"
:selected="selectedUsers"
@update:search="onSearch"
@update:all="onAll"
/>
<div>
<p>当前选中: {{ selectedUsers }}</p>
<p>全选状态: {{ isAllSelected }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import CustomCheckboxGroup from './CustomCheckboxGroup.vue'
const userOptions = ref([
{ value: 'user1', label: '用户1' },
{ value: 'user2', label: '用户2' },
{ value: 'user3', label: '用户3' },
{ value: 'user4', label: '用户4' },
{ value: 'user5', label: '用户5' },
])
const selectedUsers = ref(['user1', 'user2'])
const isAllSelected = ref(false)
const onSearch = (value) => {
console.log('搜索内容:', value)
}
const onAll = (value) => {
console.log('全选状态:', value)
}
</script>3. 带搜索和全选的完整组件代码
<template>
<div class="checkbox-group">
<a-input
v-model:value="searchText"
placeholder="请输入搜索内容"
@update:search="handleSearch"
/>
<a-checkbox-group
v-model:value="selectedItems"
:options="filteredOptions"
@change="handleChange"
@update:all="handleAll"
/>
<div>
<p>当前选中: {{ selectedItems }}</p>
<p>全选状态: {{ isAllSelected }}</p>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import { CheckboxGroup, Input } from '@ant-design/antd-vue'
const props = defineProps({
options: {
type: Array,
required: true
},
selected: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:selected', 'update:search', 'update:all'])
const searchText = ref('')
const selectedItems = ref(props.selected)
const isAllSelected = ref(false)
const filteredOptions = computed(() => {
if (!searchText.value) return props.options
return props.options.filter(item =>
item.label.toLowerCase().includes(searchText.value.toLowerCase())
)
})
const handleSearch = (value) => {
searchText.value = value
emit('update:search', value)
}
const handleChange = (value) => {
selectedItems.value = value
emit('update:selected', value)
// 判断是否全选
const allSelected = filteredOptions.value.every(item =>
selectedItems.value.includes(item.value)
)
isAllSelected.value = allSelected
emit('update:all', allSelected)
}
const handleAll = (value) => {
isAllSelected.value = value
if (value) {
selectedItems.value = filteredOptions.value.map(item => item.value)
} else {
selectedItems.value = []
}
emit('update:selected', selectedItems.value)
}
</script>五、完整案例
1. 用户权限管理案例
<template>
<div>
<h2>用户权限管理</h2>
<CustomCheckboxGroup
:options="userOptions"
:selected="selectedUsers"
@update:search="onSearch"
@update:all="onAll"
/>
<div>
<p>当前选中: {{ selectedUsers }}</p>
<p>全选状态: {{ isAllSelected }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import CustomCheckboxGroup from './CustomCheckboxGroup.vue'
const userOptions = ref([
{ value: 'user1', label: '用户1' },
{ value: 'user2', label: '用户2' },
{ value: 'user3', label: '用户3' },
{ value: 'user4', label: '用户4' },
{ value: 'user5', label: '用户5' },
])
const selectedUsers = ref(['user1', 'user2'])
const isAllSelected = ref(false)
const onSearch = (value) => {
console.log('搜索内容:', value)
}
const onAll = (value) => {
console.log('全选状态:', value)
}
</script>2. 动态数据加载案例
<template>
<div>
<CustomCheckboxGroup
:options="userOptions"
:selected="selectedUsers"
@update:search="onSearch"
@update:all="onAll"
/>
<div>
<p>当前选中: {{ selectedUsers }}</p>
<p>全选状态: {{ isAllSelected }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import CustomCheckboxGroup from './CustomCheckboxGroup.vue'
const userOptions = ref([])
const selectedUsers = ref([])
const isAllSelected = ref(false)
onMounted(async () => {
// 模拟从后端获取数据
const data = await fetchData()
userOptions.value = data
})
const fetchData = async () => {
// 模拟网络请求
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ value: 'user1', label: '用户1' },
{ value: 'user2', label: '用户2' },
{ value: 'user3', label: '用户3' },
{ value: 'user4', label: '用户4' },
{ value: 'user5', label: '用户5' },
])
}, 500)
})
}
const onSearch = (value) => {
console.log('搜索内容:', value)
}
const onAll = (value) => {
console.log('全选状态:', value)
}
</script>六、源码解析
1. 状态管理
const searchText = ref('')
const selectedItems = ref(props.selected)
const isAllSelected = ref(false)searchText用于存储搜索关键词selectedItems存储当前选中项isAllSelected存储全选状态
2. 过滤逻辑
const filteredOptions = computed(() => {
if (!searchText.value) return props.options
return props.options.filter(item =>
item.label.toLowerCase().includes(searchText.value.toLowerCase())
)
})- 使用 computed 计算属性实现响应式过滤
- 支持大小写不敏感的搜索
- 当搜索内容为空时返回原始数据
3. 事件处理
const handleSearch = (value) => {
searchText.value = value
emit('update:search', value)
}
const handleChange = (value) => {
selectedItems.value = value
emit('update:selected', value)
// 判断是否全选
const allSelected = filteredOptions.value.every(item =>
selectedItems.value.includes(item.value)
)
isAllSelected.value = allSelected
emit('update:all', allSelected)
}- 搜索事件处理:更新搜索内容并触发事件
- 选择事件处理:更新选中项并计算全选状态
- 自动触发
update:all事件
七、进阶使用
1. 动态数据加载
const fetchData = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ value: 'user1', label: '用户1' },
{ value: 'user2', label: '用户2' },
{ value: 'user3', label: '用户3' },
{ value: 'user4', label: '用户4' },
{ value: 'user5', label: '用户5' },
])
}, 500)
})
}2. 分页支持
<template>
<div>
<CustomCheckboxGroup
:options="userOptions"
:selected="selectedUsers"
@update:search="onSearch"
@update:all="onAll"
/>
<a-pagination
v-model:current="currentPage"
:total="total"
show-size-changer
@showSizeChange="handleSizeChange"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import CustomCheckboxGroup from './CustomCheckboxGroup.vue'
const userOptions = ref([])
const selectedUsers = ref([])
const currentPage = ref(1)
const total = ref(0)
const handleSizeChange = (page, pageSize) => {
// 处理分页逻辑
}
</script>八、性能与工程实践
1. 性能优化
- 虚拟滚动:对于大量数据使用
vue-virtual-scroller库 - 防抖处理:对搜索输入添加防抖
- 分页加载:避免一次性加载全部数据
- 懒加载:按需加载数据
2. 安全风险
- XSS 防护:对用户输入内容进行转义
- 数据验证:确保输入数据符合预期格式
- 权限控制:确保只有授权用户才能操作组件
3. 异常处理
const handleSearch = (value) => {
try {
searchText.value = value
emit('update:search', value)
} catch (error) {
console.error('搜索处理错误:', error)
}
}九、常见问题与踩坑
1. 全选逻辑错误
问题描述:全选状态未正确更新
解决方案:
const allSelected = filteredOptions.value.every(item =>
selectedItems.value.includes(item.value)
)2. 搜索未更新选中状态
问题描述:搜索后选中状态未重置
解决方案:在搜索处理时重置选中状态
handleSearch(value) {
searchText.value = value
selectedItems.value = []
emit('update:selected', [])
}3. 大数据性能问题
问题描述:数据量过大导致卡顿
解决方案:
- 使用虚拟滚动
- 添加防抖处理
- 分页加载数据
十、最佳实践
1. 适用场景
- 权限配置页面
- 用户管理界面
- 商品分类筛选
- 数据过滤场景
2. 不适用场景
- 简单的多选场景
- 数据量极少的场景
- 需要复杂交互的场景
3. 推荐方案
- 使用 Composition API 管理状态
- 使用 computed 计算属性处理过滤逻辑
- 通过事件分发实现组件间通信
- 对复杂场景使用自定义指令或插件
十一、总结
本篇文章深入探讨了 antd-vue Checkbox Group 组件的封装实现,重点分析了多选、全选、搜索过滤功能的实现原理。通过封装组件,我们能够实现更灵活的交互需求,同时保持代码的可维护性。
在实际开发中,应根据具体业务场景选择合适方案。对于需要复杂交互的场景,建议采用封装后的组件;对于简单场景,直接使用原生组件更合适。同时,要特别注意性能优化和安全防护,避免潜在问题。
本文提供的完整案例和代码示例,可以帮助开发者快速实现复杂功能,提高开发效率。在实际项目中,建议结合具体需求进行扩展,如支持动态加载、分页、权限控制等功能。
评论已关闭