Feat: Add segmented progress bar for data extractor with dynamic phase calculation

- Add ExtractionProgress type with phase, batch, and subProgress fields
- Implement dynamic progress calculation: 1 (login) + N (batches) + 2 (merge/import)
- Create SegmentedProgressBar component with 4 colored phases (purple/blue/amber/green)
- Show batch-level progress during download phase (e.g., 批次 1/10)
- Display sub-progress during login phase (连接数据库/解析订单号/登录 ERP)
- Update IPC handler and extractor services to report detailed progress
- Add phase status indicators (completed/active/pending) with color-coded dots
This commit is contained in:
Misaka
2026-03-04 22:16:47 +08:00
parent 4494351e52
commit 2dea1f9556
11 changed files with 299 additions and 46 deletions

View File

@@ -6,14 +6,20 @@ import { create, type IDatabaseService } from '../services/database'
import { createLogger } from '../services/logger'
import { withErrorHandling, type IpcResult } from './index'
import { ErpConnectionError, ValidationError, DatabaseQueryError } from '../types/errors'
import type { ExtractorInput, ExtractorResult } from '../types/extractor.types'
import type { ExtractorInput, ExtractorResult, ExtractionProgress } from '../types/extractor.types'
const log = createLogger('ExtractorHandler')
function sendProgress(windowId: number, message: string, progress: number): void {
function sendProgress(
windowId: number,
message: string,
progress: number,
extra?: Partial<ExtractionProgress>
): void {
try {
const progressData = { message, progress, ...extra }
webContents.getAllWebContents().forEach((wc) => {
wc.send('extractor:progress', { message, progress })
wc.send('extractor:progress', progressData)
})
} catch (error) {
log.warn('Failed to send progress event', { error })
@@ -63,7 +69,10 @@ export function registerExtractorHandlers(): void {
// Create database service using factory
log.info('Connecting to database for order resolution...')
sendProgress(windowId, '连接数据库...', 5)
sendProgress(windowId, '连接数据库...', 3.33, {
phase: 'login',
subProgress: { step: '连接数据库', current: 1, total: 3 }
})
sendLog(windowId, 'system', '正在连接数据库...')
try {
@@ -77,7 +86,10 @@ export function registerExtractorHandlers(): void {
}
// Resolve order numbers (convert productionIDs to 生产订单号)
sendProgress(windowId, '解析订单号...', 10)
sendProgress(windowId, '解析订单号...', 6.67, {
phase: 'login',
subProgress: { step: '解析订单号', current: 2, total: 3 }
})
sendLog(windowId, 'info', '正在解析订单号...')
const resolver = new OrderNumberResolver(dbService)
@@ -109,7 +121,10 @@ export function registerExtractorHandlers(): void {
headless: true
})
sendProgress(windowId, '登录 ERP 系统...', 15)
sendProgress(windowId, '登录 ERP 系统...', 9.99, {
phase: 'login',
subProgress: { step: '登录 ERP 系统', current: 3, total: 3 }
})
sendLog(windowId, 'system', '正在登录 ERP 系统...')
log.info('Logging in to ERP...')
@@ -132,8 +147,8 @@ export function registerExtractorHandlers(): void {
const modifiedInput: ExtractorInput = {
...input,
orderNumbers: validOrderNumbers,
onProgress: (message, progress) => {
sendProgress(windowId, message, progress)
onProgress: (message, progress, extra) => {
sendProgress(windowId, message, progress, extra)
sendLog(windowId, 'info', message)
},
onLog: (level, message) => {
@@ -141,9 +156,6 @@ export function registerExtractorHandlers(): void {
}
}
sendProgress(windowId, '开始提取数据...', 20)
sendLog(windowId, 'system', '提取引擎启动,开始下载数据...')
const result = await extractor.extract(modifiedInput)
// Add warnings to result errors if any