"use client"; import { useState, useEffect, useCallback, useMemo } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { listProjects, projectsApi, type ProjectRead, } from "@/lib/api/client"; import { Copy, Search, FileText, Eye, Pencil, Loader2 } from "lucide-react"; import { toast } from "sonner"; import ReactMarkdown from "react-markdown"; function parseTags(tagsStr: string | null | undefined): string[] { if (!tagsStr?.trim()) return []; return tagsStr .split(",") .map((t) => t.trim()) .filter(Boolean); } export function HistoricalReferences() { const [projects, setProjects] = useState([]); const [search, setSearch] = useState(""); const [selectedTag, setSelectedTag] = useState(""); const [loading, setLoading] = useState(true); const [previewProject, setPreviewProject] = useState(null); const [editProject, setEditProject] = useState(null); const [editRaw, setEditRaw] = useState(""); const [editMd, setEditMd] = useState(""); const [saving, setSaving] = useState(false); const load = useCallback(async () => { setLoading(true); try { const data = await listProjects( selectedTag ? { customer_tag: selectedTag } : undefined ); setProjects(data); } catch (e) { toast.error("加载历史项目失败"); setProjects([]); } finally { setLoading(false); } }, [selectedTag]); useEffect(() => { load(); }, [load]); const allTags = useMemo(() => { const set = new Set(); projects.forEach((p) => parseTags(p.customer?.tags ?? null).forEach((t) => set.add(t)) ); return Array.from(set).sort(); }, [projects]); const filtered = useMemo(() => { let list = projects; if (search.trim()) { const q = search.toLowerCase(); list = list.filter( (p) => p.raw_requirement.toLowerCase().includes(q) || (p.ai_solution_md || "").toLowerCase().includes(q) ); } return list.slice(0, 20); }, [projects, search]); const copySnippet = (text: string, label: string) => { if (!text) return; navigator.clipboard.writeText(text); toast.success(`已复制 ${label}`); }; const openPreview = (p: ProjectRead) => setPreviewProject(p); const openEdit = (p: ProjectRead) => { setEditProject(p); setEditRaw(p.raw_requirement); setEditMd(p.ai_solution_md || ""); }; const handleSaveEdit = async () => { if (!editProject) return; setSaving(true); try { await projectsApi.update(editProject.id, { raw_requirement: editRaw, ai_solution_md: editMd || null, }); toast.success("已保存"); setEditProject(null); load(); } catch (e) { toast.error("保存失败"); } finally { setSaving(false); } }; return (

历史参考

setSearch(e.target.value)} className="h-8 pl-7 text-xs" />
{loading ? (

加载中...

) : filtered.length === 0 ? (

暂无项目

) : ( filtered.map((p) => (

项目 #{p.id} {p.customer?.tags && ( {parseTags(p.customer.tags).slice(0, 2).join(", ")} )}

{p.raw_requirement.slice(0, 80)}…

)) )}
{/* 预览弹窗 */} setPreviewProject(null)}> 项目 #{previewProject?.id} {previewProject?.customer?.name && ( {previewProject.customer.name} )} 原始需求 方案
                {previewProject?.raw_requirement ?? ""}
              
{previewProject?.ai_solution_md ? ( {previewProject.ai_solution_md} ) : (

暂无方案

)}
{/* 二次编辑弹窗 */} !saving && setEditProject(null)}> 编辑 项目 #{editProject?.id}