Initial commit: 国土空间规划课程智能体 v1.0
单容器 Docker 架构的国土空间规划课程智能问答系统,集成 FastAPI 后端与 Next.js 前端。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Download, FileText, FileJson, File } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useChatStore } from "@/store/chat";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface ExportDialogProps {
|
||||
sessionId: number;
|
||||
sessionTitle: string;
|
||||
children: React.ReactNode;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export default function ExportDialog({ sessionId, sessionTitle, children, onClose }: ExportDialogProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [format, setFormat] = useState("json");
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
const { exportSession } = useChatStore();
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
const handleExport = async () => {
|
||||
setIsExporting(true);
|
||||
try {
|
||||
await exportSession(sessionId, format);
|
||||
handleClose();
|
||||
toast.success("导出成功");
|
||||
} catch (error) {
|
||||
toast.error("导出失败");
|
||||
} finally {
|
||||
setIsExporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatOptions = [
|
||||
{
|
||||
value: "json",
|
||||
label: "JSON 格式",
|
||||
description: "完整的结构化数据,包含所有元数据",
|
||||
icon: FileJson,
|
||||
},
|
||||
{
|
||||
value: "markdown",
|
||||
label: "Markdown 格式",
|
||||
description: "可读性好的文本格式,适合分享",
|
||||
icon: FileText,
|
||||
},
|
||||
{
|
||||
value: "pdf",
|
||||
label: "PDF 格式",
|
||||
description: "适合打印和正式文档",
|
||||
icon: File,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>导出会话</DialogTitle>
|
||||
<DialogDescription>
|
||||
选择导出格式来下载 "{sessionTitle}" 会话
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<RadioGroup value={format} onValueChange={setFormat}>
|
||||
{formatOptions.map((option) => {
|
||||
const Icon = option.icon;
|
||||
return (
|
||||
<div key={option.value} className="flex items-start space-x-3">
|
||||
<RadioGroupItem value={option.value} id={option.value} />
|
||||
<div className="flex-1">
|
||||
<Label htmlFor={option.value} className="flex items-center gap-2 cursor-pointer">
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="font-medium">{option.label}</span>
|
||||
</Label>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
{option.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleExport} disabled={isExporting}>
|
||||
{isExporting ? (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
|
||||
导出中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user