/* * app.js * 主逻辑:渲染工具列表、处理交互、调用 API 或本地模拟。 */ (function () { 'use strict'; // ---------- DOM ---------- const toolsGrid = document.getElementById('toolsGrid'); const panel = document.getElementById('panel'); const panelTitle = document.getElementById('panelTitle'); const panelDesc = document.getElementById('panelDesc'); const inputBox = document.getElementById('inputBox'); const outputBox = document.getElementById('outputBox'); const runBtn = document.getElementById('runBtn'); const copyBtn = document.getElementById('copyBtn'); const clearBtn = document.getElementById('clearBtn'); const closePanel = document.getElementById('closePanel'); const settingsBtn = document.getElementById('settingsBtn'); const settingsModal = document.getElementById('settingsModal'); const apiBase = document.getElementById('apiBase'); const apiKey = document.getElementById('apiKey'); const apiModel = document.getElementById('apiModel'); const saveSettings = document.getElementById('saveSettings'); const cancelSettings = document.getElementById('cancelSettings'); const yearEl = document.getElementById('year'); // ---------- 状态 ---------- let currentTool = null; const SETTINGS_KEY = 'ai-tool-station-settings'; // ---------- 初始化 ---------- function init() { yearEl.textContent = new Date().getFullYear(); renderTools(); loadSettings(); bindEvents(); } // 渲染工具卡片 function renderTools() { toolsGrid.innerHTML = ''; TOOLS.forEach((tool) => { const card = document.createElement('button'); card.className = 'tool-card'; card.type = 'button'; card.innerHTML = ` ${tool.icon}
${escapeHtml(tool.name)}
${escapeHtml(tool.desc)}
`; card.addEventListener('click', () => openTool(tool)); toolsGrid.appendChild(card); }); } // 打开某个工具 function openTool(tool) { currentTool = tool; panelTitle.textContent = `${tool.icon} ${tool.name}`; panelDesc.textContent = tool.desc; inputBox.placeholder = tool.placeholder || '请输入内容...'; inputBox.value = ''; outputBox.textContent = ''; panel.hidden = false; panel.scrollIntoView({ behavior: 'smooth', block: 'start' }); inputBox.focus(); } // 运行工具 async function run() { if (!currentTool) return; const input = inputBox.value.trim(); if (!input) { outputBox.textContent = '⚠️ 请先输入内容。'; return; } runBtn.disabled = true; runBtn.textContent = '⏳ 生成中...'; outputBox.textContent = '正在生成,请稍候...'; try { const settings = getSettings(); let result; if (settings.apiKey && settings.apiBase && settings.apiModel) { result = await callRealAPI(settings, currentTool, input); } else { // 模拟延迟,体验更真实 await sleep(400); result = mockRun(currentTool.id, input); } outputBox.textContent = result; } catch (err) { outputBox.textContent = '❌ 出错了:' + (err && err.message ? err.message : String(err)); } finally { runBtn.disabled = false; runBtn.textContent = '✨ 运行'; } } // 调用真实 OpenAI 兼容 API async function callRealAPI(settings, tool, input) { const base = settings.apiBase.replace(/\/+$/, ''); const url = base + '/chat/completions'; const body = { model: settings.apiModel, messages: [ { role: 'system', content: '你是一个专业、简洁的 AI 助手,用中文回答。' }, { role: 'user', content: tool.prompt(input) } ], temperature: 0.7 }; const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + settings.apiKey }, body: JSON.stringify(body) }); if (!resp.ok) { const t = await resp.text().catch(() => ''); throw new Error(`HTTP ${resp.status} ${t.slice(0, 200)}`); } const data = await resp.json(); const content = data && data.choices && data.choices[0] && data.choices[0].message ? data.choices[0].message.content : ''; return content || '(API 返回为空)'; } // ---------- 设置 ---------- function getSettings() { try { return JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}'); } catch (_) { return {}; } } function loadSettings() { const s = getSettings(); apiBase.value = s.apiBase || ''; apiKey.value = s.apiKey || ''; apiModel.value = s.apiModel || ''; } function saveSettingsToStorage() { const s = { apiBase: apiBase.value.trim(), apiKey: apiKey.value.trim(), apiModel: apiModel.value.trim() }; localStorage.setItem(SETTINGS_KEY, JSON.stringify(s)); } // ---------- 事件绑定 ---------- function bindEvents() { runBtn.addEventListener('click', run); copyBtn.addEventListener('click', async () => { const text = outputBox.textContent || ''; if (!text) return; try { await navigator.clipboard.writeText(text); copyBtn.textContent = '✅ 已复制'; setTimeout(() => (copyBtn.textContent = '📋 复制结果'), 1200); } catch (_) { // 降级 const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); copyBtn.textContent = '✅ 已复制'; setTimeout(() => (copyBtn.textContent = '📋 复制结果'), 1200); } }); clearBtn.addEventListener('click', () => { inputBox.value = ''; outputBox.textContent = ''; inputBox.focus(); }); closePanel.addEventListener('click', () => { panel.hidden = true; currentTool = null; }); settingsBtn.addEventListener('click', () => { loadSettings(); settingsModal.hidden = false; }); cancelSettings.addEventListener('click', () => { settingsModal.hidden = true; }); saveSettings.addEventListener('click', () => { saveSettingsToStorage(); settingsModal.hidden = true; }); // 点击遮罩关闭 settingsModal.addEventListener('click', (e) => { if (e.target === settingsModal) settingsModal.hidden = true; }); // 快捷键:Ctrl/Cmd + Enter 运行 inputBox.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); run(); } }); } // ---------- 工具函数 ---------- function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } function escapeHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } // 启动 init(); })();