新增 AI 解析、評分結果

This commit is contained in:
2025-09-22 17:35:22 +08:00
parent 9d4c586ad3
commit 1d6b1b61b7
16 changed files with 1954 additions and 404 deletions

View File

@@ -1,6 +1,6 @@
"use client"
import { useState, useCallback } from "react"
import { useState, useCallback, useEffect } from "react"
import { Sidebar } from "@/components/sidebar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
@@ -22,6 +22,7 @@ interface UploadedFile {
status: "uploading" | "completed" | "error"
progress: number
url?: string
file?: File // 儲存原始文件對象
}
export default function UploadPage() {
@@ -30,24 +31,52 @@ export default function UploadPage() {
const [projectTitle, setProjectTitle] = useState("")
const [projectDescription, setProjectDescription] = useState("")
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [analysisProgress, setAnalysisProgress] = useState(0)
const { toast } = useToast()
// 動態進度條效果
useEffect(() => {
let progressInterval: NodeJS.Timeout | null = null
if (isAnalyzing) {
setAnalysisProgress(0)
progressInterval = setInterval(() => {
setAnalysisProgress((prev) => {
if (prev >= 90) {
// 在90%時停止,等待實際完成
return prev
}
// 模擬不規則的進度增長
const increment = Math.random() * 8 + 2 // 2-10之間的隨機增量
return Math.min(prev + increment, 90)
})
}, 500) // 每500ms更新一次
} else {
setAnalysisProgress(0)
}
return () => {
if (progressInterval) {
clearInterval(progressInterval)
}
}
}, [isAnalyzing])
const onDrop = useCallback((acceptedFiles: File[]) => {
const newFiles: UploadedFile[] = acceptedFiles.map((file) => ({
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
name: file.name,
size: file.size,
type: file.type,
status: "uploading",
progress: 0,
status: "completed", // 直接標記為完成,因為我們會在評審時處理文件
progress: 100,
file: file, // 儲存原始文件對象
}))
setFiles((prev) => [...prev, ...newFiles])
// 模擬上傳進度
newFiles.forEach((file) => {
simulateUpload(file.id)
})
console.log('📁 文件已準備就緒:', newFiles.map(f => f.name).join(', '))
}, [])
const { getRootProps, getInputProps, isDragActive } = useDropzone({
@@ -61,27 +90,6 @@ export default function UploadPage() {
maxSize: 100 * 1024 * 1024, // 100MB
})
const simulateUpload = (fileId: string) => {
const interval = setInterval(() => {
setFiles((prev) =>
prev.map((file) => {
if (file.id === fileId) {
const newProgress = Math.min(file.progress + Math.random() * 30, 100)
const status = newProgress === 100 ? "completed" : "uploading"
return { ...file, progress: newProgress, status }
}
return file
}),
)
}, 500)
setTimeout(() => {
clearInterval(interval)
setFiles((prev) =>
prev.map((file) => (file.id === fileId ? { ...file, progress: 100, status: "completed" } : file)),
)
}, 3000)
}
const removeFile = (fileId: string) => {
setFiles((prev) => prev.filter((file) => file.id !== fileId))
@@ -105,7 +113,7 @@ export default function UploadPage() {
return <FileText className="h-8 w-8 text-gray-500" />
}
const startAnalysis = () => {
const startAnalysis = async () => {
if (files.length === 0 && !websiteUrl.trim()) {
toast({
title: "請上傳文件或提供網站連結",
@@ -126,16 +134,76 @@ export default function UploadPage() {
setIsAnalyzing(true)
// 模擬分析過程
setTimeout(() => {
setIsAnalyzing(false)
toast({
title: "分析完成",
description: "評審結果已生成,請查看結果頁面",
try {
console.log('🚀 開始 AI 評審流程...')
console.log('📝 專案標題:', projectTitle)
console.log('📋 專案描述:', projectDescription)
console.log('📁 上傳文件數量:', files.length)
console.log('🌐 網站連結:', websiteUrl)
// 準備表單數據
const formData = new FormData()
formData.append('projectTitle', projectTitle)
formData.append('projectDescription', projectDescription)
if (files.length > 0) {
// 只處理第一個文件(可以後續擴展支援多文件)
const firstFile = files[0]
if (firstFile.file) {
formData.append('file', firstFile.file)
console.log('📄 處理文件:', firstFile.name, '大小:', firstFile.size)
} else {
throw new Error('文件對象遺失,請重新上傳')
}
}
if (websiteUrl.trim()) {
formData.append('websiteUrl', websiteUrl)
console.log('🌐 處理網站連結:', websiteUrl)
}
// 發送評審請求
console.log('📤 發送評審請求到 API...')
const response = await fetch('/api/evaluate', {
method: 'POST',
body: formData,
})
// 這裡會導向到結果頁面
window.location.href = "/results"
}, 5000)
const result = await response.json()
if (result.success) {
console.log('✅ AI 評審完成!')
console.log('📊 評審結果:', result.data)
// 設置進度為100%
setAnalysisProgress(100)
// 等待一下讓用戶看到100%的進度
await new Promise(resolve => setTimeout(resolve, 500))
toast({
title: "評審完成",
description: `總分: ${result.data.evaluation.totalScore}/${result.data.evaluation.maxTotalScore}`,
})
// 儲存結果到 localStorage 以便結果頁面使用
localStorage.setItem('evaluationResult', JSON.stringify(result.data))
// 導向到結果頁面
window.location.href = "/results"
} else {
throw new Error(result.error || '評審失敗')
}
} catch (error) {
console.error('❌ AI 評審失敗:', error)
toast({
title: "評審失敗",
description: error instanceof Error ? error.message : "請稍後再試",
variant: "destructive",
})
} finally {
setIsAnalyzing(false)
}
}
return (
@@ -332,8 +400,11 @@ export default function UploadPage() {
<span>...</span>
<span> 3-5 </span>
</div>
<Progress value={33} className="h-2" />
<p className="text-xs text-muted-foreground mt-2">AI </p>
<Progress value={analysisProgress} className="h-2" />
<div className="flex items-center justify-between text-xs text-muted-foreground mt-2">
<span>AI </span>
<span>{Math.round(analysisProgress)}%</span>
</div>
</div>
)}
</div>