对于使用ant design组件库的疑问
用ant design 的tag来制作组件封装然后实现应该筛选功能的话,我得考虑什么,具体筛选是点击tag之后会得到对应的筛选后的信息,我的后端接口应该是放在主页面没问题,然后传类别和该类别的tag,但是tag是能选择了,那数据还是没筛选啊,听前辈说是ant design的组件有个回调函数,但是我不会用,而且这个ant design的tag组件还有自带的api之类的东西,但是我不会用,首先我该怎么实现筛选的逻辑,回调函数是干什么的,然后我怎么用自带的api。
(最好不要在不了解一段代码的前提下,修改或增加代码的逻辑)
要使用 Ant Design Design 的 Tag 组件实现筛选功能,核心是通过 **状态管理** + **Tag 交互回调** + **数据过滤/接口请求** 来完成。下面分步骤拆解逻辑、回调函数的作用及 API 使用方法:
### 一、筛选功能的核心逻辑
筛选的本质是:**用户点击 Tag 后,记录选中的筛选条件,然后根据条件过滤数据(或重新请求接口)**。
具体流程:
1. **维护选中状态**:用一个变量(如数组)存储用户当前选中的 Tag 值(例如 `selectedTags: ['电影', '音乐']`)。
2. **Tag 交互触发状态变更**:用户点击 Tag 时,通过回调函数更新选中状态(添加/移除 Tag 值)。
3. **根据状态筛选数据**:
- 若数据在前端(已获取全量数据):用 `filter` 方法根据 `selectedTags` 过滤数据。
- 若数据需要后端筛选:将 `selectedTags` 作为参数传给后端接口,重新请求数据。
### 二、Tag 组件的回调函数作用
Ant Design 的 Tag 组件(尤其是 `CheckableTag`,即“可选择标签”)的回调函数是 **用户交互与状态更新的桥梁**,核心回调如下:
| 组件 | 回调函数 | 作用 |
|--------------|----------------|----------------------------------------------------------------------|
| `Tag` | `onClose` | 当 Tag 可关闭时(设置了 `closeIcon`),点击关闭按钮触发,用于移除标签。 |
| `CheckableTag`| `onChange` | 点击 Tag 时触发(切换选中状态),返回当前是否选中(`checked: boolean`),用于更新选中状态。 |
**为什么用 `CheckableTag` 而不是普通 `Tag`?**
普通 `Tag` 更适合“展示/删除标签”,而 `CheckableTag` 自带“选中/未选中”状态切换(类似复选框),更适合筛选场景(支持多选)。
### 四、Ant Design Tag 常用 API 说明
#### 1. 基础 `Tag` 组件(用于展示/关闭标签,较少用于筛选)
| 参数 | 说明 | 示例 |
|-------------|-------------------------------|---------------------------------------|
| `color` | 标签颜色(预设值或色值) | `<Tag color="success">成功</Tag>` |
| `closeIcon` | 自定义关闭按钮(开启可关闭) | `<Tag closeIcon={<CloseOutlined />} onClose={handleClose}>` |
| `onClose` | 关闭标签时的回调 | `(e) => { console.log('关闭标签'); }` |
#### 2. `CheckableTag` 组件(筛选核心,支持选中状态)
| 参数 | 说明 | 示例 |
|------------|-------------------------------|-------------------------------------------|
| `checked` | 是否选中(受控属性) | `checked={selectedTags.includes(tag)}` |
| `onChange` | 点击切换选中状态的回调 | `(checked) => handleTagChange(tag, checked)` |
| `icon` | 标签前的图标(>=5.27.0 支持) | `<CheckableTag icon={<MusicOutlined />}>音乐</CheckableTag>` |
### 总结
1. **筛选逻辑**:通过 `selectedTags` 记录选中条件,状态变化时触发数据过滤/接口请求。
2. **回调函数**:`CheckableTag` 的 `onChange` 是核心,用于更新选中状态;`Tag` 的 `onClose` 用于移除标签(适合“已选标签列表”场景)。
3. **API 用法**:`CheckableTag` 需通过 `checked` 绑定状态,`onChange` 处理交互;可结合 `icon` 等属性增强视觉效果。
这是我的代码,能点击但是没实现筛选功能,是不是少了什么。父组件
<template>
<div class="p-6 bg-gray-100 items-start justify-start">
<Tag :tags="parentObject"></Tag>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import Tag from '@/components/Tag/Tag.vue';
const parentObject = reactive([
{
label: '模型类型',
tagsData: [
'全部',
'文本生成',
'推理模型',
'音频理解',
'视频理解',
'图片处理',
'图片理解',
'图片生成',
'向量模型',
'视频生成',
'全模态',
'语音合成',
'语音识别',
'排序模型'
],
selectTags: [true, true, true, false, false, false, false, false, false, false, false, false, false, false]
},
{
label: '上下文长度',
tagsData: ['全部', '16K以下', '16K-64K', '64K以上'],
selectTags: [true, true, true, false]
}
]);
子组件
<template>
<div v-for="group in tags" :key="group.label" class="pb-2 space-y-4">
<span style="margin-right: 40px">{{ group.label }}</span>
<a-space :size="[0, 8]" wrap>
<a-checkable-tag
v-for="(tag, index) in group.tagsData"
:key="tag"
v-model:checked="group.selectTags[index]"
@change="(checked) => handleChange(tag, checked)"
>
{{ tag }}
</a-checkable-tag>
</a-space>
</div>
<div class="pb-5"></div>
</template>
<script setup>
import { reactive } from 'vue';
// const tagsData = reactive(['Movies', 'Books', 'Music', 'Sports']);
// const selectTags = reactive([true, false, false, false]);
const handleChange = (tag, checked) => {
console.log(tag, checked);
};
defineOptions({ name: 'Tag' });
const props = defineProps({
tags: {
type: Array,
required: true
}
});
</script>
代码已经实现了「Tag 选中状态切换」,但没实现筛选功能,核心缺失两点:**1. 筛选条件的跨组件传递(子组件→父组件);2. 父组件根据筛选条件过滤数据**。下面分步骤补全逻辑:
### 一、先明确筛选功能的核心流程
1. 子组件(Tag):用户点击 Tag 时,收集当前「所有分组的选中标签」(比如“模型类型”选了哪些、“上下文长度”选了哪些)。
2. 父子通信:子组件将收集到的「筛选条件」传给父组件(通过 Vue 的 `emit` 事件)。
3. 父组件:接收筛选条件,用条件过滤目标数据(比如过滤“模型列表”),并渲染筛选结果。
### 二、第一步:子组件补全「收集筛选条件 + 向父组件传值」
子组件当前只打印了 `tag` 和 `checked`,但没有记录「每个分组最终选中的标签」,也没把结果传给父组件。需要:
1. 监听 `group.selectTags` 的变化(因为 `v-model:checked` 绑定的是这个数组,选中状态变了它会更新)。
2. 实时收集每个分组的「选中标签」,并通过 `emit` 传给父组件。
#### 子组件(Tag.vue)修改代码:
```vue
<template>
<div v-for="group in tags" :key="group.label" class="pb-2 space-y-4">
<span style="margin-right: 40px">{{ group.label }}</span>
<a-space :size="[0, 8]" wrap>
<a-checkable-tag
v-for="(tag, index) in group.tagsData"
:key="tag"
v-model:checked="group.selectTags[index]"
@change="(checked) => handleChange(group, tag, index, checked)"
>
{{ tag }}
</a-checkable-tag>
</a-space>
</div>
<div class="pb-5"></div>
</template>
<script setup>
import { watch, onMounted } from 'vue';
// 1. 定义向父组件传递筛选条件的事件
const emit = defineEmits(['update:filterParams']);
// 2. 处理 Tag 变化:实时收集所有分组的选中标签
const collectFilterParams = (tags) => {
// 最终要传给父组件的筛选条件格式(示例):
// { "模型类型": ["全部", "文本生成"], "上下文长度": ["全部"] }
const filterParams = {};
tags.forEach(group => {
// 遍历每个分组,找到 selectTags 为 true 的标签
const selectedTags = group.tagsData.filter(
(_, index) => group.selectTags[index] === true
);
// 给筛选条件赋值(键:分组label,值:选中的标签数组)
filterParams[group.label] = selectedTags;
});
// 3. 把筛选条件传给父组件
emit('update:filterParams', filterParams);
};
// 4. Tag 点击时触发(可选:也可以用 watch 监听 selectTags 变化)
const handleChange = (group, tag, index, checked) => {
console.log(`分组:${group.label},标签:${tag},是否选中:${checked}`);
// 每次点击后,重新收集筛选条件并传值
collectFilterParams(props.tags);
};
// 5. 初始化时也收集一次(避免页面加载时没筛选条件)
onMounted(() => {
collectFilterParams(props.tags);
});
// 6. 监听 selectTags 变化(防止手动修改数组时没触发传值,可选但建议加)
watch(
() => props.tags,
(newTags) => {
collectFilterParams(newTags);
},
{ deep: true } // 深度监听数组内部元素变化
);
// 接收父组件传来的 tags props
defineOptions({ name: 'Tag' });
const props = defineProps({
tags: {
type: Array,
required: true,
// 确保 props 格式正确(每个分组必须有 label/tagsData/selectTags)
validator: (value) => {
return value.every(group =>
group.label && Array.isArray(group.tagsData) && Array.isArray(group.selectTags)
);
}
}
});
</script>
```
### 三、第二步:父组件补全「接收筛选条件 + 过滤数据」
父组件当前只传了 `parentObject` 给子组件,但没有:
1. 接收子组件传来的筛选条件;
2. 定义「需要筛选的目标数据」(比如“模型列表”);
3. 根据筛选条件过滤数据并渲染。
#### 父组件修改代码:
```vue
<template>
<div class="p-6 bg-gray-100 items-start justify-start">
<!-- 1. 子组件:通过 @update:filterParams 接收筛选条件 -->
<Tag
:tags="parentObject"
@update:filterParams="handleFilterParamsChange"
/>
<!-- 3. 渲染筛选后的结果 -->
<div class="mt-6">
<h3>筛选结果(共 {{ filteredModelList.length }} 条)</h3>
<div class="mt-2 bg-white p-4 rounded-md">
<div v-if="filteredModelList.length === 0">暂无匹配数据</div>
<div v-else v-for="model in filteredModelList" :key="model.id" class="py-2 border-b">
{{ model.name }}(模型类型:{{ model.type }},上下文长度:{{ model.contextLength }})
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import Tag from '@/components/Tag/Tag.vue';
// 2. 定义「需要筛选的目标数据」(示例:模型列表,实际可能从接口获取)
const modelList = reactive([
{ id: 1, name: 'GPT-4', type: '文本生成', contextLength: '16K以下' },
{ id: 2, name: 'Stable Diffusion', type: '图片生成', contextLength: '16K-64K' },
{ id: 3, name: 'Whisper', type: '语音识别', contextLength: '64K以上' },
{ id: 4, name: 'CLIP', type: '图片理解', contextLength: '16K以下' },
{ id: 5, name: 'Gemini Pro', type: '全模态', contextLength: '16K-64K' },
]);
// 3. 存储筛选后的结果(初始为全部数据)
const filteredModelList = ref([...modelList]);
// 4. 接收子组件传来的筛选条件,并执行筛选
const handleFilterParamsChange = (filterParams) => {
console.log('父组件接收的筛选条件:', filterParams);
// filterParams 格式:{ "模型类型": ["全部", "文本生成"], "上下文长度": ["全部"] }
// 执行筛选逻辑
filteredModelList.value = modelList.filter(model => {
// 条件1:模型类型匹配(若选中“全部”,则直接满足;否则匹配选中的类型)
const typeCondition = filterParams['模型类型'].includes('全部')
? true
: filterParams['模型类型'].includes(model.type);
// 条件2:上下文长度匹配(同理)
const contextCondition = filterParams['上下文长度'].includes('全部')
? true
: filterParams['上下文长度'].includes(model.contextLength);
// 两个条件都满足才保留
return typeCondition && contextCondition;
});
};
// 父组件传给子组件的 Tag 分组数据(保持不变)
const parentObject = reactive([
{
label: '模型类型',
tagsData: [
'全部', '文本生成', '推理模型', '音频理解', '视频理解',
'图片处理', '图片理解', '图片生成', '向量模型', '视频生成',
'全模态', '语音合成', '语音识别', '排序模型'
],
selectTags: [true, true, true, false, false, false, false, false, false, false, false, false, false, false]
},
{
label: '上下文长度',
tagsData: ['全部', '16K以下', '16K-64K', '64K以上'],
selectTags: [true, true, true, false]
}
]);
</script>
```
### 四、关键问题解释:为什么之前没实现筛选?
1. **缺失「筛选条件的传递」**:
子组件只改了 `group.selectTags` 的状态,但没把“哪些标签被选中”告诉父组件——父组件不知道用户选了什么,自然无法筛选。
补全:通过 `emit` 事件,子组件把收集到的 `filterParams` 传给父组件。
2. **缺失「目标数据和过滤逻辑」**:
筛选需要「原始数据」(比如 `modelList`)和「过滤规则」(比如“模型类型匹配且上下文长度匹配”),之前父组件没有这两部分,所以没地方执行筛选。
补全:定义 `modelList` 作为原始数据,在 `handleFilterParamsChange` 中写过滤逻辑,更新 `filteredModelList` 并渲染。
### 五、优化建议(可选)
1. 处理「全部」标签的逻辑:
目前如果选中“全部”,会忽略其他选中的标签(因为 `includes('全部')` 直接返回 `true`),可以优化为:**若选中“全部”,则该分组只保留“全部”,取消其他标签的选中状态**(避免“全部”和其他标签同时选中的矛盾)。
2. 从接口获取数据:
如果数据量较大,父组件的 `modelList` 可以从后端接口获取,筛选时也可以把 `filterParams` 传给接口(后端筛选),而不是前端过滤。
3. 初始筛选:
页面加载时,`onMounted` 会触发一次筛选,确保初始状态也能展示正确的结果。
这样修改后,点击 Tag 就能实时筛选并展示数据了,你可以根据实际的“目标数据结构”调整 `handleFilterParamsChange` 里的过滤逻辑。

浙公网安备 33010602011771号