mirror of
https://github.com/CzBiX/qb-web.git
synced 2026-02-03 02:24:38 +08:00
Add tag filter (#145)
This commit is contained in:
@@ -65,7 +65,7 @@ import Vue from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { Torrent, Category } from '@/types';
|
||||
import { Torrent, Category, Tag } from '@/types';
|
||||
import FilterGroup from './drawer/FilterGroup.vue';
|
||||
import api from '../Api';
|
||||
import { formatSize } from '@/filters';
|
||||
@@ -141,7 +141,9 @@ interface MenuChildrenItem extends MenuItem {
|
||||
'isDataReady',
|
||||
'allTorrents',
|
||||
'allCategories',
|
||||
'allTags',
|
||||
'torrentGroupByCategory',
|
||||
'torrentGroupByTag',
|
||||
'torrentGroupBySite',
|
||||
'torrentGroupByState',
|
||||
]),
|
||||
@@ -165,7 +167,9 @@ export default class Drawer extends Vue {
|
||||
isDataReady!: boolean
|
||||
allTorrents!: Torrent[]
|
||||
allCategories!: Category[]
|
||||
allTags!: Tag[]
|
||||
torrentGroupByCategory!: {[category: string]: Torrent[]}
|
||||
torrentGroupByTag!: {[tag: string]: Torrent[]}
|
||||
torrentGroupBySite!: {[site: string]: Torrent[]}
|
||||
torrentGroupByState!: {[state: string]: Torrent[]}
|
||||
|
||||
@@ -214,6 +218,24 @@ export default class Drawer extends Vue {
|
||||
});
|
||||
}
|
||||
|
||||
buildTagGroup(): MenuChildrenItem[] {
|
||||
return [{
|
||||
key: '',
|
||||
name: tr('untagged'),
|
||||
}].concat(this.allTags).map((tag) => {
|
||||
let value = this.torrentGroupByTag[tag.key];
|
||||
if (isUndefined(value)) {
|
||||
value = [];
|
||||
}
|
||||
const size = formatSize(sumBy(value, 'size'));
|
||||
const title = `${tag.name} (${value.length})`;
|
||||
const append = `[${size}]`;
|
||||
return {
|
||||
icon: 'mdi-folder', title, key: tag.key, append,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
buildSiteGroup(): MenuChildrenItem[] {
|
||||
return sortBy(Object.entries(this.torrentGroupBySite).map(([key, value]) => {
|
||||
const size = formatSize(sumBy(value, 'size'));
|
||||
@@ -263,6 +285,20 @@ export default class Drawer extends Vue {
|
||||
],
|
||||
});
|
||||
|
||||
filterGroups.push({
|
||||
icon: 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
title: tr('tag', 0),
|
||||
model: null,
|
||||
select: 'tag',
|
||||
children: [
|
||||
{
|
||||
icon: 'mdi-folder', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
},
|
||||
...this.buildTagGroup(),
|
||||
],
|
||||
});
|
||||
|
||||
filterGroups.push({
|
||||
icon: 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
|
||||
@@ -248,7 +248,7 @@ import api from '../Api'
|
||||
import { formatSize } from '@/filters'
|
||||
import { DialogType, TorrentFilter, ConfigPayload, DialogConfig, SnackBarConfig } from '@/store/types'
|
||||
import Component from 'vue-class-component'
|
||||
import { Torrent, Category } from '@/types'
|
||||
import { Torrent, Category, Tag } from '@/types'
|
||||
import { Watch } from 'vue-property-decorator'
|
||||
|
||||
function getStateInfo(state: string) {
|
||||
@@ -337,8 +337,10 @@ function getStateInfo(state: string) {
|
||||
...mapGetters([
|
||||
'isDataReady',
|
||||
'allTorrents',
|
||||
'allTags',
|
||||
'allCategories',
|
||||
'torrentGroupByCategory',
|
||||
'torrentGroupByTag',
|
||||
'torrentGroupBySite',
|
||||
'torrentGroupByState',
|
||||
]),
|
||||
@@ -413,7 +415,9 @@ export default class Torrents extends Vue {
|
||||
isDataReady!: boolean
|
||||
allTorrents!: Torrent[]
|
||||
allCategories!: Category[]
|
||||
allTags!: Tag[]
|
||||
torrentGroupByCategory!: {[category: string]: Torrent[]}
|
||||
torrentGroupByTag!: {[tag: string]: Torrent[]}
|
||||
torrentGroupBySite!: {[site: string]: Torrent[]}
|
||||
torrentGroupByState!: {[state: string]: Torrent[]}
|
||||
filter!: TorrentFilter
|
||||
@@ -444,6 +448,9 @@ export default class Torrents extends Vue {
|
||||
if (this.filter.category !== null) {
|
||||
list = intersection(list, this.torrentGroupByCategory[this.filter.category]);
|
||||
}
|
||||
if (this.filter.tag !== null) {
|
||||
list = intersection(list, this.torrentGroupByTag[this.filter.tag]);
|
||||
}
|
||||
if (this.filter.state !== null) {
|
||||
list = intersection(list, this.torrentGroupByState[this.filter.state]);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ export default {
|
||||
all: '全部',
|
||||
category: '分类',
|
||||
uncategorized: '未分类',
|
||||
tag: '标签',
|
||||
untagged: '无标签',
|
||||
others: '其他',
|
||||
sites: '站点',
|
||||
files: '文件',
|
||||
|
||||
@@ -53,6 +53,12 @@ const store = new Vuex.Store<RootState>({
|
||||
}
|
||||
delete payload.categories_removed;
|
||||
}
|
||||
if (payload.tags_removed) {
|
||||
for (const key of payload.tags_removed) {
|
||||
Vue.delete(mainData, key);
|
||||
}
|
||||
delete payload.categories_removed;
|
||||
}
|
||||
stateMerge(mainData, payload);
|
||||
}
|
||||
},
|
||||
@@ -94,9 +100,38 @@ const store = new Vuex.Store<RootState>({
|
||||
(value, key) => merge({}, value, { key }));
|
||||
return sortBy(categories, 'name');
|
||||
},
|
||||
allTags(state) {
|
||||
if (!state.mainData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const finalTags: any[] = []
|
||||
for (const tag of state.mainData.tags) {
|
||||
finalTags.push({
|
||||
"key": tag,
|
||||
"name": tag,
|
||||
});
|
||||
}
|
||||
return sortBy(finalTags, 'name');
|
||||
},
|
||||
torrentGroupByCategory(state, getters) {
|
||||
return groupBy(getters.allTorrents, torrent => torrent.category);
|
||||
},
|
||||
torrentGroupByTag(state, getters) {
|
||||
const result: any = {}
|
||||
for (const torrent of getters.allTorrents) {
|
||||
const tags: any[] = torrent.tags.split(",");
|
||||
tags.forEach(tag => {
|
||||
let list: any[] = result[tag]
|
||||
if (!list) {
|
||||
list = []
|
||||
result[tag] = list;
|
||||
}
|
||||
list.push(torrent);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
torrentGroupBySite(state, getters) {
|
||||
return groupBy(getters.allTorrents, (torrent) => {
|
||||
if (!torrent.tracker) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface AddFormState {
|
||||
export interface TorrentFilter {
|
||||
state: string;
|
||||
category: string;
|
||||
tag: string;
|
||||
site: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@ export interface SimpleCategory {
|
||||
savePath?: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ServerState {
|
||||
alltime_dl: number;
|
||||
alltime_ul: number;
|
||||
@@ -95,6 +100,7 @@ export interface ServerState {
|
||||
|
||||
export interface MainData {
|
||||
categories: Record<string, Category>;
|
||||
tags: [string];
|
||||
server_state: ServerState;
|
||||
torrents: Record<string, BaseTorrent>;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ describe('all torrents getter', () => {
|
||||
store.replaceState(mockState({
|
||||
mainData: {
|
||||
categories: {},
|
||||
tags: [""],
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
server_state: undefined as any,
|
||||
torrents: {
|
||||
|
||||
Reference in New Issue
Block a user