Skip to content

Commit e09ced4

Browse files
refactor(advanced-search): 重构高级搜索逻辑并修复条件检查
- 将高级搜索条件检查函数 `hasAnyAdvancedCondition` 提取到工具文件 - 修复路由守卫和搜索输入验证中的条件检查逻辑 - 重构搜索历史应用逻辑,提取公共函数 `applyQueryToForm` - 优化数据存储中搜索历史的标准化处理 - 修复本地数据库精确匹配查询的 SQL 生成问题 - 更新组件类型声明和路由配置以支持重构
1 parent cb7b4b8 commit e09ced4

9 files changed

Lines changed: 92 additions & 114 deletions

File tree

components.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ declare module 'vue' {
121121
NProgress: typeof import('naive-ui')['NProgress']
122122
NQrCode: typeof import('naive-ui')['NQrCode']
123123
NRadio: typeof import('naive-ui')['NRadio']
124+
NRadioButton: typeof import('naive-ui')['NRadioButton']
124125
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
125126
NResult: typeof import('naive-ui')['NResult']
126127
NScrollbar: typeof import('naive-ui')['NScrollbar']
@@ -154,6 +155,8 @@ declare module 'vue' {
154155
RouterLink: typeof import('vue-router')['RouterLink']
155156
RouterView: typeof import('vue-router')['RouterView']
156157
ScalingModal: typeof import('./src/components/Modal/ScalingModal.vue')['default']
158+
SearchAdvanced: typeof import('./src/components/Search/SearchAdvanced.vue')['default']
159+
SearchAdvancedLocal: typeof import('./src/components/Search/SearchAdvancedLocal.vue')['default']
157160
SearchDefault: typeof import('./src/components/Search/SearchDefault.vue')['default']
158161
SearchInp: typeof import('./src/components/Search/SearchInp.vue')['default']
159162
SearchInpMenu: typeof import('./src/components/Menu/SearchInpMenu.vue')['default']

electron/main/database/LocalMusicDB.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const buildAdvancedSearchWhere = (query: AdvancedSearchQuery) => {
5858
const v = normalizeInput(value);
5959
if (!v) return;
6060
if (match === "exact") {
61-
where.push(`COALESCE(${column}, '') = ?`);
61+
where.push(`${column} = ?`);
6262
params.push(v);
6363
return;
6464
}

src/components/Search/SearchAdvanced.vue

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<n-modal
33
:show="show"
44
preset="card"
5-
title="在线高级搜索"
5+
title="高级搜索"
66
style="width: min(520px, calc(100vw - 24px))"
77
@update:show="emit('update:show', $event)"
88
>
@@ -113,11 +113,7 @@ const historyOptions = computed<SelectOption[]>(() => {
113113
}));
114114
});
115115
116-
const applyHistory = (value: number | null) => {
117-
if (value === null) return;
118-
const list = dataStore.advancedSearchHistory.filter((q) => (q.mode ?? "auto") !== "local");
119-
const q = list[value];
120-
if (!q) return;
116+
const applyQueryToForm = (q: AdvancedSearchQuery) => {
121117
form.match = q.match ?? "contains";
122118
form.keywords = q.keywords ?? "";
123119
form.title = q.title ?? "";
@@ -127,9 +123,17 @@ const applyHistory = (value: number | null) => {
127123
form.maxDuration = q.maxDuration;
128124
};
129125
126+
const applyHistory = (value: number | null) => {
127+
if (value === null) return;
128+
const list = dataStore.advancedSearchHistory.filter((q) => (q.mode ?? "auto") !== "local");
129+
const q = list[value];
130+
if (!q) return;
131+
applyQueryToForm(q);
132+
};
133+
130134
const submit = () => {
131135
const query: AdvancedSearchQuery = {
132-
mode: "online",
136+
mode: "auto",
133137
match: form.match ?? "contains",
134138
keywords: form.keywords?.trim() || undefined,
135139
title: form.title?.trim() || undefined,
@@ -153,13 +157,7 @@ watch(
153157
if (!show) return;
154158
const last = dataStore.advancedSearchHistory.find((q) => (q.mode ?? "auto") !== "local");
155159
if (!last) return;
156-
form.match = last.match ?? "contains";
157-
form.keywords = last.keywords ?? "";
158-
form.title = last.title ?? "";
159-
form.artist = last.artist ?? "";
160-
form.album = last.album ?? "";
161-
form.minDuration = last.minDuration;
162-
form.maxDuration = last.maxDuration;
160+
applyQueryToForm(last);
163161
},
164162
);
165163
</script>

src/components/Search/SearchAdvancedLocal.vue

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,7 @@ const historyOptions = computed<SelectOption[]>(() => {
254254
}));
255255
});
256256
257-
const applyHistory = (value: number | null) => {
258-
if (value === null) return;
259-
const list = dataStore.advancedSearchHistory.filter((q) => q.mode === "local");
260-
const q = list[value];
261-
if (!q) return;
257+
const applyQueryToForm = (q: AdvancedSearchQuery) => {
262258
form.match = q.match ?? "contains";
263259
form.keywords = q.keywords ?? "";
264260
form.title = q.title ?? "";
@@ -276,6 +272,14 @@ const applyHistory = (value: number | null) => {
276272
form.maxTrackNumber = q.maxTrackNumber;
277273
};
278274
275+
const applyHistory = (value: number | null) => {
276+
if (value === null) return;
277+
const list = dataStore.advancedSearchHistory.filter((q) => q.mode === "local");
278+
const q = list[value];
279+
if (!q) return;
280+
applyQueryToForm(q);
281+
};
282+
279283
const submit = () => {
280284
const query: AdvancedSearchQuery = {
281285
mode: "local",
@@ -324,22 +328,7 @@ watch(
324328
if (!show) return;
325329
const last = dataStore.advancedSearchHistory.find((q) => q.mode === "local");
326330
if (!last) return;
327-
form.match = last.match ?? "contains";
328-
form.keywords = last.keywords ?? "";
329-
form.title = last.title ?? "";
330-
form.artist = last.artist ?? "";
331-
form.album = last.album ?? "";
332-
form.minDuration = last.minDuration;
333-
form.maxDuration = last.maxDuration;
334-
form.inPath = last.inPath;
335-
form.path = last.path ?? "";
336-
form.minBitrate = last.minBitrate;
337-
form.maxBitrate = last.maxBitrate;
338-
form.minSize = last.minSize;
339-
form.maxSize = last.maxSize;
340-
form.minTrackNumber = last.minTrackNumber;
341-
form.maxTrackNumber = last.maxTrackNumber;
331+
applyQueryToForm(last);
342332
},
343333
);
344334
</script>
345-

src/components/Search/SearchInp.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</template>
2121
<template #suffix>
2222
<n-button
23-
v-if="statusStore.searchFocus"
23+
v-if="statusStore.searchFocus && (settingStore.useOnlineService || isElectron)"
2424
:focusable="false"
2525
quaternary
2626
size="tiny"
@@ -54,9 +54,11 @@ import { songDetail } from "@/api/song";
5454
import { formatSongsList } from "@/utils/format";
5555
import SearchInpMenu from "@/components/Menu/SearchInpMenu.vue";
5656
import SearchAdvanced from "@/components/Search/SearchAdvanced.vue";
57+
import { isElectron } from "@/utils/env";
5758
import {
5859
buildAdvancedSearchRouteQuery,
5960
formatAdvancedSearchSummary,
61+
hasAnyAdvancedCondition,
6062
parseAdvancedSearchQuery,
6163
} from "@/utils/advancedSearch";
6264
import type { AdvancedSearchQuery } from "@shared";
@@ -131,6 +133,10 @@ const openAdvancedSearch = () => {
131133
};
132134
133135
const toAdvancedSearch = (query: AdvancedSearchQuery) => {
136+
if (!hasAnyAdvancedCondition(query)) {
137+
window.$message.info("请至少设置一个搜索条件");
138+
return;
139+
}
134140
statusStore.searchFocus = false;
135141
searchInputRef.value?.blur();
136142
advancedSearchShow.value = false;
@@ -158,6 +164,11 @@ const setSearchHistory = (keyword: string) => {
158164
159165
// 更换搜索框关键词
160166
const updatePlaceholder = async () => {
167+
if (!settingStore.useOnlineService) {
168+
searchPlaceholder.value = "搜索本地音乐";
169+
searchRealkeyword.value = "";
170+
return;
171+
}
161172
if (!settingStore.enableSearchKeyword) {
162173
searchPlaceholder.value = "搜索音乐 / 视频";
163174
return;
@@ -187,7 +198,9 @@ const toSearch = async (key: any, type: string = "keyword") => {
187198
key = searchRealkeyword.value?.trim();
188199
}
189200
// 更新推荐
190-
updatePlaceholder();
201+
if (settingStore.useOnlineService) {
202+
updatePlaceholder();
203+
}
191204
// 前往搜索
192205
switch (type) {
193206
case "keyword":

src/router/routes.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AppLayout from "@/layout/AppLayout.vue";
2+
import { hasAnyAdvancedCondition, parseAdvancedSearchQuery } from "@/utils/advancedSearch";
23
import { type RouteRecordRaw } from "vue-router";
34

45
/**
@@ -28,25 +29,15 @@ const appRoutes: Array<RouteRecordRaw> = [
2829
next({ path: "/403" });
2930
return;
3031
}
31-
const hasAny =
32-
!!to.query.keywords ||
33-
!!to.query.title ||
34-
!!to.query.artist ||
35-
!!to.query.album ||
36-
!!to.query.minDuration ||
37-
!!to.query.maxDuration ||
38-
!!to.query.inPath ||
39-
!!to.query.path ||
40-
!!to.query.minBitrate ||
41-
!!to.query.maxBitrate ||
42-
!!to.query.minSize ||
43-
!!to.query.maxSize ||
44-
!!to.query.minTrackNumber ||
45-
!!to.query.maxTrackNumber;
46-
if (!hasAny) {
32+
const advancedQuery = parseAdvancedSearchQuery(to.query);
33+
if (!hasAnyAdvancedCondition(advancedQuery)) {
4734
next({ path: "/403" });
4835
return;
4936
}
37+
if (to.name !== "search-advanced") {
38+
next({ name: "search-advanced", query: to.query });
39+
return;
40+
}
5041
next();
5142
},
5243
redirect: (to) => {

src/stores/data.ts

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -499,43 +499,27 @@ export const useDataStore = defineStore("data", {
499499
}
500500
},
501501
addAdvancedSearchHistory(query: AdvancedSearchQuery) {
502+
const normalizeString = (v?: string) => (typeof v === "string" ? v.trim() || undefined : undefined);
503+
const normalizeNumber = (v?: number) =>
504+
typeof v === "number" && Number.isFinite(v) ? v : undefined;
505+
502506
const normalized: AdvancedSearchQuery = {
503507
mode: query.mode,
504508
match: query.match,
505-
keywords: query.keywords?.trim() || undefined,
506-
title: query.title?.trim() || undefined,
507-
artist: query.artist?.trim() || undefined,
508-
album: query.album?.trim() || undefined,
509-
minDuration:
510-
typeof query.minDuration === "number" && Number.isFinite(query.minDuration)
511-
? query.minDuration
512-
: undefined,
513-
maxDuration:
514-
typeof query.maxDuration === "number" && Number.isFinite(query.maxDuration)
515-
? query.maxDuration
516-
: undefined,
517-
inPath: query.inPath?.trim() || undefined,
518-
path: query.path?.trim() || undefined,
519-
minBitrate:
520-
typeof query.minBitrate === "number" && Number.isFinite(query.minBitrate)
521-
? query.minBitrate
522-
: undefined,
523-
maxBitrate:
524-
typeof query.maxBitrate === "number" && Number.isFinite(query.maxBitrate)
525-
? query.maxBitrate
526-
: undefined,
527-
minSize:
528-
typeof query.minSize === "number" && Number.isFinite(query.minSize) ? query.minSize : undefined,
529-
maxSize:
530-
typeof query.maxSize === "number" && Number.isFinite(query.maxSize) ? query.maxSize : undefined,
531-
minTrackNumber:
532-
typeof query.minTrackNumber === "number" && Number.isFinite(query.minTrackNumber)
533-
? query.minTrackNumber
534-
: undefined,
535-
maxTrackNumber:
536-
typeof query.maxTrackNumber === "number" && Number.isFinite(query.maxTrackNumber)
537-
? query.maxTrackNumber
538-
: undefined,
509+
keywords: normalizeString(query.keywords),
510+
title: normalizeString(query.title),
511+
artist: normalizeString(query.artist),
512+
album: normalizeString(query.album),
513+
minDuration: normalizeNumber(query.minDuration),
514+
maxDuration: normalizeNumber(query.maxDuration),
515+
inPath: normalizeString(query.inPath),
516+
path: normalizeString(query.path),
517+
minBitrate: normalizeNumber(query.minBitrate),
518+
maxBitrate: normalizeNumber(query.maxBitrate),
519+
minSize: normalizeNumber(query.minSize),
520+
maxSize: normalizeNumber(query.maxSize),
521+
minTrackNumber: normalizeNumber(query.minTrackNumber),
522+
maxTrackNumber: normalizeNumber(query.maxTrackNumber),
539523
};
540524

541525
const key = JSON.stringify(normalized);

src/utils/advancedSearch.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,18 @@ export const buildAdvancedSearchRouteQuery = (q: AdvancedSearchQuery) => {
7070
const query: Record<string, string> = { advanced: "1" };
7171
if (q.mode) query.mode = q.mode;
7272
if (q.match) query.match = q.match;
73-
if (q.keywords?.trim()) query.keywords = q.keywords.trim();
74-
if (q.title?.trim()) query.title = q.title.trim();
75-
if (q.artist?.trim()) query.artist = q.artist.trim();
76-
if (q.album?.trim()) query.album = q.album.trim();
73+
if (q.keywords) query.keywords = q.keywords;
74+
if (q.title) query.title = q.title;
75+
if (q.artist) query.artist = q.artist;
76+
if (q.album) query.album = q.album;
7777
if (typeof q.minDuration === "number" && Number.isFinite(q.minDuration)) {
7878
query.minDuration = String(q.minDuration);
7979
}
8080
if (typeof q.maxDuration === "number" && Number.isFinite(q.maxDuration)) {
8181
query.maxDuration = String(q.maxDuration);
8282
}
83-
if (q.inPath?.trim()) query.inPath = q.inPath.trim();
84-
if (q.path?.trim()) query.path = q.path.trim();
83+
if (q.inPath) query.inPath = q.inPath;
84+
if (q.path) query.path = q.path;
8585
if (typeof q.minBitrate === "number" && Number.isFinite(q.minBitrate)) {
8686
query.minBitrate = String(q.minBitrate);
8787
}
@@ -153,6 +153,25 @@ export const buildOnlineKeywords = (q: AdvancedSearchQuery) => {
153153
return parts.join(" ");
154154
};
155155

156+
export const hasAnyAdvancedCondition = (q: AdvancedSearchQuery) => {
157+
return (
158+
!!q.keywords?.trim() ||
159+
!!q.title?.trim() ||
160+
!!q.artist?.trim() ||
161+
!!q.album?.trim() ||
162+
(typeof q.minDuration === "number" && Number.isFinite(q.minDuration)) ||
163+
(typeof q.maxDuration === "number" && Number.isFinite(q.maxDuration)) ||
164+
!!q.inPath?.trim() ||
165+
!!q.path?.trim() ||
166+
(typeof q.minBitrate === "number" && Number.isFinite(q.minBitrate)) ||
167+
(typeof q.maxBitrate === "number" && Number.isFinite(q.maxBitrate)) ||
168+
(typeof q.minSize === "number" && Number.isFinite(q.minSize)) ||
169+
(typeof q.maxSize === "number" && Number.isFinite(q.maxSize)) ||
170+
(typeof q.minTrackNumber === "number" && Number.isFinite(q.minTrackNumber)) ||
171+
(typeof q.maxTrackNumber === "number" && Number.isFinite(q.maxTrackNumber))
172+
);
173+
};
174+
156175
const getSongArtistText = (song: SongType) => {
157176
if (typeof song.artists === "string") return song.artists;
158177
return song.artists.map((a) => a.name).filter(Boolean).join(" / ");

src/views/Local/layout.vue

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ import SearchAdvancedLocal from "@/components/Search/SearchAdvancedLocal.vue";
184184
import { useDataStore, useLocalStore, useSettingStore } from "@/stores";
185185
import type { SongType } from "@/types/main";
186186
import type { AdvancedSearchQuery } from "@shared";
187-
import { buildAdvancedSearchRouteQuery } from "@/utils/advancedSearch";
187+
import { buildAdvancedSearchRouteQuery, hasAnyAdvancedCondition } from "@/utils/advancedSearch";
188188
import { formatSongsList } from "@/utils/format";
189189
import { fuzzySearch, renderIcon } from "@/utils/helper";
190190
import { openBatchList, openCreatePlaylist, openLocalMusicDirectoryModal } from "@/utils/modal";
@@ -236,25 +236,6 @@ const filteredSearchResult = ref<SongType[]>([]);
236236
const localSearchFocus = ref(false);
237237
const localAdvancedSearchShow = ref(false);
238238
239-
const hasAnyAdvancedCondition = (q: AdvancedSearchQuery) => {
240-
return (
241-
!!q.keywords?.trim() ||
242-
!!q.title?.trim() ||
243-
!!q.artist?.trim() ||
244-
!!q.album?.trim() ||
245-
typeof q.minDuration === "number" ||
246-
typeof q.maxDuration === "number" ||
247-
!!q.inPath?.trim() ||
248-
!!q.path?.trim() ||
249-
typeof q.minBitrate === "number" ||
250-
typeof q.maxBitrate === "number" ||
251-
typeof q.minSize === "number" ||
252-
typeof q.maxSize === "number" ||
253-
typeof q.minTrackNumber === "number" ||
254-
typeof q.maxTrackNumber === "number"
255-
);
256-
};
257-
258239
const openLocalAdvancedSearch = () => {
259240
localSearchFocus.value = false;
260241
localAdvancedSearchShow.value = true;

0 commit comments

Comments
 (0)