添加了标签排除功能 (#9)
This commit is contained in:
38
src/App.vue
38
src/App.vue
@@ -40,23 +40,29 @@
|
|||||||
|
|
||||||
<a-space direction="vertical" size="medium" style="width: 100%;">
|
<a-space direction="vertical" size="medium" style="width: 100%;">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="8">
|
<a-col :span="6">
|
||||||
<a-input v-model="searchConditions[category.name].name" placeholder="搜索名称" allow-clear
|
<a-input v-model="searchConditions[category.name].name" placeholder="搜索名称" allow-clear
|
||||||
@change="filterData(category.name)" />
|
@change="filterData(category.name)" />
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="8">
|
<a-col :span="6">
|
||||||
<a-select v-model="searchConditions[category.name].author" placeholder="选择作者" style="width: 100%;"
|
<a-select v-model="searchConditions[category.name].author" placeholder="选择作者" style="width: 100%;"
|
||||||
allow-clear @change="filterData(category.name)">
|
allow-clear @change="filterData(category.name)">
|
||||||
<a-option v-for="author in getUniqueAuthors(category)" :key="author" :value="author">{{ author
|
<a-option v-for="author in getUniqueAuthors(category)" :key="author" :value="author">{{ author
|
||||||
}}</a-option>
|
}}</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="8">
|
<a-col :span="6">
|
||||||
<a-select v-model="searchConditions[category.name].tags" placeholder="选择标签" style="width: 100%;"
|
<a-select v-model="searchConditions[category.name].tags" placeholder="选择标签" style="width: 100%;"
|
||||||
:filter-option="handleTagFilter" allow-clear @change="handleTagSelect(category.name)" multiple>
|
:filter-option="handleTagFilter" allow-clear @change="handleTagSelect(category.name)" multiple>
|
||||||
<a-option v-for="tag in getUniqueTags(category)" :key="tag" :value="tag">{{ tag }}</a-option>
|
<a-option v-for="tag in getUniqueTags(category)" :key="tag" :value="tag">{{ tag }}</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-select v-model="searchConditions[category.name].etags" placeholder="排除标签" style="width: 100%;"
|
||||||
|
:filter-option="handleTagFilter" allow-clear @change="handleTagSelect(category.name)" multiple>
|
||||||
|
<a-option v-for="tag in getUniqueTags(category)" :key="tag" :value="tag">{{ tag }}</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<!-- 大盒子,整体高度 calc(100vh - 200px) -->
|
<!-- 大盒子,整体高度 calc(100vh - 200px) -->
|
||||||
<div style="height: calc(100vh - 200px); display: flex; flex-direction: column; flex: 1">
|
<div style="height: calc(100vh - 200px); display: flex; flex-direction: column; flex: 1">
|
||||||
@@ -79,7 +85,7 @@
|
|||||||
<template #tags="{ record }">
|
<template #tags="{ record }">
|
||||||
<a-space :style="{ flexWrap: 'wrap', rowGap: '8px' }">
|
<a-space :style="{ flexWrap: 'wrap', rowGap: '8px' }">
|
||||||
<a-tag v-for="tag in record.tags" :key="tag" :color="getTagColor(tag)"
|
<a-tag v-for="tag in record.tags" :key="tag" :color="getTagColor(tag)"
|
||||||
style="cursor: pointer" @click="handleTagClick(tag, category.name)">{{ tag }}</a-tag>
|
style="cursor: pointer" @click="handleTagClick(tag, category.name)" @contextmenu.prevent="handleTagRightClick(tag, category.name)">{{ tag }}</a-tag>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #operations="{ record }">
|
<template #operations="{ record }">
|
||||||
@@ -308,6 +314,7 @@ const fetchRepoData = async () => {
|
|||||||
name: '',
|
name: '',
|
||||||
author: '',
|
author: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
|
etags: [],
|
||||||
path: ''
|
path: ''
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -428,12 +435,20 @@ const filterData = (categoryName) => {
|
|||||||
|
|
||||||
const filtered = [];
|
const filtered = [];
|
||||||
traverseCategory(category, (item) => {
|
traverseCategory(category, (item) => {
|
||||||
|
// 名称匹配:如果条件为空或项名称满足拼音匹配则认为匹配
|
||||||
const nameMatch = !condition.name || isPinyinMatch(item.name, condition.name);
|
const nameMatch = !condition.name || isPinyinMatch(item.name, condition.name);
|
||||||
|
// 作者匹配:如果条件为空或项作者与条件一致则认为匹配
|
||||||
const authorMatch = !condition.author || item.author === condition.author;
|
const authorMatch = !condition.author || item.author === condition.author;
|
||||||
// 修改标签匹配逻辑
|
// 标签匹配:如果 condition.tags 为空或者 item.tags 包含条件中的所有标签,则认为匹配
|
||||||
const tagMatch = condition.tags.length === 0 || (Array.isArray(item.tags) && condition.tags.every(tag => item.tags.includes(tag)));
|
const tagMatch = condition.tags.length === 0 || (Array.isArray(item.tags) && condition.tags.every(tag => item.tags.includes(tag)));
|
||||||
|
// 排除标签匹配:如果 condition.etags 有内容,则要求 item.tags 不包含其中任一标签
|
||||||
|
const etagMatch = condition.etags.length === 0 || (Array.isArray(item.tags) && condition.etags.every(excludeTag => !item.tags.includes(excludeTag)));
|
||||||
|
// 路径匹配:满足条件时 item.path 存在且以指定条件开头,但不完全等于该条件
|
||||||
const pathMatch = !condition.path || (item.path && item.path.startsWith(condition.path) && item.path !== condition.path);
|
const pathMatch = !condition.path || (item.path && item.path.startsWith(condition.path) && item.path !== condition.path);
|
||||||
if (nameMatch && authorMatch && tagMatch && pathMatch && (item.type === 'file' || (category.name === 'js' && item.type === 'directory'))) {
|
|
||||||
|
// 当各条件均满足,且类型为 'file' 或(如果当前分类为 'js' 则允许 'directory')时,将该项加入过滤结果
|
||||||
|
if (nameMatch && authorMatch && tagMatch && etagMatch && pathMatch &&
|
||||||
|
(item.type === 'file' || (category.name === 'js' && item.type === 'directory'))) {
|
||||||
filtered.push(item);
|
filtered.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -441,12 +456,14 @@ const filterData = (categoryName) => {
|
|||||||
filteredData[categoryName] = filtered;
|
filteredData[categoryName] = filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const initializeSearchConditions = () => {
|
const initializeSearchConditions = () => {
|
||||||
repoDataRaw.value.forEach(category => {
|
repoDataRaw.value.forEach(category => {
|
||||||
searchConditions[category.name] = {
|
searchConditions[category.name] = {
|
||||||
name: '',
|
name: '',
|
||||||
author: '',
|
author: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
|
etags: [],
|
||||||
path: ''
|
path: ''
|
||||||
};
|
};
|
||||||
filteredData[category.name] = [];
|
filteredData[category.name] = [];
|
||||||
@@ -615,6 +632,15 @@ const handleTagClick = (tag, categoryName) => {
|
|||||||
filterData(categoryName);
|
filterData(categoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleTagRightClick = (tag, categoryName) => {
|
||||||
|
if (!searchConditions[categoryName].etags.includes(tag)) {
|
||||||
|
searchConditions[categoryName].etags.push(tag);
|
||||||
|
} else {
|
||||||
|
searchConditions[categoryName].etags = searchConditions[categoryName].etags.filter(t => t !== tag);
|
||||||
|
}
|
||||||
|
filterData(categoryName);
|
||||||
|
}
|
||||||
|
|
||||||
const handleTagFilter = (value, option) => {
|
const handleTagFilter = (value, option) => {
|
||||||
return isPinyinMatch(option.value, value);
|
return isPinyinMatch(option.value, value);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user