feat: AI分析单页展示 + 主题切换 + 移动端适配

- AI分析页改为直接展示最新报告,历史存数据库
- 新增浅色/深色/系统自动主题切换
- 市场看板、AI分析页适配主题变量
- 移动端表格可滑动、按钮自动换行
- Mermaid图表支持
- Markdown表格渲染支持(remark-gfm)
This commit is contained in:
Sakurasan
2026-09-01 12:33:27 +08:00
parent 912a224b6b
commit 6478e0d403
16 changed files with 614 additions and 407 deletions
+7
View File
@@ -17,6 +17,13 @@ export interface AiReport {
created_at: string;
}
export async function fetchAiLatestReport(): Promise<AiReport> {
const resp = await fetch(`${API_BASE}/api/ai-analysis/latest`);
if (!resp.ok) throw new Error(`请求失败 (${resp.status})`);
const result = await resp.json();
return result.data;
}
export async function fetchAiReports(): Promise<AiReport[]> {
const resp = await fetch(`${API_BASE}/api/ai-analysis`);
if (!resp.ok) throw new Error(`请求失败 (${resp.status})`);
+56
View File
@@ -0,0 +1,56 @@
import { useState, useEffect, useCallback } from "react";
type Theme = "light" | "dark" | "system";
function getSystemTheme(): "light" | "dark" {
if (typeof window === "undefined") return "light";
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function getResolvedTheme(theme: Theme): "light" | "dark" {
return theme === "system" ? getSystemTheme() : theme;
}
function applyTheme(resolved: "light" | "dark") {
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(resolved);
root.setAttribute("data-theme", resolved);
}
export function useTheme() {
const [theme, setThemeState] = useState<Theme>(() => {
if (typeof window === "undefined") return "system";
return (localStorage.getItem("theme") as Theme) || "system";
});
const [resolved, setResolved] = useState<"light" | "dark">(() => getResolvedTheme(theme));
const setTheme = useCallback((newTheme: Theme) => {
setThemeState(newTheme);
localStorage.setItem("theme", newTheme);
const r = getResolvedTheme(newTheme);
setResolved(r);
applyTheme(r);
}, []);
// 初始应用
useEffect(() => {
applyTheme(resolved);
}, []);
// 监听系统主题变化
useEffect(() => {
if (theme !== "system") return;
const mq = window.matchMedia("(prefers-color-scheme: dark)");
const handler = () => {
const r = getSystemTheme();
setResolved(r);
applyTheme(r);
};
mq.addEventListener("change", handler);
return () => mq.removeEventListener("change", handler);
}, [theme]);
return { theme, resolved, setTheme };
}