38865b0f7d
- Auto-create system KBs from data/knowledge_base/ subdirectories on startup - Upgrade existing user KBs to system KBs when matching directory found - Admin can upload to system KBs (saved to knowledge_base dir) - Regular users see only their own KBs on knowledge page - Fix API permission checks for system KBs (list, detail, documents, update, delete) - Add is_superuser to UserResponse and frontend User type - Fix sidebar session menu visibility for long titles Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Download, FileText, FileJson } 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,
|
|
},
|
|
];
|
|
|
|
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>
|
|
);
|
|
}
|