FakeAPI

假数据生成器 · 支持用户 / 文章 / 评论 / 产品 / 地址

预览

等待生成
选择参数后点击「生成数据」
/* ─── 全局 i18n 系统 ─── */ (function(){ 'use strict'; const LANG_KEY = 'fakeapi_lang'; const DEFAULT_LANG = 'zh'; let currentLang = localStorage.getItem(LANG_KEY) || DEFAULT_LANG; const translations = { zh: { appTitle: 'FakeAPI', appSubtitle: '假数据生成器 · 支持用户 / 文章 / 评论 / 产品 / 地址', typeLabel: '数据类型', countLabel: '数量', fieldsLabel: '包含字段', formatLabel: '输出格式', generateBtn: '生成数据', previewTitle: '预览', statsWaiting: '等待生成', placeholder: '选择参数后点击「生成数据」', copyBtn: '复制到剪贴板', downloadBtn: '下载文件', regenerateBtn: '重新生成', toastCopied: '已复制到剪贴板', toastDownloaded: '文件已下载', toastError: '生成失败,请重试' }, en: { appTitle: 'FakeAPI', appSubtitle: 'Fake Data Generator · Users / Articles / Comments / Products / Addresses', typeLabel: 'Data Type', countLabel: 'Count', fieldsLabel: 'Fields', formatLabel: 'Format', generateBtn: 'Generate', previewTitle: 'Preview', statsWaiting: 'Awaiting generation', placeholder: 'Select parameters and click „Generate”', copyBtn: 'Copy to Clipboard', downloadBtn: 'Download File', regenerateBtn: 'Regenerate', toastCopied: 'Copied to clipboard', toastDownloaded: 'File downloaded', toastError: 'Generation failed, please retry' } }; function t(key) { const lang = translations[currentLang]; return lang && lang[key] !== undefined ? lang[key] : (translations[DEFAULT_LANG][key] || key); } window.__ = t; function applyI18n() { document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); if (key) { const text = t(key); if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { el.placeholder = text; } else { el.textContent = text; } } }); } window.switchLang = function(lang) { if (translations[lang]) { currentLang = lang; localStorage.setItem(LANG_KEY, lang); applyI18n(); // 更新动态文本(统计、toast等由各自逻辑触发) if (typeof updateStatsUI === 'function') updateStatsUI(state.currentData); } }; // 初始化时应用 document.addEventListener('DOMContentLoaded', applyI18n); })(); /* ─── 定义 escHtml(原有脚本缺失) ─── */ if (typeof escHtml === 'undefined') { window.escHtml = function(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; } /* ─── 补全被截断的脚本 ─── */ (function() { // 确保 state 已定义 if (typeof state === 'undefined') return; // ── 数字输入强制 min/max ── const countInput = document.getElementById('countInput'); if (countInput) { const clamp = function() { let val = parseInt(countInput.value, 10); if (isNaN(val)) val = parseInt(countInput.min, 10) || 5; const min = parseInt(countInput.min, 10) || 1; const max = parseInt(countInput.max, 10) || 1000; val = Math.max(min, Math.min(max, val)); countInput.value = val; }; countInput.addEventListener('change', clamp); countInput.addEventListener('blur', clamp); } // ── 生成按钮 loading + 防重复 ── const generateBtn = document.getElementById('generateBtn'); const regenerateBtn = document.getElementById('regenerateBtn'); const copyBtn = document.getElementById('copyBtn'); const downloadBtn = document.getElementById('downloadBtn'); function setLoading(btn, loading) { if (!btn) return; if (loading) { btn.classList.add('loading'); btn.disabled = true; } else { btn.classList.remove('loading'); btn.disabled = false; } } // ── 主生成函数 ── window.generateData = function() { try { setLoading(generateBtn, true); if (regenerateBtn) setLoading(regenerateBtn, true); const type = state.currentType || 'user'; const count = parseInt(countInput.value, 10) || 5; const format = state.currentFormat || 'json'; const fields = getFieldsForType(type); const enabled = state.enabledFields[type] || fields.map(f => f.key); const data = []; for (let i = 0; i < count; i++) { const item = {}; fields.forEach(f => { if (enabled.includes(f.key)) { item[f.key] = f.gen(); } }); data.push(item); } state.currentData = data; renderPreview(data, format); updateStatsUI(data); } catch (err) { console.error('Generation failed:', err); showToast(window.__ ? window.__('toastError') : '生成失败,请重试', 'error'); } finally { setLoading(generateBtn, false); if (regenerateBtn) setLoading(regenerateBtn, false); } }; // ── 渲染预览 ── window.renderPreview = function(data, format) { const previewBody = document.getElementById('previewBody'); const placeholder = document.getElementById('placeholder'); if (!previewBody) return; // 隐藏占位 if (placeholder) placeholder.style.display = 'none'; let html = ''; if (format === 'json') { html = syntaxHighlight(JSON.stringify(data, null, 2)); } else { // CSV const fields = getFieldsForType(state.currentType); const enabled = state.enabledFields[state.currentType] || fields.map(f => f.key); const header = enabled.join(','); let rows = data.map(item => enabled.map(k => item[k]).join(',')).join('
'); html = escHtml(header) + '
' + rows; } previewBody.innerHTML = html || '
' + (window.__ ? window.__('placeholder') : '暂无数据') + '
'; }; // ── 语法高亮 ── function syntaxHighlight(json) { const lines = json.split('\n'); return lines.map(line => { // 简单的转义 + 高亮 let str = escHtml(line); str = str.replace(/("(?:[^"\\]|\\.)*")\s*:/g, '$1:'); str = str.replace(/("(?:[^"\\]|\\.)*")/g, '$1'); str = str.replace(/\b(\d+\.?\d*)\b/g, '$1'); str = str.replace(/\b(true|false)\b/g, '$1'); return str; }).join('\n'); } // ── 更新统计 ── window.updateStatsUI = function(data) { const statsInfo = document.getElementById('statsInfo'); if (!statsInfo) return; if (data && data.length) { statsInfo.textContent = data.length + ' 条记录 · ' + state.currentFormat.toUpperCase(); } else { statsInfo.textContent = window.__ ? window.__('statsWaiting') : '等待生成'; } }; // ── Toast ── window.showToast = function(msg, type) { const el = document.getElementById('toast'); if (!el) return; el.textContent = msg; el.className = 'toast' + (type ? ' ' + type : ''); el.classList.add('show'); clearTimeout(el._hideTimer); el._hideTimer = setTimeout(() => el.classList.remove('show'), 3000); }; // ── 绑定生成事件 ── if (generateBtn) { generateBtn.addEventListener('click', function(e) { e.preventDefault(); generateData(); }); } if (regenerateBtn) { regenerateBtn.addEventListener('click', function(e) { e.preventDefault(); generateData(); }); } // ── 复制 ── if (copyBtn) { copyBtn.addEventListener('click', function() { try { const text = state.currentData ? JSON.stringify(state.currentData, null, 2) : ''; if (!text) { showToast('没有数据可复制', 'error'); return; } navigator.clipboard.writeText(text).then(() => { showToast(window.__ ? window.__('toastCopied') : '已复制到剪贴板', 'success'); }).catch(() => { // 降级 const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); showToast(window.__ ? window.__('toastCopied') : '已复制到剪贴板', 'success'); }); } catch (err) { showToast('复制失败', 'error'); } }); } // ── 下载 ── if (downloadBtn) { downloadBtn.addEventListener('click', function() { try { const data = state.currentData; if (!data || !data.length) { showToast('没有数据可下载', 'error'); return; } const format = state.currentFormat; const content = format === 'json' ? JSON.stringify(data, null, 2) : toCSV(data); const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'fakeapi_data.' + format; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); showToast(window.__ ? window.__('toastDownloaded') : '文件已下载', 'success'); } catch (err) { showToast('下载失败', 'error'); } }); } // ── CSV 转换 ── function toCSV(data) { if (!data || !data.length) return ''; const headers = Object.keys(data[0]); const lines = [headers.join(',')]; data.forEach(row => { const vals = headers.map(h => { let v = row[h]; if (typeof v === 'string' && (v.includes(',') || v.includes('\n') || v.includes('"'))) { v = '"' + v.replace(/"/g, '""') + '"'; } return v; }); lines.push(vals.join(',')); }); return lines.join('\n'); } // ── 生成时已有的监听(补全被截断的点击处理) ── // 注意:原有脚本中的点击处理已不完整,我们在此重新绑定,但为了防止重复,先移除旧监听再绑定 const fieldContainer = document.getElementById('fieldCheckboxes'); if (fieldContainer) { // 移除旧的内联 onclick(已截断,但可能会有残留事件),使用事件委托 fieldContainer.addEventListener('click', function(e) { const label = e.target.closest('.field-checkbox'); if (!label) return; label.classList.toggle('checked'); const key = label.dataset.key; if (!key) return; const type = state.currentType; if (!state.enabledFields[type]) state.enabledFields[type] = (getFieldsForType(type) || []).map(f => f.key); const arr = state.enabledFields[type]; if (label.classList.contains('checked')) { if (!arr.includes(key)) arr.push(key); } else { const idx = arr.indexOf(key); if (idx > -1) arr.splice(idx, 1); } }); } // ── 导航栏初始化 ── if (typeof NavBar !== 'undefined' && NavBar.init) { try { NavBar.init(); } catch(e) { console.warn('NavBar init failed', e); } } })(); /* ─── 全局错误保护 ─── */ window.addEventListener('error', function(e) { console.error('Global error caught:', e.message); // 避免界面完全崩溃 return true; }); window.addEventListener('unhandledrejection', function(e) { console.error('Unhandled promise rejection:', e.reason); });