mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 11:49:02 +08:00
fix: 字体选择列表加载不了的问题
feat: 整理项目文件
This commit is contained in:
+27
-4
@@ -29,8 +29,8 @@ import { useTranslation } from "react-i18next"
|
||||
import { Sidebar } from "./components/Sidebar"
|
||||
import { ContentArea } from "./components/ContentArea"
|
||||
import { OOBE } from "./components/OOBE/OOBE"
|
||||
import { OAuthLogin } from "./components/OAuthLogin"
|
||||
import { OAuthCallback } from "./components/OAuthCallback"
|
||||
import { OAuthLogin } from "./components/OAuth/OAuthLogin"
|
||||
import { OAuthCallback } from "./components/OAuth/OAuthCallback"
|
||||
import { ThemeProvider, useTheme } from "./contexts/ThemeContext"
|
||||
import { MOBILE_NAV_ITEMS, MobileNavKey, sanitizeMobileNavKeys } from "./shared/mobileNavigation"
|
||||
|
||||
@@ -39,6 +39,23 @@ const DEFAULT_MOBILE_BOTTOM_PRIMARY_KEYS: MobileNavKey[] = DEFAULT_MOBILE_BOTTOM
|
||||
0,
|
||||
4
|
||||
)
|
||||
const SYSTEM_FONT_STACK =
|
||||
'"PingFang SC", "PingFangTC-Regular", "Hiragino Sans GB", "Hiragino Sans", "STHeiti", "Heiti SC", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif'
|
||||
|
||||
const resolveFontFamily = (fontValue?: string): string => {
|
||||
if (!fontValue || fontValue === "system") return SYSTEM_FONT_STACK
|
||||
if (fontValue.startsWith("system-")) {
|
||||
const family = fontValue.slice("system-".length).trim()
|
||||
if (family) return `"${family}", ${SYSTEM_FONT_STACK}`
|
||||
}
|
||||
return SYSTEM_FONT_STACK
|
||||
}
|
||||
|
||||
const applyGlobalFontFamily = (fontValue?: string) => {
|
||||
const fontFamily = resolveFontFamily(fontValue)
|
||||
document.documentElement.style.setProperty("--ss-font-family", fontFamily)
|
||||
document.body.style.fontFamily = fontFamily
|
||||
}
|
||||
|
||||
function MainContent(): React.JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
@@ -163,6 +180,7 @@ function MainContent(): React.JSX.Element {
|
||||
const settingsRes = await (window as any).api.getAllSettings()
|
||||
if (settingsRes?.success && settingsRes.data) {
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(settingsRes.data.mobile_bottom_nav_items))
|
||||
applyGlobalFontFamily(settingsRes.data.font_family)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +196,13 @@ function MainContent(): React.JSX.Element {
|
||||
|
||||
api
|
||||
.onSettingChanged((change: { key?: string; value?: unknown }) => {
|
||||
if (change?.key !== "mobile_bottom_nav_items") return
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(change.value))
|
||||
if (change?.key === "mobile_bottom_nav_items") {
|
||||
setMobileBottomNavItems(normalizeStoredBottomKeys(change.value))
|
||||
return
|
||||
}
|
||||
if (change?.key === "font_family") {
|
||||
applyGlobalFontFamily(String(change.value || "system"))
|
||||
}
|
||||
})
|
||||
.then((fn: () => void) => {
|
||||
if (disposed) {
|
||||
|
||||
@@ -18,8 +18,8 @@ import { type ImmutableTree } from "@react-awesome-query-builder/antd"
|
||||
import type { ColumnsType } from "antd/es/table"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { fetchAllTags } from "./TagEditorDialog"
|
||||
import { ActionEditor } from "./AutoScoreManagerPage/ActionEditor"
|
||||
import { TriggerRuleBuilder } from "./AutoScoreManagerPage/TriggerRuleBuilder"
|
||||
import { ActionEditor } from "./AutoScore/ActionEditor"
|
||||
import { TriggerRuleBuilder } from "./AutoScore/TriggerRuleBuilder"
|
||||
import {
|
||||
actionDraftsToPayload,
|
||||
actionsToDrafts,
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
type ActionDraft,
|
||||
type AutoScoreRule,
|
||||
type AutoScoreTagOption,
|
||||
} from "./AutoScoreManagerPage/AutoScoreUtils"
|
||||
} from "./AutoScore/AutoScoreUtils"
|
||||
|
||||
interface StudentItem {
|
||||
id: number
|
||||
|
||||
+117
-36
@@ -16,7 +16,7 @@ import {
|
||||
Typography,
|
||||
} from "antd"
|
||||
import { ThemeQuickSettings } from "./ThemeQuickSettings"
|
||||
import { OAuthLogin } from "./OAuthLogin"
|
||||
import { OAuthLogin } from "./OAuth/OAuthLogin"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { changeLanguage, getCurrentLanguage, languageOptions, AppLanguage } from "../i18n"
|
||||
import { useResponsive } from "../hooks/useResponsive"
|
||||
@@ -39,6 +39,62 @@ interface FontOption {
|
||||
fontFamily: string
|
||||
}
|
||||
|
||||
const SYSTEM_FONT_STACK =
|
||||
'"PingFang SC", "PingFangTC-Regular", "Hiragino Sans GB", "Hiragino Sans", "STHeiti", "Heiti SC", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif'
|
||||
|
||||
const defaultFontOptions: FontOption[] = [
|
||||
{
|
||||
value: "system",
|
||||
label: "系统默认",
|
||||
fontFamily: SYSTEM_FONT_STACK,
|
||||
},
|
||||
{
|
||||
value: "system-Microsoft YaHei UI",
|
||||
label: "Microsoft YaHei UI",
|
||||
fontFamily: '"Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Microsoft YaHei",
|
||||
label: "Microsoft YaHei",
|
||||
fontFamily: '"Microsoft YaHei", "微软雅黑", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-PingFang SC",
|
||||
label: "PingFang SC",
|
||||
fontFamily: '"PingFang SC", "Hiragino Sans GB", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Noto Sans CJK SC",
|
||||
label: "Noto Sans CJK SC",
|
||||
fontFamily: '"Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Segoe UI",
|
||||
label: "Segoe UI",
|
||||
fontFamily: '"Segoe UI", sans-serif',
|
||||
},
|
||||
{
|
||||
value: "system-Arial",
|
||||
label: "Arial",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
},
|
||||
]
|
||||
|
||||
const mergeFontOptions = (options: FontOption[]): FontOption[] => {
|
||||
const map = new Map<string, FontOption>()
|
||||
for (const option of options) {
|
||||
if (!map.has(option.value)) {
|
||||
map.set(option.value, option)
|
||||
}
|
||||
}
|
||||
return Array.from(map.values())
|
||||
}
|
||||
|
||||
const findFontOption = (options: FontOption[], value?: string): FontOption | undefined => {
|
||||
if (!value) return options.find((item) => item.value === "system") || options[0]
|
||||
return options.find((item) => item.value === value) || options.find((item) => item.value === "system")
|
||||
}
|
||||
|
||||
const applyFontFamily = (fontFamily: string) => {
|
||||
document.documentElement.style.setProperty("--ss-font-family", fontFamily)
|
||||
document.body.style.fontFamily = fontFamily
|
||||
@@ -76,8 +132,9 @@ export const Settings: React.FC<{
|
||||
search_keyboard_layout: "qwerty26",
|
||||
disable_search_keyboard: false,
|
||||
})
|
||||
const [fontOptions, setFontOptions] = useState<FontOption[]>([])
|
||||
const [isLoadingFonts, setIsLoadingFonts] = useState(true)
|
||||
const [fontOptions, setFontOptions] = useState<FontOption[]>(defaultFontOptions)
|
||||
const [isLoadingFonts, setIsLoadingFonts] = useState(false)
|
||||
const fontOptionsRef = useRef<FontOption[]>(defaultFontOptions)
|
||||
|
||||
const [securityStatus, setSecurityStatus] = useState<{
|
||||
permission: permissionLevel
|
||||
@@ -168,6 +225,10 @@ export const Settings: React.FC<{
|
||||
)
|
||||
}, [permission, t])
|
||||
|
||||
useEffect(() => {
|
||||
fontOptionsRef.current = fontOptions
|
||||
}, [fontOptions])
|
||||
|
||||
const emitDataUpdated = (category: "events" | "students" | "reasons" | "all") => {
|
||||
window.dispatchEvent(new CustomEvent("ss:data-updated", { detail: { category } }))
|
||||
}
|
||||
@@ -187,54 +248,73 @@ export const Settings: React.FC<{
|
||||
}
|
||||
}
|
||||
|
||||
const loadSystemFonts = async () => {
|
||||
if (!(window as any).api?.getSystemFonts) return
|
||||
|
||||
const loadSystemFonts = async (selectedFontValue?: string) => {
|
||||
setIsLoadingFonts(true)
|
||||
|
||||
const applySelectedFont = (options: FontOption[]) => {
|
||||
const current = findFontOption(options, selectedFontValue || settings.font_family)
|
||||
if (current) applyFontFamily(current.fontFamily)
|
||||
}
|
||||
|
||||
const api = (window as any).api
|
||||
if (!api?.getSystemFonts) {
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
setIsLoadingFonts(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await (window as any).api.getSystemFonts()
|
||||
const res = await api.getSystemFonts()
|
||||
if (res.success && Array.isArray(res.data)) {
|
||||
const fontList = res.data
|
||||
.map((name: string) => ({
|
||||
value: `system-${name}`,
|
||||
label: name,
|
||||
fontFamily: `"${name}"`,
|
||||
})) as FontOption[]
|
||||
const remoteOptions = res.data
|
||||
.map((name: string) => String(name || "").trim())
|
||||
.filter((name: string) => Boolean(name))
|
||||
.map(
|
||||
(name: string) =>
|
||||
({
|
||||
value: `system-${name}`,
|
||||
label: name,
|
||||
fontFamily: `"${name}", ${SYSTEM_FONT_STACK}`,
|
||||
}) satisfies FontOption
|
||||
)
|
||||
|
||||
setFontOptions(fontList)
|
||||
|
||||
if (settings.font_family) {
|
||||
const currentFontOpt = fontList.find((f) => f.value === settings.font_family)
|
||||
if (currentFontOpt) {
|
||||
applyFontFamily(currentFontOpt.fontFamily)
|
||||
}
|
||||
}
|
||||
const mergedOptions = mergeFontOptions([...defaultFontOptions, ...remoteOptions])
|
||||
setFontOptions(mergedOptions)
|
||||
applySelectedFont(mergedOptions)
|
||||
} else {
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load system fonts:", error)
|
||||
setFontOptions(defaultFontOptions)
|
||||
applySelectedFont(defaultFontOptions)
|
||||
} finally {
|
||||
setIsLoadingFonts(false)
|
||||
}
|
||||
}
|
||||
|
||||
const loadAll = async () => {
|
||||
if (!(window as any).api) return
|
||||
const res = await (window as any).api.getAllSettings()
|
||||
const api = (window as any).api
|
||||
if (!api) {
|
||||
setFontOptions(defaultFontOptions)
|
||||
setIsLoadingFonts(false)
|
||||
return
|
||||
}
|
||||
|
||||
let savedFontFamily = settings.font_family || "system"
|
||||
const res = await api.getAllSettings()
|
||||
if (res.success && res.data) {
|
||||
setSettings(res.data)
|
||||
setPgConnectionString(res.data.pg_connection_string || "")
|
||||
setPgConnectionStatus(res.data.pg_connection_status || { connected: true, type: "sqlite" })
|
||||
if (res.data.font_family) {
|
||||
const fontOpt = fontOptions.find((f) => f.value === res.data.font_family)
|
||||
if (fontOpt) {
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
}
|
||||
}
|
||||
savedFontFamily = res.data.font_family || "system"
|
||||
}
|
||||
const authRes = await (window as any).api.authGetStatus()
|
||||
const authRes = await api.authGetStatus()
|
||||
if (authRes.success && authRes.data) setSecurityStatus(authRes.data)
|
||||
await loadMcpStatus()
|
||||
await loadSystemFonts()
|
||||
await loadSystemFonts(savedFontFamily)
|
||||
}
|
||||
|
||||
const loadAboutContent = async () => {
|
||||
@@ -272,11 +352,12 @@ export const Settings: React.FC<{
|
||||
if (change?.key === "auto_score_enabled")
|
||||
return { ...prev, auto_score_enabled: change.value }
|
||||
if (change?.key === "font_family") {
|
||||
const fontOpt = fontOptions.find((f) => f.value === change.value)
|
||||
const value = String(change.value || "system")
|
||||
const fontOpt = findFontOption(fontOptionsRef.current, value)
|
||||
if (fontOpt) {
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
}
|
||||
return { ...prev, font_family: change.value }
|
||||
return { ...prev, font_family: value }
|
||||
}
|
||||
return prev
|
||||
})
|
||||
@@ -726,12 +807,12 @@ export const Settings: React.FC<{
|
||||
<Select
|
||||
value={settings.font_family || "system"}
|
||||
onChange={async (v) => {
|
||||
const fontOpt = fontOptions.find((f) => f.value === v)
|
||||
const fontOpt = findFontOption(fontOptions, String(v))
|
||||
if (!fontOpt) return
|
||||
applyFontFamily(fontOpt.fontFamily)
|
||||
setSettings((prev) => ({ ...prev, font_family: v }))
|
||||
setSettings((prev) => ({ ...prev, font_family: String(v) }))
|
||||
if ((window as any).api) {
|
||||
const res = await (window as any).api.setSetting("font_family", v)
|
||||
const res = await (window as any).api.setSetting("font_family", String(v))
|
||||
if (res.success) {
|
||||
messageApi.success(t("settings.general.saved"))
|
||||
} else {
|
||||
|
||||
@@ -48,6 +48,7 @@ export type settingsKey =
|
||||
| "window_zoom"
|
||||
| "search_keyboard_layout"
|
||||
| "disable_search_keyboard"
|
||||
| "font_family"
|
||||
| "themes_custom"
|
||||
| "auto_score_enabled"
|
||||
| "auto_score_rules"
|
||||
@@ -63,6 +64,7 @@ export interface settingsSpec {
|
||||
window_zoom: number
|
||||
search_keyboard_layout: "t9" | "qwerty26"
|
||||
disable_search_keyboard: boolean
|
||||
font_family: string
|
||||
themes_custom: themeConfig[]
|
||||
auto_score_enabled: boolean
|
||||
auto_score_rules: autoScoreRule[]
|
||||
@@ -311,6 +313,8 @@ const api = {
|
||||
key: K,
|
||||
value: settingsSpec[K]
|
||||
): Promise<{ success: boolean }> => invoke("settings_set", { key, value }),
|
||||
getSystemFonts: (): Promise<{ success: boolean; data: string[]; message?: string }> =>
|
||||
invoke("settings_get_system_fonts"),
|
||||
onSettingChanged: (callback: (change: settingChange) => void): Promise<UnlistenFn> => {
|
||||
return listen<settingChange>("settings:changed", (event) => {
|
||||
callback(event.payload)
|
||||
|
||||
Reference in New Issue
Block a user