详情面板支持切换数据源
This commit is contained in:
@@ -326,6 +326,7 @@ function HomeClient() {
|
||||
rate={movie.rate}
|
||||
type='movie'
|
||||
from='douban'
|
||||
douban_id={movie.id ? parseInt(movie.id) : undefined}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -485,6 +486,7 @@ function HomeClient() {
|
||||
rate={tvShow.rate}
|
||||
type='tv'
|
||||
from='douban'
|
||||
douban_id={tvShow.id ? parseInt(tvShow.id) : undefined}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -531,6 +533,7 @@ function HomeClient() {
|
||||
rate={varietyShow.rate}
|
||||
type='tv'
|
||||
from='douban'
|
||||
douban_id={varietyShow.id ? parseInt(varietyShow.id) : undefined}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -96,6 +96,12 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
const [showImageViewer, setShowImageViewer] = useState(false);
|
||||
const [selectedImage, setSelectedImage] = useState<string>('');
|
||||
|
||||
// 数据源状态管理
|
||||
const [currentSource, setCurrentSource] = useState<'douban' | 'bangumi' | 'cms' | 'tmdb'>('tmdb');
|
||||
const [originalSource, setOriginalSource] = useState<'douban' | 'bangumi' | 'cms' | 'tmdb'>('tmdb');
|
||||
const [isUsingTmdb, setIsUsingTmdb] = useState(false);
|
||||
const [originalDetailData, setOriginalDetailData] = useState<DetailData | null>(null);
|
||||
|
||||
// 拖动滚动状态
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isMouseDown, setIsMouseDown] = useState(false);
|
||||
@@ -218,17 +224,27 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// 如果正在使用 TMDB 数据,强制使用 TMDB
|
||||
if (isUsingTmdb && title) {
|
||||
await fetchTmdbData();
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先使用苹果CMS数据(短剧等)
|
||||
// 如果 cmsData 存在但 desc 为空,尝试通过 source-detail API 获取
|
||||
if (cmsData) {
|
||||
setCurrentSource('cms');
|
||||
setOriginalSource('cms');
|
||||
if (cmsData.desc) {
|
||||
// 有 desc,直接使用
|
||||
setDetailData({
|
||||
const data = {
|
||||
title: title,
|
||||
intro: cmsData.desc,
|
||||
episodesCount: cmsData.episodes?.length,
|
||||
poster: poster ? processImageUrl(poster) : poster,
|
||||
});
|
||||
};
|
||||
setDetailData(data);
|
||||
setOriginalDetailData(data);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -241,13 +257,15 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setDetailData({
|
||||
const detailData = {
|
||||
title: data.title || title,
|
||||
intro: data.desc || '',
|
||||
episodesCount: data.episodes?.length || cmsData.episodes?.length,
|
||||
poster: data.poster ? processImageUrl(data.poster) : poster,
|
||||
year: data.year,
|
||||
});
|
||||
};
|
||||
setDetailData(detailData);
|
||||
setOriginalDetailData(detailData);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -260,6 +278,8 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
|
||||
// 优先使用 Bangumi ID(因为 isBangumi 为 true 时,doubanId 实际上是 bangumiId)
|
||||
if (bangumiId || (isBangumi && doubanId)) {
|
||||
setCurrentSource('bangumi');
|
||||
setOriginalSource('bangumi');
|
||||
const actualBangumiId = bangumiId || doubanId;
|
||||
const response = await fetch(`https://api.bgm.tv/v0/subjects/${actualBangumiId}`);
|
||||
if (!response.ok) {
|
||||
@@ -267,7 +287,7 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
setDetailData({
|
||||
const detailData = {
|
||||
title: data.name_cn || data.name,
|
||||
originalTitle: data.name,
|
||||
year: data.date ? data.date.substring(0, 4) : undefined,
|
||||
@@ -282,19 +302,23 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
genres: data.tags?.map((tag: any) => tag.name).slice(0, 5),
|
||||
episodesCount: data.eps,
|
||||
releaseDate: data.date,
|
||||
});
|
||||
};
|
||||
setDetailData(detailData);
|
||||
setOriginalDetailData(detailData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用豆瓣ID
|
||||
if (doubanId && !isBangumi) {
|
||||
setCurrentSource('douban');
|
||||
setOriginalSource('douban');
|
||||
const response = await fetch(`/api/douban/detail?id=${doubanId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('获取豆瓣详情失败');
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
setDetailData({
|
||||
const detailData = {
|
||||
title: data.title,
|
||||
originalTitle: data.original_title,
|
||||
year: data.year,
|
||||
@@ -313,113 +337,18 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
languages: data.languages,
|
||||
duration: data.durations?.[0],
|
||||
episodesCount: data.episodes_count,
|
||||
});
|
||||
};
|
||||
setDetailData(detailData);
|
||||
setOriginalDetailData(detailData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 TMDB 搜索
|
||||
if (title) {
|
||||
// 移除季度信息进行搜索
|
||||
let searchTitle = title;
|
||||
let extractedSeasonNumber = seasonNumber;
|
||||
|
||||
// 匹配各种季度格式: 第一季、第1季、第一部、Season 1、S1等
|
||||
const seasonPatterns = [
|
||||
/第([一二三四五六七八九十\d]+)[季部]/,
|
||||
/Season\s*(\d+)/i,
|
||||
/S(\d+)/i,
|
||||
];
|
||||
|
||||
for (const pattern of seasonPatterns) {
|
||||
const match = title.match(pattern);
|
||||
if (match) {
|
||||
searchTitle = title.replace(pattern, '').trim();
|
||||
// 如果没有传入seasonNumber,尝试从标题中提取
|
||||
if (!extractedSeasonNumber) {
|
||||
const seasonStr = match[1];
|
||||
// 中文数字转数字
|
||||
const chineseNumbers: Record<string, number> = {
|
||||
'一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
|
||||
'六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
|
||||
};
|
||||
extractedSeasonNumber = chineseNumbers[seasonStr] || parseInt(seasonStr) || undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const searchResponse = await fetch(
|
||||
`/api/tmdb/search?query=${encodeURIComponent(searchTitle)}`
|
||||
);
|
||||
if (!searchResponse.ok) {
|
||||
throw new Error('搜索失败');
|
||||
}
|
||||
const searchData = await searchResponse.json();
|
||||
|
||||
if (searchData.results && searchData.results.length > 0) {
|
||||
const result = searchData.results[0];
|
||||
const detailId = result.id;
|
||||
const mediaType = result.media_type || type;
|
||||
|
||||
// 获取详情
|
||||
const detailResponse = await fetch(`/api/tmdb/detail?id=${detailId}&type=${mediaType}`);
|
||||
if (!detailResponse.ok) {
|
||||
throw new Error('获取TMDB详情失败');
|
||||
}
|
||||
const detailResult = await detailResponse.json();
|
||||
|
||||
// 如果有季度信息,尝试获取季度详情
|
||||
let seasonData = null;
|
||||
if (extractedSeasonNumber && mediaType === 'tv') {
|
||||
try {
|
||||
const seasonResponse = await fetch(
|
||||
`/api/tmdb/seasons?id=${detailId}&season=${extractedSeasonNumber}`
|
||||
);
|
||||
if (seasonResponse.ok) {
|
||||
seasonData = await seasonResponse.json();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取季度信息失败', err);
|
||||
}
|
||||
}
|
||||
|
||||
setDetailData({
|
||||
title: mediaType === 'movie' ? detailResult.title : detailResult.name,
|
||||
originalTitle:
|
||||
mediaType === 'movie' ? detailResult.original_title : detailResult.original_name,
|
||||
year:
|
||||
mediaType === 'movie'
|
||||
? detailResult.release_date?.substring(0, 4)
|
||||
: detailResult.first_air_date?.substring(0, 4),
|
||||
poster: detailResult.poster_path
|
||||
? processImageUrl(getTMDBImageUrl(detailResult.poster_path, 'w500'))
|
||||
: poster,
|
||||
rating: detailResult.vote_average
|
||||
? {
|
||||
value: detailResult.vote_average,
|
||||
count: detailResult.vote_count,
|
||||
}
|
||||
: undefined,
|
||||
intro: seasonData?.overview || detailResult.overview,
|
||||
genres: detailResult.genres?.map((g: any) => g.name),
|
||||
countries: detailResult.production_countries?.map((c: any) => c.name),
|
||||
languages: detailResult.spoken_languages?.map((l: any) => l.name),
|
||||
duration: detailResult.runtime ? `${detailResult.runtime}分钟` : undefined,
|
||||
episodesCount: seasonData?.episodes?.length || detailResult.number_of_episodes,
|
||||
releaseDate:
|
||||
mediaType === 'movie' ? detailResult.release_date : detailResult.first_air_date,
|
||||
status: detailResult.status,
|
||||
tagline: detailResult.tagline,
|
||||
seasons: detailResult.number_of_seasons,
|
||||
overview: detailResult.overview,
|
||||
tmdbId: detailId,
|
||||
mediaType: mediaType,
|
||||
seasonNumber: extractedSeasonNumber,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('未找到相关内容');
|
||||
setCurrentSource('tmdb');
|
||||
setOriginalSource('tmdb');
|
||||
await fetchTmdbData();
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('缺少必要的查询参数');
|
||||
@@ -431,8 +360,251 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 提取 TMDB 数据获取逻辑为独立函数
|
||||
const fetchTmdbData = async () => {
|
||||
setCurrentSource('tmdb');
|
||||
// 移除季度信息进行搜索
|
||||
let searchTitle = title;
|
||||
let extractedSeasonNumber = seasonNumber;
|
||||
|
||||
// 匹配各种季度格式: 第一季、第1季、第一部、Season 1、S1等
|
||||
const seasonPatterns = [
|
||||
/第([一二三四五六七八九十\d]+)[季部]/,
|
||||
/Season\s*(\d+)/i,
|
||||
/S(\d+)/i,
|
||||
];
|
||||
|
||||
for (const pattern of seasonPatterns) {
|
||||
const match = title.match(pattern);
|
||||
if (match) {
|
||||
searchTitle = title.replace(pattern, '').trim();
|
||||
// 如果没有传入seasonNumber,尝试从标题中提取
|
||||
if (!extractedSeasonNumber) {
|
||||
const seasonStr = match[1];
|
||||
// 中文数字转数字
|
||||
const chineseNumbers: Record<string, number> = {
|
||||
'一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
|
||||
'六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
|
||||
};
|
||||
extractedSeasonNumber = chineseNumbers[seasonStr] || parseInt(seasonStr) || undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const searchResponse = await fetch(
|
||||
`/api/tmdb/search?query=${encodeURIComponent(searchTitle)}`
|
||||
);
|
||||
if (!searchResponse.ok) {
|
||||
throw new Error('搜索失败');
|
||||
}
|
||||
const searchData = await searchResponse.json();
|
||||
|
||||
if (searchData.results && searchData.results.length > 0) {
|
||||
const result = searchData.results[0];
|
||||
const detailId = result.id;
|
||||
const mediaType = result.media_type || type;
|
||||
|
||||
// 获取详情
|
||||
const detailResponse = await fetch(`/api/tmdb/detail?id=${detailId}&type=${mediaType}`);
|
||||
if (!detailResponse.ok) {
|
||||
throw new Error('获取TMDB详情失败');
|
||||
}
|
||||
const detailResult = await detailResponse.json();
|
||||
|
||||
// 如果有季度信息,尝试获取季度详情
|
||||
let seasonData = null;
|
||||
if (extractedSeasonNumber && mediaType === 'tv') {
|
||||
try {
|
||||
const seasonResponse = await fetch(
|
||||
`/api/tmdb/seasons?id=${detailId}&season=${extractedSeasonNumber}`
|
||||
);
|
||||
if (seasonResponse.ok) {
|
||||
seasonData = await seasonResponse.json();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取季度信息失败', err);
|
||||
}
|
||||
}
|
||||
|
||||
setDetailData({
|
||||
title: mediaType === 'movie' ? detailResult.title : detailResult.name,
|
||||
originalTitle:
|
||||
mediaType === 'movie' ? detailResult.original_title : detailResult.original_name,
|
||||
year:
|
||||
mediaType === 'movie'
|
||||
? detailResult.release_date?.substring(0, 4)
|
||||
: detailResult.first_air_date?.substring(0, 4),
|
||||
poster: detailResult.poster_path
|
||||
? processImageUrl(getTMDBImageUrl(detailResult.poster_path, 'w500'))
|
||||
: poster,
|
||||
rating: detailResult.vote_average
|
||||
? {
|
||||
value: detailResult.vote_average,
|
||||
count: detailResult.vote_count,
|
||||
}
|
||||
: undefined,
|
||||
intro: seasonData?.overview || detailResult.overview,
|
||||
genres: detailResult.genres?.map((g: any) => g.name),
|
||||
countries: detailResult.production_countries?.map((c: any) => c.name),
|
||||
languages: detailResult.spoken_languages?.map((l: any) => l.name),
|
||||
duration: detailResult.runtime ? `${detailResult.runtime}分钟` : undefined,
|
||||
episodesCount: seasonData?.episodes?.length || detailResult.number_of_episodes,
|
||||
releaseDate:
|
||||
mediaType === 'movie' ? detailResult.release_date : detailResult.first_air_date,
|
||||
status: detailResult.status,
|
||||
tagline: detailResult.tagline,
|
||||
seasons: detailResult.number_of_seasons,
|
||||
overview: detailResult.overview,
|
||||
tmdbId: detailId,
|
||||
mediaType: mediaType,
|
||||
seasonNumber: extractedSeasonNumber,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('未找到相关内容');
|
||||
};
|
||||
|
||||
fetchDetail();
|
||||
}, [isOpen, doubanId, bangumiId, isBangumi, tmdbId, title, type, seasonNumber, poster, cmsData, sourceId, source]);
|
||||
}, [isOpen, doubanId, bangumiId, isBangumi, tmdbId, title, type, seasonNumber, poster, cmsData, sourceId, source, isUsingTmdb]);
|
||||
|
||||
// 切换数据源的函数
|
||||
const handleToggleSource = async () => {
|
||||
if (currentSource === 'tmdb') {
|
||||
// 切换回原始数据源
|
||||
if (originalDetailData) {
|
||||
setDetailData(originalDetailData);
|
||||
setCurrentSource(originalSource);
|
||||
setError(null);
|
||||
}
|
||||
} else {
|
||||
// 切换到 TMDB
|
||||
// 保存当前数据
|
||||
if (detailData && !originalDetailData) {
|
||||
setOriginalDetailData(detailData);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await fetchTmdbDataForToggle();
|
||||
} catch (err) {
|
||||
console.error('切换到TMDB失败:', err);
|
||||
setError(err instanceof Error ? err.message : '切换到TMDB失败');
|
||||
// 切换失败,但保持 currentSource 为 tmdb,这样可以显示切换回按钮
|
||||
setCurrentSource('tmdb');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 用于切换时获取 TMDB 数据
|
||||
const fetchTmdbDataForToggle = async () => {
|
||||
// 移除季度信息进行搜索
|
||||
let searchTitle = title;
|
||||
let extractedSeasonNumber = seasonNumber;
|
||||
|
||||
// 匹配各种季度格式: 第一季、第1季、第一部、Season 1、S1等
|
||||
const seasonPatterns = [
|
||||
/第([一二三四五六七八九十\d]+)[季部]/,
|
||||
/Season\s*(\d+)/i,
|
||||
/S(\d+)/i,
|
||||
];
|
||||
|
||||
for (const pattern of seasonPatterns) {
|
||||
const match = title.match(pattern);
|
||||
if (match) {
|
||||
searchTitle = title.replace(pattern, '').trim();
|
||||
// 如果没有传入seasonNumber,尝试从标题中提取
|
||||
if (!extractedSeasonNumber) {
|
||||
const seasonStr = match[1];
|
||||
// 中文数字转数字
|
||||
const chineseNumbers: Record<string, number> = {
|
||||
'一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
|
||||
'六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
|
||||
};
|
||||
extractedSeasonNumber = chineseNumbers[seasonStr] || parseInt(seasonStr) || undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const searchResponse = await fetch(
|
||||
`/api/tmdb/search?query=${encodeURIComponent(searchTitle)}`
|
||||
);
|
||||
if (!searchResponse.ok) {
|
||||
throw new Error('搜索失败');
|
||||
}
|
||||
const searchData = await searchResponse.json();
|
||||
|
||||
if (searchData.results && searchData.results.length > 0) {
|
||||
const result = searchData.results[0];
|
||||
const detailId = result.id;
|
||||
const mediaType = result.media_type || type;
|
||||
|
||||
// 获取详情
|
||||
const detailResponse = await fetch(`/api/tmdb/detail?id=${detailId}&type=${mediaType}`);
|
||||
if (!detailResponse.ok) {
|
||||
throw new Error('获取TMDB详情失败');
|
||||
}
|
||||
const detailResult = await detailResponse.json();
|
||||
|
||||
// 如果有季度信息,尝试获取季度详情
|
||||
let seasonData = null;
|
||||
if (extractedSeasonNumber && mediaType === 'tv') {
|
||||
try {
|
||||
const seasonResponse = await fetch(
|
||||
`/api/tmdb/seasons?id=${detailId}&season=${extractedSeasonNumber}`
|
||||
);
|
||||
if (seasonResponse.ok) {
|
||||
seasonData = await seasonResponse.json();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取季度信息失败', err);
|
||||
}
|
||||
}
|
||||
|
||||
setDetailData({
|
||||
title: mediaType === 'movie' ? detailResult.title : detailResult.name,
|
||||
originalTitle:
|
||||
mediaType === 'movie' ? detailResult.original_title : detailResult.original_name,
|
||||
year:
|
||||
mediaType === 'movie'
|
||||
? detailResult.release_date?.substring(0, 4)
|
||||
: detailResult.first_air_date?.substring(0, 4),
|
||||
poster: detailResult.poster_path
|
||||
? processImageUrl(getTMDBImageUrl(detailResult.poster_path, 'w500'))
|
||||
: poster,
|
||||
rating: detailResult.vote_average
|
||||
? {
|
||||
value: detailResult.vote_average,
|
||||
count: detailResult.vote_count,
|
||||
}
|
||||
: undefined,
|
||||
intro: seasonData?.overview || detailResult.overview,
|
||||
genres: detailResult.genres?.map((g: any) => g.name),
|
||||
countries: detailResult.production_countries?.map((c: any) => c.name),
|
||||
languages: detailResult.spoken_languages?.map((l: any) => l.name),
|
||||
duration: detailResult.runtime ? `${detailResult.runtime}分钟` : undefined,
|
||||
episodesCount: seasonData?.episodes?.length || detailResult.number_of_episodes,
|
||||
releaseDate:
|
||||
mediaType === 'movie' ? detailResult.release_date : detailResult.first_air_date,
|
||||
status: detailResult.status,
|
||||
tagline: detailResult.tagline,
|
||||
seasons: detailResult.number_of_seasons,
|
||||
overview: detailResult.overview,
|
||||
tmdbId: detailId,
|
||||
mediaType: mediaType,
|
||||
seasonNumber: extractedSeasonNumber,
|
||||
});
|
||||
setCurrentSource('tmdb');
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('未找到相关内容');
|
||||
};
|
||||
|
||||
// 异步获取季度和集数详情(仅TMDB)
|
||||
useEffect(() => {
|
||||
@@ -608,8 +780,43 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="p-6 text-center">
|
||||
<p className="text-red-500 dark:text-red-400">{error}</p>
|
||||
<div className="p-6">
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-red-500 dark:text-red-400">{error}</p>
|
||||
</div>
|
||||
|
||||
{/* 数据源显示和切换 - 错误时也显示 */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">数据来源:</span>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300 uppercase">
|
||||
{currentSource === 'douban' && 'Douban'}
|
||||
{currentSource === 'bangumi' && 'Bangumi'}
|
||||
{currentSource === 'cms' && 'CMS'}
|
||||
{currentSource === 'tmdb' && 'TMDB'}
|
||||
</span>
|
||||
</div>
|
||||
{currentSource !== 'tmdb' && (
|
||||
<button
|
||||
onClick={handleToggleSource}
|
||||
disabled={loading}
|
||||
className="px-3 py-1.5 text-sm rounded-lg bg-green-500 hover:bg-green-600 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
切换到 TMDB
|
||||
</button>
|
||||
)}
|
||||
{currentSource === 'tmdb' && originalSource !== 'tmdb' && originalDetailData && (
|
||||
<button
|
||||
onClick={handleToggleSource}
|
||||
disabled={loading}
|
||||
className="px-3 py-1.5 text-sm rounded-lg bg-gray-500 hover:bg-gray-600 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
切换回 {originalSource === 'douban' ? 'Douban' : originalSource === 'bangumi' ? 'Bangumi' : 'CMS'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -907,6 +1114,39 @@ const DetailPanel: React.FC<DetailPanelProps> = ({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 数据源显示和切换 */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">数据来源:</span>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300 uppercase">
|
||||
{currentSource === 'douban' && 'Douban'}
|
||||
{currentSource === 'bangumi' && 'Bangumi'}
|
||||
{currentSource === 'cms' && 'CMS'}
|
||||
{currentSource === 'tmdb' && 'TMDB'}
|
||||
</span>
|
||||
</div>
|
||||
{currentSource !== 'tmdb' && (
|
||||
<button
|
||||
onClick={handleToggleSource}
|
||||
disabled={loading}
|
||||
className="px-3 py-1.5 text-sm rounded-lg bg-green-500 hover:bg-green-600 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
切换到 TMDB
|
||||
</button>
|
||||
)}
|
||||
{currentSource === 'tmdb' && originalSource !== 'tmdb' && originalDetailData && (
|
||||
<button
|
||||
onClick={handleToggleSource}
|
||||
disabled={loading}
|
||||
className="px-3 py-1.5 text-sm rounded-lg bg-gray-500 hover:bg-gray-600 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
切换回 {originalSource === 'douban' ? 'Douban' : originalSource === 'bangumi' ? 'Bangumi' : 'CMS'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user