"use client"; import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import ReactMarkdown from "react-markdown"; import { Wand2, Save, FileSpreadsheet, FileDown, Loader2 } from "lucide-react"; import { toast } from "sonner"; import { customersApi, projectsApi, downloadFile, downloadFileAsBlob, type CustomerRead, type QuoteGenerateResponse, } from "@/lib/api/client"; export default function WorkspacePage() { const [customers, setCustomers] = useState([]); const [customerId, setCustomerId] = useState(""); const [rawText, setRawText] = useState(""); const [solutionMd, setSolutionMd] = useState(""); const [projectId, setProjectId] = useState(null); const [lastQuote, setLastQuote] = useState(null); const [analyzing, setAnalyzing] = useState(false); const [saving, setSaving] = useState(false); const [generatingQuote, setGeneratingQuote] = useState(false); const loadCustomers = useCallback(async () => { try { const list = await customersApi.list(); setCustomers(list); if (list.length > 0 && !customerId) setCustomerId(String(list[0].id)); } catch (e) { toast.error("加载客户列表失败"); } }, [customerId]); useEffect(() => { loadCustomers(); }, [loadCustomers]); const handleAnalyze = async () => { if (!customerId || !rawText.trim()) { toast.error("请选择客户并输入原始需求"); return; } setAnalyzing(true); try { const res = await projectsApi.analyze({ customer_id: Number(customerId), raw_text: rawText.trim(), }); setSolutionMd(res.ai_solution_md); setProjectId(res.project_id); setLastQuote(null); toast.success("方案已生成,可在右侧编辑"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : "AI 解析失败"; toast.error(msg); } finally { setAnalyzing(false); } }; const handleSaveToArchive = async () => { if (projectId == null) { toast.error("请先进行 AI 解析"); return; } setSaving(true); try { await projectsApi.update(projectId, { ai_solution_md: solutionMd }); toast.success("已保存到项目档案"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : "保存失败"; toast.error(msg); } finally { setSaving(false); } }; const handleDraftQuote = async () => { if (projectId == null) { toast.error("请先进行 AI 解析并保存"); return; } setGeneratingQuote(true); try { const res = await projectsApi.generateQuote(projectId); setLastQuote(res); toast.success("报价单已生成"); downloadFile(res.excel_path, `quote_project_${projectId}.xlsx`); } catch (e: unknown) { const msg = e instanceof Error ? e.message : "生成报价失败"; toast.error(msg); } finally { setGeneratingQuote(false); } }; const handleExportPdf = async () => { if (lastQuote?.pdf_path) { downloadFile(lastQuote.pdf_path, `quote_project_${projectId}.pdf`); toast.success("PDF 已下载"); return; } if (projectId == null) { toast.error("请先进行 AI 解析"); return; } setGeneratingQuote(true); try { const res = await projectsApi.generateQuote(projectId); setLastQuote(res); await downloadFileAsBlob(res.pdf_path, `quote_project_${projectId}.pdf`); toast.success("PDF 已下载"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : "生成 PDF 失败"; toast.error(msg); } finally { setGeneratingQuote(false); } }; return (
{/* Left Panel — 40% */}
原始需求