增加离线下载功能

This commit is contained in:
mtvpls
2025-12-14 00:22:38 +08:00
parent d467c5067b
commit e711d165eb
11 changed files with 2039 additions and 18 deletions
+61 -4
View File
@@ -16,7 +16,11 @@ interface DownloadEpisodeSelectorProps {
/** 当前集数索引(0开始) */
currentEpisodeIndex: number;
/** 下载回调 - 支持批量下载 */
onDownload: (episodeIndexes: number[]) => void;
onDownload: (episodeIndexes: number[], offlineMode: boolean) => void;
/** 是否启用离线下载功能 */
enableOfflineDownload?: boolean;
/** 是否有离线下载权限(管理员或站长) */
hasOfflinePermission?: boolean;
}
/**
@@ -30,12 +34,17 @@ const DownloadEpisodeSelector: React.FC<DownloadEpisodeSelectorProps> = ({
videoTitle,
currentEpisodeIndex,
onDownload,
enableOfflineDownload = false,
hasOfflinePermission = false,
}) => {
// 多选状态 - 使用 Set 存储选中的集数索引
const [selectedEpisodes, setSelectedEpisodes] = useState<Set<number>>(
new Set([currentEpisodeIndex])
);
// 离线下载模式
const [offlineMode, setOfflineMode] = useState(false);
// 每页显示的集数
const episodesPerPage = 50;
const pageCount = Math.ceil(totalEpisodes / episodesPerPage);
@@ -105,7 +114,7 @@ const DownloadEpisodeSelector: React.FC<DownloadEpisodeSelectorProps> = ({
const handleDownload = () => {
const episodeIndexes = Array.from(selectedEpisodes).sort((a, b) => a - b);
onDownload(episodeIndexes);
onDownload(episodeIndexes, offlineMode);
onClose();
};
@@ -161,6 +170,50 @@ const DownloadEpisodeSelector: React.FC<DownloadEpisodeSelectorProps> = ({
</div>
</div>
{/* 离线下载开关 - 仅管理员和站长可见 */}
{enableOfflineDownload && hasOfflinePermission && (
<div className='flex items-center justify-between px-6 py-3 bg-blue-50 dark:bg-blue-900/10 border-b border-blue-100 dark:border-blue-900/30'>
<div className='flex items-center gap-3'>
{/* 服务器图标 */}
<div className='flex-shrink-0 w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center'>
<svg className='w-5 h-5 text-blue-600 dark:text-blue-400' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01' />
</svg>
</div>
<div className='flex-1'>
<div className='flex items-center gap-2'>
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
线
</h3>
{offlineMode && (
<span className='px-2 py-0.5 text-xs font-medium bg-blue-500 text-white rounded'>
</span>
)}
</div>
<p className='text-xs text-gray-600 dark:text-gray-400 mt-0.5'>
</p>
</div>
</div>
{/* 开关 */}
<button
onClick={() => setOfflineMode(!offlineMode)}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
offlineMode
? 'bg-blue-600'
: 'bg-gray-300 dark:bg-gray-600'
}`}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white shadow-sm transition-transform ${
offlineMode ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</div>
)}
{/* 分页标签 */}
{pageCount > 1 && (
<div className='flex items-center gap-4 px-6 py-3 border-b border-gray-200 dark:border-gray-700'>
@@ -283,9 +336,13 @@ const DownloadEpisodeSelector: React.FC<DownloadEpisodeSelectorProps> = ({
<button
onClick={handleDownload}
disabled={selectedEpisodes.size === 0}
className='px-4 py-2 text-sm font-medium text-white bg-green-500 hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-700 rounded-md transition-colors shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-green-500'
className={`px-4 py-2 text-sm font-medium text-white rounded-md transition-colors shadow-md disabled:opacity-50 disabled:cursor-not-allowed ${
offlineMode && enableOfflineDownload && hasOfflinePermission
? 'bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800'
: 'bg-green-500 hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-700 disabled:hover:bg-green-500'
}`}
>
{selectedEpisodes.size > 0 && `(${selectedEpisodes.size})`}
{offlineMode && enableOfflineDownload && hasOfflinePermission ? '离线' : ''} {selectedEpisodes.size > 0 && `(${selectedEpisodes.size})`}
</button>
</div>
</div>