修复了豆瓣读书
This commit is contained in:
parent
1700c1bc44
commit
1cff530f8a
@ -9,11 +9,22 @@ interface WorldHeatmapProps {
|
||||
|
||||
const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||
const chartRef = useRef<HTMLDivElement>(null);
|
||||
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartRef.current) return;
|
||||
|
||||
const chart = echarts.init(chartRef.current);
|
||||
// 确保之前的实例被正确销毁
|
||||
if (chartInstanceRef.current) {
|
||||
chartInstanceRef.current.dispose();
|
||||
}
|
||||
|
||||
// 初始化图表并保存实例引用
|
||||
const chart = echarts.init(chartRef.current, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false
|
||||
});
|
||||
chartInstanceRef.current = chart;
|
||||
|
||||
const mergedWorldData = {
|
||||
...worldData,
|
||||
@ -41,17 +52,44 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||
|
||||
echarts.registerMap('merged-world', mergedWorldData as any);
|
||||
|
||||
// 检查当前是否为暗色模式
|
||||
const isDarkMode = document.documentElement.classList.contains('dark');
|
||||
|
||||
// 根据当前模式设置颜色
|
||||
const getChartColors = () => {
|
||||
return {
|
||||
textColor: isDarkMode ? '#ffffff' : '#374151',
|
||||
borderColor: isDarkMode ? '#374151' : '#e5e7eb',
|
||||
unvisitedColor: isDarkMode ? '#1f2937' : '#e5e7eb',
|
||||
visitedColor: isDarkMode ? '#059669' : '#10b981',
|
||||
emphasisColor: isDarkMode ? '#059669' : '#10b981',
|
||||
tooltipBgColor: isDarkMode ? '#111827' : '#ffffff',
|
||||
};
|
||||
};
|
||||
|
||||
const colors = getChartColors();
|
||||
|
||||
// 使用动态颜色方案
|
||||
const option = {
|
||||
title: {
|
||||
text: '我的旅行足迹',
|
||||
left: 'center',
|
||||
top: 20
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: colors.textColor,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: ({name}: {name: string}) => {
|
||||
const visited = visitedPlaces.includes(name);
|
||||
return `${name}<br/>${visited ? '✓ 已去过' : '尚未去过'}`;
|
||||
},
|
||||
backgroundColor: colors.tooltipBgColor,
|
||||
borderColor: colors.borderColor,
|
||||
textStyle: {
|
||||
color: colors.textColor
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
@ -62,13 +100,14 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||
{ value: 0, label: '未去过' }
|
||||
],
|
||||
inRange: {
|
||||
color: ['#e0e0e0', '#91cc75']
|
||||
color: [colors.unvisitedColor, colors.visitedColor]
|
||||
},
|
||||
outOfRange: {
|
||||
color: ['#e0e0e0']
|
||||
color: [colors.unvisitedColor]
|
||||
},
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
color: colors.textColor,
|
||||
fontWeight: 500
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
@ -78,12 +117,18 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||
roam: true,
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true
|
||||
show: true,
|
||||
color: colors.textColor
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: '#91cc75'
|
||||
areaColor: colors.emphasisColor
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: colors.borderColor,
|
||||
borderWidth: 1,
|
||||
borderType: 'solid'
|
||||
},
|
||||
data: mergedWorldData.features.map((feature: any) => ({
|
||||
name: feature.properties.name,
|
||||
value: visitedPlaces.includes(feature.properties.name) ? 1 : 0
|
||||
@ -93,20 +138,102 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
|
||||
// 确保图表初始化后立即调整大小以适应容器
|
||||
chart.resize();
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize();
|
||||
const handleResize = () => {
|
||||
if (chartInstanceRef.current) {
|
||||
chartInstanceRef.current.resize();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 监听暗色模式变化并更新图表
|
||||
const darkModeObserver = new MutationObserver(() => {
|
||||
if (!chartRef.current) return;
|
||||
|
||||
// 检查当前是否为暗色模式
|
||||
const newIsDarkMode = document.documentElement.classList.contains('dark');
|
||||
|
||||
if (chartInstanceRef.current) {
|
||||
// 更新颜色设置
|
||||
const newColors = {
|
||||
textColor: newIsDarkMode ? '#ffffff' : '#374151',
|
||||
borderColor: newIsDarkMode ? '#4b5563' : '#d1d5db',
|
||||
unvisitedColor: newIsDarkMode ? '#1f2937' : '#e5e7eb',
|
||||
visitedColor: newIsDarkMode ? '#059669' : '#10b981',
|
||||
emphasisColor: newIsDarkMode ? '#059669' : '#10b981',
|
||||
tooltipBgColor: newIsDarkMode ? '#111827' : '#ffffff',
|
||||
};
|
||||
|
||||
// 更新图表选项
|
||||
const newOption = {
|
||||
title: {
|
||||
textStyle: {
|
||||
color: newColors.textColor
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: newColors.tooltipBgColor,
|
||||
borderColor: newColors.borderColor,
|
||||
textStyle: {
|
||||
color: newColors.textColor
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
inRange: {
|
||||
color: [newColors.unvisitedColor, newColors.visitedColor]
|
||||
},
|
||||
outOfRange: {
|
||||
color: [newColors.unvisitedColor]
|
||||
},
|
||||
textStyle: {
|
||||
color: newColors.textColor
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
color: newColors.textColor
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: newColors.emphasisColor
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: newColors.borderColor,
|
||||
borderWidth: 1,
|
||||
borderType: 'solid'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// 应用新选项
|
||||
chartInstanceRef.current.setOption(newOption);
|
||||
}
|
||||
});
|
||||
|
||||
darkModeObserver.observe(document.documentElement, { attributes: true });
|
||||
|
||||
return () => {
|
||||
chart.dispose();
|
||||
window.removeEventListener('resize', () => {
|
||||
chart.resize();
|
||||
});
|
||||
if (chartInstanceRef.current) {
|
||||
chartInstanceRef.current.dispose();
|
||||
chartInstanceRef.current = null;
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
darkModeObserver.disconnect();
|
||||
};
|
||||
}, [visitedPlaces]);
|
||||
|
||||
return <div ref={chartRef} style={{ width: '100%', height: '600px' }} />;
|
||||
return (
|
||||
<div
|
||||
ref={chartRef}
|
||||
className="w-full h-[600px] md:h-[500px] lg:h-[600px] xl:h-[700px] dark:[&_.echarts-tooltip]:bg-[#111827] dark:[&_.echarts-tooltip]:border-[#374151] dark:[&_.echarts-tooltip]:text-white dark:[&_.echarts-title]:text-white dark:[&_.echarts-visual-map]:text-white dark:[&_.echarts-map]:border-[#4b5563] dark:[&_.echarts-map-emphasis]:text-white dark:[&_.echarts-map-emphasis]:bg-[#059669] dark:[&_.echarts-map-unvisited]:bg-[#1f2937] dark:[&_.echarts-map-visited]:bg-[#059669]"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorldHeatmap;
|
@ -4,6 +4,16 @@ import { load } from 'cheerio';
|
||||
// 添加服务器渲染标记
|
||||
export const prerender = false;
|
||||
|
||||
// 生成随机的bid Cookie值
|
||||
function generateBid() {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < 11; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
const url = new URL(request.url);
|
||||
const type = url.searchParams.get('type') || 'movie';
|
||||
@ -25,12 +35,19 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
doubanUrl = `https://movie.douban.com/people/${doubanId}/collect?start=${start}&sort=time&rating=all&filter=all&mode=grid`;
|
||||
}
|
||||
|
||||
// 生成随机bid
|
||||
const bid = generateBid();
|
||||
|
||||
const response = await fetch(doubanUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'Referer': 'https://movie.douban.com/'
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
|
||||
'Sec-Fetch-Site': 'none',
|
||||
'Sec-Fetch-Mode': 'navigate',
|
||||
'Sec-Fetch-User': '?1',
|
||||
'Sec-Fetch-Dest': 'document',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||
'Cookie': `bid=${bid}`
|
||||
}
|
||||
});
|
||||
|
||||
@ -58,25 +75,62 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
}
|
||||
|
||||
const items: DoubanItem[] = [];
|
||||
$('.item.comment-item').each((_, element) => {
|
||||
|
||||
// 尝试不同的选择器
|
||||
let itemSelector = '.item.comment-item';
|
||||
let itemCount = $(itemSelector).length;
|
||||
|
||||
if (itemCount === 0) {
|
||||
// 尝试其他可能的选择器
|
||||
itemSelector = '.subject-item';
|
||||
itemCount = $(itemSelector).length;
|
||||
}
|
||||
|
||||
$(itemSelector).each((_, element) => {
|
||||
const $element = $(element);
|
||||
|
||||
const imageUrl = $element.find('.pic img').attr('src') || '';
|
||||
const title = $element.find('.title a em').text().trim();
|
||||
const subtitle = $element.find('.title a').text().replace(title, '').trim();
|
||||
const link = $element.find('.title a').attr('href') || '';
|
||||
const intro = $element.find('.intro').text().trim();
|
||||
|
||||
// 获取评分,从rating1-t到rating5-t
|
||||
// 根据选择器调整查找逻辑
|
||||
let imageUrl = '';
|
||||
let title = '';
|
||||
let subtitle = '';
|
||||
let link = '';
|
||||
let intro = '';
|
||||
let rating = 0;
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
if ($element.find(`.rating${i}-t`).length > 0) {
|
||||
rating = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let date = '';
|
||||
|
||||
const date = $element.find('.date').text().trim();
|
||||
if (itemSelector === '.item.comment-item') {
|
||||
// 原始逻辑
|
||||
imageUrl = $element.find('.pic img').attr('src') || '';
|
||||
title = $element.find('.title a em').text().trim();
|
||||
subtitle = $element.find('.title a').text().replace(title, '').trim();
|
||||
link = $element.find('.title a').attr('href') || '';
|
||||
intro = $element.find('.intro').text().trim();
|
||||
|
||||
// 获取评分,从rating1-t到rating5-t
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
if ($element.find(`.rating${i}-t`).length > 0) {
|
||||
rating = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
date = $element.find('.date').text().trim();
|
||||
} else if (itemSelector === '.subject-item') {
|
||||
// 新的图书页面结构
|
||||
imageUrl = $element.find('.pic img').attr('src') || '';
|
||||
title = $element.find('.info h2 a').text().trim();
|
||||
link = $element.find('.info h2 a').attr('href') || '';
|
||||
intro = $element.find('.info .pub').text().trim();
|
||||
|
||||
// 获取评分
|
||||
const ratingClass = $element.find('.rating-star').attr('class') || '';
|
||||
const ratingMatch = ratingClass.match(/rating(\d)-t/);
|
||||
if (ratingMatch) {
|
||||
rating = parseInt(ratingMatch[1]);
|
||||
}
|
||||
|
||||
date = $element.find('.info .date').text().trim();
|
||||
}
|
||||
|
||||
items.push({
|
||||
imageUrl,
|
||||
|
Loading…
Reference in New Issue
Block a user