This commit is contained in:
ryan
2026-07-13 15:17:45 +08:00
parent 08e61ce833
commit e0eb4e5975
334 changed files with 17212 additions and 12321 deletions
+16 -15
View File
@@ -1,24 +1,24 @@
import { isCancelError } from '@/lib/services/core'
import { toast } from 'sonner'
import { isCancelError } from '@/lib/services/core';
import { toast } from 'sonner';
/**
* 错误处理选项
*/
interface HandleContextErrorOptions {
/** 是否显示 toast 提示 */
showToast?: boolean
showToast?: boolean;
/** 是否在控制台记录错误 */
logError?: boolean
logError?: boolean;
}
/**
* 统一处理 Context 中的错误
*
*
* @param error - 捕获的错误对象
* @param defaultMessage - 默认错误消息
* @param options - 错误处理选项
* @returns 标准化的 Error 对象
*
*
* @example
* ```typescript
* try {
@@ -35,27 +35,28 @@ interface HandleContextErrorOptions {
export function handleContextError(
error: unknown,
defaultMessage: string,
options: HandleContextErrorOptions = {}
options: HandleContextErrorOptions = {},
): Error {
const { showToast = false, logError = true } = options
const { showToast = false, logError = true } = options;
// 取消的请求不算错误
if (isCancelError(error)) {
return new Error('请求已取消')
return new Error('请求已取消');
}
const errorMessage = error instanceof Error ? error.message : defaultMessage
const errorObject = error instanceof Error ? error : new Error(defaultMessage)
const errorMessage = error instanceof Error ? error.message : defaultMessage;
const errorObject =
error instanceof Error ? error : new Error(defaultMessage);
if (logError) {
console.error(defaultMessage, error)
console.error(defaultMessage, error);
}
if (showToast) {
toast.error(defaultMessage, {
description: errorMessage
})
description: errorMessage,
});
}
return errorObject
return errorObject;
}
+72 -42
View File
@@ -1,4 +1,4 @@
import PinyinMatch from 'pinyin-match'
import PinyinMatch from 'pinyin-match';
type MatchResult = [number, number] | false;
type MatchFunction = (input: string, keys: string) => MatchResult;
@@ -30,14 +30,14 @@ const match = ((): MatchFunction | null => {
})();
export interface SearchItem {
id: string
title: string
description: string
url: string
category: 'page' | 'feature' | 'setting' | 'admin'
keywords: string[]
icon?: string
matchRange?: [number, number]
id: string;
title: string;
description: string;
url: string;
category: 'page' | 'feature' | 'setting' | 'admin';
keywords: string[];
icon?: string;
matchRange?: [number, number];
}
/**
@@ -105,7 +105,15 @@ export const searchData: SearchItem[] = [
description: '管理系统登录注册与认证源配置 (管理员专属)',
url: '/admin/settings',
category: 'admin',
keywords: ['admin', '管理员', '系统设置', '安全', 'security', 'oidc', 'login'],
keywords: [
'admin',
'管理员',
'系统设置',
'安全',
'security',
'oidc',
'login',
],
},
{
id: 'admin-system',
@@ -129,9 +137,17 @@ export const searchData: SearchItem[] = [
description: '下发与排查后台异步定时任务 (管理员专属)',
url: '/admin/tasks',
category: 'admin',
keywords: ['admin', '管理员', '任务', '异步', 'tasks', 'scheduler', 'worker'],
keywords: [
'admin',
'管理员',
'任务',
'异步',
'tasks',
'scheduler',
'worker',
],
},
]
];
/**
* 搜索功能
@@ -139,47 +155,61 @@ export const searchData: SearchItem[] = [
* @param isAdmin 是否为管理员
* @returns 匹配的搜索结果
*/
export function searchItems(query: string, isAdmin: boolean = false): SearchItem[] {
const trimmedQuery = query.trim()
export function searchItems(
query: string,
isAdmin: boolean = false,
): SearchItem[] {
const trimmedQuery = query.trim();
// 非管理员不能搜索 admin 类别项
const filteredData = isAdmin
? searchData
: searchData.filter(item => item.category !== 'admin')
const filteredData = isAdmin
? searchData
: searchData.filter((item) => item.category !== 'admin');
if (!trimmedQuery) {
return filteredData
return filteredData;
}
return filteredData.map(item => {
// 优先匹配标题
const titleMatch = typeof match === 'function' ? match(item.title, trimmedQuery) : null
if (titleMatch) {
return { ...item, matchRange: titleMatch as [number, number] }
}
return filteredData
.map((item) => {
// 优先匹配标题
const titleMatch =
typeof match === 'function' ? match(item.title, trimmedQuery) : null;
if (titleMatch) {
return { ...item, matchRange: titleMatch as [number, number] };
}
// 匹配描述
if (typeof match === 'function' && match(item.description, trimmedQuery)) {
return item
}
// 匹配描述
if (
typeof match === 'function' &&
match(item.description, trimmedQuery)
) {
return item;
}
// 匹配关键词
if (item.keywords.some(keyword => typeof match === 'function' && match(keyword, trimmedQuery))) {
return item
}
// 匹配关键词
if (
item.keywords.some(
(keyword) =>
typeof match === 'function' && match(keyword, trimmedQuery),
)
) {
return item;
}
return null
}).filter((item): item is SearchItem => item !== null)
return null;
})
.filter((item): item is SearchItem => item !== null)
.sort((a, b) => {
// 标题匹配优先
if (a.matchRange && !b.matchRange) return -1
if (!a.matchRange && b.matchRange) return 1
if (a.matchRange && !b.matchRange) return -1;
if (!a.matchRange && b.matchRange) return 1;
// 如果都是标题匹配,按匹配位置排序
if (a.matchRange && b.matchRange) {
return a.matchRange[0] - b.matchRange[0]
return a.matchRange[0] - b.matchRange[0];
}
return 0
})
return 0;
});
}