设置页新增搜索小键盘布局并支持26键

This commit is contained in:
JSR
2026-03-18 19:55:50 +08:00
parent 51545dd0df
commit 5b57ee1411
6 changed files with 165 additions and 20 deletions
+25
View File
@@ -8,6 +8,7 @@ pub struct SettingsSpec {
pub is_wizard_completed: bool,
pub log_level: String,
pub window_zoom: f64,
pub search_keyboard_layout: String,
pub themes_custom: JsonValue,
pub auto_score_enabled: bool,
pub auto_score_rules: JsonValue,
@@ -22,6 +23,7 @@ impl Default for SettingsSpec {
is_wizard_completed: false,
log_level: "info".to_string(),
window_zoom: 1.0,
search_keyboard_layout: "qwerty26".to_string(),
themes_custom: JsonValue::Array(vec![]),
auto_score_enabled: false,
auto_score_rules: JsonValue::Array(vec![]),
@@ -37,6 +39,7 @@ pub enum SettingsKey {
IsWizardCompleted,
LogLevel,
WindowZoom,
SearchKeyboardLayout,
ThemesCustom,
AutoScoreEnabled,
AutoScoreRules,
@@ -51,6 +54,7 @@ impl SettingsKey {
SettingsKey::IsWizardCompleted => "is_wizard_completed",
SettingsKey::LogLevel => "log_level",
SettingsKey::WindowZoom => "window_zoom",
SettingsKey::SearchKeyboardLayout => "search_keyboard_layout",
SettingsKey::ThemesCustom => "themes_custom",
SettingsKey::AutoScoreEnabled => "auto_score_enabled",
SettingsKey::AutoScoreRules => "auto_score_rules",
@@ -65,6 +69,7 @@ impl SettingsKey {
"is_wizard_completed" => Some(SettingsKey::IsWizardCompleted),
"log_level" => Some(SettingsKey::LogLevel),
"window_zoom" => Some(SettingsKey::WindowZoom),
"search_keyboard_layout" => Some(SettingsKey::SearchKeyboardLayout),
"themes_custom" => Some(SettingsKey::ThemesCustom),
"auto_score_enabled" => Some(SettingsKey::AutoScoreEnabled),
"auto_score_rules" => Some(SettingsKey::AutoScoreRules),
@@ -270,6 +275,22 @@ impl SettingsService {
},
);
defs.insert(
SettingsKey::SearchKeyboardLayout,
SettingDefinition {
kind: SettingValueKind::String,
default_value: SettingsValue::String("qwerty26".to_string()),
write_permission: PermissionRequirement::Admin,
validate: Some(|v| {
if let SettingsValue::String(s) = v {
matches!(s.as_str(), "t9" | "qwerty26")
} else {
false
}
}),
},
);
defs.insert(
SettingsKey::ThemesCustom,
SettingDefinition {
@@ -434,6 +455,10 @@ impl SettingsService {
SettingsValue::Number(n) => n,
_ => 1.0,
},
search_keyboard_layout: match self.get_value(SettingsKey::SearchKeyboardLayout) {
SettingsValue::String(s) => s,
_ => "qwerty26".to_string(),
},
themes_custom: match self.get_value(SettingsKey::ThemesCustom) {
SettingsValue::Json(j) => j,
_ => JsonValue::Array(vec![]),
+90 -20
View File
@@ -24,6 +24,7 @@ interface reason {
}
type SortType = "alphabet" | "surname" | "score"
type SearchKeyboardLayout = "t9" | "qwerty26"
const T9_KEY_MAP: Record<string, string> = {
a: "2",
@@ -62,6 +63,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
const [sortType, setSortType] = useState<SortType>("alphabet")
const [searchKeyword, setSearchKeyword] = useState("")
const [showPinyinKeyboard, setShowPinyinKeyboard] = useState(false)
const [searchKeyboardLayout, setSearchKeyboardLayout] = useState<SearchKeyboardLayout>("qwerty26")
const scrollContainerRef = useRef<HTMLDivElement>(null)
const groupRefs = useRef<Record<string, HTMLDivElement | null>>({})
@@ -125,6 +127,46 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
return () => window.removeEventListener("ss:data-updated", onDataUpdated as any)
}, [fetchData])
useEffect(() => {
const api = (window as any).api
if (!api) return
let disposed = false
let unlisten: (() => void) | null = null
api
.getSetting("search_keyboard_layout")
.then((res: any) => {
if (disposed) return
const value = res?.data
if (value === "t9" || value === "qwerty26") {
setSearchKeyboardLayout(value)
}
})
.catch(() => void 0)
api
.onSettingChanged((change: any) => {
if (change?.key !== "search_keyboard_layout") return
if (change?.value === "t9" || change?.value === "qwerty26") {
setSearchKeyboardLayout(change.value)
}
})
.then((fn: () => void) => {
if (disposed) {
fn()
return
}
unlisten = fn
})
.catch(() => void 0)
return () => {
disposed = true
if (unlisten) unlisten()
}
}, [])
useEffect(() => {
const onDocumentClick = (e: MouseEvent) => {
const target = e.target as Node | null
@@ -136,7 +178,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
return () => document.removeEventListener("mousedown", onDocumentClick)
}, [])
const pinyinKeyRows = [
const t9KeyRows = [
[
{ digit: "2", letters: "ABC" },
{ digit: "3", letters: "DEF" },
@@ -154,6 +196,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
],
]
const qwertyKeyRows = [
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
["z", "x", "c", "v", "b", "n", "m", "⌫"],
]
const toT9Digits = (text: string) =>
text
.toLowerCase()
@@ -161,12 +209,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
.map((ch) => T9_KEY_MAP[ch] || "")
.join("")
const handlePinyinKeyPress = (digit: string) => {
if (digit === "⌫") {
const handleSearchKeyPress = (keyValue: string) => {
if (keyValue === "⌫") {
setSearchKeyword((prev) => prev.slice(0, -1))
return
}
setSearchKeyword((prev) => `${prev}${digit}`)
setSearchKeyword((prev) => `${prev}${keyValue}`)
}
const getDisplayText = (name: string) => {
@@ -861,7 +909,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
position: "absolute",
top: "calc(100% + 8px)",
left: 0,
width: "220px",
width: searchKeyboardLayout === "qwerty26" ? "260px" : "220px",
padding: "8px",
borderRadius: "10px",
border: "1px solid var(--ss-border-color)",
@@ -871,23 +919,45 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{pinyinKeyRows.map((row, rowIndex) => (
<div
key={`pinyin-row-${rowIndex}`}
style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "6px" }}
>
{row.map((keyItem) => (
<Button
key={keyItem.digit}
size="small"
onClick={() => handlePinyinKeyPress(keyItem.digit)}
style={{ height: "28px", fontSize: "11px", padding: 0 }}
{searchKeyboardLayout === "qwerty26"
? qwertyKeyRows.map((row, rowIndex) => (
<div
key={`qwerty-row-${rowIndex}`}
style={{
display: "grid",
gridTemplateColumns: `repeat(${row.length}, 1fr)`,
gap: "6px",
}}
>
{keyItem.digit === "⌫" ? "⌫" : `${keyItem.digit} ${keyItem.letters}`}
</Button>
{row.map((keyItem) => (
<Button
key={keyItem}
size="small"
onClick={() => handleSearchKeyPress(keyItem)}
style={{ height: "28px", fontSize: "11px", padding: 0 }}
>
{keyItem === "⌫" ? "⌫" : keyItem.toUpperCase()}
</Button>
))}
</div>
))
: t9KeyRows.map((row, rowIndex) => (
<div
key={`pinyin-row-${rowIndex}`}
style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "6px" }}
>
{row.map((keyItem) => (
<Button
key={keyItem.digit}
size="small"
onClick={() => handleSearchKeyPress(keyItem.digit)}
style={{ height: "28px", fontSize: "11px", padding: 0 }}
>
{keyItem.digit === "⌫" ? "⌫" : `${keyItem.digit} ${keyItem.letters}`}
</Button>
))}
</div>
))}
</div>
))}
</div>
</div>
)}
+32
View File
@@ -9,6 +9,7 @@ type appSettings = {
is_wizard_completed: boolean
log_level: "debug" | "info" | "warn" | "error"
window_zoom?: string
search_keyboard_layout?: "t9" | "qwerty26"
auto_score_enabled?: boolean
}
@@ -20,6 +21,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
is_wizard_completed: false,
log_level: "info",
window_zoom: "1.0",
search_keyboard_layout: "qwerty26",
})
const [securityStatus, setSecurityStatus] = useState<{
@@ -108,6 +110,8 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
if (change?.key === "is_wizard_completed")
return { ...prev, is_wizard_completed: change.value }
if (change?.key === "window_zoom") return { ...prev, window_zoom: change.value }
if (change?.key === "search_keyboard_layout")
return { ...prev, search_keyboard_layout: change.value }
if (change?.key === "auto_score_enabled")
return { ...prev, auto_score_enabled: change.value }
return prev
@@ -377,6 +381,34 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
<Divider />
<Form layout="horizontal" labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
<Form.Item label={t("settings.searchKeyboard.title")}>
<Select
value={settings.search_keyboard_layout || "qwerty26"}
onChange={async (v) => {
if (!(window as any).api) return
const next = String(v) as "t9" | "qwerty26"
const res = await (window as any).api.setSetting("search_keyboard_layout", next)
if (res.success) {
setSettings((prev) => ({ ...prev, search_keyboard_layout: next }))
messageApi.success(t("settings.general.saved"))
} else {
messageApi.error(res.message || t("settings.general.saveFailed"))
}
}}
style={{ width: "320px" }}
disabled={!canAdmin}
options={[
{ value: "qwerty26", label: t("settings.searchKeyboard.options.qwerty26") },
{ value: "t9", label: t("settings.searchKeyboard.options.t9") },
]}
/>
<div
style={{ marginTop: "4px", fontSize: "12px", color: "var(--ss-text-secondary)" }}
>
{t("settings.searchKeyboard.hint")}
</div>
</Form.Item>
<Form.Item label={t("settings.interfaceZoom")}>
<Select
value={settings.window_zoom || "1.0"}
+8
View File
@@ -127,6 +127,14 @@
"darkMode": "Dark",
"primaryColor": "Primary Color",
"backgroundGradient": "Background Gradient",
"searchKeyboard": {
"title": "Search Keyboard",
"hint": "Choose the pop-up keyboard layout under the search box",
"options": {
"qwerty26": "26-key (QWERTY)",
"t9": "9-key (T9)"
}
},
"interfaceZoom": "Interface Zoom",
"zoomHint": "Adjust the overall size of the application interface",
"zoomUpdated": "Interface zoom updated",
+8
View File
@@ -127,6 +127,14 @@
"darkMode": "深色",
"primaryColor": "主色",
"backgroundGradient": "背景渐变",
"searchKeyboard": {
"title": "搜索小键盘",
"hint": "选择搜索框下方弹出的小键盘布局",
"options": {
"qwerty26": "26 键(QWERTY",
"t9": "9 键(T9"
}
},
"interfaceZoom": "界面缩放",
"zoomHint": "调节应用界面的整体大小",
"zoomUpdated": "界面缩放已更新",
+2
View File
@@ -21,6 +21,7 @@ export type settingsKey =
| "is_wizard_completed"
| "log_level"
| "window_zoom"
| "search_keyboard_layout"
| "themes_custom"
| "auto_score_enabled"
| "auto_score_rules"
@@ -32,6 +33,7 @@ export interface settingsSpec {
is_wizard_completed: boolean
log_level: string
window_zoom: number
search_keyboard_layout: "t9" | "qwerty26"
themes_custom: themeConfig[]
auto_score_enabled: boolean
auto_score_rules: any[]