diff --git a/src/renderer/src/components/cleaner/CleanerExecutionBar.tsx b/src/renderer/src/components/cleaner/CleanerExecutionBar.tsx new file mode 100644 index 0000000..35e34bc --- /dev/null +++ b/src/renderer/src/components/cleaner/CleanerExecutionBar.tsx @@ -0,0 +1,121 @@ +import React from 'react' +import { Play, Settings2, ToggleLeft, ToggleRight } from 'lucide-react' + +interface CleanerExecutionBarProps { + filteredCount: number + selectedCount: number + dryRun: boolean + setDryRun: (value: boolean) => void + headless: boolean + setHeadless: (value: boolean) => void + processConcurrency: number + updateProcessConcurrency: (value: number) => void + showSettingsMenu: boolean + setShowSettingsMenu: (open: boolean) => void + handleExecuteDeletion: () => Promise + isRunning: boolean + executeButtonRef: React.RefObject +} + +export function CleanerExecutionBar({ + filteredCount, + selectedCount, + dryRun, + setDryRun, + headless, + setHeadless, + processConcurrency, + updateProcessConcurrency, + showSettingsMenu, + setShowSettingsMenu, + handleExecuteDeletion, + isRunning, + executeButtonRef +}: CleanerExecutionBarProps): React.JSX.Element { + return ( +
+
+
+ 共计 {filteredCount} 条记录 | 已选中{' '} + {selectedCount} 条 +
+
+ +
+
+ + {showSettingsMenu && ( +
+
+
+
+
预览模式 (Dry-Run)
+
+ 仅执行页面操作定位,不保存更改 +
+
+ +
+
+
+
+
后台模式 (Headless)
+
+ 浏览器在后台运行,不显示界面 +
+
+ +
+
+
+
+
并行处理数量
+
+ 同时处理详情页数量,范围 1-20 +
+
+ 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/renderer/src/components/cleaner/CleanerResultsTable.tsx b/src/renderer/src/components/cleaner/CleanerResultsTable.tsx new file mode 100644 index 0000000..5d4d424 --- /dev/null +++ b/src/renderer/src/components/cleaner/CleanerResultsTable.tsx @@ -0,0 +1,167 @@ +import React from 'react' +import { CheckSquare, Square } from 'lucide-react' +import type { ValidationResult } from '../../hooks/cleaner/types' + +interface CleanerResultsTableProps { + validationResults: ValidationResult[] + filteredResults: ValidationResult[] + selectedItems: Set + isAdmin: boolean + managers: string[] + editingCell: { rowIndex: number; field: string } | null + editValue: string + setEditValue: (value: string) => void + inputRef: React.RefObject + handleCheckboxToggle: (materialCode: string) => void + handleAssignManagerOnSelect: (materialCode: string) => void + startEdit: (rowIndex: number, field: string) => void + saveEdit: () => void + cancelEdit: () => void +} + +export function CleanerResultsTable({ + validationResults, + filteredResults, + selectedItems, + isAdmin, + managers, + editingCell, + editValue, + setEditValue, + inputRef, + handleCheckboxToggle, + handleAssignManagerOnSelect, + startEdit, + saveEdit, + cancelEdit +}: CleanerResultsTableProps): React.JSX.Element { + const handleSelectionChange = (materialCode: string) => { + handleCheckboxToggle(materialCode) + + if (!isAdmin) { + handleAssignManagerOnSelect(materialCode) + } + } + + return ( +
+ + + + + + + + + + + + + {filteredResults.length === 0 ? ( + + + + ) : ( + filteredResults.map((result, rowIndex) => { + const isChecked = selectedItems.has(result.materialCode) + const noManager = !result.managerName?.trim() + const isEditingManager = + editingCell?.rowIndex === rowIndex && editingCell?.field === 'managerName' + const rowClassName = `${isChecked ? 'bg-blue-50/30' : 'hover:bg-slate-50'} transition-colors ${noManager && isChecked ? 'bg-amber-50/20' : ''}` + const managerCellClassName = noManager + ? 'text-amber-500 text-xs italic' + : 'text-slate-700' + + return ( + + + + + + + + + ) + }) + )} + +
选择材料名称材料代码规格型号 + 负责人 (双击编辑) +
+ {validationResults.length === 0 + ? '暂无数据,请点击左侧按钮获取并校验物料' + : '当前筛选条件下暂无数据'} +
+ {isChecked ? ( + handleSelectionChange(result.materialCode)} + size={16} + className="text-blue-600 inline cursor-pointer" + /> + ) : ( + handleSelectionChange(result.materialCode)} + size={16} + className="text-slate-300 inline cursor-pointer" + /> + )} + + {result.materialName} + + {result.materialCode} + + {result.specification || '-'} + + {result.model || '-'} + + {isAdmin && isEditingManager ? ( + + ) : isAdmin ? ( +
{ + e.stopPropagation() + startEdit(rowIndex, 'managerName') + }} + > + {result.managerName || ( + 双击编辑 + )} +
+ ) : ( + result.managerName || '空 (待分配)' + )} +
+
+ ) +} diff --git a/src/renderer/src/components/cleaner/CleanerSidebar.tsx b/src/renderer/src/components/cleaner/CleanerSidebar.tsx new file mode 100644 index 0000000..6198900 --- /dev/null +++ b/src/renderer/src/components/cleaner/CleanerSidebar.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import { DatabaseZap, Layers, Users } from 'lucide-react' + +interface CleanerSidebarProps { + valMode: 'full' | 'filtered' + setValMode: (mode: 'full' | 'filtered') => void + managers: string[] + selectedManagers: Set + setSelectedManagers: React.Dispatch>> +} + +export function CleanerSidebar({ + valMode, + setValMode, + managers, + selectedManagers, + setSelectedManagers +}: CleanerSidebarProps): React.JSX.Element { + return ( +
+
+

+ + 校验数据来源 +

+ +
+ + + +
+
+ +
+
+

+ + 筛选 (按负责人) +

+
+ + +
+
+ +
+ {managers.map((manager) => ( + + ))} + {managers.length === 0 &&
暂无负责人数据
} +
+
+
+ ) +} diff --git a/src/renderer/src/components/cleaner/CleanerToolbar.tsx b/src/renderer/src/components/cleaner/CleanerToolbar.tsx new file mode 100644 index 0000000..93292f0 --- /dev/null +++ b/src/renderer/src/components/cleaner/CleanerToolbar.tsx @@ -0,0 +1,143 @@ +import React from 'react' +import { + CheckSquare, + Eye, + EyeOff, + FileSpreadsheet, + FileText, + HardDrive, + Search, + Settings2, + Square +} from 'lucide-react' +import type { ValidationResult } from '../../hooks/cleaner/types' + +interface CleanerToolbarProps { + validationResults: ValidationResult[] + filteredResults: ValidationResult[] + selectedItems: Set + setSelectedItems: React.Dispatch>> + setHiddenItems: React.Dispatch>> + isValidationRunning: boolean + isRunning: boolean + isExporting: boolean + isTypeDialogOpen: boolean + setIsTypeDialogOpen: (open: boolean) => void + handleValidation: () => Promise + handleConfirmDeletion: () => Promise + handleExportResults: () => Promise + setIsReportViewerOpen: (open: boolean) => void + typeManagementButtonRef: React.RefObject +} + +export function CleanerToolbar({ + validationResults, + filteredResults, + selectedItems, + setSelectedItems, + setHiddenItems, + isValidationRunning, + isRunning, + isExporting, + setIsTypeDialogOpen, + handleValidation, + handleConfirmDeletion, + handleExportResults, + setIsReportViewerOpen, + typeManagementButtonRef +}: CleanerToolbarProps): React.JSX.Element { + return ( + <> +
+ +
+ +
+
+ + + +
+ + + + +
+ + +
+ +
+ + + +
+
+ + ) +} diff --git a/src/renderer/src/pages/CleanerPage.tsx b/src/renderer/src/pages/CleanerPage.tsx index 3559a72..4e95f04 100644 --- a/src/renderer/src/pages/CleanerPage.tsx +++ b/src/renderer/src/pages/CleanerPage.tsx @@ -1,21 +1,8 @@ import React from 'react' -import { - Play, - ToggleLeft, - ToggleRight, - DatabaseZap, - Layers, - Users, - Search, - CheckSquare, - Square, - EyeOff, - Eye, - HardDrive, - Settings2, - FileSpreadsheet, - FileText -} from 'lucide-react' +import { CleanerExecutionBar } from '../components/cleaner/CleanerExecutionBar' +import { CleanerResultsTable } from '../components/cleaner/CleanerResultsTable' +import { CleanerSidebar } from '../components/cleaner/CleanerSidebar' +import { CleanerToolbar } from '../components/cleaner/CleanerToolbar' import MaterialTypeManagementDialog from '../components/MaterialTypeManagementDialog' import ExecutionReportDialog from '../components/ExecutionReportDialog' import ReportViewerDialog from '../components/ReportViewerDialog' @@ -79,433 +66,67 @@ const CleanerPage: React.FC = () => { return (
- {/* 左栏:数据源与执行控制区 (仅 Admin 可见) */} {isAdmin && ( -
- {/* 1. 数据来源选择 */} -
-

- - 校验数据来源 -

- -
- - - -
-
- - {/* 2. 负责人筛选 */} -
-
-

- - 筛选 (按负责人) -

-
- - -
-
- -
- {managers.map((manager) => ( - - ))} - {managers.length === 0 && ( -
暂无负责人数据
- )} -
-
-
+ )} - {/* 右栏:结果表格与工具栏 */}
- {/* 获取物料状态按钮 */} -
- -
+ - {/* 顶部操作条 */} -
-
- - + -
- - - - -
- - -
- -
- - - -
-
- - {/* 表格区域 */} -
- - - - - - - - - - - - - {filteredResults.length === 0 ? ( - - - - ) : ( - filteredResults.map((result, rowIndex) => { - const isChecked = selectedItems.has(result.materialCode) - const trClass = isChecked ? 'bg-blue-50/30' : 'hover:bg-slate-50' - const noManager = !result.managerName?.trim() - const managerCellClass = noManager - ? 'text-amber-500 text-xs italic' - : 'text-slate-700' - const isEditingManager = - editingCell?.rowIndex === rowIndex && editingCell?.field === 'managerName' - - return ( - - - - - - - - - ) - }) - )} - -
选择材料名称材料代码规格型号 - 负责人 (双击编辑) -
- {validationResults.length === 0 - ? '暂无数据,请点击左侧按钮获取并校验物料' - : '当前筛选条件下暂无数据'} -
- {isChecked ? ( - { - handleCheckboxToggle(result.materialCode) - if (!isAdmin) { - handleAssignManagerOnSelect(result.materialCode) - } - }} - size={16} - className="text-blue-600 inline cursor-pointer" - /> - ) : ( - { - handleCheckboxToggle(result.materialCode) - if (!isAdmin) { - handleAssignManagerOnSelect(result.materialCode) - } - }} - size={16} - className="text-slate-300 inline cursor-pointer" - /> - )} - - {result.materialName} - - {result.materialCode} - - {result.specification || '-'} - - {result.model || '-'} - - {isAdmin && isEditingManager ? ( - - ) : isAdmin ? ( -
{ - e.stopPropagation() - startEdit(rowIndex, 'managerName') - }} - > - {result.managerName || ( - 双击编辑 - )} -
- ) : ( - result.managerName || '空 (待分配)' - )} -
-
- - {/* 底部状态与执行区 */} -
-
-
- 共计 {filteredResults.length} 条记录 | 已选中{' '} - {selectedItems.size} 条 -
-
- -
-
- - {showSettingsMenu && ( -
-
-
-
-
预览模式 (Dry-Run)
-
- 仅执行页面操作定位,不保存更改 -
-
- -
-
-
-
-
- 后台模式 (Headless) -
-
- 浏览器在后台运行,不显示界面 -
-
- -
-
-
-
-
并行处理数量
-
- 同时处理详情页数量,范围 1-20 -
-
- updateProcessConcurrency(Number(e.target.value))} - className="flex-1 h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-blue-600" - /> - - {processConcurrency} - -
-
-
-
-
- )} -
- -
-
+
{/* Material Type Management Dialog */}