diff --git a/packages/drawnix/src/components/ttd-dialog/batch-image-generation.scss b/packages/drawnix/src/components/ttd-dialog/batch-image-generation.scss index 563d8358..3d73f48d 100644 --- a/packages/drawnix/src/components/ttd-dialog/batch-image-generation.scss +++ b/packages/drawnix/src/components/ttd-dialog/batch-image-generation.scss @@ -139,6 +139,7 @@ width: 40px; min-width: 40px; text-align: center; + vertical-align: middle; // 垂直居中 input[type="checkbox"] { width: 16px; @@ -147,6 +148,11 @@ accent-color: #F39C12; } } + + // 行号列垂直居中 + td.row-number { + vertical-align: middle; + } .col-prompt { width: 35%; min-width: 200px; } .col-size { width: 80px; min-width: 80px; } .col-images { width: 150px; min-width: 150px; } @@ -193,7 +199,7 @@ cursor: cell; position: relative; display: flex; - align-items: flex-start; + align-items: center; // 垂直居中 user-select: none; // 禁用文本选择,防止浏览器原生选择干扰单元格选择 box-sizing: border-box; diff --git a/packages/drawnix/src/components/ttd-dialog/batch-image-generation.tsx b/packages/drawnix/src/components/ttd-dialog/batch-image-generation.tsx index 734f8a10..7b05142d 100644 --- a/packages/drawnix/src/components/ttd-dialog/batch-image-generation.tsx +++ b/packages/drawnix/src/components/ttd-dialog/batch-image-generation.tsx @@ -129,6 +129,7 @@ const BatchImageGeneration: React.FC = ({ onSwitchToS const fileInputRef = useRef(null); const batchImportInputRef = useRef(null); const excelImportInputRef = useRef(null); + const lastSelectedRowRef = useRef(null); // 上次选择的行,用于 Shift 多选 // 保存到本地缓存 useEffect(() => { @@ -596,25 +597,25 @@ const BatchImageGeneration: React.FC = ({ onSwitchToS // 选择失败的行 const selectFailedRows = useCallback(() => { - const failedCells: CellPosition[] = []; + const failedRowIndices: number[] = []; tasks.forEach((task, rowIndex) => { const { status } = getRowTasksInfo(task); if (status === 'failed' || status === 'partial') { - failedCells.push({ row: rowIndex, col: 'prompt' }); + failedRowIndices.push(rowIndex); } }); - if (failedCells.length === 0) { + if (failedRowIndices.length === 0) { MessagePlugin.info(language === 'zh' ? '没有失败的行' : 'No failed rows'); return; } - setSelectedCells(failedCells); - setActiveCell(failedCells[0]); + // 选中失败行的 checkbox + setSelectedRows(new Set(failedRowIndices)); MessagePlugin.success( language === 'zh' - ? `已选中 ${failedCells.length} 个失败行` - : `Selected ${failedCells.length} failed rows` + ? `已选中 ${failedRowIndices.length} 个失败行` + : `Selected ${failedRowIndices.length} failed rows` ); }, [tasks, getRowTasksInfo, language]); @@ -631,17 +632,31 @@ const BatchImageGeneration: React.FC = ({ onSwitchToS setSelectedRows(newSelectedRows); }, [tasks, selectedRows]); - // 切换单行选择(checkbox) - const toggleRowSelection = useCallback((rowIndex: number) => { + // 切换单行选择(checkbox),支持 Shift 多选 + const toggleRowSelection = useCallback((rowIndex: number, shiftKey: boolean = false) => { setSelectedRows(prev => { const newSet = new Set(prev); - if (newSet.has(rowIndex)) { - newSet.delete(rowIndex); + + if (shiftKey && lastSelectedRowRef.current !== null) { + // Shift + 点击:选择范围内的所有行 + const start = Math.min(lastSelectedRowRef.current, rowIndex); + const end = Math.max(lastSelectedRowRef.current, rowIndex); + for (let i = start; i <= end; i++) { + newSet.add(i); + } } else { - newSet.add(rowIndex); + // 普通点击:切换选择 + if (newSet.has(rowIndex)) { + newSet.delete(rowIndex); + } else { + newSet.add(rowIndex); + } } + return newSet; }); + // 记录本次选择的行 + lastSelectedRowRef.current = rowIndex; }, []); // 全选/取消全选(checkbox) @@ -993,7 +1008,11 @@ const BatchImageGeneration: React.FC = ({ onSwitchToS
handleCellClick(e, rowIndex, col)} - onDoubleClick={() => handleCellDoubleClick(rowIndex, col)} + onDoubleClick={() => { + // 双击进入编辑模式,显示下拉菜单 + selectCell(rowIndex, col); + setEditingCell({ row: rowIndex, col }); + }} > {isEditing ? ( toggleRowSelection(rowIndex)} + onClick={(e) => { + e.stopPropagation(); + toggleRowSelection(rowIndex, e.shiftKey); + }} + onChange={() => {}} // 由 onClick 处理 /> {rowIndex + 1}