載入評審結果中...
+diff --git a/app/api/evaluate/route.ts b/app/api/evaluate/route.ts new file mode 100644 index 0000000..c622d54 --- /dev/null +++ b/app/api/evaluate/route.ts @@ -0,0 +1,139 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { GeminiService } from '@/lib/services/gemini'; +import { CriteriaItemService } from '@/lib/services/database'; + +export async function POST(request: NextRequest) { + try { + const formData = await request.formData(); + const projectTitle = formData.get('projectTitle') as string; + const projectDescription = formData.get('projectDescription') as string; + const file = formData.get('file') as File; + const websiteUrl = formData.get('websiteUrl') as string; + + console.log('🚀 開始處理評審請求...'); + console.log('📝 專案標題:', projectTitle); + console.log('📋 專案描述:', projectDescription); + console.log('📁 上傳文件:', file ? file.name : '無'); + console.log('🌐 網站連結:', websiteUrl || '無'); + + // 驗證必填欄位 + if (!projectTitle?.trim()) { + return NextResponse.json( + { success: false, error: '請填寫專案標題' }, + { status: 400 } + ); + } + + if (!file && !websiteUrl?.trim()) { + return NextResponse.json( + { success: false, error: '請上傳文件或提供網站連結' }, + { status: 400 } + ); + } + + // 獲取評分標準 + console.log('📊 載入評分標準...'); + const templates = await CriteriaItemService.getAllTemplates(); + console.log('📊 載入的模板:', templates); + + if (!templates || templates.length === 0) { + return NextResponse.json( + { success: false, error: '未找到評分標準,請先設定評分標準' }, + { status: 400 } + ); + } + + const criteria = templates[0].items || []; + console.log('📊 評分項目:', criteria); + console.log('✅ 評分標準載入完成,共', criteria.length, '個項目'); + + let content = ''; + + if (file) { + // 處理文件上傳 + console.log('📄 開始處理上傳文件...'); + + // 檢查文件類型 + const allowedTypes = [ + 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'video/mp4', + 'video/avi', + 'video/quicktime', + 'application/pdf', + 'text/plain' // 添加純文字格式用於測試 + ]; + + if (!allowedTypes.includes(file.type)) { + console.log('❌ 不支援的文件格式:', file.type); + return NextResponse.json( + { success: false, error: `不支援的文件格式: ${file.type}` }, + { status: 400 } + ); + } + + // 檢查文件大小 (100MB) + const maxSize = 100 * 1024 * 1024; + if (file.size > maxSize) { + return NextResponse.json( + { success: false, error: '文件大小超過 100MB 限制' }, + { status: 400 } + ); + } + + // 提取內容 + content = await GeminiService.extractPPTContent(file); + } else if (websiteUrl) { + // 處理網站連結 + console.log('🌐 開始處理網站連結...'); + content = `網站內容分析: ${websiteUrl} + +由於目前是簡化版本,無法直接抓取網站內容。 +實際應用中應該使用網頁抓取技術來獲取網站內容進行分析。 + +專案描述: ${projectDescription || '無'}`; + } + + // 使用 Gemini AI 進行評分 + console.log('🤖 開始 AI 評分...'); + const evaluation = await GeminiService.analyzePresentation( + content, + projectTitle, + projectDescription || '', + criteria + ); + + // 儲存評審結果到資料庫(可選) + // TODO: 實作結果儲存功能 + + console.log('🎉 評審完成!'); + console.log('📊 最終評分結果:'); + console.log(' 總分:', evaluation.totalScore, '/', evaluation.maxTotalScore); + console.log(' 各項目評分:'); + evaluation.results.forEach(result => { + console.log(` - ${result.criteriaName}: ${result.score}/${result.maxScore}`); + }); + + return NextResponse.json({ + success: true, + data: { + evaluation, + projectTitle, + projectDescription, + fileInfo: file ? { + name: file.name, + size: file.size, + type: file.type + } : null, + websiteUrl: websiteUrl || null + } + }); + + } catch (error) { + console.error('❌ 評審處理失敗:', error); + return NextResponse.json( + { success: false, error: '評審處理失敗,請稍後再試' }, + { status: 500 } + ); + } +} diff --git a/app/results/page.tsx b/app/results/page.tsx index 5c78e20..89c2af7 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useState } from "react" +import { useState, useEffect } from "react" import { Sidebar } from "@/components/sidebar" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" @@ -88,31 +88,105 @@ const mockResults = { ], } -const chartData = mockResults.criteria.map((item) => ({ - name: item.name, - score: item.score, - maxScore: item.maxScore, - percentage: (item.score / item.maxScore) * 100, -})) - -const pieData = mockResults.criteria.map((item) => ({ - name: item.name, - value: item.weightedScore, - weight: item.weight, -})) - -const radarData = mockResults.criteria.map((item) => ({ - subject: item.name, - score: item.score, - fullMark: item.maxScore, -})) +// 圖表數據將在組件內部動態生成 const COLORS = ["#0891b2", "#6366f1", "#f59e0b", "#dc2626", "#10b981"] export default function ResultsPage() { const [activeTab, setActiveTab] = useState("overview") + const [evaluationData, setEvaluationData] = useState(null) + const [isLoading, setIsLoading] = useState(true) const { toast } = useToast() + useEffect(() => { + // 從 localStorage 獲取評審結果 + const storedData = localStorage.getItem('evaluationResult') + if (storedData) { + try { + const data = JSON.parse(storedData) + setEvaluationData(data) + } catch (error) { + console.error('解析評審結果失敗:', error) + toast({ + title: "數據錯誤", + description: "無法載入評審結果,請重新進行評審", + variant: "destructive", + }) + } + } else { + toast({ + title: "無評審結果", + description: "請先進行評審以查看結果", + variant: "destructive", + }) + } + setIsLoading(false) + }, [toast]) + + // 如果正在載入,顯示載入狀態 + if (isLoading) { + return ( +
載入評審結果中...
+沒有找到評審結果
+ +- {mockResults.projectTitle} - 分析完成於 {mockResults.analysisDate} + {safeResults.projectTitle} - 分析完成於 {safeResults.analysisDate}
- 內容組織有序,各部分銜接自然,建議在未來的作品中繼續保持這種清晰的邏輯架構。 -
-- 資訊準確且豐富,專業度高,這是您的核心優勢,請繼續發揮。 -
-+ {strength.description} +
+當前創新性得分較低,建議:
-視覺呈現有改進空間,建議:
-{improvement.description}
+{action.description}
+重新設計視覺版面,減少文字密度,增加圖表元素
-- 研究行業最新趨勢,為內容增加創新元素和獨特觀點 -
-建立個人風格的簡報模板,形成獨特的表達方式
-AI 正在深度分析您的內容,請稍候
+ +