修复了豆瓣读书
This commit is contained in:
parent
1700c1bc44
commit
1cff530f8a
@ -9,11 +9,22 @@ interface WorldHeatmapProps {
|
|||||||
|
|
||||||
const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
||||||
const chartRef = useRef<HTMLDivElement>(null);
|
const chartRef = useRef<HTMLDivElement>(null);
|
||||||
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chartRef.current) return;
|
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 = {
|
const mergedWorldData = {
|
||||||
...worldData,
|
...worldData,
|
||||||
@ -41,17 +52,44 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
|||||||
|
|
||||||
echarts.registerMap('merged-world', mergedWorldData as any);
|
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 = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: '我的旅行足迹',
|
text: '我的旅行足迹',
|
||||||
left: 'center',
|
left: 'center',
|
||||||
top: 20
|
top: 20,
|
||||||
|
textStyle: {
|
||||||
|
color: colors.textColor,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: ({name}: {name: string}) => {
|
formatter: ({name}: {name: string}) => {
|
||||||
const visited = visitedPlaces.includes(name);
|
const visited = visitedPlaces.includes(name);
|
||||||
return `${name}<br/>${visited ? '✓ 已去过' : '尚未去过'}`;
|
return `${name}<br/>${visited ? '✓ 已去过' : '尚未去过'}`;
|
||||||
|
},
|
||||||
|
backgroundColor: colors.tooltipBgColor,
|
||||||
|
borderColor: colors.borderColor,
|
||||||
|
textStyle: {
|
||||||
|
color: colors.textColor
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visualMap: {
|
visualMap: {
|
||||||
@ -62,13 +100,14 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
|||||||
{ value: 0, label: '未去过' }
|
{ value: 0, label: '未去过' }
|
||||||
],
|
],
|
||||||
inRange: {
|
inRange: {
|
||||||
color: ['#e0e0e0', '#91cc75']
|
color: [colors.unvisitedColor, colors.visitedColor]
|
||||||
},
|
},
|
||||||
outOfRange: {
|
outOfRange: {
|
||||||
color: ['#e0e0e0']
|
color: [colors.unvisitedColor]
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#333'
|
color: colors.textColor,
|
||||||
|
fontWeight: 500
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
@ -78,12 +117,18 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
|||||||
roam: true,
|
roam: true,
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
show: true
|
show: true,
|
||||||
|
color: colors.textColor
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
areaColor: '#91cc75'
|
areaColor: colors.emphasisColor
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderColor: colors.borderColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderType: 'solid'
|
||||||
|
},
|
||||||
data: mergedWorldData.features.map((feature: any) => ({
|
data: mergedWorldData.features.map((feature: any) => ({
|
||||||
name: feature.properties.name,
|
name: feature.properties.name,
|
||||||
value: visitedPlaces.includes(feature.properties.name) ? 1 : 0
|
value: visitedPlaces.includes(feature.properties.name) ? 1 : 0
|
||||||
@ -93,20 +138,102 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
chart.setOption(option);
|
chart.setOption(option);
|
||||||
|
|
||||||
|
// 确保图表初始化后立即调整大小以适应容器
|
||||||
|
chart.resize();
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
const handleResize = () => {
|
||||||
chart.resize();
|
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 () => {
|
return () => {
|
||||||
chart.dispose();
|
if (chartInstanceRef.current) {
|
||||||
window.removeEventListener('resize', () => {
|
chartInstanceRef.current.dispose();
|
||||||
chart.resize();
|
chartInstanceRef.current = null;
|
||||||
});
|
}
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
darkModeObserver.disconnect();
|
||||||
};
|
};
|
||||||
}, [visitedPlaces]);
|
}, [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;
|
export default WorldHeatmap;
|
@ -4,6 +4,16 @@ import { load } from 'cheerio';
|
|||||||
// 添加服务器渲染标记
|
// 添加服务器渲染标记
|
||||||
export const prerender = false;
|
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 }) => {
|
export const GET: APIRoute = async ({ request }) => {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const type = url.searchParams.get('type') || 'movie';
|
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`;
|
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, {
|
const response = await fetch(doubanUrl, {
|
||||||
headers: {
|
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',
|
'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',
|
||||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
'Sec-Fetch-Site': 'none',
|
||||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
'Referer': 'https://movie.douban.com/'
|
'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[] = [];
|
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 $element = $(element);
|
||||||
|
|
||||||
const imageUrl = $element.find('.pic img').attr('src') || '';
|
// 根据选择器调整查找逻辑
|
||||||
const title = $element.find('.title a em').text().trim();
|
let imageUrl = '';
|
||||||
const subtitle = $element.find('.title a').text().replace(title, '').trim();
|
let title = '';
|
||||||
const link = $element.find('.title a').attr('href') || '';
|
let subtitle = '';
|
||||||
const intro = $element.find('.intro').text().trim();
|
let link = '';
|
||||||
|
let intro = '';
|
||||||
// 获取评分,从rating1-t到rating5-t
|
|
||||||
let rating = 0;
|
let rating = 0;
|
||||||
for (let i = 1; i <= 5; i++) {
|
let date = '';
|
||||||
if ($element.find(`.rating${i}-t`).length > 0) {
|
|
||||||
rating = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
items.push({
|
||||||
imageUrl,
|
imageUrl,
|
||||||
|
Loading…
Reference in New Issue
Block a user