feat: 冰朔开源仓库 · 独立代码仓库站点 · 左文件树+右模块卡片 · 热插拔 · MODULE-COVER-001 · 在线使用弹窗 · 编号本地保存
This commit is contained in:
parent
d33af46076
commit
b96e3615f6
619
cover-site/index.html
Normal file
619
cover-site/index.html
Normal file
@ -0,0 +1,619 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>冰朔开源仓库 · 模块市场</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--panel: rgba(22,27,34,0.95);
|
||||
--border: rgba(48,54,61,0.8);
|
||||
--border-h: rgba(88,166,255,0.4);
|
||||
--text: #c9d1d9;
|
||||
--dim: #8b949e;
|
||||
--accent: #58a6ff;
|
||||
--green: #3fb950;
|
||||
--gold: #d2991d;
|
||||
--code-bg: #161b22;
|
||||
}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;min-height:100vh;font-size:15px}
|
||||
|
||||
/* ── 仓库头 ── */
|
||||
.repo-bar {
|
||||
background: var(--code-bg);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 16px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.repo-bar .icon { font-size: 20px; }
|
||||
.repo-bar .path { font-size: 14px; color: var(--accent); font-weight: 600; }
|
||||
.repo-bar .sep { color: var(--dim); margin: 0 4px; }
|
||||
.repo-bar .branch {
|
||||
padding: 2px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
color: var(--dim);
|
||||
}
|
||||
.repo-bar .badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 11px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.repo-bar .badge.free { border-color: var(--green); color: var(--green); }
|
||||
.repo-bar .badge.api { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
/* ── 主布局 ── */
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 240px 1fr;
|
||||
min-height: calc(100vh - 56px);
|
||||
}
|
||||
@media(max-width:860px){ .layout{grid-template-columns:1fr} }
|
||||
|
||||
/* ── 左侧文件树 ── */
|
||||
.sidebar {
|
||||
background: var(--code-bg);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 12px 0;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 56px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
.sidebar .sect {
|
||||
padding: 8px 16px;
|
||||
font-size: 11px;
|
||||
color: var(--dim);
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.file-item {
|
||||
padding: 6px 16px 6px 28px;
|
||||
font-size: 13px;
|
||||
color: var(--dim);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.file-item:hover { color: var(--text); background: rgba(88,166,255,0.06); }
|
||||
.file-item.active { color: var(--accent); background: rgba(88,166,255,0.1); }
|
||||
.file-item .fi { font-size: 14px; width: 18px; text-align: center; flex-shrink: 0; }
|
||||
|
||||
/* ── 右侧内容区 ── */
|
||||
.content {
|
||||
padding: 24px;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
/* Module Card */
|
||||
.module-card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.module-card:hover { border-color: var(--border-h); }
|
||||
.module-header {
|
||||
padding: 20px 24px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.module-header:hover { background: rgba(88,166,255,0.04); }
|
||||
.module-header .left { display: flex; align-items: center; gap: 12px; }
|
||||
.module-header .icon { font-size: 28px; }
|
||||
.module-header .info h3 { font-size: 16px; font-weight: 600; }
|
||||
.module-header .info .mod-id {
|
||||
font-size: 11px;
|
||||
color: var(--dim);
|
||||
font-family: monospace;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.module-header .tags { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.module-header .tag {
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--dim);
|
||||
}
|
||||
.module-header .arrow { color: var(--dim); transition: transform 0.2s; font-size: 14px; }
|
||||
.module-header .arrow.open { transform: rotate(90deg); }
|
||||
|
||||
.module-body {
|
||||
padding: 0 24px 24px;
|
||||
display: none;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.module-body.open { display: block; }
|
||||
.module-body .desc { font-size: 14px; color: var(--dim); line-height: 1.8; margin: 16px 0; }
|
||||
.module-body .section-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--dim);
|
||||
letter-spacing: 1px;
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
.module-body .code-block {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 14px 18px;
|
||||
font-family: 'SF Mono','Fira Code','Consolas',monospace;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.7;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
color: var(--text);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.code-block .cmt { color: #636e7b; }
|
||||
.code-block .str { color: #a5d6ff; }
|
||||
.code-block .key { color: #ff7b72; }
|
||||
.code-block .num { color: #79c0ff; }
|
||||
.code-block .fn { color: #d2a8ff; }
|
||||
|
||||
/* Use Button */
|
||||
.use-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 20px;
|
||||
background: linear-gradient(135deg, #238636, #2ea043);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
text-decoration: none;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.use-btn:hover { background: #2ea043; transform: translateY(-1px); }
|
||||
|
||||
/* ── 在线使用模态窗口 ── */
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.7);
|
||||
z-index: 100;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.modal-overlay.show { display: flex; }
|
||||
.modal {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 360px;
|
||||
gap: 0;
|
||||
}
|
||||
@media(max-width:860px){ .modal{grid-template-columns:1fr} }
|
||||
.modal-left {
|
||||
padding: 28px;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.modal-right {
|
||||
padding: 28px;
|
||||
background: var(--code-bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 16px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--dim);
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
/* Input inside modal */
|
||||
.input-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
|
||||
select, input, textarea {
|
||||
padding: 10px 14px;
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
}
|
||||
select:focus, input:focus, textarea:focus { border-color: var(--accent); }
|
||||
textarea { min-height: 80px; resize: vertical; line-height: 1.7; }
|
||||
|
||||
.preview-area {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(13,17,23,0.5);
|
||||
border-radius: 8px;
|
||||
border: 1px dashed var(--border);
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.preview-img { max-width: 100%; max-height: 400px; border-radius: 4px; display: block; }
|
||||
|
||||
.intent-item {
|
||||
display: flex; gap: 10px; padding: 10px;
|
||||
background: var(--code-bg); border: 1px solid var(--border);
|
||||
border-radius: 6px; margin-bottom: 6px; font-size: 12px;
|
||||
}
|
||||
.intent-item.user { border-left: 2px solid var(--green); }
|
||||
.intent-item.system { border-left: 2px solid var(--accent); }
|
||||
.intent-who { font-size: 10px; letter-spacing: 1px; margin-bottom: 2px; }
|
||||
.intent-item.user .intent-who { color: var(--green); }
|
||||
.intent-item.system .intent-who { color: var(--accent); }
|
||||
.intent-step { font-weight: 600; margin-bottom: 2px; }
|
||||
.intent-detail { color: var(--dim); line-height: 1.4; }
|
||||
|
||||
.design-id-badge {
|
||||
text-align: center; padding: 12px;
|
||||
background: rgba(63,185,80,0.06);
|
||||
border: 1px solid rgba(63,185,80,0.15);
|
||||
border-radius: 8px; margin-bottom: 10px;
|
||||
}
|
||||
.design-id-badge .id { font-size: 16px; font-weight: 700; color: var(--green); font-family: monospace; margin-top: 4px; }
|
||||
|
||||
.btn {
|
||||
display: block; width: 100%; padding: 11px;
|
||||
background: linear-gradient(135deg, rgba(88,166,255,0.2), rgba(130,100,200,0.15));
|
||||
border: 1px solid var(--border); border-radius: 6px;
|
||||
color: var(--accent); text-align: center; text-decoration: none;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer; margin-top: 6px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.btn:hover { border-color: var(--accent); }
|
||||
.btn.primary { background: #238636; color: #fff; border-color: #238636; }
|
||||
.btn.primary:hover { background: #2ea043; }
|
||||
|
||||
.copy-toast {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--green);
|
||||
color: #0d1117;
|
||||
padding: 10px 24px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
z-index: 200;
|
||||
display: none;
|
||||
}
|
||||
.copy-toast.show { display: block; animation: fadeInOut 2s ease; }
|
||||
@keyframes fadeInOut {
|
||||
0% { opacity: 0; transform: translateX(-50%) translateY(10px); }
|
||||
20% { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
80% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 仓库头 -->
|
||||
<div class="repo-bar">
|
||||
<span class="icon">📦</span>
|
||||
<span class="path">bingshuo / open-modules</span>
|
||||
<span class="branch">main</span>
|
||||
<span class="badge free">✅ 开源免费</span>
|
||||
<span class="badge api">🔌 API Ready</span>
|
||||
<span style="font-size:12px;color:var(--dim);margin-left:auto">模块热插拔 · 用你自己的AI进来读取</span>
|
||||
</div>
|
||||
|
||||
<!-- 主布局 -->
|
||||
<div class="layout">
|
||||
|
||||
<!-- 左侧文件树 -->
|
||||
<div class="sidebar">
|
||||
<div class="sect">仓库文件</div>
|
||||
<div class="file-item"><span class="fi">📄</span> README.md</div>
|
||||
<div class="file-item"><span class="fi">📄</span> package.json</div>
|
||||
<div class="file-item"><span class="fi">📄</span> server.js</div>
|
||||
<div class="file-item"><span class="fi">📄</span> generate.js</div>
|
||||
<div class="file-item"><span class="fi">📄</span> renderer.js</div>
|
||||
<div class="sect">templates/</div>
|
||||
<div class="file-item"><span class="fi">📄</span> registry.js</div>
|
||||
<div class="file-item"><span class="fi">📄</span> xiaohongshu.js</div>
|
||||
<div class="sect">public/</div>
|
||||
<div class="file-item"><span class="fi">📄</span> index.html</div>
|
||||
<div class="sect">模块注册表</div>
|
||||
<div class="file-item active"><span class="fi">📦</span> MODULE-COVER-001</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="content">
|
||||
|
||||
<h2 style="font-size:20px;margin-bottom:4px;">开放模块</h2>
|
||||
<p style="font-size:13px;color:var(--dim);margin-bottom:24px;">
|
||||
每个模块有唯一编号。用你自己的编程 AI 软件连入此仓库,Agent 引导后即可直接调用。
|
||||
仓库不提供模型 API —— 走你 AI 软件自己的模型。
|
||||
</p>
|
||||
|
||||
<!-- 模块卡片 1:图片生成 -->
|
||||
<div class="module-card">
|
||||
<div class="module-header" onclick="toggleModule(this)">
|
||||
<div class="left">
|
||||
<span class="icon">🎨</span>
|
||||
<div class="info">
|
||||
<h3>图片模块 · 语言驱动封面生成器</h3>
|
||||
<div class="mod-id">MODULE-COVER-001</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:12px;">
|
||||
<div class="tags">
|
||||
<span class="tag">📕 小红书</span>
|
||||
<span class="tag">1080×1440</span>
|
||||
<span class="tag">MIT</span>
|
||||
</div>
|
||||
<span class="arrow">▶</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="module-body">
|
||||
<p class="desc">
|
||||
输入自然语言描述,系统翻译为设计逻辑,生成独一无二的封面图片。
|
||||
纯 HTML/CSS 排版渲染,<strong>不依赖任何 AI 绘画模型</strong>,无需 GPU,无需 API 密钥。
|
||||
封面底部带有此仓库域名水印。
|
||||
</p>
|
||||
|
||||
<div class="section-title">技术栈 · 全免费</div>
|
||||
<div class="code-block"><span class="cmt">// 全部开源 · 全部免费 · 不花一分钱</span>
|
||||
<span class="key">渲染引擎</span>: Puppeteer + Chrome Headless
|
||||
<span class="key">Web框架</span>: Express.js
|
||||
<span class="key">排版引擎</span>: 纯 HTML/CSS(不调用任何 AI 模型)
|
||||
<span class="key">意图分析</span>: 自研文本分析引擎
|
||||
|
||||
<span class="cmt">// 不需要 GPU · 不需要 API 密钥 · 不需要付费模型</span></div>
|
||||
|
||||
<div class="section-title">如何让 AI 调用</div>
|
||||
<div class="code-block"><span class="cmt">// 1. 用你的编程 AI 软件连入此仓库</span>
|
||||
<span class="cmt">// 仓库地址:guanghulab.com/code/bingshuo/guanghulab</span>
|
||||
<span class="cmt">// 2. Agent 引导你走完唤醒路径</span>
|
||||
<span class="cmt">// 3. 告诉 AI:调用模块 MODULE-COVER-001</span>
|
||||
<span class="cmt">// 4. AI 读取模块代码,理解参数,直接执行</span>
|
||||
<span class="cmt">// 5. 左边生成你的设计逻辑链 + 唯一编号</span>
|
||||
<span class="cmt">// 6. 右边预览 + 下载</span>
|
||||
|
||||
<span class="cmt">// API 端点(AI 或手动调用)</span>
|
||||
<span class="key">GET</span> <span class="str">/api/templates</span> → 获取模板列表
|
||||
<span class="key">POST</span> <span class="str">/api/generate</span> → 生成封面
|
||||
|
||||
<span class="cmt">// 参数</span>
|
||||
{ "templateId": "<span class="str">xiaohongshu</span>",
|
||||
"presetId": "<span class="str">tech</span>",
|
||||
"title": "<span class="str">标题</span>",
|
||||
"body": "<span class="str">正文</span>",
|
||||
"layout": "<span class="str">hero</span>" }</div>
|
||||
|
||||
<a href="#" class="use-btn" onclick="openTry(event)">🚀 在线使用</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 更多模块占位 -->
|
||||
<div class="module-card" style="opacity:0.5;cursor:not-allowed">
|
||||
<div class="module-header">
|
||||
<div class="left">
|
||||
<span class="icon">📋</span>
|
||||
<div class="info">
|
||||
<h3>更多模块即将上线</h3>
|
||||
<div class="mod-id">MODULE-XXX-00X</div>
|
||||
</div>
|
||||
</div>
|
||||
<span style="font-size:12px;color:var(--dim)">热插拔 · 随时部署</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 在线使用模态 -->
|
||||
<div class="modal-overlay" id="modal">
|
||||
<div class="modal" style="position:relative;">
|
||||
<button class="modal-close" onclick="closeModal()">✕</button>
|
||||
|
||||
<div class="modal-left">
|
||||
<h3 style="font-size:16px;margin-bottom:16px;">🎨 图片模块 · 在线使用</h3>
|
||||
|
||||
<div class="input-row">
|
||||
<select id="preset"><option value="tech" selected>🔷 科技蓝</option><option value="warm">🟠 奶油暖调</option><option value="minimal">⬜ 极简黑白</option><option value="rose">🟣 玫瑰粉调</option><option value="green">🟢 文艺绿植</option></select>
|
||||
<select id="layout"><option value="default">标准排版</option><option value="hero" selected>大标题封面</option><option value="quote">金句居中</option></select>
|
||||
</div>
|
||||
<input type="text" id="title" placeholder="标题" style="margin-bottom:10px;">
|
||||
<textarea id="body" placeholder="正文..."></textarea>
|
||||
<button class="btn primary" id="genBtn" onclick="gen()">执行设计逻辑</button>
|
||||
<div style="display:none;padding:10px;background:rgba(255,100,100,0.08);border:1px solid rgba(255,100,100,0.2);border-radius:6px;color:#ff7b72;font-size:12px;margin-top:10px" id="err"></div>
|
||||
|
||||
<div class="design-id-badge" id="idBadge" style="display:none;margin-top:16px;">
|
||||
<span style="font-size:10px;color:var(--dim);">DESIGN ID · 证明这个设计是你的</span>
|
||||
<div class="id">---</div>
|
||||
<span style="font-size:10px;color:var(--dim);">编号已保存到本地 · 署名可选</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:16px;">
|
||||
<div style="font-size:12px;color:var(--dim);margin-bottom:10px;letter-spacing:1px;">意图链 · 设计逻辑</div>
|
||||
<div id="chain"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-right">
|
||||
<div style="font-size:12px;color:var(--dim);margin-bottom:10px;letter-spacing:1px;">封面预览</div>
|
||||
<div class="preview-area" id="previewArea">
|
||||
<div style="text-align:center;color:var(--dim);" id="empty">
|
||||
<div style="font-size:36px;margin-bottom:8px;opacity:0.3;">🎨</div>
|
||||
<div style="font-size:12px;">点击执行<br>生成封面</div>
|
||||
</div>
|
||||
<img class="preview-img" id="previewImg" style="display:none;" alt="封面">
|
||||
</div>
|
||||
<a class="btn primary" id="dlBtn" href="#" download style="display:none;">💾 下载 PNG</a>
|
||||
<button class="btn" id="saveBtn" onclick="saveId()" style="display:none;">📋 保存设计编号到本地</button>
|
||||
<div style="font-size:11px;color:var(--dim);margin-top:8px;text-align:center;">
|
||||
封面带 cover.guanghulab.com 水印<br>
|
||||
署名可选 · 设计属于你
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast -->
|
||||
<div class="copy-toast" id="toast"></div>
|
||||
|
||||
<script>
|
||||
function toggleModule(el) {
|
||||
const body = el.nextElementSibling;
|
||||
const arrow = el.querySelector('.arrow');
|
||||
body.classList.toggle('open');
|
||||
arrow.classList.toggle('open');
|
||||
}
|
||||
|
||||
function openTry(e) {
|
||||
e.preventDefault();
|
||||
document.getElementById('modal').classList.add('show');
|
||||
// 预设内容(不显示任何编程AI名字)
|
||||
if(!document.getElementById('title').value) {
|
||||
document.getElementById('title').value = '零基础手搓AI做图小系统';
|
||||
document.getElementById('body').value = 'AI辅助 + 自己手搓 · 0经验也能开发\n\n- 开源到个人代码仓库\n- 全免费 · 无需GPU\n- 语言驱动排版 · 意图透明\n- 封面也用它自己生成';
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('modal').classList.remove('show');
|
||||
document.getElementById('previewImg').style.display = 'none';
|
||||
document.getElementById('empty').style.display = 'block';
|
||||
document.getElementById('dlBtn').style.display = 'none';
|
||||
document.getElementById('saveBtn').style.display = 'none';
|
||||
document.getElementById('idBadge').style.display = 'none';
|
||||
document.getElementById('chain').innerHTML = '';
|
||||
document.getElementById('err').style.display = 'none';
|
||||
}
|
||||
|
||||
let currentId = '';
|
||||
const SAVED_KEY = 'cover-studio-ids';
|
||||
|
||||
async function gen() {
|
||||
const title = document.getElementById('title').value.trim();
|
||||
const body = document.getElementById('body').value.trim();
|
||||
if(!title && !body) { showErr('请输入标题或正文'); return; }
|
||||
|
||||
document.getElementById('err').style.display = 'none';
|
||||
document.getElementById('empty').style.display = 'none';
|
||||
document.getElementById('previewImg').style.display = 'none';
|
||||
document.getElementById('dlBtn').style.display = 'none';
|
||||
document.getElementById('saveBtn').style.display = 'none';
|
||||
document.getElementById('idBadge').style.display = 'none';
|
||||
document.getElementById('chain').innerHTML = '';
|
||||
document.getElementById('genBtn').disabled = true;
|
||||
document.getElementById('genBtn').textContent = '编译中...';
|
||||
|
||||
currentId = 'DSG-' + Date.now().toString(36).toUpperCase().slice(-8);
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
templateId: 'xiaohongshu',
|
||||
presetId: document.getElementById('preset').value,
|
||||
title, body,
|
||||
layout: document.getElementById('layout').value
|
||||
})
|
||||
});
|
||||
if(!r.ok) throw new Error('生成失败');
|
||||
const d = await r.json();
|
||||
|
||||
document.getElementById('previewImg').src = d.url;
|
||||
document.getElementById('previewImg').style.display = 'block';
|
||||
document.getElementById('dlBtn').href = d.url;
|
||||
document.getElementById('dlBtn').style.display = 'block';
|
||||
document.getElementById('saveBtn').style.display = 'block';
|
||||
document.getElementById('idBadge').style.display = 'block';
|
||||
document.getElementById('idBadge').querySelector('.id').textContent = currentId;
|
||||
|
||||
// 意图链
|
||||
if(d.intent_chain) {
|
||||
document.getElementById('chain').innerHTML = d.intent_chain.map(s =>
|
||||
'<div class="intent-item '+(s.who==='用户'?'user':'system')+'">'+
|
||||
'<div style="font-size:14px;flex-shrink:0;">'+s.icon+'</div>'+
|
||||
'<div>'+
|
||||
'<div class="intent-who">'+s.who+'</div>'+
|
||||
'<div class="intent-step">'+s.step+'</div>'+
|
||||
'<div class="intent-detail">'+s.detail+'</div>'+
|
||||
'</div></div>'
|
||||
).join('');
|
||||
}
|
||||
|
||||
// 自动保存到本地
|
||||
saveToLocal();
|
||||
|
||||
} catch(e) {
|
||||
document.getElementById('empty').style.display = 'block';
|
||||
showErr(e.message);
|
||||
} finally {
|
||||
document.getElementById('genBtn').disabled = false;
|
||||
document.getElementById('genBtn').textContent = '执行设计逻辑';
|
||||
}
|
||||
}
|
||||
|
||||
function saveToLocal() {
|
||||
const saved = JSON.parse(localStorage.getItem(SAVED_KEY) || '[]');
|
||||
saved.push({
|
||||
id: currentId,
|
||||
title: document.getElementById('title').value,
|
||||
time: new Date().toISOString(),
|
||||
});
|
||||
localStorage.setItem(SAVED_KEY, JSON.stringify(saved.slice(-50)));
|
||||
}
|
||||
|
||||
function saveId() {
|
||||
toast('✅ 设计编号 ' + currentId + ' 已保存到本地');
|
||||
}
|
||||
|
||||
function showErr(m) {
|
||||
const e = document.getElementById('err');
|
||||
e.textContent = m;
|
||||
e.style.display = 'block';
|
||||
setTimeout(() => e.style.display = 'none', 4000);
|
||||
}
|
||||
|
||||
function toast(m) {
|
||||
const t = document.getElementById('toast');
|
||||
t.textContent = m;
|
||||
t.classList.add('show');
|
||||
setTimeout(() => t.classList.remove('show'), 2000);
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if((e.metaKey || e.ctrlKey) && e.key === 'Enter' && document.getElementById('modal').classList.contains('show')) gen();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user