From cdd60356eb1e6ec72db7e95b3ffd3fac707ba90f Mon Sep 17 00:00:00 2001 From: katelya Date: Wed, 3 Sep 2025 14:06:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=BA=90=E6=89=B9=E9=87=8F=E6=93=8D=E4=BD=9C=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=8C=85=E6=8B=AC=E6=89=B9=E9=87=8F=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=92=8C=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 228 +++++++++++++++++++++++++++++++++++------ 1 file changed, 198 insertions(+), 30 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 10bdca7..da8ebbe 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -626,6 +626,8 @@ const VideoSourceConfig = ({ const [sources, setSources] = useState([]); const [showAddForm, setShowAddForm] = useState(false); const [orderChanged, setOrderChanged] = useState(false); + const [batchMode, setBatchMode] = useState(false); + const [selectedSources, setSelectedSources] = useState>(new Set()); const [newSource, setNewSource] = useState({ name: '', key: '', @@ -721,6 +723,105 @@ const VideoSourceConfig = ({ }); }; + // 批量操作相关函数 + const handleToggleBatchMode = () => { + setBatchMode(!batchMode); + setSelectedSources(new Set()); // 切换模式时清空选择 + }; + + const handleSelectSource = (key: string, checked: boolean) => { + const newSelected = new Set(selectedSources); + if (checked) { + newSelected.add(key); + } else { + newSelected.delete(key); + } + setSelectedSources(newSelected); + }; + + const handleSelectAll = (checked: boolean) => { + if (checked) { + // 只选择可删除的视频源(from !== 'config') + const deletableSources = sources.filter(source => source.from !== 'config'); + setSelectedSources(new Set(deletableSources.map(source => source.key))); + } else { + setSelectedSources(new Set()); + } + }; + + const handleBatchDelete = async () => { + if (selectedSources.size === 0) { + showError('请先选择要删除的视频源'); + return; + } + + const selectedArray = Array.from(selectedSources); + const result = await Swal.fire({ + title: '确认批量删除', + text: `即将删除 ${selectedArray.length} 个视频源,此操作不可撤销!`, + icon: 'warning', + showCancelButton: true, + confirmButtonText: '确认删除', + cancelButtonText: '取消', + confirmButtonColor: '#ef4444', + cancelButtonColor: '#6b7280' + }); + + if (!result.isConfirmed) return; + + // 批量删除 + let successCount = 0; + let errorCount = 0; + const errors: string[] = []; + + for (const key of selectedArray) { + try { + await callSourceApi({ action: 'delete', key }); + successCount++; + } catch (error) { + errorCount++; + const sourceName = sources.find(s => s.key === key)?.name || key; + errors.push(`${sourceName}: ${error instanceof Error ? error.message : '删除失败'}`); + } + } + + // 显示删除结果 + if (errorCount === 0) { + showSuccess(`成功删除 ${successCount} 个视频源`); + setSelectedSources(new Set()); // 清空选择 + setBatchMode(false); // 退出批量模式 + } else { + await Swal.fire({ + title: '删除完成', + html: ` +
+

✅ 成功删除: ${successCount} 个

+

❌ 删除失败: ${errorCount} 个

+ ${errors.length > 0 ? ` +
+ 查看错误详情 +
+ ${errors.map(err => `
${err}
`).join('')} +
+
+ ` : ''} +
+ `, + icon: successCount > 0 ? 'warning' : 'error', + confirmButtonText: '确定' + }); + + // 清空已成功删除的选择项 + const failedKeys = new Set( + errors.map(err => { + const keyMatch = err.split(':')[0]; + return sources.find(s => s.name === keyMatch)?.key; + }).filter((key): key is string => Boolean(key)) + ); + setSelectedSources(failedKeys); + } + }; + // 导出配置 const handleExportConfig = () => { try { @@ -905,6 +1006,7 @@ const VideoSourceConfig = ({ style={style} className='hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors select-none' > + {/* 拖拽手柄 */} + + {/* 批量选择复选框 */} + {batchMode && ( + + handleSelectSource(source.key, e.target.checked)} + disabled={source.from === 'config'} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 disabled:opacity-50' + /> + + )} {source.name} @@ -976,40 +1091,79 @@ const VideoSourceConfig = ({ return (
- {/* 添加视频源表单 */} -
+ {/* 视频源管理工具栏 */} +

视频源列表

+
- {/* 导入按钮 */} - - - {/* 导出按钮 */} - - - {/* 添加视频源按钮 */} - + {/* 批量操作区域 */} + {!batchMode ? ( + <> + {/* 普通模式按钮 */} + + + {/* 导入导出按钮 */} +
+ + + +
+ + {/* 添加视频源按钮 */} + + + ) : ( + <> + {/* 批量模式按钮 */} + + +
+ + 已选 {selectedSources.size} 个 + + + +
+ + )}
@@ -1070,7 +1224,21 @@ const VideoSourceConfig = ({ + {/* 拖拽手柄列 */} + )} +
+ + {/* 批量选择列 */} + {batchMode && ( + + 0 && selectedSources.size === sources.filter(s => s.from !== 'config').length} + onChange={(e) => handleSelectAll(e.target.checked)} + className='w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600' + /> + 名称