diff --git a/backend/main.py b/backend/main.py index 8d91ae4..339ffe8 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,9 +4,11 @@ from fastapi import FastAPI, HTTPException from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from pydantic import BaseModel import json import re +import os from datetime import datetime from app.api import upload, llm, skills, users, datasources, projects, semantic @@ -34,6 +36,11 @@ app.add_middleware( # Initialize database tables Base.metadata.create_all(bind=engine) +# Mount static directory for reports +data_dir = os.path.join(os.path.dirname(__file__), "data", "data") +os.makedirs(data_dir, exist_ok=True) +app.mount("/reports", StaticFiles(directory=data_dir), name="reports") + app.include_router(upload.router, prefix="/api/v1") app.include_router(llm.router, prefix="/api/v1") app.include_router(skills.router, prefix="/api/v1") diff --git a/frontend/src/components/ChatInterface.tsx b/frontend/src/components/ChatInterface.tsx index f80b669..4bea4c0 100644 --- a/frontend/src/components/ChatInterface.tsx +++ b/frontend/src/components/ChatInterface.tsx @@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; -import { User, Loader2, Sparkles, ArrowUp, ChevronDown, Paperclip, Check, X, Square, Plus, Database, Wand2, Search, Zap, LayoutGrid, CheckCircle2, Table, XCircle, Settings } from "lucide-react"; +import { User, Loader2, Sparkles, ArrowUp, ChevronDown, Paperclip, Check, X, Square, Plus, Database, Wand2, Search, Zap, LayoutGrid, CheckCircle2, Table, XCircle, Settings, ExternalLink } from "lucide-react"; import { api } from "@/lib/api"; import { type ChartSpec } from "@/store/visualizationStore"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; @@ -50,6 +50,17 @@ const splitReportHtml = (content: string): { markdown: string; reportHtml: strin return { markdown, reportHtml: reportHtml || null }; }; +const HTML_FILE_REGEX = /data[\\\/]data[\\\/]([a-zA-Z0-9_\-]+\.html?)/i; + +const extractExternalReport = (content: string): string | null => { + if (!content) return null; + const match = content.match(HTML_FILE_REGEX); + if (match && match[1]) { + return `/reports/${match[1]}`; + } + return null; +}; + interface ModelConfig { id: string; name?: string; @@ -926,6 +937,7 @@ export function ChatInterface() {
{messages.map((msg) => { const { markdown, reportHtml } = splitReportHtml(msg.content); + const externalReportUrl = extractExternalReport(msg.content); return (
) : null} + {externalReportUrl ? ( +
+ + + 在新标签页中打开分析报告 + +
+ ) : null} {msg.viz ? (
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index fb6f375..db25c4b 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -20,6 +20,10 @@ export default defineConfig({ target: 'http://localhost:8000', changeOrigin: true, }, + '/reports': { + target: 'http://localhost:8000', + changeOrigin: true, + }, }, }, })