實作完整分享、刪除、下載報告功能
This commit is contained in:
@@ -7,8 +7,10 @@ import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
|
||||
import { FileText, Calendar, Search, Eye, Download, Trash2, Loader2 } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useToast } from "@/hooks/use-toast"
|
||||
|
||||
// 歷史記錄數據類型
|
||||
interface HistoryItem {
|
||||
@@ -46,6 +48,8 @@ export default function HistoryPage() {
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [deletingId, setDeletingId] = useState<string | null>(null)
|
||||
const { toast } = useToast()
|
||||
|
||||
// 載入歷史記錄數據
|
||||
useEffect(() => {
|
||||
@@ -111,6 +115,104 @@ export default function HistoryPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 下載評審報告
|
||||
const handleDownload = async (evaluationId: number, projectTitle: string) => {
|
||||
try {
|
||||
toast({
|
||||
title: "報告下載中",
|
||||
description: "評審報告 PDF 正在生成,請稍候...",
|
||||
});
|
||||
|
||||
// 調用下載 API
|
||||
const response = await fetch(`/api/evaluation/${evaluationId}/download`);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || '下載失敗');
|
||||
}
|
||||
|
||||
// 獲取 PDF Blob
|
||||
const pdfBlob = await response.blob();
|
||||
|
||||
// 創建下載連結
|
||||
const url = window.URL.createObjectURL(pdfBlob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
|
||||
// 從 Content-Disposition 標頭獲取檔案名稱,或使用預設名稱
|
||||
const contentDisposition = response.headers.get('Content-Disposition');
|
||||
let fileName = `評審報告_${projectTitle}_${new Date().toISOString().split('T')[0]}.pdf`;
|
||||
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename="(.+)"/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
// 顯示成功提示
|
||||
toast({
|
||||
title: "下載完成",
|
||||
description: "評審報告已成功下載",
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('下載失敗:', error);
|
||||
toast({
|
||||
title: "下載失敗",
|
||||
description: error instanceof Error ? error.message : '下載過程中發生錯誤',
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 刪除評審報告
|
||||
const handleDelete = async (evaluationId: number, projectTitle: string) => {
|
||||
try {
|
||||
setDeletingId(evaluationId.toString());
|
||||
|
||||
const response = await fetch(`/api/evaluation/${evaluationId}/delete`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// 從本地狀態中移除已刪除的項目
|
||||
setHistoryData(prev => prev.filter(item => item.evaluation_id !== evaluationId));
|
||||
|
||||
// 重新載入統計數據
|
||||
const statsResponse = await fetch('/api/history/stats');
|
||||
const statsResult = await statsResponse.json();
|
||||
if (statsResult.success) {
|
||||
setStatsData(statsResult.data);
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "刪除成功",
|
||||
description: `評審報告「${projectTitle}」已成功刪除`,
|
||||
});
|
||||
} else {
|
||||
throw new Error(result.error || '刪除失敗');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('刪除失敗:', error);
|
||||
toast({
|
||||
title: "刪除失敗",
|
||||
description: error instanceof Error ? error.message : '刪除過程中發生錯誤',
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setDeletingId(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Sidebar />
|
||||
@@ -264,7 +366,11 @@ export default function HistoryPage() {
|
||||
<Eye className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleDownload(item.evaluation_id!, item.title)}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
</>
|
||||
@@ -273,9 +379,48 @@ export default function HistoryPage() {
|
||||
處理中...
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="sm" className="text-destructive hover:text-destructive">
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
disabled={deletingId === item.evaluation_id?.toString()}
|
||||
>
|
||||
{deletingId === item.evaluation_id?.toString() ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Trash2 className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>確認刪除</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
您確定要刪除評審報告「{item.title}」嗎?此操作將永久刪除:
|
||||
<br />
|
||||
• 評審結果和分數
|
||||
<br />
|
||||
• 專案文件和上傳的檔案
|
||||
<br />
|
||||
• 所有相關的評語和建議
|
||||
<br />
|
||||
<br />
|
||||
<strong>此操作無法復原!</strong>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => item.evaluation_id && handleDelete(item.evaluation_id, item.title)}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
確認刪除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user