Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,12 @@ SMTP_PASS=

WIKIDOT_BOT_USER=
WIKIDOT_BOT_PASS=

# 开发环境身份切换开关(/api/dev/login)
# 该接口能直接签发任意角色的 JWT,默认关闭。
# 本地调试时设为 1,并先执行 node scripts/dev-seed.js 造账号。
# 生产环境即使误设也不会生效(NODE_ENV=production 时无条件 404)。
ENABLE_DEV_LOGIN=

# dev-seed.js 写入的测试账号口令(可选,未设置时用内置默认值)
DEV_SEED_PASSWORD=
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ WikitDB-Server/
- **白然** — WikitDB LOGO 设计师
- **Kakushi** - Wikit创始人,Wikit API运维
- **UMOU** - 贡献者
- **OxygenNine** - ui-redesign
- 每一位为 Wikidot 社区贡献原创内容的创作者

## 许可证
Expand Down
72 changes: 62 additions & 10 deletions components/AuthorActivityChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@ import React, { useEffect, useRef, useState } from 'react';
// 核心修复:直接使用 auto 全自动注册,彻底解决漏引组件导致的致命闪退
import Chart from 'chart.js/auto';

const isDark = () => typeof document !== 'undefined' && document.documentElement.classList.contains('dark');

// canvas 无法读取 CSS 变量,按当前主题返回具体色值
const getChartColors = () => {
if (isDark()) {
return {
bar: 'rgba(139, 92, 246, 0.85)',
barBorder: 'rgb(139, 92, 246)',
tooltipBg: 'rgba(24, 24, 27, 0.96)',
tooltipTitle: '#f4f4f5',
tooltipBody: '#a1a1aa',
tooltipBorder: 'rgba(63, 63, 70, 1)',
grid: 'rgba(255, 255, 255, 0.05)',
ticks: '#a1a1aa',
};
}
return {
bar: 'rgba(139, 92, 246, 0.85)',
barBorder: 'rgb(139, 92, 246)',
tooltipBg: 'rgba(255, 255, 255, 0.96)',
tooltipTitle: '#18181b',
tooltipBody: '#52525b',
tooltipBorder: 'rgba(228, 228, 231, 1)',
grid: 'rgba(0, 0, 0, 0.06)',
ticks: '#52525b',
};
};

export default function AuthorActivityChart({ data = [] }) {
const canvasRef = useRef(null);
const chartInstance = useRef(null);
Expand Down Expand Up @@ -55,6 +83,8 @@ export default function AuthorActivityChart({ data = [] }) {

const ctx = canvasRef.current.getContext('2d');

const colors = getChartColors();

chartInstance.current = new Chart(ctx, {
type: 'bar',
data: {
Expand All @@ -63,8 +93,8 @@ export default function AuthorActivityChart({ data = [] }) {
{
label: '发布页面数',
data: pagesData,
backgroundColor: 'rgba(99, 102, 241, 0.85)',
borderColor: 'rgb(99, 102, 241)',
backgroundColor: colors.bar,
borderColor: colors.barBorder,
borderWidth: 1,
barPercentage: 0.9,
categoryPercentage: 1.0,
Expand All @@ -77,10 +107,10 @@ export default function AuthorActivityChart({ data = [] }) {
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(23,23,23,0.96)',
titleColor: '#fff',
bodyColor: 'rgb(200,200,200)',
borderColor: 'rgba(255,255,255,0.1)',
backgroundColor: colors.tooltipBg,
titleColor: colors.tooltipTitle,
bodyColor: colors.tooltipBody,
borderColor: colors.tooltipBorder,
borderWidth: 1,
callbacks: {
label: function(context) {
Expand All @@ -98,16 +128,38 @@ export default function AuthorActivityChart({ data = [] }) {
scales: {
x: {
grid: { display: false },
ticks: { color: 'rgb(110, 118, 129)', maxRotation: 45 }
ticks: { color: colors.ticks, maxRotation: 45 }
},
y: {
beginAtZero: true,
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: 'rgb(110, 118, 129)', stepSize: 1 }
grid: { color: colors.grid },
ticks: { color: colors.ticks, stepSize: 1 }
}
}
}
});

// 亮暗主题切换时重设 canvas 配色(canvas 不支持 CSS 变量,需 JS 侧感知)
const applyThemeColors = () => {
const chart = chartInstance.current;
if (!chart) return;
const c = getChartColors();
chart.data.datasets[0].backgroundColor = c.bar;
chart.data.datasets[0].borderColor = c.barBorder;
chart.options.plugins.tooltip.backgroundColor = c.tooltipBg;
chart.options.plugins.tooltip.titleColor = c.tooltipTitle;
chart.options.plugins.tooltip.bodyColor = c.tooltipBody;
chart.options.plugins.tooltip.borderColor = c.tooltipBorder;
chart.options.scales.x.ticks.color = c.ticks;
chart.options.scales.y.ticks.color = c.ticks;
chart.options.scales.y.grid.color = c.grid;
chart.update('none');
};

const themeObserver = new MutationObserver(applyThemeColors);
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });

return () => themeObserver.disconnect();
} catch (err) {
console.error("图表引擎渲染异常:", err);
setChartError(err.message);
Expand All @@ -118,7 +170,7 @@ export default function AuthorActivityChart({ data = [] }) {
return (
<div className="w-full h-full relative min-h-[260px]">
{chartError && (
<div className="absolute inset-0 flex items-center justify-center text-sm text-red-400 bg-gray-900/50 rounded-lg">
<div className="absolute inset-0 flex items-center justify-center text-sm text-red-400 bg-sunken rounded-lg">
图表渲染失败: {chartError}
</div>
)}
Expand Down
8 changes: 4 additions & 4 deletions components/ErrorBoundary.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ export default class ErrorBoundary extends React.Component {
if (this.state.hasError) {
return (
<div className="min-h-[60vh] flex items-center justify-center p-6">
<div className="text-center max-w-md bg-gray-800/50 rounded-2xl border border-red-500/30 p-8">
<div className="text-center max-w-md bg-panel rounded-2xl border border-red-500/30 p-8">
<i className="fa-solid fa-triangle-exclamation text-red-500 text-4xl mb-4"></i>
<h2 className="text-xl font-bold text-white mb-2">页面渲染出现异常</h2>
<p className="text-gray-400 text-sm mb-4 break-all">{this.state.message}</p>
<h2 className="text-xl font-bold text-fg mb-2">页面渲染出现异常</h2>
<p className="text-fg-3 text-sm mb-4 break-all">{this.state.message}</p>
<button
type="button"
onClick={() => window.location.reload()}
className="px-5 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors text-sm font-medium"
className="px-5 py-2 bg-accent-solid text-accent-fg rounded-lg hover:bg-accent-solid-hover transition-colors text-sm font-medium"
>
<i className="fa-solid fa-rotate mr-1.5"></i>刷新页面
</button>
Expand Down
2 changes: 1 addition & 1 deletion components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Footer = () => {

return (
<>
<footer className="relative bg-gray-800/50 py-8 text-sm font-medium text-gray-300 text-center">
<footer className="relative bg-sunken border-t border-line py-8 text-sm font-medium text-fg-3 text-center">
{`© ${copyrightDate} - `}{config.SITE_AUTHOR}
</footer>
</>
Expand Down
Loading
Loading