mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-14 00:03:42 +08:00
121 lines
5.3 KiB
TypeScript
121 lines
5.3 KiB
TypeScript
import { useState } from 'react';
|
|
import { SourceManager } from '@/components/settings/SourceManager';
|
|
import type { VideoSource } from '@/lib/types';
|
|
import { DEFAULT_SOURCES } from '@/lib/api/default-sources';
|
|
|
|
interface SourceSettingsProps {
|
|
sources: VideoSource[];
|
|
onSourcesChange: (sources: VideoSource[]) => void;
|
|
onRestoreDefaults: () => void;
|
|
onAddSource: () => void;
|
|
onEditSource?: (source: VideoSource) => void;
|
|
}
|
|
|
|
export function SourceSettings({
|
|
sources,
|
|
onSourcesChange,
|
|
onRestoreDefaults,
|
|
onAddSource,
|
|
onEditSource,
|
|
}: SourceSettingsProps) {
|
|
const [showAllSources, setShowAllSources] = useState(false);
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
|
|
const filteredSources = sources.filter(source =>
|
|
source.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
source.baseUrl.toLowerCase().includes(searchQuery.toLowerCase())
|
|
);
|
|
|
|
const displayedSources = showAllSources || searchQuery
|
|
? filteredSources
|
|
: filteredSources.slice(0, 10);
|
|
|
|
const handleToggle = (id: string) => {
|
|
const updated = sources.map(s =>
|
|
s.id === id ? { ...s, enabled: !s.enabled } : s
|
|
);
|
|
onSourcesChange(updated);
|
|
};
|
|
|
|
const handleDelete = (id: string) => {
|
|
const updated = sources.filter(s => s.id !== id);
|
|
onSourcesChange(updated);
|
|
};
|
|
|
|
const handleReorder = (id: string, direction: 'up' | 'down') => {
|
|
const currentIndex = sources.findIndex(s => s.id === id);
|
|
if (currentIndex === -1) return;
|
|
|
|
const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
|
if (newIndex < 0 || newIndex >= sources.length) return;
|
|
|
|
const updated = [...sources];
|
|
[updated[currentIndex], updated[newIndex]] = [updated[newIndex], updated[currentIndex]];
|
|
|
|
// Update priorities
|
|
updated.forEach((s, idx) => s.priority = idx + 1);
|
|
onSourcesChange(updated);
|
|
};
|
|
|
|
return (
|
|
<div className="bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] shadow-[var(--shadow-sm)] p-6 mb-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-xl font-semibold text-[var(--text-color)]">视频源管理</h2>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={onRestoreDefaults}
|
|
className="px-4 py-2 rounded-[var(--radius-2xl)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] text-sm font-medium hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
|
|
>
|
|
恢复默认
|
|
</button>
|
|
<button
|
|
onClick={onAddSource}
|
|
className="px-4 py-2 rounded-[var(--radius-2xl)] bg-[var(--accent-color)] text-white text-sm font-semibold hover:brightness-110 hover:-translate-y-0.5 shadow-[var(--shadow-sm)] transition-all duration-200 cursor-pointer"
|
|
>
|
|
+ 添加源
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<p className="text-sm text-[var(--text-color-secondary)] mb-6">
|
|
管理视频来源,调整优先级和启用状态
|
|
</p>
|
|
|
|
{/* Search Bar */}
|
|
<div className="relative mb-4">
|
|
<input
|
|
type="text"
|
|
placeholder="搜索源..."
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
className="w-full px-4 py-2 pl-10 rounded-[var(--radius-2xl)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] placeholder-[var(--text-color-secondary)] focus:outline-none focus:ring-2 focus:ring-[var(--accent-color)] transition-all duration-200"
|
|
/>
|
|
<svg
|
|
className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-color-secondary)]"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</div>
|
|
|
|
<SourceManager
|
|
sources={displayedSources}
|
|
onToggle={handleToggle}
|
|
onDelete={handleDelete}
|
|
onReorder={handleReorder}
|
|
onEdit={onEditSource}
|
|
defaultIds={DEFAULT_SOURCES.map(s => s.id)}
|
|
/>
|
|
{!searchQuery && sources.length > 10 && (
|
|
<button
|
|
onClick={() => setShowAllSources(!showAllSources)}
|
|
className="w-full mt-4 px-4 py-3 rounded-[var(--radius-2xl)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] text-sm font-medium hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
|
|
>
|
|
{showAllSources ? '收起' : `显示全部 (${sources.length})`}
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|