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
This commit is contained in:
@@ -10,6 +10,7 @@ import type { UserType, ConnectionTestResult, SaveSettingsResult } from '../type
|
|||||||
import { IPC_CHANNELS } from '../../shared/ipc-channels'
|
import { IPC_CHANNELS } from '../../shared/ipc-channels'
|
||||||
import { ValidationError } from '../types/errors'
|
import { ValidationError } from '../types/errors'
|
||||||
import { withErrorHandling, type IpcResult } from './index'
|
import { withErrorHandling, type IpcResult } from './index'
|
||||||
|
import type { CleanerConfig } from '../types/config.schema'
|
||||||
|
|
||||||
const log = createLogger('SettingsHandler')
|
const log = createLogger('SettingsHandler')
|
||||||
|
|
||||||
@@ -174,4 +175,26 @@ export function registerSettingsHandlers(): void {
|
|||||||
}, 'settings:testDbConnection')
|
}, 'settings:testDbConnection')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ipcMain.handle(IPC_CHANNELS.CONFIG_GET_CLEANER, async (): Promise<IpcResult<CleanerConfig>> => {
|
||||||
|
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<CleanerConfig>): Promise<IpcResult<CleanerConfig>> => {
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ const DEFAULT_CONFIG: FullConfig = {
|
|||||||
enableCrud: false,
|
enableCrud: false,
|
||||||
defaultManager: ''
|
defaultManager: ''
|
||||||
},
|
},
|
||||||
|
cleaner: {
|
||||||
|
queryBatchSize: 100,
|
||||||
|
processConcurrency: 1
|
||||||
|
},
|
||||||
orderResolution: {
|
orderResolution: {
|
||||||
tableName: '',
|
tableName: '',
|
||||||
productionIdField: '',
|
productionIdField: '',
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ export const validationConfigSchema = z.object({
|
|||||||
defaultManager: z.string().default('')
|
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<typeof cleanerConfigSchema>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订单号解析配置 Schema
|
* 订单号解析配置 Schema
|
||||||
*/
|
*/
|
||||||
@@ -131,6 +140,7 @@ export const fullConfigSchema = z.object({
|
|||||||
paths: pathsConfigSchema,
|
paths: pathsConfigSchema,
|
||||||
extraction: extractionConfigSchema,
|
extraction: extractionConfigSchema,
|
||||||
validation: validationConfigSchema,
|
validation: validationConfigSchema,
|
||||||
|
cleaner: cleanerConfigSchema,
|
||||||
orderResolution: orderResolutionSchema,
|
orderResolution: orderResolutionSchema,
|
||||||
logging: loggingConfigSchema
|
logging: loggingConfigSchema
|
||||||
})
|
})
|
||||||
|
|||||||
7
src/preload/index.d.ts
vendored
7
src/preload/index.d.ts
vendored
@@ -21,6 +21,7 @@ import type {
|
|||||||
} from '../main/types/settings.types'
|
} from '../main/types/settings.types'
|
||||||
import type { IpcResult } from '../main/ipc'
|
import type { IpcResult } from '../main/ipc'
|
||||||
import type { LogLevel } from '../shared/ipc-channels'
|
import type { LogLevel } from '../shared/ipc-channels'
|
||||||
|
import type { CleanerConfig } from '../main/types/config.schema'
|
||||||
|
|
||||||
export interface ResolverAPI {
|
export interface ResolverAPI {
|
||||||
resolve: (input: ResolverInput) => Promise<IpcResult<ResolverResponse>>
|
resolve: (input: ResolverInput) => Promise<IpcResult<ResolverResponse>>
|
||||||
@@ -110,6 +111,11 @@ export interface UserErpConfigAPI {
|
|||||||
getAll: () => Promise<IpcResult<Array<{ username: string; erpUrl: string; erpUsername: string }>>>
|
getAll: () => Promise<IpcResult<Array<{ username: string; erpUrl: string; erpUsername: string }>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConfigAPI {
|
||||||
|
getCleaner: () => Promise<IpcResult<CleanerConfig>>
|
||||||
|
updateCleaner: (updates: Partial<CleanerConfig>) => Promise<IpcResult<CleanerConfig>>
|
||||||
|
}
|
||||||
|
|
||||||
export interface LoggerAPI {
|
export interface LoggerAPI {
|
||||||
log: (level: LogLevel, message: string, context?: Record<string, unknown>) => void
|
log: (level: LogLevel, message: string, context?: Record<string, unknown>) => void
|
||||||
}
|
}
|
||||||
@@ -137,6 +143,7 @@ declare global {
|
|||||||
settings: SettingsAPI
|
settings: SettingsAPI
|
||||||
materialType: MaterialTypeAPI
|
materialType: MaterialTypeAPI
|
||||||
userErpConfig: UserErpConfigAPI
|
userErpConfig: UserErpConfigAPI
|
||||||
|
config: ConfigAPI
|
||||||
logger: LoggerAPI
|
logger: LoggerAPI
|
||||||
}
|
}
|
||||||
api: unknown
|
api: unknown
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {
|
|||||||
} from '../main/types/validation.types'
|
} from '../main/types/validation.types'
|
||||||
import type { IpcResult } from '../main/ipc'
|
import type { IpcResult } from '../main/ipc'
|
||||||
import { IPC_CHANNELS, type LogLevel } from '../shared/ipc-channels'
|
import { IPC_CHANNELS, type LogLevel } from '../shared/ipc-channels'
|
||||||
|
import type { CleanerConfig } from '../main/types/config.schema'
|
||||||
|
|
||||||
type ErpSettingsPayload = {
|
type ErpSettingsPayload = {
|
||||||
erp?: {
|
erp?: {
|
||||||
@@ -195,6 +196,12 @@ const api = {
|
|||||||
getAll: (): Promise<IpcResult> => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL)
|
getAll: (): Promise<IpcResult> => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
config: {
|
||||||
|
getCleaner: (): Promise<IpcResult<CleanerConfig>> => invokeIpc(IPC_CHANNELS.CONFIG_GET_CLEANER),
|
||||||
|
updateCleaner: (updates: Partial<CleanerConfig>): Promise<IpcResult<CleanerConfig>> =>
|
||||||
|
invokeIpc(IPC_CHANNELS.CONFIG_UPDATE_CLEANER, updates)
|
||||||
|
},
|
||||||
|
|
||||||
logger: {
|
logger: {
|
||||||
log: (level: LogLevel, message: string, context?: Record<string, unknown>): void => {
|
log: (level: LogLevel, message: string, context?: Record<string, unknown>): void => {
|
||||||
ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, {
|
ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, {
|
||||||
|
|||||||
@@ -78,18 +78,8 @@ export function useCleaner() {
|
|||||||
const saved = sessionStorage.getItem('cleaner_headless')
|
const saved = sessionStorage.getItem('cleaner_headless')
|
||||||
return saved ? saved === 'true' : true
|
return saved ? saved === 'true' : true
|
||||||
})
|
})
|
||||||
const [queryBatchSize, setQueryBatchSize] = useState(() => {
|
const [queryBatchSize, setQueryBatchSize] = useState(100)
|
||||||
const saved = sessionStorage.getItem('cleaner_queryBatchSize')
|
const [processConcurrency, setProcessConcurrency] = useState(1)
|
||||||
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 [showSettingsMenu, setShowSettingsMenu] = useState(false)
|
const [showSettingsMenu, setShowSettingsMenu] = useState(false)
|
||||||
|
|
||||||
// Inline editing state for manager field (Admin only)
|
// 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(() => {
|
useEffect(() => {
|
||||||
sessionStorage.setItem('cleaner_dryRun', dryRun.toString())
|
sessionStorage.setItem('cleaner_dryRun', dryRun.toString())
|
||||||
}, [dryRun])
|
}, [dryRun])
|
||||||
@@ -182,13 +188,15 @@ export function useCleaner() {
|
|||||||
sessionStorage.setItem('cleaner_headless', headless.toString())
|
sessionStorage.setItem('cleaner_headless', headless.toString())
|
||||||
}, [headless])
|
}, [headless])
|
||||||
|
|
||||||
useEffect(() => {
|
const updateProcessConcurrency = async (value: number) => {
|
||||||
sessionStorage.setItem('cleaner_queryBatchSize', queryBatchSize.toString())
|
const clamped = Math.max(1, Math.min(20, value))
|
||||||
}, [queryBatchSize])
|
setProcessConcurrency(clamped)
|
||||||
|
try {
|
||||||
useEffect(() => {
|
await window.electron.config.updateCleaner({ processConcurrency: clamped })
|
||||||
sessionStorage.setItem('cleaner_processConcurrency', processConcurrency.toString())
|
} catch (err) {
|
||||||
}, [processConcurrency])
|
console.error('Failed to update cleaner config:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sessionStorage.setItem('cleaner_validationMode', valMode)
|
sessionStorage.setItem('cleaner_validationMode', valMode)
|
||||||
@@ -529,6 +537,7 @@ export function useCleaner() {
|
|||||||
setQueryBatchSize,
|
setQueryBatchSize,
|
||||||
processConcurrency,
|
processConcurrency,
|
||||||
setProcessConcurrency,
|
setProcessConcurrency,
|
||||||
|
updateProcessConcurrency,
|
||||||
showSettingsMenu,
|
showSettingsMenu,
|
||||||
setShowSettingsMenu,
|
setShowSettingsMenu,
|
||||||
filteredResults,
|
filteredResults,
|
||||||
|
|||||||
@@ -46,10 +46,8 @@ const CleanerPage: React.FC = () => {
|
|||||||
setIsTypeDialogOpen,
|
setIsTypeDialogOpen,
|
||||||
headless,
|
headless,
|
||||||
setHeadless,
|
setHeadless,
|
||||||
queryBatchSize,
|
|
||||||
setQueryBatchSize,
|
|
||||||
processConcurrency,
|
processConcurrency,
|
||||||
setProcessConcurrency,
|
updateProcessConcurrency,
|
||||||
showSettingsMenu,
|
showSettingsMenu,
|
||||||
setShowSettingsMenu,
|
setShowSettingsMenu,
|
||||||
filteredResults,
|
filteredResults,
|
||||||
@@ -463,41 +461,24 @@ const CleanerPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-slate-100 pt-3 space-y-3">
|
<div className="border-t border-slate-100 pt-3 space-y-3">
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-slate-800">批量查询数量</div>
|
|
||||||
<div className="text-xs text-slate-500 mt-0.5">
|
|
||||||
每批查询订单数,范围 1-100
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
max={100}
|
|
||||||
value={queryBatchSize}
|
|
||||||
onChange={(e) => {
|
|
||||||
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"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-slate-800">并行处理数量</div>
|
<div className="text-sm font-medium text-slate-800">并行处理数量</div>
|
||||||
<div className="text-xs text-slate-500 mt-0.5">
|
<div className="text-xs text-slate-500 mt-0.5">
|
||||||
同时处理详情页数量,范围 1-20
|
同时处理详情页数量,范围 1-20
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-2 flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="range"
|
||||||
min={1}
|
min={1}
|
||||||
max={20}
|
max={20}
|
||||||
value={processConcurrency}
|
value={processConcurrency}
|
||||||
onChange={(e) => {
|
onChange={(e) => updateProcessConcurrency(Number(e.target.value))}
|
||||||
const raw = Number(e.target.value)
|
className="flex-1 h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-blue-600"
|
||||||
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"
|
|
||||||
/>
|
/>
|
||||||
|
<span className="text-sm font-medium text-slate-700 w-8 text-center">
|
||||||
|
{processConcurrency}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -84,6 +84,12 @@ export const IPC_CHANNELS = {
|
|||||||
USER_ERP_CONFIG_TEST_CONNECTION: 'user-erp-config:testConnection',
|
USER_ERP_CONFIG_TEST_CONNECTION: 'user-erp-config:testConnection',
|
||||||
USER_ERP_CONFIG_GET_ALL: 'user-erp-config:getAll',
|
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
|
||||||
LOGGER_FORWARD: 'logger:forward'
|
LOGGER_FORWARD: 'logger:forward'
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
Reference in New Issue
Block a user