From 8a56ac5a893d34f97c619623cc0a474d812672ad Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 28 Sep 2026 22:11:03 +0800 Subject: [PATCH] =?UTF-8?q?AI=20=E7=94=9F=E6=88=90:=20=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=BC=80=E7=AE=B1=E5=8D=B3=E7=94=A8=E7=9A=84=20AI=20=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E8=81=9A=E5=90=88=E7=AB=99=EF=BC=8C=E5=86=85=E7=BD=AE?= =?UTF-8?q?=E5=A4=9A=E7=A7=8D=E7=83=AD=E9=97=A8=20AI=20=E5=B0=8F=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=EF=BC=88=E6=96=87=E6=9C=AC=E6=94=B9=E5=86=99=E3=80=81?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=A7=A3=E9=87=8A=E3=80=81=E6=96=87=E6=A1=88?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=80=81=E7=BF=BB=E8=AF=91=E3=80=81=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E7=AD=89=EF=BC=89=EF=BC=8C=E6=94=AF=E6=8C=81=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E9=83=A8=E7=BD=B2=EF=BC=8C=E7=95=8C=E9=9D=A2=E7=8E=B0?= =?UTF-8?q?=E4=BB=A3=E7=BE=8E=E8=A7=82=EF=BC=8C=E8=B0=81=E7=94=A8=E8=B0=81?= =?UTF-8?q?=E7=81=AB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 83 +++++++++++++++++ css/style.css | 237 +++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 73 +++++++++++++++ js/app.js | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++ js/tools.js | 83 +++++++++++++++++ 5 files changed, 717 insertions(+) create mode 100644 README.md create mode 100644 css/style.css create mode 100644 index.html create mode 100644 js/app.js create mode 100644 js/tools.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..70e328f --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# AI Tool Station 🚀 + +一个开箱即用的 **AI 工具聚合站**,内置多种热门 AI 小工具,界面现代美观,一键部署,谁用谁火。 + +## ✨ 功能特性 + +- 🎨 现代化 UI,响应式设计,移动端友好 +- 🧰 内置多个 AI 小工具: + - 文本改写 + - 代码解释 + - 文案生成 + - 中英翻译 + - 内容总结 +- ⚡ 纯前端实现,零依赖,一键部署到任意静态托管 +- 🔌 支持接入任意 OpenAI 兼容 API(可选) +- 🧪 内置本地模拟模式,无需 API Key 也能体验 + +## 📁 目录结构 + +``` +ai-tool-station/ +├── index.html # 主页面 +├── css/ +│ └── style.css # 样式文件 +├── js/ +│ ├── app.js # 主逻辑 +│ └── tools.js # AI 工具定义与模拟实现 +├── README.md +``` + +## 🚀 一键部署 + +### 方式一:直接打开 + +下载后双击 `index.html` 即可在浏览器中使用。 + +### 方式二:Vercel / Netlify + +1. 将本项目推送到 GitHub +2. 在 Vercel / Netlify 中导入仓库 +3. 无需构建命令,直接部署 + +### 方式三:GitHub Pages + +1. 推送到 GitHub 仓库 +2. Settings → Pages → 选择 `main` 分支根目录 +3. 访问 `https://<你的用户名>.github.io/<仓库名>/` + +### 方式四:本地服务 + +```bash +python3 -m http.server 8080 +# 打开 http://localhost:8080 +``` + +## 🔑 接入真实 AI API(可选) + +在页面右上角点击 ⚙️ 设置,填入: + +- API Base URL(例如 `https://api.openai.com/v1`) +- API Key +- 模型名称(例如 `gpt-4o-mini`) + +保存后所有工具将调用真实 API。留空则使用内置模拟模式。 + +## 🧩 添加新工具 + +编辑 `js/tools.js`,在 `TOOLS` 数组中添加一个新的工具对象即可: + +```js +{ + id: 'my-tool', + name: '我的工具', + icon: '✨', + desc: '工具描述', + placeholder: '请输入内容...', + prompt: (input) => `请处理以下内容:\n${input}` +} +``` + +## 📄 License + +MIT diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..d359aea --- /dev/null +++ b/css/style.css @@ -0,0 +1,237 @@ +:root { + --bg: #0b0d17; + --bg-2: #12142400; + --card: #151827; + --card-hover: #1c2033; + --border: #262a3d; + --text: #e8eaf2; + --muted: #8a90a8; + --primary: #6d5efc; + --primary-2: #22d3ee; + --accent: #ff5c8a; + --radius: 14px; + --shadow: 0 10px 30px rgba(0, 0, 0, 0.35); +} + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + background: radial-gradient(1200px 600px at 20% -10%, #1a1f3a 0%, transparent 60%), + radial-gradient(900px 500px at 100% 0%, #1e2a4a 0%, transparent 55%), + var(--bg); + color: var(--text); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", + "Hiragino Sans GB", "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif; + min-height: 100vh; + -webkit-font-smoothing: antialiased; +} + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 24px; + border-bottom: 1px solid var(--border); + backdrop-filter: blur(10px); + position: sticky; + top: 0; + z-index: 10; + background: rgba(11, 13, 23, 0.7); +} + +.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; } +.logo { font-size: 22px; } +.brand-name { letter-spacing: 0.3px; } + +.container { + max-width: 1080px; + margin: 0 auto; + padding: 40px 20px 80px; +} + +.hero { text-align: center; margin-bottom: 40px; } +.hero h1 { + font-size: clamp(28px, 5vw, 44px); + margin: 0 0 12px; + font-weight: 800; + letter-spacing: -0.5px; +} +.hero .grad { + background: linear-gradient(90deg, var(--primary), var(--primary-2)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} +.hero p { color: var(--muted); margin: 0; font-size: 16px; } + +.tools-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 16px; +} + +.tool-card { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 20px; + cursor: pointer; + transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease; + text-align: left; + color: var(--text); + font: inherit; +} +.tool-card:hover { + background: var(--card-hover); + border-color: #3a3f5c; + transform: translateY(-3px); +} +.tool-icon { font-size: 26px; display: block; margin-bottom: 10px; } +.tool-name { font-size: 16px; font-weight: 700; margin-bottom: 6px; } +.tool-desc { font-size: 13px; color: var(--muted); line-height: 1.5; } + +.panel { + margin-top: 32px; + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; + box-shadow: var(--shadow); +} +.panel-head { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; +} +.panel-head h2 { margin: 0; font-size: 20px; } +.panel-desc { color: var(--muted); margin: 0 0 16px; font-size: 14px; } + +.input-box { + width: 100%; + min-height: 140px; + resize: vertical; + background: #0f1220; + color: var(--text); + border: 1px solid var(--border); + border-radius: 10px; + padding: 14px; + font-size: 14px; + line-height: 1.6; + font-family: inherit; + outline: none; + transition: border-color 0.15s ease; +} +.input-box:focus { border-color: var(--primary); } + +.actions { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin: 14px 0; +} + +.primary-btn, .ghost-btn { + border-radius: 10px; + padding: 10px 16px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s ease; + border: 1px solid transparent; + font-family: inherit; +} + +.primary-btn { + background: linear-gradient(90deg, var(--primary), #8b7dff); + color: white; + box-shadow: 0 6px 18px rgba(109, 94, 252, 0.35); +} +.primary-btn:hover { filter: brightness(1.08); } +.primary-btn:disabled { opacity: 0.6; cursor: not-allowed; } + +.ghost-btn { + background: transparent; + color: var(--text); + border-color: var(--border); +} +.ghost-btn:hover { background: #1b1f33; border-color: #3a3f5c; } + +.output { + background: #0f1220; + border: 1px dashed var(--border); + border-radius: 10px; + padding: 16px; + min-height: 80px; + font-size: 14px; + line-height: 1.7; + white-space: pre-wrap; + word-break: break-word; + color: #d6d9e6; +} + +.footer { + text-align: center; + color: var(--muted); + font-size: 13px; + padding: 24px; + border-top: 1px solid var(--border); +} + +/* Modal */ +.modal { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + z-index: 100; +} +.modal-card { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; + width: 100%; + max-width: 420px; + box-shadow: var(--shadow); +} +.modal-card h3 { margin: 0 0 6px; } +.hint { color: var(--muted); font-size: 13px; margin: 0 0 16px; } +.modal-card label { + display: block; + font-size: 13px; + color: var(--muted); + margin-bottom: 12px; +} +.modal-card input { + display: block; + width: 100%; + margin-top: 6px; + background: #0f1220; + color: var(--text); + border: 1px solid var(--border); + border-radius: 8px; + padding: 10px 12px; + font-size: 14px; + outline: none; + font-family: inherit; +} +.modal-card input:focus { border-color: var(--primary); } +.modal-actions { + display: flex; + gap: 10px; + justify-content: flex-end; + margin-top: 8px; +} + +@media (max-width: 520px) { + .container { padding: 24px 14px 60px; } + .panel { padding: 18px; } + .actions { flex-direction: column; } + .primary-btn, .ghost-btn { width: 100%; text-align: center; } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..5a2ee33 --- /dev/null +++ b/index.html @@ -0,0 +1,73 @@ + + + + + + AI Tool Station · 一键部署的 AI 工具站 + + + + +
+
+ + AI Tool Station +
+ +
+ +
+
+

一键部署的 AI 工具站

+

内置多个热门 AI 小工具,开箱即用,谁用谁火 🔥

+
+ +
+ +
+ + +
+ + + + + + + + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..4000307 --- /dev/null +++ b/js/app.js @@ -0,0 +1,241 @@ +/* + * 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(); +})(); diff --git a/js/tools.js b/js/tools.js new file mode 100644 index 0000000..07d3da6 --- /dev/null +++ b/js/tools.js @@ -0,0 +1,83 @@ +/* + * tools.js + * AI 工具定义与本地模拟实现。 + * 添加新工具:往 TOOLS 数组里追加一个对象即可。 + */ + +// 工具列表:id / name / icon / desc / placeholder / prompt +const TOOLS = [ + { + id: 'rewrite', + name: '文本改写', + icon: '✍️', + desc: '把一段文字改写得更通顺、更专业。', + placeholder: '粘贴需要改写的文字...', + prompt: (input) => `请将以下内容改写得更通顺、更专业,保持原意:\n\n${input}` + }, + { + id: 'explain-code', + name: '代码解释', + icon: '🧑‍💻', + desc: '解释一段代码的作用和逻辑。', + placeholder: '粘贴一段代码...', + prompt: (input) => `请用简洁的中文解释以下代码的作用、关键逻辑和可能的坑:\n\n${input}` + }, + { + id: 'copywriting', + name: '文案生成', + icon: '📣', + desc: '根据主题生成吸睛的营销文案。', + placeholder: '输入主题,例如:一款便携咖啡机', + prompt: (input) => `请为主题「${input}」写 3 条不同风格的营销文案,每条不超过 50 字。` + }, + { + id: 'translate', + name: '中英翻译', + icon: '🌐', + desc: '自动识别中英文并互译。', + placeholder: '输入中文或英文...', + prompt: (input) => `请将以下内容翻译成另一种语言(中文↔英文),只输出译文:\n\n${input}` + }, + { + id: 'summary', + name: '内容总结', + icon: '📝', + desc: '把长文提炼成要点。', + placeholder: '粘贴一段长文...', + prompt: (input) => `请将以下内容总结为 3-5 条要点,用短句输出:\n\n${input}` + }, + { + id: 'name-idea', + name: '起名灵感', + icon: '💡', + desc: '为产品 / 项目 / 频道起名。', + placeholder: '输入你的产品描述或关键词...', + prompt: (input) => `请根据以下描述,给出 10 个有创意的名字,并附一句话理由:\n\n${input}` + } +]; + +/** + * 本地模拟实现:在没有配置 API 时使用。 + * 根据工具 id 返回一段合理的假数据,让页面可以演示。 + */ +function mockRun(toolId, input) { + const text = (input || '').trim(); + if (!text) return '⚠️ 请先输入内容。'; + + switch (toolId) { + case 'rewrite': + return `【改写结果】\n${text.replace(/。/g, '。\n')}\n\n(以上为示例改写,接入真实 API 后效果更佳)`; + case 'explain-code': + return `【代码解释】\n这段代码主要做了以下几件事:\n1. 定义并初始化相关变量;\n2. 对输入进行处理与判断;\n3. 输出最终结果。\n\n原始代码长度:${text.length} 字符。`; + case 'copywriting': + return `【文案示例】\n1. 主题「${text}」,让生活更简单。\n2. 遇见「${text}」,遇见更好的自己。\n3. 「${text}」,你值得拥有。`; + case 'translate': + return `【翻译示例】\n${text}\n\n(此处为模拟翻译,接入 API 后自动中英互译)`; + case 'summary': + return `【总结要点】\n• 内容长度:${text.length} 字符\n• 核心主题:与「${text.slice(0, 12)}...」相关\n• 建议:接入真实 API 获取高质量总结`; + case 'name-idea': + return `【起名灵感】\n1. Nova${text.slice(0, 4)}\n2. Lumo\n3. 星火\n4. Echo\n5. 极光\n6. Pulse\n7. 山海\n8. Vertex\n9. 微光\n10. Orbit`; + default: + return '(模拟结果)' + text; + } +}