diff --git a/app/competition/page.tsx b/app/competition/page.tsx index f454025..b0421f6 100644 --- a/app/competition/page.tsx +++ b/app/competition/page.tsx @@ -152,8 +152,12 @@ export default function CompetitionPage() { 返回主頁
-
- +
+ 強茂集團 AI 展示平台

競賽專區

diff --git a/app/layout.tsx b/app/layout.tsx index 57a9bf9..527f7c3 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -15,6 +15,13 @@ export default function RootLayout({ }) { return ( + + + + + + + @@ -29,5 +36,15 @@ export default function RootLayout({ } export const metadata = { - generator: 'v0.dev' - }; + title: '強茂集團 AI 展示平台', + description: '企業內部 AI 應用展示與競賽管理系統', + generator: 'v0.dev', + icons: { + icon: [ + { url: '/favicon.ico', sizes: 'any' }, + { url: '/ai-cloud.png', type: 'image/png' } + ], + shortcut: '/favicon.ico', + apple: '/ai-cloud.png', + }, +}; diff --git a/app/page.tsx b/app/page.tsx index 704298a..a73724c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -479,8 +479,12 @@ export default function AIShowcasePlatform() {
setShowCompetition(false)}> -
- +
+ 強茂集團 AI 展示平台

強茂集團 AI 展示平台

diff --git a/app/register/page.tsx b/app/register/page.tsx index d210d3f..13862fc 100644 --- a/app/register/page.tsx +++ b/app/register/page.tsx @@ -176,8 +176,12 @@ export default function RegisterPage() { {/* Header */}
-
- +
+ 強茂集團 AI 展示平台

強茂集團 AI 展示平台

diff --git a/components/admin/admin-layout.tsx b/components/admin/admin-layout.tsx index 3774d57..0454fd5 100644 --- a/components/admin/admin-layout.tsx +++ b/components/admin/admin-layout.tsx @@ -264,8 +264,12 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout {/* Logo */}
-
- +
+ 強茂集團 AI 展示平台
{sidebarOpen && (
diff --git a/components/chat-bot.tsx b/components/chat-bot.tsx index 38d540c..0be300f 100644 --- a/components/chat-bot.tsx +++ b/components/chat-bot.tsx @@ -25,8 +25,8 @@ interface Message { quickQuestions?: string[] } -const DEEPSEEK_API_KEY = process.env.NEXT_PUBLIC_DEEPSEEK_API_KEY || "sk-30cac9533e5b451fa1e277fe34a7f64b" -const DEEPSEEK_API_URL = process.env.NEXT_PUBLIC_DEEPSEEK_API_URL || "https://api.deepseek.com/v1/chat/completions" +const GEMINI_API_KEY = process.env.NEXT_PUBLIC_GEMINI_API_KEY || "AIzaSyAN3pEJr_Vn2xkCidGZAq9eQqsMVvpj8g4" +const GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent" const systemPrompt = generateSystemPrompt() @@ -77,63 +77,62 @@ export function ChatBot() { .trim() } - const callDeepSeekAPI = async (userMessage: string): Promise => { + const callGeminiAPI = async (userMessage: string): Promise => { try { // 構建對話歷史,只保留最近的幾條對話 const recentMessages = messages .filter(msg => msg.sender === "user") .slice(-5) // 只保留最近5條用戶消息 - .map(msg => ({ - role: "user" as const, - content: msg.text - })) + .map(msg => msg.text) - const response = await fetch(DEEPSEEK_API_URL, { + // 構建完整的對話內容 + const conversationHistory = recentMessages.length > 0 + ? `之前的對話:\n${recentMessages.map(msg => `用戶:${msg}`).join('\n')}\n\n` + : '' + + const fullPrompt = `${systemPrompt}\n\n${conversationHistory}用戶:${userMessage}` + + const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, { method: "POST", headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${DEEPSEEK_API_KEY}` + "Content-Type": "application/json" }, body: JSON.stringify({ - model: "deepseek-chat", - messages: [ - { - role: "system", - content: systemPrompt - }, - ...recentMessages, - { - role: "user", - content: userMessage - } - ], - max_tokens: 300, - temperature: 0.7, - stream: false + contents: [{ + parts: [{ + text: fullPrompt + }] + }], + generationConfig: { + maxOutputTokens: 300, + temperature: 0.7, + topP: 0.8, + topK: 10 + } }) }) if (!response.ok) { const errorText = await response.text() - console.error("API Error:", response.status, errorText) + console.error("Gemini API Error:", response.status, errorText) throw new Error(`API request failed: ${response.status} - ${errorText}`) } const data = await response.json() - if (!data.choices || !data.choices[0] || !data.choices[0].message) { - console.error("Invalid API response:", data) + if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) { + console.error("Invalid Gemini API response:", data) throw new Error("Invalid API response format") } - const rawResponse = data.choices[0].message.content || "抱歉,我現在無法回答您的問題,請稍後再試。" + const rawResponse = data.candidates[0].content.parts[0].text || "抱歉,我現在無法回答您的問題,請稍後再試。" return cleanResponse(rawResponse) } catch (error) { - console.error("DeepSeek API error:", error) + console.error("Gemini API error:", error) // 根據錯誤類型提供不同的錯誤信息 if (error instanceof Error) { - if (error.message.includes('401')) { + if (error.message.includes('401') || error.message.includes('403')) { return "API 密鑰無效,請聯繫管理員檢查配置。" } else if (error.message.includes('429')) { return "API 請求過於頻繁,請稍後再試。" @@ -261,7 +260,7 @@ export function ChatBot() { setIsLoading(true) try { - const aiResponse = await callDeepSeekAPI(text) + const aiResponse = await callGeminiAPI(text) const botMessage: Message = { id: (Date.now() + 1).toString(), diff --git a/contexts/competition-context.tsx b/contexts/competition-context.tsx index cd0f617..f6c8e7e 100644 --- a/contexts/competition-context.tsx +++ b/contexts/competition-context.tsx @@ -133,14 +133,20 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { const competitionsData = await competitionsResponse.json() console.log('📋 競賽API回應:', competitionsData) - if (competitionsData.success && competitionsData.data) { - // 確保每個競賽都有judges屬性 - const competitionsWithJudges = competitionsData.data.map((comp: any) => ({ - ...comp, - judges: comp.judges || [] - })) - setCompetitions(competitionsWithJudges) - console.log('✅ 競賽數據載入成功:', competitionsWithJudges.length, '個競賽') + if (competitionsData.success) { + if (competitionsData.data && competitionsData.data.length > 0) { + // 確保每個競賽都有judges屬性 + const competitionsWithJudges = competitionsData.data.map((comp: any) => ({ + ...comp, + judges: comp.judges || [] + })) + setCompetitions(competitionsWithJudges) + console.log('✅ 競賽數據載入成功:', competitionsWithJudges.length, '個競賽') + } else { + // 沒有競賽資料是正常情況,不報錯 + setCompetitions([]) + console.log('ℹ️ 暫無競賽數據') + } } else { console.error('❌ 競賽數據載入失敗:', competitionsData.message) // 設置空數組以避免undefined錯誤 @@ -152,14 +158,20 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { const currentData = await currentResponse.json() console.log('🏆 當前競賽API回應:', currentData) - if (currentData.success && currentData.data) { - // 確保當前競賽也有judges屬性 - const currentCompetitionWithJudges = { - ...currentData.data, - judges: currentData.data.judges || [] + if (currentData.success) { + if (currentData.data) { + // 確保當前競賽也有judges屬性 + const currentCompetitionWithJudges = { + ...currentData.data, + judges: currentData.data.judges || [] + } + setCurrentCompetition(currentCompetitionWithJudges) + console.log('✅ 當前競賽載入成功:', currentCompetitionWithJudges.name) + } else { + // 沒有當前競賽是正常情況,不報錯 + setCurrentCompetition(null) + console.log('ℹ️ 暫無當前競賽') } - setCurrentCompetition(currentCompetitionWithJudges) - console.log('✅ 當前競賽載入成功:', currentCompetitionWithJudges.name) } else { console.error('❌ 當前競賽載入失敗:', currentData.message) } @@ -170,9 +182,14 @@ export function CompetitionProvider({ children }: { children: ReactNode }) { const judgesData = await judgesResponse.json() console.log('評審API回應:', judgesData) - if (judgesData.success && judgesData.data) { - setJudges(judgesData.data) - console.log('✅ 評審數據載入成功:', judgesData.data.length, '個評審') + if (judgesData.success) { + if (judgesData.data && judgesData.data.length > 0) { + setJudges(judgesData.data) + console.log('✅ 評審數據載入成功:', judgesData.data.length, '個評審') + } else { + setJudges([]) + console.log('ℹ️ 暫無評審數據') + } } else { console.error('❌ 評審數據載入失敗:', judgesData.message) setJudges([]) diff --git a/env.example b/env.example index 2da76f8..a28f8d4 100644 --- a/env.example +++ b/env.example @@ -28,7 +28,12 @@ DB_CONNECTION_TIMEOUT=5000 DB_RETRY_ATTEMPTS=3 DB_RETRY_DELAY=2000 -# DeepSeek API 配置 +# ===== AI API 配置 ===== +# Gemini API 配置 (主要使用) +NEXT_PUBLIC_GEMINI_API_KEY=AIzaSyAN3pEJr_Vn2xkCidGZAq9eQqsMVvpj8g4 +NEXT_PUBLIC_GEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent + +# DeepSeek API 配置 (備用) NEXT_PUBLIC_DEEPSEEK_API_KEY=your_deepseek_api_key_here NEXT_PUBLIC_DEEPSEEK_API_URL=https://api.deepseek.com/v1/chat/completions diff --git a/lib/ai-knowledge-base.ts b/lib/ai-knowledge-base.ts index a78f9db..b899103 100644 --- a/lib/ai-knowledge-base.ts +++ b/lib/ai-knowledge-base.ts @@ -194,13 +194,13 @@ export const platformKnowledge = { // 常見問題 faq: { - "忘記密碼怎麼辦": "點擊登入頁面的「忘記密碼」連結,輸入註冊時的電子郵件,系統會發送重設密碼的連結到您的信箱。", - "如何修改個人資料": "登入後點擊右上角頭像,選擇「個人資料」,即可修改姓名、部門、頭像等信息。", - "為什麼看不到某些競賽": "可能是因為競賽尚未開始、已結束,或者您沒有參與權限。請聯繫管理員確認。", - "評分什麼時候會公布": "評分結果會在競賽結束後由管理員統一公布,請關注平台通知。", - "如何聯繫管理員": "可以通過平台內的通知系統或直接發送郵件給管理員。", - "作品提交後可以修改嗎": "作品提交後無法修改,請在提交前仔細檢查所有信息。", - "如何查看我的參賽記錄": "登入後進入「我的競賽」頁面,可以查看所有參賽記錄和狀態。" + "忘記密碼怎麼辦": "1. 點擊登入頁面「忘記密碼」\n2. 輸入註冊電子郵件\n3. 檢查信箱重設連結\n4. 按連結重設密碼", + "如何修改個人資料": "1. 登入後點擊右上角頭像\n2. 選擇「個人資料」\n3. 修改姓名、部門、頭像\n4. 保存更改", + "為什麼看不到某些競賽": "可能原因:\n• 競賽尚未開始\n• 競賽已結束\n• 沒有參與權限\n請聯繫管理員確認", + "評分什麼時候會公布": "評分公布時間:\n• 競賽結束後\n• 由管理員統一公布\n• 關注平台通知", + "如何聯繫管理員": "聯繫方式:\n• 平台內通知系統\n• 直接發送郵件\n• 查看管理員聯絡資訊", + "作品提交後可以修改嗎": "作品提交規則:\n• 提交後無法修改\n• 請提交前仔細檢查\n• 確認所有信息正確", + "如何查看我的參賽記錄": "查看步驟:\n1. 登入平台\n2. 進入「我的競賽」\n3. 查看參賽記錄和狀態" }, // 技術信息 @@ -235,13 +235,15 @@ ${Object.entries(platformKnowledge.modules.backend).map(([key, value]) => 回答指南: 1. 用友善、專業的語氣回答用戶問題 -2. 提供具體的操作步驟和實用建議 -3. 回答要簡潔明瞭,避免過長的文字 -4. 如果問題涉及具體操作,請提供詳細步驟 -5. 如果不知道答案,請誠實說明並建議聯繫管理員 -6. 不要使用任何 Markdown 格式,只使用純文字回答 -7. 不要使用 **、*、#、- 等符號 -8. 回答長度控制在 200 字以內 +2. 回答要簡潔明瞭,優先使用條列式格式 +3. 操作步驟用數字編號,每個步驟一行 +4. 重要信息用簡短要點列出 +5. 避免長段落文字,多用換行分段 +6. 如果不知道答案,請誠實說明並建議聯繫管理員 +7. 不要使用任何 Markdown 格式,只使用純文字回答 +8. 不要使用 **、*、#、- 等符號 +9. 回答長度控制在 150 字以內 +10. 優先提供實用的操作步驟和要點 常見問題快速回答: ${Object.entries(platformKnowledge.faq).map(([question, answer]) => diff --git a/public/ai-cloud.png b/public/ai-cloud.png new file mode 100644 index 0000000..e6149af Binary files /dev/null and b/public/ai-cloud.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e6149af Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..753922b Binary files /dev/null and b/public/logo.png differ