style: format code with Prettier

Apply Prettier formatting to maintain consistent code style across the codebase.
Changes include formatting improvements for:
- IPC handlers (database, file, material-type, resolver, user-erp-config, validation)
- Type definitions (ipc-api.types)
- React components (MaterialTypeManagementDialog)
- React hooks (useAuth, useCleaner, useValidation)
- Pages (SettingsPage)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
test
2026-03-08 15:17:40 +08:00
parent 57c3452d82
commit c4fff84848
12 changed files with 217 additions and 146 deletions

View File

@@ -21,7 +21,10 @@ const mysqlServices = new Map<string, MySqlService>()
const sqlServerServices = new Map<string, SqlServerService>() const sqlServerServices = new Map<string, SqlServerService>()
const cleanupBoundWindows = new Set<string>() const cleanupBoundWindows = new Set<string>()
function bindWindowCleanup(windowId: string, sender: { once: (event: string, listener: () => void) => void }): void { function bindWindowCleanup(
windowId: string,
sender: { once: (event: string, listener: () => void) => void }
): void {
if (cleanupBoundWindows.has(windowId)) { if (cleanupBoundWindows.has(windowId)) {
return return
} }
@@ -31,7 +34,9 @@ function bindWindowCleanup(windowId: string, sender: { once: (event: string, lis
const sqlServer = getSqlServerService(windowId) const sqlServer = getSqlServerService(windowId)
if (mysql) { if (mysql) {
mysql.disconnect().catch((error) => log.warn('MySQL disconnect on window destroy failed', { error })) mysql
.disconnect()
.catch((error) => log.warn('MySQL disconnect on window destroy failed', { error }))
deleteMySqlService(windowId) deleteMySqlService(windowId)
} }
@@ -99,39 +104,48 @@ export function registerDatabaseHandlers(): void {
IPC_CHANNELS.DATABASE_MYSQL_CONNECT, IPC_CHANNELS.DATABASE_MYSQL_CONNECT,
async (event, config: MySqlConfig): Promise<IpcResult<void>> => { async (event, config: MySqlConfig): Promise<IpcResult<void>> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
// Use window ID as connection identifier // Use window ID as connection identifier
const windowId = (event.sender as { id: number }).id.toString() const windowId = (event.sender as { id: number }).id.toString()
bindWindowCleanup(windowId, event.sender as { once: (event: string, listener: () => void) => void }) bindWindowCleanup(
log.info('Connecting to MySQL', { windowId }) windowId,
const service = new MySqlService(config) event.sender as { once: (event: string, listener: () => void) => void }
await service.connect() )
setMySqlService(windowId, service) log.info('Connecting to MySQL', { windowId })
log.info('MySQL connected', { windowId }) const service = new MySqlService(config)
await service.connect()
setMySqlService(windowId, service)
log.info('MySQL connected', { windowId })
}, 'database:mysql:connect') }, 'database:mysql:connect')
} }
) )
// Disconnect from MySQL // Disconnect from MySQL
ipcMain.handle(IPC_CHANNELS.DATABASE_MYSQL_DISCONNECT, async (event): Promise<IpcResult<void>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.DATABASE_MYSQL_DISCONNECT,
const windowId = (event.sender as { id: number }).id.toString() async (event): Promise<IpcResult<void>> => {
const service = getMySqlService(windowId) return withErrorHandling(async () => {
if (service) { const windowId = (event.sender as { id: number }).id.toString()
await service.disconnect() const service = getMySqlService(windowId)
deleteMySqlService(windowId) if (service) {
log.info('MySQL disconnected', { windowId }) await service.disconnect()
} deleteMySqlService(windowId)
}, 'database:mysql:disconnect') log.info('MySQL disconnected', { windowId })
}) }
}, 'database:mysql:disconnect')
}
)
// Check if MySQL is connected // Check if MySQL is connected
ipcMain.handle(IPC_CHANNELS.DATABASE_MYSQL_IS_CONNECTED, async (event): Promise<IpcResult<boolean>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.DATABASE_MYSQL_IS_CONNECTED,
const windowId = (event.sender as { id: number }).id.toString() async (event): Promise<IpcResult<boolean>> => {
const service = getMySqlService(windowId) return withErrorHandling(async () => {
return service ? service.isConnected() : false const windowId = (event.sender as { id: number }).id.toString()
}, 'database:mysql:isConnected') const service = getMySqlService(windowId)
}) return service ? service.isConnected() : false
}, 'database:mysql:isConnected')
}
)
// Execute MySQL query // Execute MySQL query
ipcMain.handle( ipcMain.handle(
@@ -160,7 +174,10 @@ export function registerDatabaseHandlers(): void {
async (event, config: SqlServerConfig): Promise<IpcResult<void>> => { async (event, config: SqlServerConfig): Promise<IpcResult<void>> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
const windowId = (event.sender as { id: number }).id.toString() const windowId = (event.sender as { id: number }).id.toString()
bindWindowCleanup(windowId, event.sender as { once: (event: string, listener: () => void) => void }) bindWindowCleanup(
windowId,
event.sender as { once: (event: string, listener: () => void) => void }
)
log.info('Connecting to SQL Server', { windowId }) log.info('Connecting to SQL Server', { windowId })
const service = new SqlServerService(config) const service = new SqlServerService(config)
await service.connect() await service.connect()
@@ -171,26 +188,32 @@ export function registerDatabaseHandlers(): void {
) )
// Disconnect from SQL Server // Disconnect from SQL Server
ipcMain.handle(IPC_CHANNELS.DATABASE_SQLSERVER_DISCONNECT, async (event): Promise<IpcResult<void>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.DATABASE_SQLSERVER_DISCONNECT,
const windowId = (event.sender as { id: number }).id.toString() async (event): Promise<IpcResult<void>> => {
const service = getSqlServerService(windowId) return withErrorHandling(async () => {
if (service) { const windowId = (event.sender as { id: number }).id.toString()
await service.disconnect() const service = getSqlServerService(windowId)
deleteSqlServerService(windowId) if (service) {
log.info('SQL Server disconnected', { windowId }) await service.disconnect()
} deleteSqlServerService(windowId)
}, 'database:sqlserver:disconnect') log.info('SQL Server disconnected', { windowId })
}) }
}, 'database:sqlserver:disconnect')
}
)
// Check if SQL Server is connected // Check if SQL Server is connected
ipcMain.handle(IPC_CHANNELS.DATABASE_SQLSERVER_IS_CONNECTED, async (event): Promise<IpcResult<boolean>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.DATABASE_SQLSERVER_IS_CONNECTED,
const windowId = (event.sender as { id: number }).id.toString() async (event): Promise<IpcResult<boolean>> => {
const service = getSqlServerService(windowId) return withErrorHandling(async () => {
return service ? service.isConnected() : false const windowId = (event.sender as { id: number }).id.toString()
}, 'database:sqlserver:isConnected') const service = getSqlServerService(windowId)
}) return service ? service.isConnected() : false
}, 'database:sqlserver:isConnected')
}
)
// Execute SQL Server query // Execute SQL Server query
ipcMain.handle( ipcMain.handle(

View File

@@ -31,13 +31,16 @@ function normalizeAndValidatePath(inputPath: string): string {
} }
export function registerFileHandlers(): void { export function registerFileHandlers(): void {
ipcMain.handle(IPC_CHANNELS.FILE_READ, async (_event, filePath: string): Promise<IpcResult<string>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.FILE_READ,
const safePath = normalizeAndValidatePath(filePath) async (_event, filePath: string): Promise<IpcResult<string>> => {
log.debug('Reading file', { filePath: safePath }) return withErrorHandling(async () => {
return await fs.readFile(safePath, 'utf-8') const safePath = normalizeAndValidatePath(filePath)
}, 'file:read') log.debug('Reading file', { filePath: safePath })
}) return await fs.readFile(safePath, 'utf-8')
}, 'file:read')
}
)
ipcMain.handle( ipcMain.handle(
IPC_CHANNELS.FILE_WRITE, IPC_CHANNELS.FILE_WRITE,
@@ -52,36 +55,45 @@ export function registerFileHandlers(): void {
} }
) )
ipcMain.handle(IPC_CHANNELS.FILE_EXISTS, async (_event, filePath: string): Promise<IpcResult<boolean>> => { ipcMain.handle(
return withErrorHandling(async () => { IPC_CHANNELS.FILE_EXISTS,
const safePath = normalizeAndValidatePath(filePath) async (_event, filePath: string): Promise<IpcResult<boolean>> => {
try { return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
try {
await fs.access(safePath)
return true
} catch {
return false
}
}, 'file:exists')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_LIST,
async (_event, dirPath: string): Promise<IpcResult<string[]>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(dirPath)
log.debug('Listing directory', { dirPath: safePath })
const entries = await fs.readdir(safePath, { withFileTypes: true })
return entries
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
.sort()
}, 'file:list')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_OPEN_PATH,
async (_event, filePath: string): Promise<IpcResult<void>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
log.debug('Opening path in explorer', { filePath: safePath })
await fs.access(safePath) await fs.access(safePath)
return true await shell.openPath(safePath)
} catch { }, 'file:openPath')
return false }
} )
}, 'file:exists')
})
ipcMain.handle(IPC_CHANNELS.FILE_LIST, async (_event, dirPath: string): Promise<IpcResult<string[]>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(dirPath)
log.debug('Listing directory', { dirPath: safePath })
const entries = await fs.readdir(safePath, { withFileTypes: true })
return entries
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
.sort()
}, 'file:list')
})
ipcMain.handle(IPC_CHANNELS.FILE_OPEN_PATH, async (_event, filePath: string): Promise<IpcResult<void>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
log.debug('Opening path in explorer', { filePath: safePath })
await fs.access(safePath)
await shell.openPath(safePath)
}, 'file:openPath')
})
} }

View File

@@ -47,10 +47,7 @@ export function registerMaterialTypeHandlers(): void {
*/ */
ipcMain.handle( ipcMain.handle(
IPC_CHANNELS.MATERIAL_TYPE_GET_BY_MANAGER, IPC_CHANNELS.MATERIAL_TYPE_GET_BY_MANAGER,
async ( async (_event, managerName: string): Promise<IpcResult<MaterialTypeRecord[]>> => {
_event,
managerName: string
): Promise<IpcResult<MaterialTypeRecord[]>> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
const records = await dao.getMaterialsByManager(managerName) const records = await dao.getMaterialsByManager(managerName)
return records return records
@@ -127,4 +124,3 @@ export function registerMaterialTypeHandlers(): void {
log.info('Material type handlers registered') log.info('Material type handlers registered')
} }

View File

@@ -106,7 +106,9 @@ export function registerResolverHandlers(): void {
async ( async (
_event, _event,
inputs: string[] inputs: string[]
): Promise<IpcResult<Array<{ input: string; type: 'productionId' | 'orderNumber' | 'unknown' }>>> => { ): Promise<
IpcResult<Array<{ input: string; type: 'productionId' | 'orderNumber' | 'unknown' }>>
> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
// Create a mock resolver without database connection // Create a mock resolver without database connection
const resolver = new OrderNumberResolver({ const resolver = new OrderNumberResolver({

View File

@@ -42,7 +42,10 @@ export function registerUserErpConfigHandlers(): void {
const credentials = await erpConfigService.getCurrentUserErpConfig() const credentials = await erpConfigService.getCurrentUserErpConfig()
if (!credentials) { if (!credentials) {
throw new ValidationError('未找到 ERP 配置。请先配置 ERP 账号和密码。', 'VAL_INVALID_INPUT') throw new ValidationError(
'未找到 ERP 配置。请先配置 ERP 账号和密码。',
'VAL_INVALID_INPUT'
)
} }
const configManager = ConfigManager.getInstance() const configManager = ConfigManager.getInstance()
@@ -86,7 +89,10 @@ export function registerUserErpConfigHandlers(): void {
ipcMain.handle( ipcMain.handle(
IPC_CHANNELS.USER_ERP_CONFIG_TEST_CONNECTION, IPC_CHANNELS.USER_ERP_CONFIG_TEST_CONNECTION,
async (_event, credentials: ErpCredentialsRequest): Promise<IpcResult<ConnectionTestResult>> => { async (
_event,
credentials: ErpCredentialsRequest
): Promise<IpcResult<ConnectionTestResult>> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
if (!credentials.username || !credentials.password) { if (!credentials.username || !credentials.password) {
throw new ValidationError( throw new ValidationError(
@@ -139,4 +145,3 @@ export function registerUserErpConfigHandlers(): void {
} }
) )
} }

View File

@@ -497,18 +497,21 @@ export function registerValidationHandlers(): void {
/** /**
* Get unique manager names * Get unique manager names
*/ */
ipcMain.handle(IPC_CHANNELS.MATERIALS_GET_MANAGERS, async (_event): Promise<{ managers: string[] }> => { ipcMain.handle(
try { IPC_CHANNELS.MATERIALS_GET_MANAGERS,
const dao = new MaterialsToBeDeletedDAO() async (_event): Promise<{ managers: string[] }> => {
const managers = await dao.getManagers() try {
return { managers } const dao = new MaterialsToBeDeletedDAO()
} catch (error) { const managers = await dao.getManagers()
log.error('Get managers error', { return { managers }
error: error instanceof Error ? error.message : String(error) } catch (error) {
}) log.error('Get managers error', {
return { managers: [] } error: error instanceof Error ? error.message : String(error)
})
return { managers: [] }
}
} }
}) )
/** /**
* Update manager for a single material * Update manager for a single material

View File

@@ -76,9 +76,7 @@ export interface ExtractorAPI {
* Run ERP data extractor * Run ERP data extractor
* @param input - Extractor input parameters * @param input - Extractor input parameters
*/ */
runExtractor: ( runExtractor: (input: ExtractorInput) => Promise<IpcResult<ExtractorResult>>
input: ExtractorInput
) => Promise<IpcResult<ExtractorResult>>
/** /**
* Subscribe to progress updates * Subscribe to progress updates
* @param callback - Callback function receiving progress data * @param callback - Callback function receiving progress data
@@ -101,9 +99,7 @@ export interface CleanerAPI {
* Run ERP cleaner service * Run ERP cleaner service
* @param input - Cleaner input parameters * @param input - Cleaner input parameters
*/ */
runCleaner: ( runCleaner: (input: CleanerInput) => Promise<IpcResult<CleanerResult>>
input: CleanerInput
) => Promise<IpcResult<CleanerResult>>
/** /**
* Export validation results to Excel * Export validation results to Excel

View File

@@ -154,13 +154,16 @@ export const MaterialTypeManagementDialog: React.FC<MaterialTypeManagementDialog
}, []) }, [])
// Start editing a cell // Start editing a cell
const startEdit = useCallback((rowIndex: number, field: string) => { const startEdit = useCallback(
const row = rows[rowIndex] (rowIndex: number, field: string) => {
if (row.state === 'deleted') return const row = rows[rowIndex]
if (row.state === 'deleted') return
setEditingCell({ rowIndex, field }) setEditingCell({ rowIndex, field })
setEditValue(row.record[field as keyof MaterialTypeRecord] as string) setEditValue(row.record[field as keyof MaterialTypeRecord] as string)
}, [rows]) },
[rows]
)
// Save edit // Save edit
const saveEdit = useCallback(() => { const saveEdit = useCallback(() => {
@@ -252,7 +255,9 @@ export const MaterialTypeManagementDialog: React.FC<MaterialTypeManagementDialog
toUpdate, toUpdate,
toDelete toDelete
}) })
const payload = result.success ? (result.data as { stats?: { success?: number; failed?: number } } | undefined) : undefined const payload = result.success
? (result.data as { stats?: { success?: number; failed?: number } } | undefined)
: undefined
if (result.success) { if (result.success) {
alert( alert(

View File

@@ -56,7 +56,10 @@ export function useAuth(): UseAuthReturn {
return true return true
}, []) }, [])
const silentLogin = useCallback(async (): Promise<{ success: boolean; requiresUserSelection?: boolean }> => { const silentLogin = useCallback(async (): Promise<{
success: boolean
requiresUserSelection?: boolean
}> => {
setState((prev) => ({ ...prev, loading: true, error: null })) setState((prev) => ({ ...prev, loading: true, error: null }))
const result = await window.electron.auth.silentLogin() const result = await window.electron.auth.silentLogin()

View File

@@ -100,7 +100,9 @@ export function useCleaner() {
// Load managers // Load managers
if (admin) { if (admin) {
const resp = await window.electron.materials.getManagers() const resp = await window.electron.materials.getManagers()
const managersPayload = resp.success ? (resp.data as { managers: string[] } | undefined) : undefined const managersPayload = resp.success
? (resp.data as { managers: string[] } | undefined)
: undefined
const managerList = managersPayload?.managers ?? [] const managerList = managersPayload?.managers ?? []
setManagers(managerList) setManagers(managerList)
setSelectedManagers(new Set(managerList)) setSelectedManagers(new Set(managerList))
@@ -108,7 +110,9 @@ export function useCleaner() {
// Get shared Production IDs // Get shared Production IDs
const result = await window.electron.validation.getSharedProductionIds() const result = await window.electron.validation.getSharedProductionIds()
const idsPayload = result.success ? (result.data as { productionIds?: string[] } | undefined) : undefined const idsPayload = result.success
? (result.data as { productionIds?: string[] } | undefined)
: undefined
setSharedProductionIdsCount(idsPayload?.productionIds?.length ?? 0) setSharedProductionIdsCount(idsPayload?.productionIds?.length ?? 0)
} catch (err) { } catch (err) {
console.error('Initialization failed:', err) console.error('Initialization failed:', err)
@@ -297,7 +301,9 @@ export function useCleaner() {
if (materialsToUpsert.length > 0) { if (materialsToUpsert.length > 0) {
const res = await window.electron.materials.upsertBatch(materialsToUpsert) const res = await window.electron.materials.upsertBatch(materialsToUpsert)
const payload = res.success ? (res.data as { stats?: { success?: number } } | undefined) : undefined const payload = res.success
? (res.data as { stats?: { success?: number } } | undefined)
: undefined
if (!res.success) throw new Error(res.error || '写入物料失败') if (!res.success) throw new Error(res.error || '写入物料失败')
msgParts.push(`写入/更新成功:${payload?.stats?.success || 0}`) msgParts.push(`写入/更新成功:${payload?.stats?.success || 0}`)
} }
@@ -314,7 +320,9 @@ export function useCleaner() {
// Reload managers if admin // Reload managers if admin
if (isAdmin) { if (isAdmin) {
const resp = await window.electron.materials.getManagers() const resp = await window.electron.materials.getManagers()
const payload = resp.success ? (resp.data as { managers?: string[] } | undefined) : undefined const payload = resp.success
? (resp.data as { managers?: string[] } | undefined)
: undefined
setManagers(payload?.managers ?? []) setManagers(payload?.managers ?? [])
} }
} catch (err) { } catch (err) {

View File

@@ -52,28 +52,39 @@ export function useValidation(): UseValidationReturn {
error: null error: null
}) })
const validate = useCallback(async (request: ValidationRequest): Promise<ValidationResponse | null> => { const validate = useCallback(
setState((prev) => ({ ...prev, loading: true, error: null })) async (request: ValidationRequest): Promise<ValidationResponse | null> => {
const response = await window.electron.validation.validate(request) setState((prev) => ({ ...prev, loading: true, error: null }))
if (!response.success || !response.data) { const response = await window.electron.validation.validate(request)
setState((prev) => ({ ...prev, loading: false, error: response.error || 'Validation failed' })) if (!response.success || !response.data) {
return response.success ? null : { success: false, error: response.error } setState((prev) => ({
} ...prev,
loading: false,
error: response.error || 'Validation failed'
}))
return response.success ? null : { success: false, error: response.error }
}
const result = response.data as ValidationResponse const result = response.data as ValidationResponse
if (!result.success) { if (!result.success) {
setState((prev) => ({ ...prev, loading: false, error: result.error || 'Validation failed' })) setState((prev) => ({
...prev,
loading: false,
error: result.error || 'Validation failed'
}))
return result
}
setState({
loading: false,
data: result.results || null,
stats: result.stats || null,
error: null
})
return result return result
} },
[]
setState({ )
loading: false,
data: result.results || null,
stats: result.stats || null,
error: null
})
return result
}, [])
const setSharedProductionIds = useCallback(async (ids: string[]): Promise<void> => { const setSharedProductionIds = useCallback(async (ids: string[]): Promise<void> => {
await window.electron.validation.setSharedProductionIds(ids) await window.electron.validation.setSharedProductionIds(ids)
@@ -88,7 +99,10 @@ export function useValidation(): UseValidationReturn {
return payload.productionIds || [] return payload.productionIds || []
}, []) }, [])
const getCleanerData = useCallback(async (): Promise<{ orderNumbers: string[]; materialCodes: string[] } | null> => { const getCleanerData = useCallback(async (): Promise<{
orderNumbers: string[]
materialCodes: string[]
} | null> => {
const result = await window.electron.validation.getCleanerData() const result = await window.electron.validation.getCleanerData()
if (!result.success || !result.data) { if (!result.success || !result.data) {
return null return null

View File

@@ -27,7 +27,9 @@ const SettingsPage: React.FC = () => {
setIsLoading(true) setIsLoading(true)
// ERP credentials are loaded from database (current user's config) // ERP credentials are loaded from database (current user's config)
const response = await window.electron.settings.getSettings() const response = await window.electron.settings.getSettings()
const config = response.success ? (response.data as { erp?: ErpCredentials } | undefined) : undefined const config = response.success
? (response.data as { erp?: ErpCredentials } | undefined)
: undefined
// Extract ERP credentials from the config // Extract ERP credentials from the config
if (config?.erp) { if (config?.erp) {
@@ -60,7 +62,9 @@ const SettingsPage: React.FC = () => {
password: credentials.password password: credentials.password
} }
}) })
const saveData = result.success ? (result.data as { success?: boolean; error?: string } | undefined) : undefined const saveData = result.success
? (result.data as { success?: boolean; error?: string } | undefined)
: undefined
if (result.success && saveData?.success !== false) { if (result.success && saveData?.success !== false) {
setIsModified(false) setIsModified(false)