AirShelf/v1/mockups/mockup-A.html
iye f420af2069
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6s
chore: 全量推送 · 累积页面改动 + Next.js 工程骨架 + v1/ 归档 + 文档
页面 (电商AI平台/)
- account / team / settings / index / products / projects: 累积迭代
- restraint.css: 设计 token 补充
- login.html / register.html: 新增登录注册页
- _ARCHIVE.md: 归档说明

Next.js 工程骨架
- app/ + components/: 新一代 SPA 雏形 (page / layout / sidebar / topbar / GridBg / Icon)
- package.json / package-lock.json / next.config.mjs / tsconfig.json / postcss.config.mjs / next-env.d.ts

历史归档 / 文档
- v1/: 原 V1 静态稿镜像 (含 mockup-A/B/C)
- PRD.md / deployment-guide.md / _check.html
- ui参考/ / screenshots/

杂项
- .gitignore 调整
- 删除根 README.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:16:46 +08:00

472 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>方案 A · 分步向导 · 新建商品(AI 生成)</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../assets/restraint.css">
<style>
body { background: var(--bg); }
.demo-bar {
padding: 16px 24px;
background: var(--card);
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 16px;
font-family: 'Inter', sans-serif;
}
.demo-bar .ti { font-weight: 700; font-size: 15px; }
.demo-bar .desc { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--ink-3); }
/* Wizard - full screen modal */
.wiz-shell {
background: var(--bg);
min-height: calc(100vh - 60px);
display: flex; flex-direction: column;
position: relative; overflow: hidden;
}
.wiz-shell .grid-bg {
position: absolute; inset: 0; pointer-events: none;
background-image:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><g stroke='%23B8B3A4' stroke-width='1' fill='none'><path d='M-5 0 L5 0 M0 -5 L0 5'/><path d='M235 0 L245 0 M240 -5 L240 5'/></g></svg>"),
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><g stroke='%23E2DED2' stroke-width='1' fill='none' stroke-dasharray='1.5 4'><path d='M240 0 L240 240'/><path d='M0 240 L240 240'/></g></svg>");
background-size: 240px 240px, 240px 240px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 30%, transparent 90%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 30%, transparent 90%);
}
/* Top bar */
.wiz-top {
position: relative; z-index: 2;
padding: 14px 32px;
border-bottom: 1px solid var(--border);
background: var(--bg);
display: flex; align-items: center; gap: 24px;
}
.wiz-top .home {
display: flex; align-items: center; gap: 8px;
color: var(--ink-2);
cursor: pointer;
font-size: 13px;
}
.wiz-top .home:hover { color: var(--ink); }
.wiz-top .ti { font-weight: 700; font-size: 16px; }
.wiz-top .ti span { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); margin-left: 6px; letter-spacing: .04em; }
/* Stepper */
.wiz-stepper {
flex: 1;
display: flex; align-items: center; justify-content: center; gap: 0;
max-width: 640px; margin: 0 auto;
}
.ws-step {
display: flex; align-items: center; gap: 8px;
cursor: pointer; user-select: none;
}
.ws-step .num {
width: 24px; height: 24px;
border: 1px solid var(--border);
background: var(--card);
color: var(--ink-3);
font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700;
display: grid; place-items: center;
}
.ws-step.done .num { background: var(--ink); color: #FFF; border-color: var(--ink); }
.ws-step.active .num { background: var(--orange); color: #FFF; border-color: var(--orange); }
.ws-step .lbl { font-size: 12.5px; color: var(--ink-3); font-weight: 500; }
.ws-step.done .lbl { color: var(--ink-2); }
.ws-step.active .lbl { color: var(--ink); font-weight: 600; }
.ws-line { width: 60px; height: 1px; background: var(--border); margin: 0 12px; }
.ws-line.done { background: var(--ink); }
.wiz-top .x {
width: 32px; height: 32px;
border: 1px solid var(--border);
display: grid; place-items: center;
cursor: pointer;
color: var(--ink-2);
}
.wiz-top .x:hover { color: var(--orange); border-color: var(--orange); }
/* Body */
.wiz-body {
position: relative; z-index: 1;
flex: 1;
padding: 40px 60px 100px;
overflow-y: auto;
max-width: 1200px;
width: 100%;
margin: 0 auto;
}
.step-h { margin-bottom: 28px; text-align: center; }
.step-h h2 { font-size: 24px; font-weight: 700; letter-spacing: -.018em; }
.step-h p { font-size: 13px; color: var(--ink-2); margin-top: 8px; }
.step-h .mono-tag { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; margin-top: 8px; display: inline-block; padding: 2px 8px; background: var(--bg-soft); border: 1px solid var(--border-soft); }
.step-pane { display: none; }
.step-pane.active { display: block; animation: fade .2s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
/* Step 1: 商品信息 */
.s1-grid { display: grid; grid-template-columns: 380px 1fr; gap: 32px; max-width: 880px; margin: 0 auto; }
.upload-big {
aspect-ratio: 4/5;
background: var(--card);
border: 1.5px dashed var(--border);
display: grid; place-items: center;
text-align: center;
cursor: pointer;
padding: 24px;
color: var(--ink-2);
transition: border-color .15s, background .15s;
}
.upload-big:hover { border-color: var(--orange); background: var(--orange-tint); color: var(--orange); }
.upload-big .ic-cam { width: 48px; height: 48px; border: 1px solid currentColor; display: grid; place-items: center; margin: 0 auto 14px; }
.upload-big.has-file { border-style: solid; border-color: var(--orange); padding: 0; background: var(--bg-soft); }
.upload-big.has-file .placeholder { width: 100%; height: 100%; }
.up-hint { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); text-align: center; margin-top: 10px; letter-spacing: .02em; }
/* Step 2: 生成头图 4 选 1 */
.gen-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; max-width: 1100px; margin: 0 auto; }
.gen-card {
aspect-ratio: 1;
background: var(--card);
border: 1px solid var(--border);
cursor: pointer;
position: relative;
transition: border-color .12s;
overflow: hidden;
}
.gen-card:hover { border-color: var(--ink-3); }
.gen-card .placeholder { width: 100%; height: 100%; }
.gen-card.selected { border: 2px solid var(--orange); }
.gen-card.selected::after {
content: '✓';
position: absolute; top: 8px; right: 8px;
width: 24px; height: 24px;
background: var(--orange); color: #FFF;
display: grid; place-items: center;
font-weight: 700; font-size: 13px;
}
.gen-card .badge {
position: absolute; bottom: 8px; left: 8px;
font-family: 'JetBrains Mono', monospace; font-size: 10px;
background: rgba(255,255,255,.92); color: var(--ink-2);
border: 1px solid var(--border);
padding: 2px 6px; letter-spacing: .04em;
}
.regen-bar {
display: flex; align-items: center; justify-content: center; gap: 12px;
margin-top: 24px;
}
/* Step 3: 模特 + 上身图 */
.s3-grid { max-width: 1100px; margin: 0 auto; }
.s3-section { margin-bottom: 32px; }
.s3-section h3 { font-size: 14px; font-weight: 600; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
.s3-section h3 .num-mono { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); padding: 1px 6px; background: var(--bg-soft); border: 1px solid var(--border-soft); letter-spacing: .04em; }
.model-row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
.model-card {
aspect-ratio: 3/4;
background: var(--card);
border: 1px solid var(--border);
cursor: pointer;
position: relative;
}
.model-card:hover { border-color: var(--ink-3); }
.model-card.selected { border: 2px solid var(--orange); }
.model-card.selected::after { content: '✓'; position: absolute; top: 6px; right: 6px; width: 20px; height: 20px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 11px; }
.model-card .lbl-bottom { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(255,255,255,.92); border-top: 1px solid var(--border); padding: 4px 8px; font-size: 10.5px; color: var(--ink-2); font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
.model-card .placeholder { width: 100%; height: 100%; }
.gen-grid.multi .gen-card.selected { border: 2px solid var(--orange); }
.gen-grid.multi .gen-card.selected::after { content: '✓'; }
/* Step 4: 确认 */
.s4-shell { max-width: 760px; margin: 0 auto; }
.preview-box { background: var(--card); border: 1px solid var(--border); padding: 0; margin-bottom: 16px; }
.preview-box .ph-stack { display: grid; grid-template-columns: 240px 1fr; gap: 0; }
.preview-box .ph-stack > .placeholder { aspect-ratio: 4/5; }
.preview-box .info { padding: 24px 28px; }
.preview-box .info h3 { font-size: 18px; font-weight: 700; margin-bottom: 4px; }
.preview-box .info .meta { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--ink-3); margin-bottom: 14px; letter-spacing: .02em; }
.preview-box .info dl { font-size: 12.5px; color: var(--ink-2); }
.preview-box .info dl > div { display: flex; padding: 6px 0; border-bottom: 1px solid var(--border); }
.preview-box .info dl > div:last-child { border-bottom: 0; }
.preview-box .info dl dt { width: 80px; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .04em; }
.preview-box .info dl dd { flex: 1; color: var(--ink); }
.selected-thumbs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 12px; }
.selected-thumbs .placeholder { aspect-ratio: 1; }
/* Foot bar */
.wiz-foot {
position: sticky; bottom: 0; z-index: 5;
padding: 14px 32px;
background: var(--card);
border-top: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between;
}
.wiz-foot .info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
.wiz-foot .actions { display: flex; gap: 10px; align-items: center; }
/* Field overrides */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.bullet-list { list-style: none; padding: 0; }
.bullet-list li { display: flex; gap: 8px; align-items: center; padding: 8px 10px; background: var(--bg-soft); border: 1px solid var(--border); margin-bottom: 6px; font-size: 13px; }
.bullet-list .num { width: 18px; height: 18px; background: var(--card); border: 1px solid var(--border); font-size: 11px; color: var(--ink-2); display: grid; place-items: center; flex-shrink: 0; font-family: 'JetBrains Mono', monospace; }
.form-card { background: var(--card); border: 1px solid var(--border); padding: 24px; position: relative; }
.form-card::before, .form-card::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.form-card::before { top: -7px; left: -7px; }
.form-card::after { bottom: -7px; right: -7px; }
</style>
</head>
<body>
<!-- Demo bar (上面只是说明) -->
<div class="demo-bar">
<a href="index.html" class="btn btn-ghost btn-sm">← 返回方案对比</a>
<span class="ti">方案 A · 分步向导(Stepper Wizard)</span>
<span class="desc">// 全屏 4 步,顶部 stepper,底部固定工具栏 · 最经典最不容易迷路</span>
</div>
<!-- Wizard -->
<div class="wiz-shell">
<div class="grid-bg"></div>
<header class="wiz-top">
<div class="home"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M3 8L8 3l5 5M5 7v6h6V7"/></svg> 工作台 / 商品库</div>
<div class="ti">新建商品 <span>[ AI 生成模式 ]</span></div>
<nav class="wiz-stepper">
<a class="ws-step done" data-step="1"><div class="num"></div><div class="lbl">商品信息</div></a>
<span class="ws-line done"></span>
<a class="ws-step active" data-step="2"><div class="num">2</div><div class="lbl">生成头图</div></a>
<span class="ws-line"></span>
<a class="ws-step" data-step="3"><div class="num">3</div><div class="lbl">模特上身</div></a>
<span class="ws-line"></span>
<a class="ws-step" data-step="4"><div class="num">4</div><div class="lbl">完成创建</div></a>
</nav>
<div class="x" onclick="alert('演示原型 · 关闭按钮')"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
</header>
<div class="wiz-body">
<!-- ============= STEP 1 · 商品信息 + 上传原图 ============= -->
<section class="step-pane" data-pane="1">
<div class="step-h">
<h2>第 1 步 · 上传你的商品图,填写基础信息</h2>
<p>上传一张你自己拍摄的商品图(任何角度都行,我们会用 AI 优化),然后填一下商品的基本信息。</p>
<span class="mono-tag">// PHOTO + INFO</span>
</div>
<div class="s1-grid">
<div>
<div class="upload-big has-file">
<div class="placeholder"><span class="ph-frame">补水面膜.jpg<br>1200×1500</span></div>
</div>
<div class="up-hint">// 已上传 · 点击重新选择 · JPG / PNG · 建议 1000×1000+</div>
</div>
<div class="form-card">
<div class="field">
<label class="field-label">商品名称<span class="req">*</span></label>
<input class="input" value="透真玻尿酸补水面膜">
</div>
<div class="field-row">
<div class="field">
<label class="field-label">品类</label>
<select class="select"><option>美妆个护</option><option>数码 3C</option><option>食品饮料</option></select>
</div>
<div class="field">
<label class="field-label">参考价</label>
<input class="input" value="¥39.9">
</div>
</div>
<div class="field">
<label class="field-label">核心卖点<span class="req">*</span></label>
<ul class="bullet-list">
<li><span class="num">1</span> 透明质酸 + B5,敷完不黏不闷</li>
<li><span class="num">2</span> 30g 大容量精华液</li>
<li><span class="num">+</span> <input class="input" style="height:24px; border:0; padding:0 4px; background:transparent;" placeholder="添加新卖点"></li>
</ul>
</div>
<div class="field">
<label class="field-label">目标人群</label>
<input class="input" value="22-32 岁女性、熬夜党、敏感肌">
</div>
</div>
</div>
</section>
<!-- ============= STEP 2 · 生成头图 4 选 1 ============= -->
<section class="step-pane active" data-pane="2">
<div class="step-h">
<h2>第 2 步 · 选一张你最满意的头图</h2>
<p>AI 已基于你上传的图生成了 4 张优化后的头图,选 1 张作为商品主图。不满意可以重新生成。</p>
<span class="mono-tag">// AI generated · 4 candidates</span>
</div>
<div class="gen-grid" id="head-grid">
<div class="gen-card selected" data-id="h1"><div class="placeholder"><span class="ph-frame">候选 A · 白底简约</span></div><span class="badge">A · 1920×1920</span></div>
<div class="gen-card" data-id="h2"><div class="placeholder"><span class="ph-frame">候选 B · 木纹背景</span></div><span class="badge">B · 1920×1920</span></div>
<div class="gen-card" data-id="h3"><div class="placeholder"><span class="ph-frame">候选 C · 浅米石材</span></div><span class="badge">C · 1920×1920</span></div>
<div class="gen-card" data-id="h4"><div class="placeholder"><span class="ph-frame">候选 D · 暖光氛围</span></div><span class="badge">D · 1920×1920</span></div>
</div>
<div class="regen-bar">
<button class="btn" onclick="regen('head')">↻ 全部重新生成</button>
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); letter-spacing:.04em;">// ~¥0.20 / 4 张 · 不满意不扣费</span>
</div>
</section>
<!-- ============= STEP 3 · 选模特 + 生成上身图 ============= -->
<section class="step-pane" data-pane="3">
<div class="step-h">
<h2>第 3 步 · 选模特,生成模特上身图</h2>
<p>从模板库选一个 AI 模特,我们会让 ta 拿着你刚选的商品图生成 4 张上身参考图,选 1-4 张你想要的。</p>
<span class="mono-tag">// MODEL + WEAR</span>
</div>
<div class="s3-grid">
<div class="s3-section">
<h3>① 选模特 <span class="num-mono">12 个 AI 模板 · 已选 1</span></h3>
<div class="model-row">
<div class="model-card selected"><div class="placeholder"><span class="ph-frame">林夕 · 25</span></div><div class="lbl-bottom">林夕 · 都市女</div></div>
<div class="model-card"><div class="placeholder"><span class="ph-frame">阿楠</span></div><div class="lbl-bottom">阿楠 · 同事女</div></div>
<div class="model-card"><div class="placeholder"><span class="ph-frame">小七</span></div><div class="lbl-bottom">小七 · 学生</div></div>
<div class="model-card"><div class="placeholder"><span class="ph-frame">王姐</span></div><div class="lbl-bottom">王姐 · 居家</div></div>
<div class="model-card"><div class="placeholder"><span class="ph-frame">阿杰</span></div><div class="lbl-bottom">阿杰 · 男</div></div>
<div class="model-card"><div class="placeholder"><span class="ph-frame">+ 更多</span></div><div class="lbl-bottom">浏览全部</div></div>
</div>
</div>
<div class="s3-section">
<h3>② 选你要的上身图(可多选) <span class="num-mono">已选 2 / 4</span></h3>
<div class="gen-grid multi" id="wear-grid">
<div class="gen-card selected" data-id="w1"><div class="placeholder"><span class="ph-frame">上身 A · 持物半身</span></div><span class="badge">A</span></div>
<div class="gen-card" data-id="w2"><div class="placeholder"><span class="ph-frame">上身 B · 敷面膜中</span></div><span class="badge">B</span></div>
<div class="gen-card selected" data-id="w3"><div class="placeholder"><span class="ph-frame">上身 C · 镜前自拍</span></div><span class="badge">C</span></div>
<div class="gen-card" data-id="w4"><div class="placeholder"><span class="ph-frame">上身 D · 床边特写</span></div><span class="badge">D</span></div>
</div>
<div class="regen-bar">
<button class="btn" onclick="regen('wear')">↻ 重新生成上身图</button>
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); letter-spacing:.04em;">// ~¥0.40 / 4 张</span>
</div>
</div>
</div>
</section>
<!-- ============= STEP 4 · 完成 ============= -->
<section class="step-pane" data-pane="4">
<div class="step-h">
<h2>第 4 步 · 确认创建</h2>
<p>检查一下信息,确认无误就可以创建商品了。后续在新建项目时,这些素材会自动可用。</p>
<span class="mono-tag">// REVIEW + CREATE</span>
</div>
<div class="s4-shell">
<div class="preview-box">
<div class="ph-stack">
<div class="placeholder"><span class="ph-frame">头图 A · 1920×1920</span></div>
<div class="info">
<h3>透真玻尿酸补水面膜</h3>
<div class="meta">// 美妆个护 · ¥39.9 · AI 生成 · 模特: 林夕</div>
<dl>
<div><dt>卖点</dt><dd>透明质酸 + B5 · 30g 大容量精华</dd></div>
<div><dt>人群</dt><dd>22-32 岁女性、熬夜党、敏感肌</dd></div>
<div><dt>原图</dt><dd>1 张(已上传)</dd></div>
<div><dt>头图</dt><dd>1 张(候选 A · AI 生成)</dd></div>
<div><dt>上身图</dt><dd>2 张(候选 A · C · AI 模特林夕)</dd></div>
</dl>
</div>
</div>
</div>
<h3 style="font-size:13px; font-weight:600; margin-bottom:8px; color:var(--ink-2);">已选素材</h3>
<div class="selected-thumbs">
<div class="placeholder"><span class="ph-frame">原图</span></div>
<div class="placeholder"><span class="ph-frame">头图 A</span></div>
<div class="placeholder"><span class="ph-frame">上身 A</span></div>
<div class="placeholder"><span class="ph-frame">上身 C</span></div>
</div>
</div>
</section>
</div>
<footer class="wiz-foot">
<span class="info" id="foot-info">// STEP 2 / 4 · 已选 1 张头图</span>
<div class="actions">
<button class="btn" id="btn-prev">← 上一步</button>
<button class="btn btn-primary btn-lg" id="btn-next">下一步 →</button>
</div>
</footer>
</div>
<script>
let curStep = 2;
const TOTAL = 4;
const labels = ['', '商品信息', '生成头图', '模特上身', '完成创建'];
function go(n) {
if (n < 1 || n > TOTAL) return;
curStep = n;
document.querySelectorAll('.step-pane').forEach(p => p.classList.remove('active'));
document.querySelector(`[data-pane="${n}"]`).classList.add('active');
document.querySelectorAll('.ws-step').forEach((s, i) => {
s.classList.remove('done', 'active');
if (i + 1 < n) s.classList.add('done');
if (i + 1 === n) s.classList.add('active');
});
document.querySelectorAll('.ws-line').forEach((l, i) => {
l.classList.toggle('done', i + 1 < n);
});
document.getElementById('btn-prev').style.visibility = n === 1 ? 'hidden' : 'visible';
document.getElementById('btn-next').textContent = n === TOTAL ? '✓ 创建商品' : '下一步 →';
document.getElementById('foot-info').textContent = `// STEP ${n} / ${TOTAL} · ${labels[n]}`;
window.scrollTo({ top: 0, behavior: 'smooth' });
}
document.getElementById('btn-prev').onclick = () => go(curStep - 1);
document.getElementById('btn-next').onclick = () => {
if (curStep === TOTAL) {
alert('✓ 商品已创建!\n\n(演示原型,实际会跳回商品库)');
return;
}
go(curStep + 1);
};
document.querySelectorAll('.ws-step').forEach(s => {
s.onclick = () => go(+s.dataset.step);
});
// single select for head
document.querySelectorAll('#head-grid .gen-card').forEach(c => {
c.onclick = () => {
document.querySelectorAll('#head-grid .gen-card').forEach(x => x.classList.remove('selected'));
c.classList.add('selected');
};
});
// single select for model
document.querySelectorAll('.model-card').forEach(c => {
c.onclick = () => {
document.querySelectorAll('.model-card').forEach(x => x.classList.remove('selected'));
c.classList.add('selected');
};
});
// multi select for wear
document.querySelectorAll('#wear-grid .gen-card').forEach(c => {
c.onclick = () => c.classList.toggle('selected');
});
function regen(t) {
const el = document.querySelector(t === 'head' ? '#head-grid' : '#wear-grid');
el.style.opacity = .35;
setTimeout(() => { el.style.opacity = 1; }, 700);
}
</script>
</body>
</html>