feat(search): 后台可以自己添加搜索引擎
- 搜索引擎 id 放宽:小写字母/数字/-/_,1–30 位,internal 保留;最多 10 个
- 后台搜索框面板加「添加搜索引擎」(id 自动生成 custom-xxx)与每行「删除」
- 自定义引擎用组件库 EngineIcon 的通用地球图标(@stash/ui 0.3.3)
- 搜索地址仍要求 http(s):// + {query},默认项被删/停用时回落到站内搜索
This commit is contained in:
@@ -1606,6 +1606,13 @@ html.theme-transition .site-background {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 引擎卡片右上角:启用开关 + 删除 */
|
||||
.search-engine-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--su-space-2);
|
||||
}
|
||||
|
||||
.search-engine-fields {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
SearchField,
|
||||
Space,
|
||||
Toaster,
|
||||
type EngineIconName,
|
||||
type MenuItem,
|
||||
} from '@stash/ui'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
@@ -931,7 +930,7 @@ export default function AppNext() {
|
||||
}
|
||||
onClick={() => setEngineOpen((open) => !open)}
|
||||
>
|
||||
<EngineIcon name={searchEngine.id as EngineIconName} size={14} />
|
||||
<EngineIcon name={searchEngine.id} size={14} />
|
||||
<span>{searchEngine.name}</span>
|
||||
</button>
|
||||
|
||||
@@ -978,7 +977,7 @@ export default function AppNext() {
|
||||
className={`engine-option${engine.id === searchEngine.id ? ' is-active' : ''}`}
|
||||
onClick={() => chooseEngine(engine.id)}
|
||||
>
|
||||
<EngineIcon name={engine.id as EngineIconName} size={18} />
|
||||
<EngineIcon name={engine.id} size={18} />
|
||||
<span>{engine.name}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
ToggleRow,
|
||||
Toaster,
|
||||
toast,
|
||||
type EngineIconName,
|
||||
type IconName,
|
||||
type MenuItem,
|
||||
} from '@stash/ui'
|
||||
@@ -2092,6 +2091,32 @@ function SearchPanel({
|
||||
onChange({ ...value, engines, default: nextDefault })
|
||||
}
|
||||
|
||||
/** 自定义引擎:id 只是内部键,点「添加」自动生成,后台不用手填 */
|
||||
const addEngine = () => {
|
||||
if (value.engines.length >= 10) return
|
||||
onChange({
|
||||
...value,
|
||||
engines: [
|
||||
...value.engines,
|
||||
{
|
||||
id: `custom-${Date.now().toString(36)}`,
|
||||
name: '新搜索引擎',
|
||||
url: '',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
const removeEngine = (id: string) => {
|
||||
const engines = value.engines.filter((engine) => engine.id !== id)
|
||||
onChange({
|
||||
...value,
|
||||
engines,
|
||||
default: value.default === id ? 'internal' : value.default,
|
||||
})
|
||||
}
|
||||
|
||||
const defaultOptions = [
|
||||
{ value: 'internal', label: '站内搜索' },
|
||||
...value.engines
|
||||
@@ -2103,8 +2128,8 @@ function SearchPanel({
|
||||
<Space direction="column" gap="lg" align="stretch" className="admin-form">
|
||||
<Title level={4}>搜索框</Title>
|
||||
<Text size="sm" tone="muted">
|
||||
说明:站内搜索始终可用;外部搜索引擎可以改名称、地址和启停,地址里的 {'{query}'}{' '}
|
||||
会替换成关键词。默认项是刷新后搜索框选中的引擎。
|
||||
说明:站内搜索始终可用;外部搜索引擎可以改名称、地址和启停,也可以自己加(最多 10 个),
|
||||
地址里的 {'{query}'} 会替换成关键词。默认项是刷新后搜索框选中的引擎。
|
||||
</Text>
|
||||
|
||||
<FormField label="默认搜索项">
|
||||
@@ -2122,13 +2147,16 @@ function SearchPanel({
|
||||
key={engine.id}
|
||||
>
|
||||
<div className="search-engine-head">
|
||||
<EngineIcon name={engine.id as EngineIconName} size={18} />
|
||||
<EngineIcon name={engine.id} size={18} />
|
||||
<Text strong>{engine.name || engine.id}</Text>
|
||||
<Switch
|
||||
checked={engine.enabled}
|
||||
label={`启用 ${engine.name || engine.id}`}
|
||||
onChange={(enabled) => updateEngine(engine.id, { enabled })}
|
||||
/>
|
||||
<div className="search-engine-actions">
|
||||
<Switch
|
||||
checked={engine.enabled}
|
||||
label={`启用 ${engine.name || engine.id}`}
|
||||
onChange={(enabled) => updateEngine(engine.id, { enabled })}
|
||||
/>
|
||||
<TextButton onClick={() => removeEngine(engine.id)}>删除</TextButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="search-engine-fields">
|
||||
<FormField label="名称" htmlFor={`search-name-${engine.id}`}>
|
||||
@@ -2155,17 +2183,22 @@ function SearchPanel({
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
onChange({
|
||||
default: 'internal',
|
||||
engines: DEFAULT_SEARCH_ENGINES.map((engine) => ({ ...engine })),
|
||||
})
|
||||
}
|
||||
>
|
||||
恢复默认引擎
|
||||
</Button>
|
||||
<Space gap="sm" wrap>
|
||||
<Button variant="secondary" onClick={addEngine} disabled={value.engines.length >= 10}>
|
||||
添加搜索引擎
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
onChange({
|
||||
default: 'internal',
|
||||
engines: DEFAULT_SEARCH_ENGINES.map((engine) => ({ ...engine })),
|
||||
})
|
||||
}
|
||||
>
|
||||
恢复默认引擎
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Space>
|
||||
)
|
||||
|
||||
@@ -78,10 +78,9 @@ export function writeCachedBackground(background: BackgroundSettings): void {
|
||||
}
|
||||
}
|
||||
|
||||
export type SearchEngineId = 'google' | 'bing' | 'baidu' | 'sogou' | '360'
|
||||
|
||||
export type SearchEngineSettings = {
|
||||
id: SearchEngineId
|
||||
/** 内置引擎用固定 id(对应品牌图标);自定义引擎由后台生成 custom-xxx */
|
||||
id: string
|
||||
name: string
|
||||
/** 搜索地址模板,用 {query} 表示关键词 */
|
||||
url: string
|
||||
|
||||
Reference in New Issue
Block a user