AI 生成: 一个开箱即用的 AI 工具聚合站,内置多种热门 AI 小工具(文本改写、代码解释、文案生成、翻译、总结等),支持一键部署,界面现代美观,谁用谁火。
This commit is contained in:
@@ -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 = `
|
||||
<span class="tool-icon">${tool.icon}</span>
|
||||
<div class="tool-name">${escapeHtml(tool.name)}</div>
|
||||
<div class="tool-desc">${escapeHtml(tool.desc)}</div>
|
||||
`;
|
||||
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, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// 启动
|
||||
init();
|
||||
})();
|
||||
+83
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user