diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx index 268c3a3..2bc5e10 100644 --- a/src/pages/admin.tsx +++ b/src/pages/admin.tsx @@ -21,10 +21,10 @@ import { verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; +import { Alert, Avatar, Button, Card, Checkbox, Chip, Input, Label, Skeleton, Switch, Table, TextArea, TextField } from '@heroui/react'; import { AlertCircle, AlertTriangle, - Check, CheckCircle, ChevronDown, ChevronUp, @@ -37,61 +37,19 @@ import { User, Users, Video, + X, } from 'lucide-react'; import { GripVertical, Palette } from 'lucide-react'; -import { Alert, Avatar, Button, Card, Checkbox, Chip, Input, Label, Skeleton, Switch, Table, TextArea, TextField } from '@heroui/react'; import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AdminConfig, AdminConfigResult } from '@/lib/admin.types'; import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; import DataMigration from '@/components/DataMigration'; -import ThemeManager from '@/components/ThemeManager'; import PageLayout from '@/components/PageLayout'; +import ThemeManager from '@/components/ThemeManager'; import { AppDialog, AppFilterSelect } from '@/components/ui/HeroPrimitives'; -// 统一按钮样式系统 -const buttonStyles = { - // 主要操作按钮(蓝色)- 用于配置、设置、确认等 - primary: 'px-3 py-1.5 text-sm font-medium bg-accent hover:bg-accent-strong text-accent-foreground rounded-xl transition-colors', - // 成功操作按钮(绿色)- 用于添加、启用、保存等 - success: 'px-3 py-1.5 text-sm font-medium bg-accent hover:bg-accent-strong text-accent-foreground rounded-xl transition-colors', - // 危险操作按钮(红色)- 用于删除、禁用、重置等 - danger: 'px-3 py-1.5 text-sm font-medium bg-danger hover:bg-danger/90 text-white rounded-xl transition-colors', - // 次要操作按钮(灰色)- 用于取消、关闭等 - secondary: 'px-3 py-1.5 text-sm font-medium bg-surface-secondary hover:bg-surface-tertiary text-foreground border border-border rounded-xl transition-colors', - // 警告操作按钮(黄色)- 用于批量禁用等 - warning: 'px-3 py-1.5 text-sm font-medium bg-yellow-600 hover:bg-yellow-700 dark:bg-yellow-600 dark:hover:bg-yellow-700 text-white rounded-lg transition-colors', - // 小尺寸主要按钮 - primarySmall: 'px-2 py-1 text-xs font-medium bg-accent hover:bg-accent-strong text-accent-foreground rounded-lg transition-colors', - // 小尺寸成功按钮 - successSmall: 'px-2 py-1 text-xs font-medium bg-accent hover:bg-accent-strong text-accent-foreground rounded-lg transition-colors', - // 小尺寸危险按钮 - dangerSmall: 'px-2 py-1 text-xs font-medium bg-red-600 hover:bg-red-700 dark:bg-red-600 dark:hover:bg-red-700 text-white rounded-md transition-colors', - // 小尺寸次要按钮 - secondarySmall: 'px-2 py-1 text-xs font-medium bg-surface-secondary hover:bg-surface-tertiary text-foreground border border-border rounded-lg transition-colors', - // 小尺寸警告按钮 - warningSmall: 'px-2 py-1 text-xs font-medium bg-yellow-600 hover:bg-yellow-700 dark:bg-yellow-600 dark:hover:bg-yellow-700 text-white rounded-md transition-colors', - // 圆角小按钮(用于表格操作) - roundedPrimary: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-accent/10 text-accent hover:bg-accent/15 transition-colors', - roundedSuccess: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-accent/10 text-accent hover:bg-accent/15 transition-colors', - roundedDanger: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-red-100 text-red-800 hover:bg-red-200 dark:bg-red-900/40 dark:hover:bg-red-900/60 dark:text-red-200 transition-colors', - roundedSecondary: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-surface-secondary text-foreground hover:bg-surface-tertiary transition-colors', - roundedWarning: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 hover:bg-yellow-200 dark:bg-yellow-900/40 dark:hover:bg-yellow-900/60 dark:text-yellow-200 transition-colors', - roundedPurple: 'inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-accent/10 text-accent hover:bg-accent/15 transition-colors', - // 禁用状态 - disabled: 'px-3 py-1.5 text-sm font-medium bg-gray-400 dark:bg-gray-600 cursor-not-allowed text-white rounded-lg transition-colors', - disabledSmall: 'px-2 py-1 text-xs font-medium bg-gray-400 dark:bg-gray-600 cursor-not-allowed text-white rounded-md transition-colors', - // 开关按钮样式 - toggleOn: 'bg-accent', - toggleOff: 'bg-surface-tertiary', - toggleThumb: 'bg-surface', - toggleThumbOn: 'translate-x-6', - toggleThumbOff: 'translate-x-1', - // 快速操作按钮样式 - quickAction: 'px-3 py-1.5 text-xs font-medium text-muted bg-surface border border-border hover:bg-surface-secondary rounded-lg transition-colors', -}; - const AdminTable = ({ ariaLabel, minWidth = 'min-w-[900px]', @@ -545,6 +503,83 @@ interface LiveDataSource { from: 'config' | 'custom'; } +type ValidationResult = { + status: 'valid' | 'invalid' | 'no_results' | 'validating' | null; + message: string; + details?: { + responseTime?: number; + resultCount?: number; + error?: string; + searchKeyword?: string; + }; +}; + +const SourceValidationSummary = ({ result }: { result: ValidationResult }) => { + if (!result.status) return null; + + const statusMeta = { + valid: { + chipColor: 'success' as const, + icon: CheckCircle, + label: '检测通过', + }, + validating: { + chipColor: 'accent' as const, + icon: Settings, + label: '检测中', + }, + no_results: { + chipColor: 'warning' as const, + icon: AlertTriangle, + label: '无搜索结果', + }, + invalid: { + chipColor: 'danger' as const, + icon: AlertCircle, + label: '检测失败', + }, + }[result.status]; + + const StatusIcon = statusMeta.icon; + + return ( +
+
+
+ 检测结果 + + + + {statusMeta.label} + + + {result.message} +
+ {result.details && ( +
+ {result.details.searchKeyword && ( +
测试关键词: {result.details.searchKeyword}
+ )} + {result.details.responseTime && ( +
响应时间: {result.details.responseTime}ms
+ )} + {result.details.resultCount !== undefined && ( +
搜索结果数: {result.details.resultCount}
+ )} + {result.details.error && ( +
+ 错误信息: {result.details.error} +
+ )} +
+ )} +
+
+ ); +}; + // 自定义分类数据类型 interface CustomCategory { name?: string; @@ -2772,7 +2807,7 @@ const VideoSourceConfig = ({
{/* 添加视频源表单 */}
-

+

视频源列表

@@ -2780,7 +2815,7 @@ const VideoSourceConfig = ({ {selectedSources.size > 0 && ( <>
- + 已选 {selectedSources.size} 已选择 {selectedSources.size} 个视频源 @@ -2809,7 +2844,7 @@ const VideoSourceConfig = ({ {isLoading('batchSource_batch_delete') ? '删除中...' : '批量删除'}
-
+
)}
@@ -2845,240 +2880,189 @@ const VideoSourceConfig = ({
{showAddForm && ( -
-
- - setNewSource((prev) => ({ ...prev, name: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewSource((prev) => ({ ...prev, key: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewSource((prev) => ({ ...prev, api: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewSource((prev) => ({ ...prev, detail: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
- - {/* 新增视频源有效性检测结果显示 */} - {newSourceValidationResult.status && ( -
-
-
- 检测结果: - - {newSourceValidationResult.status === 'valid' && '✓ '} - {newSourceValidationResult.status === 'validating' && '⏳ '} - {newSourceValidationResult.status === 'no_results' && '⚠️ '} - {newSourceValidationResult.status === 'invalid' && '✗ '} - {newSourceValidationResult.message} - -
- {newSourceValidationResult.details && ( -
- {newSourceValidationResult.details.searchKeyword && ( -
测试关键词: {newSourceValidationResult.details.searchKeyword}
- )} - {newSourceValidationResult.details.responseTime && ( -
响应时间: {newSourceValidationResult.details.responseTime}ms
- )} - {newSourceValidationResult.details.resultCount !== undefined && ( -
搜索结果数: {newSourceValidationResult.details.resultCount}
- )} - {newSourceValidationResult.details.error && ( -
错误信息: {newSourceValidationResult.details.error}
- )} -
- )} -
+ + +
+ 添加视频源 + + 填写资源站名称、唯一 Key 和采集 API 地址。 + +
+
+ +
+ + + + setNewSource((prev) => ({ ...prev, name: e.target.value })) + } + variant='secondary' + /> + + + + + setNewSource((prev) => ({ ...prev, key: e.target.value })) + } + variant='secondary' + /> + + + + + setNewSource((prev) => ({ ...prev, api: e.target.value })) + } + variant='secondary' + /> + + + + + setNewSource((prev) => ({ ...prev, detail: e.target.value })) + } + variant='secondary' + /> +
- )} -
- - -
-
+ + + )} {/* 编辑视频源表单 */} {editingSource && ( -
-
-
- 编辑视频源: {editingSource.name} -
- -
-
-
- - - setEditingSource((prev) => prev ? ({ ...prev, name: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
-
- - -
-
- - - setEditingSource((prev) => prev ? ({ ...prev, api: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
-
- - - setEditingSource((prev) => prev ? ({ ...prev, detail: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> + + + + +
+ + + + setEditingSource((prev) => prev ? ({ ...prev, name: e.target.value }) : null) + } + variant='secondary' + /> + + + + +

Key 已用于数据关联,不能编辑。

+
+ + + + setEditingSource((prev) => prev ? ({ ...prev, api: e.target.value }) : null) + } + variant='secondary' + /> + + + + + setEditingSource((prev) => prev ? ({ ...prev, detail: e.target.value }) : null) + } + variant='secondary' + /> +
- {/* 有效性检测结果显示 */} - {singleValidationResult.status && ( -
-
-
- 检测结果: - - {singleValidationResult.status === 'valid' && '✓ '} - {singleValidationResult.status === 'validating' && '⏳ '} - {singleValidationResult.status === 'no_results' && '⚠️ '} - {singleValidationResult.status === 'invalid' && '✗ '} - {singleValidationResult.message} - -
- {singleValidationResult.details && ( -
- {singleValidationResult.details.searchKeyword && ( -
测试关键词: {singleValidationResult.details.searchKeyword}
- )} - {singleValidationResult.details.responseTime && ( -
响应时间: {singleValidationResult.details.responseTime}ms
- )} - {singleValidationResult.details.resultCount !== undefined && ( -
搜索结果数: {singleValidationResult.details.resultCount}
- )} - {singleValidationResult.details.error && ( -
错误信息: {singleValidationResult.details.error}
- )} -
- )} -
-
- )} -
-
- - - -
-
+ + + )} @@ -4380,7 +4364,7 @@ const LiveSourceConfig = ({
{/* 添加直播源表单 */}
-

+

直播源列表

@@ -4403,162 +4387,190 @@ const LiveSourceConfig = ({
{showAddForm && ( -
-
- - setNewLiveSource((prev) => ({ ...prev, name: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewLiveSource((prev) => ({ ...prev, key: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewLiveSource((prev) => ({ ...prev, url: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewLiveSource((prev) => ({ ...prev, epg: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - - setNewLiveSource((prev) => ({ ...prev, ua: e.target.value })) - } - className='px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> - -
-
- -
-
+ + + )} {/* 编辑直播源表单 */} {editingLiveSource && ( -
-
-
- 编辑直播源: {editingLiveSource.name} -
- -
-
-
- - - setEditingLiveSource((prev) => prev ? ({ ...prev, name: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> + + + + +
+ + + + setEditingLiveSource((prev) => prev ? ({ ...prev, name: e.target.value }) : null) + } + variant='secondary' + /> + + + + +

Key 已用于直播源关联,不能编辑。

+
+ + + + setEditingLiveSource((prev) => prev ? ({ ...prev, url: e.target.value }) : null) + } + variant='secondary' + /> + + + + + setEditingLiveSource((prev) => prev ? ({ ...prev, epg: e.target.value }) : null) + } + variant='secondary' + /> + + + + + setEditingLiveSource((prev) => prev ? ({ ...prev, ua: e.target.value }) : null) + } + variant='secondary' + /> +
-
- - -
-
- - - setEditingLiveSource((prev) => prev ? ({ ...prev, url: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
-
- - - setEditingLiveSource((prev) => prev ? ({ ...prev, epg: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
-
- - - setEditingLiveSource((prev) => prev ? ({ ...prev, ua: e.target.value }) : null) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' - /> -
-
-
- - -
-
+ + + )} {/* 直播源表格 */}