From b289fb9624ec00aab7a3d4ce154c86adfe3e57ae Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Tue, 17 Mar 2026 10:57:24 +0800 Subject: [PATCH] fix: use updateProcessConcurrency to persist slider changes to config.yaml - CleanerPage now uses updateProcessConcurrency instead of setProcessConcurrency - This ensures slider changes are persisted to config.yaml via IPC - Remove unused queryBatchSize and setProcessConcurrency from destructuring --- src/main/ipc/settings-handler.ts | 23 +++++++++++ src/main/services/config/config-manager.ts | 4 ++ src/main/types/config.schema.ts | 10 +++++ src/preload/index.d.ts | 7 ++++ src/preload/index.ts | 7 ++++ src/renderer/src/hooks/useCleaner.ts | 47 +++++++++++++--------- src/renderer/src/pages/CleanerPage.tsx | 47 +++++++--------------- src/shared/ipc-channels.ts | 6 +++ 8 files changed, 99 insertions(+), 52 deletions(-) diff --git a/src/main/ipc/settings-handler.ts b/src/main/ipc/settings-handler.ts index 16ea76a..41480ef 100644 --- a/src/main/ipc/settings-handler.ts +++ b/src/main/ipc/settings-handler.ts @@ -10,6 +10,7 @@ import type { UserType, ConnectionTestResult, SaveSettingsResult } from '../type import { IPC_CHANNELS } from '../../shared/ipc-channels' import { ValidationError } from '../types/errors' import { withErrorHandling, type IpcResult } from './index' +import type { CleanerConfig } from '../types/config.schema' const log = createLogger('SettingsHandler') @@ -174,4 +175,26 @@ export function registerSettingsHandlers(): void { }, 'settings:testDbConnection') } ) + + ipcMain.handle(IPC_CHANNELS.CONFIG_GET_CLEANER, async (): Promise> => { + return withErrorHandling(async () => { + const configManager = ConfigManager.getInstance() + const config = configManager.getConfig() + return config.cleaner + }, 'config:getCleaner') + }) + + ipcMain.handle( + IPC_CHANNELS.CONFIG_UPDATE_CLEANER, + async (_event, updates: Partial): Promise> => { + return withErrorHandling(async () => { + const configManager = ConfigManager.getInstance() + const result = await configManager.updateConfig({ cleaner: updates as CleanerConfig }) + if (!result.success) { + throw new Error(result.error) + } + return configManager.getConfig().cleaner + }, 'config:updateCleaner') + } + ) } diff --git a/src/main/services/config/config-manager.ts b/src/main/services/config/config-manager.ts index d6fc9f7..dfd65cf 100644 --- a/src/main/services/config/config-manager.ts +++ b/src/main/services/config/config-manager.ts @@ -81,6 +81,10 @@ const DEFAULT_CONFIG: FullConfig = { enableCrud: false, defaultManager: '' }, + cleaner: { + queryBatchSize: 100, + processConcurrency: 1 + }, orderResolution: { tableName: '', productionIdField: '', diff --git a/src/main/types/config.schema.ts b/src/main/types/config.schema.ts index b4ad6ea..bd96cea 100644 --- a/src/main/types/config.schema.ts +++ b/src/main/types/config.schema.ts @@ -97,6 +97,15 @@ export const validationConfigSchema = z.object({ defaultManager: z.string().default('') }) +/** + * 清理配置 Schema + */ +export const cleanerConfigSchema = z.object({ + queryBatchSize: z.number().int().min(1).max(100).default(100), + processConcurrency: z.number().int().min(1).max(20).default(1) +}) +export type CleanerConfig = z.infer + /** * 订单号解析配置 Schema */ @@ -131,6 +140,7 @@ export const fullConfigSchema = z.object({ paths: pathsConfigSchema, extraction: extractionConfigSchema, validation: validationConfigSchema, + cleaner: cleanerConfigSchema, orderResolution: orderResolutionSchema, logging: loggingConfigSchema }) diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 4adecf1..4c9c78c 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -21,6 +21,7 @@ import type { } from '../main/types/settings.types' import type { IpcResult } from '../main/ipc' import type { LogLevel } from '../shared/ipc-channels' +import type { CleanerConfig } from '../main/types/config.schema' export interface ResolverAPI { resolve: (input: ResolverInput) => Promise> @@ -110,6 +111,11 @@ export interface UserErpConfigAPI { getAll: () => Promise>> } +export interface ConfigAPI { + getCleaner: () => Promise> + updateCleaner: (updates: Partial) => Promise> +} + export interface LoggerAPI { log: (level: LogLevel, message: string, context?: Record) => void } @@ -137,6 +143,7 @@ declare global { settings: SettingsAPI materialType: MaterialTypeAPI userErpConfig: UserErpConfigAPI + config: ConfigAPI logger: LoggerAPI } api: unknown diff --git a/src/preload/index.ts b/src/preload/index.ts index ffa6b55..a65fe87 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -12,6 +12,7 @@ import type { } from '../main/types/validation.types' import type { IpcResult } from '../main/ipc' import { IPC_CHANNELS, type LogLevel } from '../shared/ipc-channels' +import type { CleanerConfig } from '../main/types/config.schema' type ErpSettingsPayload = { erp?: { @@ -195,6 +196,12 @@ const api = { getAll: (): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL) }, + config: { + getCleaner: (): Promise> => invokeIpc(IPC_CHANNELS.CONFIG_GET_CLEANER), + updateCleaner: (updates: Partial): Promise> => + invokeIpc(IPC_CHANNELS.CONFIG_UPDATE_CLEANER, updates) + }, + logger: { log: (level: LogLevel, message: string, context?: Record): void => { ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, { diff --git a/src/renderer/src/hooks/useCleaner.ts b/src/renderer/src/hooks/useCleaner.ts index 90e2d7f..19e4153 100644 --- a/src/renderer/src/hooks/useCleaner.ts +++ b/src/renderer/src/hooks/useCleaner.ts @@ -78,18 +78,8 @@ export function useCleaner() { const saved = sessionStorage.getItem('cleaner_headless') return saved ? saved === 'true' : true }) - const [queryBatchSize, setQueryBatchSize] = useState(() => { - const saved = sessionStorage.getItem('cleaner_queryBatchSize') - const value = saved ? Number(saved) : 100 - if (!Number.isFinite(value)) return 100 - return Math.min(100, Math.max(1, Math.trunc(value))) - }) - const [processConcurrency, setProcessConcurrency] = useState(() => { - const saved = sessionStorage.getItem('cleaner_processConcurrency') - const value = saved ? Number(saved) : 1 - if (!Number.isFinite(value)) return 1 - return Math.min(20, Math.max(1, Math.trunc(value))) - }) + const [queryBatchSize, setQueryBatchSize] = useState(100) + const [processConcurrency, setProcessConcurrency] = useState(1) const [showSettingsMenu, setShowSettingsMenu] = useState(false) // Inline editing state for manager field (Admin only) @@ -174,6 +164,22 @@ export function useCleaner() { } }, []) + // Load cleaner config from config.yaml on mount + useEffect(() => { + const loadCleanerConfig = async () => { + try { + const result = await window.electron.config.getCleaner() + if (result.success && result.data) { + setQueryBatchSize(result.data.queryBatchSize) + setProcessConcurrency(result.data.processConcurrency) + } + } catch (err) { + console.error('Failed to load cleaner config:', err) + } + } + loadCleanerConfig() + }, []) + useEffect(() => { sessionStorage.setItem('cleaner_dryRun', dryRun.toString()) }, [dryRun]) @@ -182,13 +188,15 @@ export function useCleaner() { sessionStorage.setItem('cleaner_headless', headless.toString()) }, [headless]) - useEffect(() => { - sessionStorage.setItem('cleaner_queryBatchSize', queryBatchSize.toString()) - }, [queryBatchSize]) - - useEffect(() => { - sessionStorage.setItem('cleaner_processConcurrency', processConcurrency.toString()) - }, [processConcurrency]) + const updateProcessConcurrency = async (value: number) => { + const clamped = Math.max(1, Math.min(20, value)) + setProcessConcurrency(clamped) + try { + await window.electron.config.updateCleaner({ processConcurrency: clamped }) + } catch (err) { + console.error('Failed to update cleaner config:', err) + } + } useEffect(() => { sessionStorage.setItem('cleaner_validationMode', valMode) @@ -529,6 +537,7 @@ export function useCleaner() { setQueryBatchSize, processConcurrency, setProcessConcurrency, + updateProcessConcurrency, showSettingsMenu, setShowSettingsMenu, filteredResults, diff --git a/src/renderer/src/pages/CleanerPage.tsx b/src/renderer/src/pages/CleanerPage.tsx index 670cb94..a661806 100644 --- a/src/renderer/src/pages/CleanerPage.tsx +++ b/src/renderer/src/pages/CleanerPage.tsx @@ -46,10 +46,8 @@ const CleanerPage: React.FC = () => { setIsTypeDialogOpen, headless, setHeadless, - queryBatchSize, - setQueryBatchSize, processConcurrency, - setProcessConcurrency, + updateProcessConcurrency, showSettingsMenu, setShowSettingsMenu, filteredResults, @@ -463,41 +461,24 @@ const CleanerPage: React.FC = () => {
-
-
批量查询数量
-
- 每批查询订单数,范围 1-100 -
- { - const raw = Number(e.target.value) - if (!Number.isFinite(raw)) return - setQueryBatchSize(Math.max(1, Math.min(100, Math.trunc(raw)))) - }} - className="mt-2 w-full rounded border border-slate-300 px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" - /> -
并行处理数量
同时处理详情页数量,范围 1-20
- { - const raw = Number(e.target.value) - if (!Number.isFinite(raw)) return - setProcessConcurrency(Math.max(1, Math.min(20, Math.trunc(raw)))) - }} - className="mt-2 w-full rounded border border-slate-300 px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" - /> +
+ updateProcessConcurrency(Number(e.target.value))} + className="flex-1 h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-blue-600" + /> + + {processConcurrency} + +
diff --git a/src/shared/ipc-channels.ts b/src/shared/ipc-channels.ts index e6cef3d..b0f8425 100644 --- a/src/shared/ipc-channels.ts +++ b/src/shared/ipc-channels.ts @@ -84,6 +84,12 @@ export const IPC_CHANNELS = { USER_ERP_CONFIG_TEST_CONNECTION: 'user-erp-config:testConnection', USER_ERP_CONFIG_GET_ALL: 'user-erp-config:getAll', + // Config + CONFIG_GET: 'config:get', + CONFIG_UPDATE: 'config:update', + CONFIG_GET_CLEANER: 'config:getCleaner', + CONFIG_UPDATE_CLEANER: 'config:updateCleaner', + // Logger LOGGER_FORWARD: 'logger:forward' } as const