diff --git a/config.json b/config.json index 9e2c589..6bd8714 100644 --- a/config.json +++ b/config.json @@ -3,26 +3,8 @@ "api_site": { "example_test": { "api": "https://example.com/api.php/provide/vod", - "name": "测试视频源", + "name": "示例视频源", "detail": "https://example.com" - }, - "demo_site1": { - "api": "https://your-api-domain.com/api.php/provide/vod", - "name": "示例视频源1", - "detail": "https://your-domain.com" - }, - "demo_site2": { - "api": "https://api.your-site.com/provide/vod", - "name": "示例视频源2" - }, - "demo_site3": { - "api": "https://video-api.example.org/api.php/provide/vod", - "name": "示例视频源3", - "detail": "https://video.example.org" - }, - "demo_site4": { - "api": "https://media.sample-domain.net/api.php/provide/vod", - "name": "示例视频源4" } } } diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 6c2387d..53a55c5 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -693,6 +693,13 @@ const VideoSourceConfig = ({ }; const handleDelete = (key: string) => { + // 检查是否为示例源 + const source = sources.find(s => s.key === key); + if (source?.from === 'config') { + showError('示例源不可删除,这些源用于演示功能'); + return; + } + callSourceApi({ action: 'delete', key }).catch(() => { console.error('操作失败', 'delete', key); }); @@ -741,8 +748,9 @@ const VideoSourceConfig = ({ const handleSelectAll = (checked: boolean) => { if (checked) { - // 选择所有视频源(包括示例源) - setSelectedSources(new Set(sources.map(source => source.key))); + // 只选择可删除的视频源(排除示例源) + const deletableSources = sources.filter(source => source.from !== 'config'); + setSelectedSources(new Set(deletableSources.map(source => source.key))); } else { setSelectedSources(new Set()); } @@ -756,44 +764,83 @@ const VideoSourceConfig = ({ const selectedArray = Array.from(selectedSources); const result = await Swal.fire({ - title: '⚡ 一键批量删除', - html: ` -

即将瞬间删除 ${selectedArray.length} 个视频源

-

包含所有选中的视频源(含示例源)

-

⚠️ 此操作不可撤销!

- `, + title: '确认批量删除', + text: `即将删除 ${selectedArray.length} 个视频源,此操作不可撤销!`, icon: 'warning', showCancelButton: true, - confirmButtonText: '⚡ 瞬间删除', + confirmButtonText: '确认删除', cancelButtonText: '取消', confirmButtonColor: '#ef4444', - cancelButtonColor: '#6b7280', - showLoaderOnConfirm: true, - allowOutsideClick: () => !Swal.isLoading(), - preConfirm: async () => { - try { - // 并行删除所有选中的视频源,实现真正的一键删除 - const deletePromises = selectedArray.map(key => - callSourceApi({ action: 'delete', key }) - ); - - await Promise.all(deletePromises); - return { success: true, count: selectedArray.length }; - } catch (error) { - Swal.showValidationMessage( - `删除失败: ${error instanceof Error ? error.message : '未知错误'}` - ); - return false; - } - } + cancelButtonColor: '#6b7280' }); - if (result.isConfirmed && result.value) { - showSuccess(`⚡ 瞬间删除成功!已删除 ${result.value.count} 个视频源`); - setSelectedSources(new Set()); - setBatchMode(false); - await refreshConfig(); + if (!result.isConfirmed) return; + + // 批量删除逐个进行,显示进度 + let successCount = 0; + let errorCount = 0; + const errors: string[] = []; + + for (let i = 0; i < selectedArray.length; i++) { + const key = selectedArray[i]; + try { + await callSourceApi({ action: 'delete', key }); + successCount++; + + // 显示进度 + if (selectedArray.length > 1) { + Swal.update({ + title: '正在删除...', + text: `进度: ${i + 1}/${selectedArray.length}`, + showConfirmButton: false, + showCancelButton: false, + allowOutsideClick: false + }); + } + } 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); + } + + await refreshConfig(); }; // 导出配置 @@ -997,12 +1044,20 @@ const VideoSourceConfig = ({ type='checkbox' checked={selectedSources.has(source.key)} onChange={(e) => handleSelectSource(source.key, 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' + 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} +
+ {source.name} + {source.from === 'config' && ( + + 示例源 + + )} +
{source.key} @@ -1041,12 +1096,18 @@ const VideoSourceConfig = ({ > {!source.disabled ? '禁用' : '启用'} - + {source.from !== 'config' ? ( + + ) : ( + + 不可删除 + + )} );