- 10 个页面 (工作台/项目/商品/流水线/资产/账户/创建向导) - V2.1 Restraint 设计规范 (冷灰底 + #FA5D19 + 8px 圆角) - 完整 design-system.html 组件库参考 - SVG line icon · stroke 1.5 全合规 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1595 lines
67 KiB
HTML
1595 lines
67 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>新建商品 · 流·Studio</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>
|
||
/* ============================================================
|
||
V2.1 product-create · 全屏新建商品工作台
|
||
- 顶部模式切换:[ 我有商品图 ] / [ AI 帮我生成图 ]
|
||
- 左侧 step 导航 + 已选素材
|
||
- 中央 canvas
|
||
- 右侧 controls
|
||
- Step 3 内嵌子 Tab(① 挑选模特 → ② 生成上身图)
|
||
============================================================ */
|
||
|
||
body { background: var(--background-base); }
|
||
|
||
.wb {
|
||
height: 100vh;
|
||
display: grid;
|
||
grid-template-rows: 56px auto 1fr;
|
||
background: var(--background-base);
|
||
}
|
||
|
||
/* ─── Top bar ─── */
|
||
.wb-top {
|
||
padding: 0 24px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border-faint);
|
||
display: flex; align-items: center; gap: 18px;
|
||
}
|
||
.wb-top .home {
|
||
display: flex; align-items: center; gap: 8px;
|
||
color: var(--black-alpha-56);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
transition: color var(--t-base);
|
||
}
|
||
.wb-top .home:hover { color: var(--accent-black); }
|
||
.wb-top .home svg { width: 14px; height: 14px; }
|
||
.wb-top .ti {
|
||
font-size: 15px; font-weight: 600;
|
||
color: var(--accent-black);
|
||
display: flex; align-items: center; gap: 10px;
|
||
}
|
||
.wb-top .ti .name-edit {
|
||
background: transparent;
|
||
border: 1px solid transparent;
|
||
border-radius: var(--r-md);
|
||
padding: 4px 10px;
|
||
font-size: 15px; font-weight: 600;
|
||
color: var(--accent-black);
|
||
font-family: inherit;
|
||
width: 280px;
|
||
transition: border-color var(--t-base), background var(--t-base);
|
||
}
|
||
.wb-top .ti .name-edit:hover { border-color: var(--black-alpha-12); background: var(--black-alpha-3); }
|
||
.wb-top .ti .name-edit:focus { border-color: var(--heat-40); background: var(--surface); box-shadow: inset 0 0 0 1px var(--heat-40); }
|
||
.wb-top .ti .mode-pill {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
color: var(--heat);
|
||
background: var(--heat-12);
|
||
border: 1px solid var(--heat-20);
|
||
border-radius: var(--r-sm);
|
||
padding: 2px 8px;
|
||
letter-spacing: .04em;
|
||
font-weight: 500;
|
||
}
|
||
.wb-top .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||
.wb-top .x {
|
||
width: 36px; height: 36px;
|
||
display: grid; place-items: center;
|
||
cursor: pointer;
|
||
color: var(--black-alpha-56);
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
transition: background var(--t-base), color var(--t-base), border-color var(--t-base);
|
||
}
|
||
.wb-top .x:hover { color: var(--accent-crimson); background: var(--crimson-bg); border-color: var(--crimson-bd); }
|
||
.wb-top .x svg { width: 14px; height: 14px; }
|
||
|
||
/* ─── Mode switch row ─── */
|
||
.mode-row {
|
||
padding: 14px 24px;
|
||
background: var(--background-lighter);
|
||
border-bottom: 1px solid var(--border-faint);
|
||
display: flex; align-items: center; gap: 14px;
|
||
}
|
||
.mode-row .lbl {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .04em;
|
||
flex-shrink: 0;
|
||
}
|
||
.mode-toggle {
|
||
display: inline-flex;
|
||
background: var(--surface);
|
||
border: 1px solid var(--black-alpha-12);
|
||
border-radius: var(--r-md);
|
||
padding: 3px;
|
||
gap: 2px;
|
||
}
|
||
.mode-toggle button {
|
||
padding: 6px 14px;
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
font-size: 12.5px; font-weight: 500;
|
||
color: var(--black-alpha-56);
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
display: flex; align-items: center; gap: 6px;
|
||
transition: background var(--t-base), color var(--t-base);
|
||
}
|
||
.mode-toggle button:hover { color: var(--accent-black); }
|
||
.mode-toggle button.active { background: var(--heat); color: var(--accent-white); font-weight: 600; }
|
||
.mode-toggle button svg { width: 13px; height: 13px; }
|
||
.mode-row .mode-hint {
|
||
margin-left: auto;
|
||
font-size: 12.5px;
|
||
color: var(--black-alpha-56);
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.mode-row .mode-hint .mono {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .04em;
|
||
}
|
||
|
||
/* ─── Main split ─── */
|
||
.wb-main { display: grid; grid-template-columns: 280px 1fr 360px; min-height: 0; }
|
||
|
||
/* ─── Left sidebar ─── */
|
||
.wb-side {
|
||
border-right: 1px solid var(--border-faint);
|
||
background: var(--background-base);
|
||
overflow-y: auto;
|
||
padding: 18px 0;
|
||
}
|
||
.step-item {
|
||
padding: 14px 20px;
|
||
display: flex; align-items: flex-start; gap: 12px;
|
||
cursor: pointer;
|
||
border-left: 3px solid transparent;
|
||
position: relative;
|
||
transition: background var(--t-base);
|
||
}
|
||
.step-item:hover { background: var(--black-alpha-4); }
|
||
.step-item.active { background: var(--heat-12); border-left-color: var(--heat); }
|
||
.step-item.locked { opacity: .55; cursor: not-allowed; }
|
||
.step-item.locked:hover { background: transparent; }
|
||
.step-item .num {
|
||
width: 26px; height: 26px;
|
||
border: 1px solid var(--black-alpha-12);
|
||
background: var(--surface);
|
||
color: var(--black-alpha-48);
|
||
font-family: var(--font-mono); font-size: 11px; font-weight: 700;
|
||
border-radius: var(--r-md);
|
||
display: grid; place-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.step-item.done .num { background: var(--accent-black); color: var(--accent-white); border-color: var(--accent-black); }
|
||
.step-item.active .num { background: var(--heat); color: var(--accent-white); border-color: var(--heat); }
|
||
.step-item .info { flex: 1; min-width: 0; }
|
||
.step-item .ti2 { font-size: 13.5px; font-weight: 600; color: var(--black-alpha-56); }
|
||
.step-item.active .ti2 { color: var(--accent-black); }
|
||
.step-item.done .ti2 { color: var(--accent-black); }
|
||
.step-item .sub {
|
||
font-size: 11.5px; color: var(--black-alpha-48);
|
||
margin-top: 4px;
|
||
font-family: var(--font-mono);
|
||
letter-spacing: .02em;
|
||
line-height: 1.5;
|
||
}
|
||
.step-item.done .sub { color: var(--black-alpha-56); font-family: var(--font-sans); letter-spacing: 0; }
|
||
|
||
/* Sub items under active step */
|
||
.substep-list { padding: 4px 20px 8px 56px; }
|
||
.substep-item {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 8px 10px;
|
||
cursor: pointer;
|
||
font-size: 12.5px; color: var(--black-alpha-48);
|
||
border-left: 2px solid var(--border-faint);
|
||
margin-left: -4px;
|
||
transition: color var(--t-base), border-color var(--t-base);
|
||
}
|
||
.substep-item:hover { color: var(--black-alpha-56); border-left-color: var(--black-alpha-24); }
|
||
.substep-item.active { color: var(--heat); border-left-color: var(--heat); font-weight: 600; }
|
||
.substep-item.done { color: var(--accent-black); }
|
||
.substep-item .num-mini {
|
||
width: 18px; height: 18px;
|
||
border: 1px solid currentColor;
|
||
border-radius: var(--r-sm);
|
||
font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
|
||
display: grid; place-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.substep-item.active .num-mini, .substep-item.done .num-mini {
|
||
background: currentColor;
|
||
color: var(--accent-white);
|
||
}
|
||
.substep-item.done .num-mini { background: var(--accent-black); border-color: var(--accent-black); }
|
||
|
||
.side-divider { height: 1px; background: var(--border-faint); margin: 14px 20px; }
|
||
.side-section-h {
|
||
padding: 4px 20px 8px;
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px; color: var(--black-alpha-48);
|
||
letter-spacing: .08em; text-transform: uppercase;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.selected-assets-side { padding: 0 20px; }
|
||
.sel-asset-label {
|
||
font-size: 11.5px;
|
||
color: var(--black-alpha-48);
|
||
margin-bottom: 6px;
|
||
font-family: var(--font-mono);
|
||
letter-spacing: .02em;
|
||
}
|
||
.sel-asset-thumb { aspect-ratio: 4/5; margin-bottom: 14px; }
|
||
.sel-asset-thumb.square { aspect-ratio: 1; }
|
||
|
||
/* ─── Center canvas ─── */
|
||
.wb-canvas {
|
||
background: var(--background-base);
|
||
overflow-y: auto;
|
||
padding: 0;
|
||
display: flex; flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Sub-tab strip on top (only in Step 3) */
|
||
.subtab-strip {
|
||
padding: 0 32px;
|
||
border-bottom: 1px solid var(--border-faint);
|
||
background: var(--surface);
|
||
display: flex; align-items: center; gap: 0;
|
||
position: sticky; top: 0; z-index: 5;
|
||
}
|
||
.subtab {
|
||
padding: 14px 18px;
|
||
font-size: 13px; font-weight: 500;
|
||
color: var(--black-alpha-56);
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
cursor: pointer;
|
||
display: flex; align-items: center; gap: 8px;
|
||
transition: color var(--t-base), background var(--t-base);
|
||
border-radius: var(--r-md) var(--r-md) 0 0;
|
||
}
|
||
.subtab:hover { color: var(--accent-black); background: var(--black-alpha-4); }
|
||
.subtab.active { color: var(--accent-black); border-bottom-color: var(--heat); }
|
||
.subtab.disabled { color: var(--black-alpha-32); cursor: not-allowed; }
|
||
.subtab.disabled:hover { background: transparent; color: var(--black-alpha-32); }
|
||
.subtab .num-circ {
|
||
width: 20px; height: 20px;
|
||
border: 1px solid currentColor;
|
||
border-radius: 50%;
|
||
font-family: var(--font-mono); font-size: 10px; font-weight: 700;
|
||
display: grid; place-items: center;
|
||
}
|
||
.subtab.done .num-circ { background: var(--accent-black); color: var(--accent-white); border-color: var(--accent-black); }
|
||
.subtab.active .num-circ { background: var(--heat); color: var(--accent-white); border-color: var(--heat); }
|
||
.subtab .arrow { color: var(--black-alpha-32); padding: 0 6px; }
|
||
.subtab-meta {
|
||
margin-left: auto;
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .04em;
|
||
}
|
||
.subtab-meta .accent { color: var(--heat); font-weight: 700; }
|
||
|
||
/* Canvas inner */
|
||
.canvas-inner { padding: 32px; flex: 1; max-width: 1280px; }
|
||
.canvas-h {
|
||
margin-bottom: 24px;
|
||
display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
|
||
}
|
||
.canvas-h h2 { font-size: 24px; font-weight: 600; letter-spacing: -.018em; line-height: 1.25; color: var(--accent-black); }
|
||
.canvas-h p { font-size: 13.5px; color: var(--black-alpha-56); margin-top: 8px; line-height: 1.6; }
|
||
.canvas-h .step-tag {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
color: var(--black-alpha-48);
|
||
padding: 4px 10px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-sm);
|
||
letter-spacing: .04em;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Step pane switching */
|
||
.step-pane { display: none; }
|
||
.step-pane.active { display: block; animation: fade .2s ease; }
|
||
@keyframes fade {
|
||
from { opacity: 0; transform: translateY(4px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.sub-pane { display: none; }
|
||
.sub-pane.active { display: block; animation: fade .2s ease; }
|
||
|
||
/* ─── Step 1: 商品信息 + 上传原图 ─── */
|
||
.form-card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
padding: 28px;
|
||
max-width: 920px;
|
||
}
|
||
.s1-grid { display: grid; grid-template-columns: 280px 1fr; gap: 28px; }
|
||
.upload-c {
|
||
aspect-ratio: 4/5;
|
||
background: var(--surface);
|
||
border: 1.5px dashed var(--black-alpha-24);
|
||
border-radius: var(--r-md);
|
||
display: grid; place-items: center;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
color: var(--black-alpha-56);
|
||
margin-bottom: 12px;
|
||
transition: border-color var(--t-base), background var(--t-base), color var(--t-base);
|
||
padding: 24px;
|
||
}
|
||
.upload-c:hover {
|
||
border-color: var(--heat);
|
||
background: var(--heat-8);
|
||
color: var(--heat);
|
||
}
|
||
.upload-c.has-file {
|
||
border-style: solid; padding: 0;
|
||
border-color: var(--heat-40);
|
||
background: var(--background-lighter);
|
||
}
|
||
.upload-c.has-file .placeholder { width: 100%; height: 100%; }
|
||
.upload-c .ic-cam {
|
||
width: 44px; height: 44px;
|
||
border: 1px solid currentColor;
|
||
border-radius: var(--r-md);
|
||
display: grid; place-items: center;
|
||
margin: 0 auto 14px;
|
||
}
|
||
.upload-c .ic-cam svg { width: 22px; height: 22px; }
|
||
.up-hint {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
text-align: center;
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||
.bullet-list { list-style: none; padding: 0; }
|
||
.bullet-list li {
|
||
display: flex; gap: 10px; align-items: center;
|
||
padding: 10px 12px;
|
||
background: var(--background-lighter);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-sm);
|
||
margin-bottom: 6px;
|
||
font-size: 13px;
|
||
color: var(--accent-black);
|
||
}
|
||
.bullet-list .num {
|
||
width: 20px; height: 20px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-sm);
|
||
font-size: 11px; color: var(--black-alpha-56);
|
||
display: grid; place-items: center;
|
||
flex-shrink: 0;
|
||
font-family: var(--font-mono);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ─── Step 2: 头图 4 选 1 ─── */
|
||
.big-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
max-width: 760px;
|
||
}
|
||
.big-card {
|
||
aspect-ratio: 1;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: border-color var(--t-base), transform var(--t-fast);
|
||
}
|
||
.big-card:hover { border-color: var(--black-alpha-32); }
|
||
.big-card .placeholder { width: 100%; height: 100%; border: 0; border-radius: 0; }
|
||
.big-card.selected {
|
||
border: 2px solid var(--heat);
|
||
box-shadow: 0 0 0 4px var(--heat-12);
|
||
}
|
||
.big-card.selected::after {
|
||
content: '';
|
||
position: absolute; top: 12px; right: 12px;
|
||
width: 28px; height: 28px;
|
||
background: var(--heat);
|
||
color: var(--accent-white);
|
||
border-radius: 50%;
|
||
display: grid; place-items: center;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 8.5l3 3 6-6' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||
background-size: 16px;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
}
|
||
.big-card .corner-info {
|
||
position: absolute; bottom: 12px; left: 12px;
|
||
background: rgba(255,255,255,.95);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-sm);
|
||
padding: 4px 10px;
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
letter-spacing: .04em;
|
||
color: var(--black-alpha-56);
|
||
}
|
||
|
||
.regen-line {
|
||
margin-top: 20px;
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
.regen-line .info {
|
||
margin-left: auto;
|
||
font-family: var(--font-mono);
|
||
font-size: 11.5px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
/* ─── Step 3 SUB 1: 模特库网格 ─── */
|
||
.filter-bar {
|
||
display: flex; align-items: center; gap: 10px;
|
||
margin-bottom: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.search-input { position: relative; width: 280px; }
|
||
.search-input svg {
|
||
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
|
||
color: var(--black-alpha-56); width: 14px; height: 14px;
|
||
z-index: 2; pointer-events: none;
|
||
}
|
||
.search-input input {
|
||
padding-left: 36px;
|
||
height: 32px;
|
||
font-size: 12.5px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--black-alpha-12);
|
||
border-radius: var(--r-md);
|
||
color: var(--accent-black);
|
||
outline: none;
|
||
width: 100%;
|
||
transition: border-color var(--t-base);
|
||
}
|
||
.search-input input:focus { border-color: var(--heat-40); box-shadow: inset 0 0 0 1px var(--heat-40); }
|
||
.filter-chip {
|
||
height: 32px; padding: 0 12px;
|
||
border: 1px solid var(--border-faint);
|
||
background: var(--surface);
|
||
border-radius: var(--r-md);
|
||
font-size: 12.5px;
|
||
color: var(--black-alpha-56);
|
||
display: inline-flex; align-items: center; gap: 5px;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
transition: background var(--t-base), border-color var(--t-base), color var(--t-base);
|
||
}
|
||
.filter-chip:hover { background: var(--black-alpha-4); border-color: var(--black-alpha-24); color: var(--accent-black); }
|
||
.filter-chip.active { border-color: var(--heat-40); color: var(--heat); background: var(--heat-12); font-weight: 600; }
|
||
.filter-chip svg { width: 11px; height: 11px; }
|
||
.results-meta {
|
||
margin-left: auto;
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .04em;
|
||
}
|
||
.results-meta .accent { color: var(--heat); font-weight: 700; }
|
||
|
||
.model-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(6, 1fr);
|
||
gap: 12px;
|
||
}
|
||
@media (max-width: 1500px) { .model-grid { grid-template-columns: repeat(5, 1fr); } }
|
||
@media (max-width: 1280px) { .model-grid { grid-template-columns: repeat(4, 1fr); } }
|
||
|
||
.model-card {
|
||
aspect-ratio: 3/4;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: border-color var(--t-base);
|
||
}
|
||
.model-card:hover { border-color: var(--black-alpha-32); }
|
||
.model-card .placeholder { width: 100%; height: 100%; border: 0; border-radius: 0; }
|
||
.model-card.selected {
|
||
border: 2px solid var(--heat);
|
||
box-shadow: 0 0 0 3px var(--heat-12);
|
||
}
|
||
.model-card.selected::after {
|
||
content: '';
|
||
position: absolute; top: 8px; right: 8px;
|
||
width: 22px; height: 22px;
|
||
background: var(--heat);
|
||
color: var(--accent-white);
|
||
border-radius: 50%;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 8.5l3 3 6-6' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||
background-size: 14px;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
z-index: 2;
|
||
}
|
||
.model-card .lbl-bottom {
|
||
position: absolute; bottom: 0; left: 0; right: 0;
|
||
background: rgba(255,255,255,.96);
|
||
border-top: 1px solid var(--border-faint);
|
||
padding: 8px 10px;
|
||
display: flex; flex-direction: column; gap: 3px;
|
||
}
|
||
.model-card .lbl-bottom .nm {
|
||
font-size: 12.5px; font-weight: 600;
|
||
color: var(--accent-black);
|
||
}
|
||
.model-card .lbl-bottom .tags {
|
||
font-family: var(--font-mono);
|
||
font-size: 9.5px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .02em;
|
||
}
|
||
.model-card .pose-tag {
|
||
position: absolute; top: 8px; left: 8px;
|
||
background: rgba(0,0,0,.6);
|
||
color: var(--accent-white);
|
||
font-family: var(--font-mono);
|
||
font-size: 9px;
|
||
border-radius: var(--r-sm);
|
||
padding: 2px 6px;
|
||
letter-spacing: .04em;
|
||
z-index: 1;
|
||
}
|
||
|
||
.scroll-loader {
|
||
margin-top: 20px;
|
||
padding: 14px 18px;
|
||
background: var(--surface);
|
||
border: 1px dashed var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
text-align: center;
|
||
font-family: var(--font-mono);
|
||
font-size: 11.5px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
/* ─── Step 3 SUB 2: 上身图 + 模特切换 ─── */
|
||
.model-switcher {
|
||
display: flex; align-items: center; gap: 8px;
|
||
margin-bottom: 20px;
|
||
padding: 12px 16px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
flex-wrap: wrap;
|
||
}
|
||
.model-switcher .lbl {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .02em;
|
||
}
|
||
.model-switcher .hint-right {
|
||
margin-left: auto;
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
/* ─── Step 4: 完成确认 ─── */
|
||
.review-box {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
padding: 32px;
|
||
max-width: 800px;
|
||
}
|
||
.review-box h3 { font-size: 22px; font-weight: 600; letter-spacing: -.012em; color: var(--accent-black); }
|
||
.review-box .review-meta {
|
||
font-family: var(--font-mono);
|
||
font-size: 11.5px;
|
||
color: var(--black-alpha-48);
|
||
margin-top: 6px;
|
||
letter-spacing: .02em;
|
||
}
|
||
.review-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 10px;
|
||
margin-top: 20px;
|
||
}
|
||
.review-grid .placeholder { aspect-ratio: 1; }
|
||
.review-summary {
|
||
margin-top: 24px;
|
||
padding: 16px 20px;
|
||
background: var(--background-lighter);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
font-size: 13px;
|
||
color: var(--black-alpha-72);
|
||
line-height: 1.7;
|
||
}
|
||
.review-summary dl > div {
|
||
display: flex;
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid var(--border-faint);
|
||
}
|
||
.review-summary dl > div:last-child { border-bottom: 0; }
|
||
.review-summary dl dt {
|
||
width: 80px;
|
||
color: var(--black-alpha-48);
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
letter-spacing: .04em;
|
||
}
|
||
.review-summary dl dd {
|
||
flex: 1;
|
||
color: var(--accent-black);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ─── Right panel ─── */
|
||
.wb-controls {
|
||
border-left: 1px solid var(--border-faint);
|
||
background: var(--background-base);
|
||
overflow-y: auto;
|
||
padding: 22px 22px 140px;
|
||
display: flex; flex-direction: column;
|
||
position: relative;
|
||
}
|
||
.ctrl-section { margin-bottom: 24px; }
|
||
.ctrl-section h3 {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .08em;
|
||
text-transform: uppercase;
|
||
font-weight: 600;
|
||
margin-bottom: 10px;
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
.ctrl-section h3 .accent {
|
||
color: var(--heat);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.hover-detail {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
padding: 16px;
|
||
}
|
||
.hover-detail .hd-name {
|
||
font-size: 16px; font-weight: 600;
|
||
color: var(--accent-black);
|
||
}
|
||
.hover-detail .hd-tags {
|
||
display: flex; gap: 5px; flex-wrap: wrap;
|
||
margin-top: 8px;
|
||
}
|
||
.hover-detail .hd-tags span {
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
color: var(--black-alpha-56);
|
||
background: var(--background-lighter);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-sm);
|
||
padding: 2px 6px;
|
||
letter-spacing: .02em;
|
||
}
|
||
.hover-detail .hd-views {
|
||
display: grid; grid-template-columns: repeat(3, 1fr);
|
||
gap: 5px; margin-top: 14px;
|
||
}
|
||
.hover-detail .hd-views .placeholder { aspect-ratio: 3/4; }
|
||
.hover-detail .hd-meta {
|
||
display: flex; flex-direction: column; gap: 7px;
|
||
margin-top: 14px; padding-top: 14px;
|
||
border-top: 1px solid var(--border-faint);
|
||
font-size: 12px;
|
||
color: var(--black-alpha-72);
|
||
}
|
||
.hover-detail .hd-meta div { display: flex; gap: 10px; }
|
||
.hover-detail .hd-meta .k {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
color: var(--black-alpha-48);
|
||
width: 60px;
|
||
letter-spacing: .04em;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.selected-list { display: flex; flex-direction: column; gap: 6px; }
|
||
.sel-row {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 8px 10px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
transition: border-color var(--t-base);
|
||
}
|
||
.sel-row:hover { border-color: var(--black-alpha-24); }
|
||
.sel-row .placeholder { width: 30px; height: 38px; flex-shrink: 0; border-radius: var(--r-sm); }
|
||
.sel-row .nm { flex: 1; font-size: 12.5px; font-weight: 600; color: var(--accent-black); }
|
||
.sel-row .x-btn {
|
||
width: 22px; height: 22px;
|
||
border-radius: var(--r-sm);
|
||
display: grid; place-items: center;
|
||
cursor: pointer;
|
||
color: var(--black-alpha-48);
|
||
transition: color var(--t-base), background var(--t-base);
|
||
}
|
||
.sel-row .x-btn:hover { color: var(--accent-crimson); background: var(--crimson-bg); }
|
||
|
||
.sel-empty {
|
||
padding: 18px 14px;
|
||
text-align: center;
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
border: 1px dashed var(--border-faint);
|
||
border-radius: var(--r-md);
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
.tip-mini {
|
||
padding: 12px 14px;
|
||
background: var(--heat-8);
|
||
border: 1px solid var(--heat-20);
|
||
border-radius: var(--r-md);
|
||
font-size: 12px;
|
||
color: var(--black-alpha-72);
|
||
line-height: 1.6;
|
||
}
|
||
.tip-mini strong {
|
||
color: var(--heat);
|
||
display: block;
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
letter-spacing: .04em;
|
||
margin-bottom: 6px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ─── Bottom bar (sticky in right column) ─── */
|
||
.wb-bottom {
|
||
position: absolute;
|
||
left: 0; right: 0; bottom: 0;
|
||
padding: 14px 22px;
|
||
background: var(--surface);
|
||
border-top: 1px solid var(--border-faint);
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
}
|
||
.wb-bottom .row1 {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
}
|
||
.cost-info {
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
color: var(--black-alpha-48);
|
||
letter-spacing: .04em;
|
||
}
|
||
.cost-info .price { color: var(--heat); font-weight: 700; }
|
||
|
||
/* ─── "我有图" 模式专用样式 ─── */
|
||
.single-mode {
|
||
display: none;
|
||
max-width: 920px;
|
||
margin: 0 auto;
|
||
}
|
||
.single-mode.active { display: block; }
|
||
.upload-grid {
|
||
display: grid; grid-template-columns: repeat(4, 1fr);
|
||
gap: 8px; margin-top: 12px;
|
||
}
|
||
.upload-grid .placeholder { aspect-ratio: 1; }
|
||
.upload-zone {
|
||
border: 1.5px dashed var(--black-alpha-24);
|
||
border-radius: var(--r-md);
|
||
padding: 28px;
|
||
text-align: center;
|
||
background: var(--background-lighter);
|
||
color: var(--black-alpha-56);
|
||
font-size: 13.5px;
|
||
cursor: pointer;
|
||
transition: border-color var(--t-base), background var(--t-base);
|
||
}
|
||
.upload-zone:hover { border-color: var(--heat); background: var(--heat-8); }
|
||
.upload-zone strong { color: var(--heat); font-weight: 600; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="wb">
|
||
|
||
<!-- ============ TOP ============ -->
|
||
<header class="wb-top">
|
||
<a class="home" href="products.html">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="m15 18-6-6 6-6"/></svg>
|
||
返回商品库
|
||
</a>
|
||
<div class="ti">
|
||
<input class="name-edit" value="透真玻尿酸补水面膜" placeholder="点击编辑商品名">
|
||
<span class="mode-pill" id="mode-pill">[ AI 生成模式 ]</span>
|
||
</div>
|
||
<div class="right">
|
||
<button class="btn btn-sm" onclick="Shell.toast('已保存草稿', '/products/draft')">保存草稿</button>
|
||
<div class="x" onclick="if(confirm('确定退出?未保存的内容会丢失。')) location.href='products.html'">
|
||
<svg viewBox="0 0 16 16"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ============ MODE SWITCH ============ -->
|
||
<div class="mode-row">
|
||
<span class="lbl">// 创建模式</span>
|
||
<div class="mode-toggle" id="mode-toggle">
|
||
<button data-mode="upload">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>
|
||
我有商品图
|
||
</button>
|
||
<button class="active" data-mode="ai">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9.663 17h4.673M12 3v1M3.34 7l.7.7M20.66 7l-.7.7M2 12h1M21 12h1M12 19a7 7 0 100-14 7 7 0 000 14z"/></svg>
|
||
AI 帮我生成图
|
||
</button>
|
||
</div>
|
||
<div class="mode-hint" id="mode-hint">
|
||
<span class="mono">// AI 模式 ·</span> 上传 1 张原图,AI 生成头图 + 模特上身图
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ MAIN ============ -->
|
||
<div class="wb-main">
|
||
|
||
<!-- LEFT SIDEBAR -->
|
||
<aside class="wb-side" id="wb-side">
|
||
|
||
<!-- AI mode steps -->
|
||
<div id="ai-steps">
|
||
<div class="side-section-h">// 创建流程 · 4 步</div>
|
||
<div class="step-item done" data-step="1"><div class="num"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l5 5L20 6"/></svg></div><div class="info"><div class="ti2">商品信息</div><div class="sub">名称、品类、价格、卖点</div></div></div>
|
||
<div class="step-item done" data-step="2"><div class="num"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l5 5L20 6"/></svg></div><div class="info"><div class="ti2">生成头图</div><div class="sub">已选 1 / 4 · 候选 A</div></div></div>
|
||
<div class="step-item active" data-step="3">
|
||
<div class="num">3</div>
|
||
<div class="info"><div class="ti2">模特上身图</div><div class="sub">// 子流程 · ① 挑模特 → ② 生上身</div></div>
|
||
</div>
|
||
<div class="substep-list" id="sub-nav">
|
||
<div class="substep-item active" data-sub="1">
|
||
<span class="num-mini">①</span>
|
||
<span>挑选模特</span>
|
||
<span style="margin-left:auto; font-family:var(--font-mono); font-size:10px; opacity:.7;" id="sub1-meta">2 / 50+</span>
|
||
</div>
|
||
<div class="substep-item" data-sub="2">
|
||
<span class="num-mini">②</span>
|
||
<span>生成上身图</span>
|
||
<span style="margin-left:auto; font-family:var(--font-mono); font-size:10px; opacity:.6;">待开始</span>
|
||
</div>
|
||
</div>
|
||
<div class="step-item locked" data-step="4"><div class="num">4</div><div class="info"><div class="ti2">完成创建</div><div class="sub">// 预览 · 提交</div></div></div>
|
||
</div>
|
||
|
||
<!-- Upload mode steps -->
|
||
<div id="upload-steps" style="display:none;">
|
||
<div class="side-section-h">// 创建流程 · 单步</div>
|
||
<div class="step-item active" data-step="u1">
|
||
<div class="num">1</div>
|
||
<div class="info"><div class="ti2">商品信息 + 图册</div><div class="sub">// 一次性填完即可创建</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="side-divider"></div>
|
||
|
||
<!-- Selected assets -->
|
||
<div class="side-section-h">// 已选素材</div>
|
||
<div class="selected-assets-side">
|
||
<div class="sel-asset-label">原图 · 1 张</div>
|
||
<div class="placeholder sel-asset-thumb"><span class="ph-frame">补水面膜<br>1200×1500</span></div>
|
||
|
||
<div id="ai-assets-side" style="display:block;">
|
||
<div class="sel-asset-label">头图 · 候选 A</div>
|
||
<div class="placeholder sel-asset-thumb square"><span class="ph-frame">A · 白底简约</span></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- CENTER CANVAS -->
|
||
<main class="wb-canvas" id="wb-canvas">
|
||
|
||
<!-- ====== AI MODE: 4 steps ====== -->
|
||
<div id="ai-mode-content">
|
||
|
||
<!-- Sub-tab strip (visible only in Step 3) -->
|
||
<div class="subtab-strip" id="subtab-strip">
|
||
<div class="subtab active" data-sub="1">
|
||
<span class="num-circ">①</span>
|
||
挑选模特
|
||
</div>
|
||
<span class="arrow"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg></span>
|
||
<div class="subtab disabled" data-sub="2">
|
||
<span class="num-circ">②</span>
|
||
生成上身图
|
||
</div>
|
||
<span class="subtab-meta" id="subtab-meta">// SUB-STEP 1 / 2 · 已选模特 <span class="accent" id="meta-count">2</span></span>
|
||
</div>
|
||
|
||
<div class="canvas-inner">
|
||
|
||
<!-- ============= STEP 1 · 商品信息 ============= -->
|
||
<section class="step-pane" data-pane="1">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>商品信息 · 上传原图</h2>
|
||
<p>上传一张你拍的商品图(任何角度都行,AI 会优化),并填写商品基本信息。</p>
|
||
</div>
|
||
<span class="step-tag">// STEP 1 / 4 · INFO + UPLOAD</span>
|
||
</div>
|
||
<div class="form-card">
|
||
<div class="s1-grid">
|
||
<div>
|
||
<div class="upload-c has-file"><div class="placeholder"><span class="ph-frame">补水面膜.jpg<br>1200×1500</span></div></div>
|
||
<div class="up-hint">// 已上传 · 点击重新选择</div>
|
||
</div>
|
||
<div>
|
||
<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" style="margin-bottom:0;">
|
||
<label class="field-label">目标人群</label>
|
||
<input class="input" value="22-32 岁女性、熬夜党、敏感肌">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============= STEP 2 · 生成头图 ============= -->
|
||
<section class="step-pane" data-pane="2">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>选一张你最满意的头图</h2>
|
||
<p>AI 已基于你上传的原图生成 4 张候选,点击你想要的那张。可重新生成。</p>
|
||
</div>
|
||
<span class="step-tag">// STEP 2 / 4 · HEAD IMAGES</span>
|
||
</div>
|
||
<div class="big-grid" id="head-grid">
|
||
<div class="big-card selected" data-id="h1"><div class="placeholder"><span class="ph-frame">候选 A · 白底简约</span></div><span class="corner-info">[ A · 1920×1920 · WHITE-BG ]</span></div>
|
||
<div class="big-card" data-id="h2"><div class="placeholder"><span class="ph-frame">候选 B · 木纹背景</span></div><span class="corner-info">[ B · 1920×1920 · WOOD ]</span></div>
|
||
<div class="big-card" data-id="h3"><div class="placeholder"><span class="ph-frame">候选 C · 浅米石材</span></div><span class="corner-info">[ C · 1920×1920 · STONE ]</span></div>
|
||
<div class="big-card" data-id="h4"><div class="placeholder"><span class="ph-frame">候选 D · 暖光氛围</span></div><span class="corner-info">[ D · 1920×1920 · AMBIENT ]</span></div>
|
||
</div>
|
||
<div class="regen-line">
|
||
<button class="btn" onclick="regen('head')">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 12a9 9 0 11-3.5-7.1L21 8M21 4v4h-4"/></svg>
|
||
全部重新生成
|
||
</button>
|
||
<span class="info">// ~¥0.20 / 4 张 · 不满意不扣费</span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============= STEP 3 · 模特上身图(子 Tab) ============= -->
|
||
<section class="step-pane active" data-pane="3">
|
||
|
||
<!-- SUB 1 · 挑选模特 -->
|
||
<div class="sub-pane active" data-sub-pane="1">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>挑选模特 · 子步骤 ①</h2>
|
||
<p>从 AI 模特库选 1 个或多个,统一白 T + 白短裤立绘。悬浮看详情和三视图。选好后点右下「下一步」自动跳到 ②。</p>
|
||
</div>
|
||
<span class="step-tag">// STEP 3 / 4 · SUB 1 · MODEL</span>
|
||
</div>
|
||
|
||
<div class="filter-bar">
|
||
<div class="search-input">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||
<input type="text" placeholder="搜索模特名称、风格…">
|
||
</div>
|
||
<button class="filter-chip active">全部 <span style="font-family:var(--font-mono); opacity:.7;">52</span></button>
|
||
<button class="filter-chip">女性</button>
|
||
<button class="filter-chip">男性</button>
|
||
<button class="filter-chip">25-30 岁</button>
|
||
<button class="filter-chip">都市白领</button>
|
||
<button class="filter-chip">学生</button>
|
||
<button class="filter-chip">居家</button>
|
||
<button class="filter-chip">+ 更多 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg></button>
|
||
<span class="results-meta">// 12 / 52 · 已选 <span class="accent" id="sel-count">2</span></span>
|
||
</div>
|
||
|
||
<div class="model-grid" id="model-grid">
|
||
<div class="model-card selected" data-id="m1" data-name="林夕" data-base="女 · 25-30" data-role="都市白领" data-tags="温柔,日常,OL,通勤">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">林夕 · 都市女</span></div>
|
||
<div class="lbl-bottom"><div class="nm">林夕</div><div class="tags">女 · 25-30 · 都市白领</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m2" data-name="阿楠" data-base="女 · 25-30" data-role="姐妹/同事" data-tags="精致,干练,短发">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">阿楠</span></div>
|
||
<div class="lbl-bottom"><div class="nm">阿楠</div><div class="tags">女 · 25-30 · 同事</div></div>
|
||
</div>
|
||
<div class="model-card selected" data-id="m3" data-name="小七" data-base="女 · 18-22" data-role="学生/Z世代" data-tags="青春,元气,校园">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">小七 · 学生</span></div>
|
||
<div class="lbl-bottom"><div class="nm">小七</div><div class="tags">女 · 18-22 · 学生</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m4" data-name="王姐" data-base="女 · 38-45" data-role="妈妈/居家" data-tags="亲和,成熟,温暖">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">王姐 · 居家</span></div>
|
||
<div class="lbl-bottom"><div class="nm">王姐</div><div class="tags">女 · 38-45 · 妈妈</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m5" data-name="阿杰" data-base="男 · 28-35" data-role="通勤男" data-tags="商务,干练,西装">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">阿杰 · 通勤男</span></div>
|
||
<div class="lbl-bottom"><div class="nm">阿杰</div><div class="tags">男 · 28-35 · 通勤</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m6" data-name="阿强" data-base="男 · 22-28" data-role="健身男" data-tags="阳光,运动,肌肉">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">阿强 · 健身男</span></div>
|
||
<div class="lbl-bottom"><div class="nm">阿强</div><div class="tags">男 · 22-28 · 健身</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m7" data-name="小苏" data-base="女 · 22-26" data-role="文艺研究生" data-tags="书卷气,静谧,文艺">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">小苏 · 文艺女</span></div>
|
||
<div class="lbl-bottom"><div class="nm">小苏</div><div class="tags">女 · 22-26 · 文艺</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m8" data-name="文文" data-base="女 · 30-38" data-role="母亲" data-tags="温柔,母性,温暖">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">文文 · 母亲</span></div>
|
||
<div class="lbl-bottom"><div class="nm">文文</div><div class="tags">女 · 30-38 · 母亲</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m9" data-name="老王" data-base="男 · 45-55" data-role="大叔" data-tags="沉稳,成熟,商务">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">老王 · 大叔</span></div>
|
||
<div class="lbl-bottom"><div class="nm">老王</div><div class="tags">男 · 45-55 · 大叔</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m10" data-name="萌萌" data-base="女 · 18-22" data-role="Z世代" data-tags="可爱,潮流,二次元">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">萌萌 · Z世代</span></div>
|
||
<div class="lbl-bottom"><div class="nm">萌萌</div><div class="tags">女 · 18-22 · Z 世代</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m11" data-name="Leo" data-base="男 · 30-38" data-role="健身教练" data-tags="专业,力量,健身房">
|
||
<span class="pose-tag">[ 立绘 · 肩上 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">Leo · 教练</span></div>
|
||
<div class="lbl-bottom"><div class="nm">Leo</div><div class="tags">男 · 30-38 · 教练</div></div>
|
||
</div>
|
||
<div class="model-card" data-id="m12" data-name="闺蜜组合" data-base="双人 · 25-30" data-role="闺蜜" data-tags="亲密,日常,搭档">
|
||
<span class="pose-tag">[ 立绘 · 双人 ]</span>
|
||
<div class="placeholder"><span class="ph-frame">闺蜜双人</span></div>
|
||
<div class="lbl-bottom"><div class="nm">闺蜜组合</div><div class="tags">双人 · 25-30</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="scroll-loader">
|
||
// 还有 40 个模特 · 滚动加载 / 用上方筛选缩小范围
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SUB 2 · 生成上身图 -->
|
||
<div class="sub-pane" data-sub-pane="2">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>生成上身图 · 子步骤 ②</h2>
|
||
<p>已为你选的每个模特生成 4 张上身图,可多选保留。点 ← 回到「① 挑选模特」改人。</p>
|
||
</div>
|
||
<span class="step-tag">// STEP 3 / 4 · SUB 2 · WEAR</span>
|
||
</div>
|
||
|
||
<div class="model-switcher">
|
||
<span class="lbl">// 当前模特:</span>
|
||
<button class="filter-chip active">林夕 · 都市白领</button>
|
||
<button class="filter-chip">小七 · 学生</button>
|
||
<span class="hint-right">// 切换模特看不同上身效果</span>
|
||
</div>
|
||
|
||
<div class="big-grid" id="wear-grid">
|
||
<div class="big-card selected"><div class="placeholder"><span class="ph-frame">上身 A · 持物半身</span></div><span class="corner-info">[ A · 持物半身 ]</span></div>
|
||
<div class="big-card"><div class="placeholder"><span class="ph-frame">上身 B · 敷面膜中</span></div><span class="corner-info">[ B · 敷面膜中 ]</span></div>
|
||
<div class="big-card selected"><div class="placeholder"><span class="ph-frame">上身 C · 镜前自拍</span></div><span class="corner-info">[ C · 镜前自拍 ]</span></div>
|
||
<div class="big-card"><div class="placeholder"><span class="ph-frame">上身 D · 床边特写</span></div><span class="corner-info">[ D · 床边特写 ]</span></div>
|
||
</div>
|
||
|
||
<div class="regen-line">
|
||
<button class="btn" onclick="regen('wear')">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 12a9 9 0 11-3.5-7.1L21 8M21 4v4h-4"/></svg>
|
||
全部重新生成
|
||
</button>
|
||
<span class="info">// ~¥0.40 / 4 张 · 已选 2 / 4 张</span>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
<!-- ============= STEP 4 · 完成 ============= -->
|
||
<section class="step-pane" data-pane="4">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>预览 · 创建</h2>
|
||
<p>检查下面的素材和信息无误后,点击右下「✓ 创建商品」。</p>
|
||
</div>
|
||
<span class="step-tag">// STEP 4 / 4 · CONFIRM</span>
|
||
</div>
|
||
<div class="review-box">
|
||
<h3>透真玻尿酸补水面膜</h3>
|
||
<div class="review-meta">// 美妆个护 · ¥39.9 · AI 生成 · 模特: 林夕 + 小七</div>
|
||
<div class="review-grid">
|
||
<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 class="review-summary">
|
||
<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 · 白底简约)</dd></div>
|
||
<div><dt>上身图</dt><dd>2 张(A + C · 模特林夕 / 小七)</dd></div>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ====== UPLOAD MODE: single page ====== -->
|
||
<div class="single-mode" id="upload-mode-content">
|
||
<div class="canvas-inner">
|
||
<div class="canvas-h">
|
||
<div>
|
||
<h2>填写商品信息 · 上传图册</h2>
|
||
<p>已有现成商品图册(主图 + 细节图)的快速通道。一次性填完,直接创建商品。</p>
|
||
</div>
|
||
<span class="step-tag">// UPLOAD MODE · ALL-IN-ONE</span>
|
||
</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><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>
|
||
<div class="upload-zone">
|
||
将图片拖到此处,或 <strong>点击上传</strong><br>
|
||
<span style="color:var(--black-alpha-48); font-size:11.5px;" class="mono">// 建议 3-6 张 · 1200×1200+ · JPG / PNG</span>
|
||
</div>
|
||
<div class="upload-grid">
|
||
<div class="placeholder"><span class="ph-frame">主图</span></div>
|
||
<div class="placeholder"><span class="ph-frame">细节</span></div>
|
||
<div class="placeholder"><span class="ph-frame">使用</span></div>
|
||
<div class="placeholder"><span class="ph-frame">+</span></div>
|
||
</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">3</span> 0 香精 0 酒精,敏感肌可用</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" style="margin-bottom:0;">
|
||
<label class="field-label">目标人群</label>
|
||
<input class="input" value="22-32 岁女性、熬夜党、敏感肌、办公室通勤">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<!-- RIGHT CONTROLS -->
|
||
<aside class="wb-controls">
|
||
|
||
<!-- AI mode controls -->
|
||
<div id="ai-controls">
|
||
<div class="ctrl-section">
|
||
<h3>// 当前悬浮 · DETAIL</h3>
|
||
<div class="hover-detail" id="hover-detail">
|
||
<div class="hd-name">林夕</div>
|
||
<div class="hd-tags" id="hd-tags">
|
||
<span>女</span><span>25-30</span><span>都市白领</span><span>温柔</span><span>OL</span><span>通勤</span>
|
||
</div>
|
||
<div class="hd-views">
|
||
<div class="placeholder"><span class="ph-frame">正面</span></div>
|
||
<div class="placeholder"><span class="ph-frame">侧面</span></div>
|
||
<div class="placeholder"><span class="ph-frame">背面</span></div>
|
||
</div>
|
||
<div class="hd-meta">
|
||
<div><span class="k">// 服装</span><span>白 T + 白短裤(默认立绘)</span></div>
|
||
<div><span class="k">// 年龄</span><span>25-30 岁 · 可微调 ±3</span></div>
|
||
<div><span class="k">// 身材</span><span>普通女 · 165cm / 50kg</span></div>
|
||
<div><span class="k">// 用过</span><span>4 个项目 · 平均评分 4.5</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ctrl-section">
|
||
<h3>// 已选模特 · <span class="accent" id="sel-count-3">2</span> 个</h3>
|
||
<div class="selected-list" id="selected-list">
|
||
<div class="sel-row" data-id="m1">
|
||
<div class="placeholder"><span class="ph-frame">林</span></div>
|
||
<div class="nm">林夕 · 都市白领</div>
|
||
<div class="x-btn" title="移除">
|
||
<svg width="12" height="12" viewBox="0 0 16 16"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
<div class="sel-row" data-id="m3">
|
||
<div class="placeholder"><span class="ph-frame">七</span></div>
|
||
<div class="nm">小七 · 学生</div>
|
||
<div class="x-btn" title="移除">
|
||
<svg width="12" height="12" viewBox="0 0 16 16"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ctrl-section">
|
||
<h3>// TIP</h3>
|
||
<div class="tip-mini">
|
||
<strong>多选会怎样</strong>
|
||
每选一个模特,② 会为其各自生成 4 张上身图。已选 2 个 = 8 张候选,可多选保留作素材。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bottom action bar -->
|
||
<div class="wb-bottom">
|
||
<div class="row1">
|
||
<span class="cost-info" id="cost-info">// 预计 <span class="price">¥0.80</span> / 8 张</span>
|
||
<span class="cost-info" id="bottom-meta">SUB 1 / 2</span>
|
||
</div>
|
||
<div class="hstack" id="action-bar">
|
||
<button class="btn" style="flex:1;" id="btn-prev-sub"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg> 上一步</button>
|
||
<button class="btn btn-primary" style="flex:1;" id="btn-next-sub">下一步 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg> 生成上身</button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script src="assets/shell.js"></script>
|
||
<script>
|
||
// ============================================================
|
||
// STATE
|
||
// ============================================================
|
||
const state = {
|
||
mode: 'ai', // 'ai' | 'upload'
|
||
step: 3, // 1-4 (AI mode); only u1 in upload mode
|
||
sub: 1 // 1 | 2 (Step 3 sub-tab)
|
||
};
|
||
|
||
// ============================================================
|
||
// MODE TOGGLE
|
||
// ============================================================
|
||
document.querySelectorAll('#mode-toggle button').forEach(b => {
|
||
b.onclick = () => {
|
||
const mode = b.dataset.mode;
|
||
state.mode = mode;
|
||
document.querySelectorAll('#mode-toggle button').forEach(x => x.classList.remove('active'));
|
||
b.classList.add('active');
|
||
|
||
const isUpload = mode === 'upload';
|
||
document.getElementById('mode-pill').textContent = isUpload ? '[ 我有商品图模式 ]' : '[ AI 生成模式 ]';
|
||
document.getElementById('mode-hint').innerHTML = isUpload
|
||
? '<span class="mono">// 上传模式 ·</span> 已有商品图册的快速通道,一次性创建'
|
||
: '<span class="mono">// AI 模式 ·</span> 上传 1 张原图,AI 生成头图 + 模特上身图';
|
||
|
||
// sidebar steps
|
||
document.getElementById('ai-steps').style.display = isUpload ? 'none' : 'block';
|
||
document.getElementById('upload-steps').style.display = isUpload ? 'block' : 'none';
|
||
document.getElementById('ai-assets-side').style.display = isUpload ? 'none' : 'block';
|
||
|
||
// canvas
|
||
document.getElementById('ai-mode-content').style.display = isUpload ? 'none' : 'block';
|
||
document.getElementById('upload-mode-content').classList.toggle('active', isUpload);
|
||
|
||
// controls panel
|
||
document.getElementById('ai-controls').style.display = isUpload ? 'none' : 'block';
|
||
|
||
// bottom action bar
|
||
const ab = document.getElementById('action-bar');
|
||
const ci = document.getElementById('cost-info');
|
||
const bm = document.getElementById('bottom-meta');
|
||
if (isUpload) {
|
||
ab.innerHTML = `
|
||
<button class="btn" style="flex:1;" onclick="location.href='products.html'">取消</button>
|
||
<button class="btn btn-primary" style="flex:1;" onclick="alert('商品已创建!')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l5 5L20 6"/></svg> 创建商品</button>
|
||
`;
|
||
ci.innerHTML = '// 上传模式 · 不消耗 token';
|
||
bm.textContent = '一次性创建';
|
||
} else {
|
||
ab.innerHTML = `
|
||
<button class="btn" style="flex:1;" id="btn-prev-sub"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg> 上一步</button>
|
||
<button class="btn btn-primary" style="flex:1;" id="btn-next-sub">下一步 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg> 生成上身</button>
|
||
`;
|
||
ci.innerHTML = '// 预计 <span class="price">¥0.80</span> / 8 张';
|
||
bm.textContent = `SUB ${state.sub} / 2`;
|
||
bindStepButtons();
|
||
}
|
||
|
||
Shell.toast('已切换模式', isUpload ? '我有商品图' : 'AI 生成图');
|
||
};
|
||
});
|
||
|
||
// ============================================================
|
||
// STEP / SUB-STEP NAVIGATION (AI mode)
|
||
// ============================================================
|
||
function showStep(n) {
|
||
state.step = n;
|
||
document.querySelectorAll('.step-pane').forEach(p => p.classList.remove('active'));
|
||
document.querySelector(`[data-pane="${n}"]`)?.classList.add('active');
|
||
|
||
// sidebar steps
|
||
document.querySelectorAll('#ai-steps .step-item').forEach(s => {
|
||
s.classList.remove('active', 'done', 'locked');
|
||
const sn = +s.dataset.step;
|
||
if (sn < n) s.classList.add('done');
|
||
else if (sn === n) s.classList.add('active');
|
||
else s.classList.add('locked');
|
||
});
|
||
|
||
// sub-tab strip visible only in step 3
|
||
document.getElementById('subtab-strip').style.display = n === 3 ? 'flex' : 'none';
|
||
|
||
// hide sub-nav when not in step 3
|
||
document.getElementById('sub-nav').style.display = n === 3 ? 'block' : 'none';
|
||
|
||
// update bottom action bar
|
||
updateBottom();
|
||
document.querySelector('.wb-canvas').scrollTop = 0;
|
||
}
|
||
|
||
function showSub(n) {
|
||
state.sub = n;
|
||
document.querySelectorAll('.sub-pane').forEach(p => p.classList.remove('active'));
|
||
document.querySelector(`[data-sub-pane="${n}"]`)?.classList.add('active');
|
||
|
||
document.querySelectorAll('.subtab').forEach(t => {
|
||
t.classList.remove('active', 'done', 'disabled');
|
||
const tn = +t.dataset.sub;
|
||
if (tn < n) t.classList.add('done');
|
||
else if (tn === n) t.classList.add('active');
|
||
});
|
||
|
||
document.querySelectorAll('.substep-item').forEach(t => {
|
||
t.classList.remove('active', 'done');
|
||
const tn = +t.dataset.sub;
|
||
if (tn < n) t.classList.add('done');
|
||
else if (tn === n) t.classList.add('active');
|
||
});
|
||
|
||
document.getElementById('subtab-meta').innerHTML = n === 1
|
||
? `// SUB-STEP 1 / 2 · 已选模特 <span class="accent">${selectedCount()}</span>`
|
||
: '// SUB-STEP 2 / 2 · 上身图 4 选 N · 多选保留';
|
||
|
||
updateBottom();
|
||
document.querySelector('.wb-canvas').scrollTop = 0;
|
||
}
|
||
|
||
function updateBottom() {
|
||
const ci = document.getElementById('cost-info');
|
||
const bm = document.getElementById('bottom-meta');
|
||
const btnPrev = document.getElementById('btn-prev-sub');
|
||
const btnNext = document.getElementById('btn-next-sub');
|
||
|
||
if (state.step === 3) {
|
||
bm.textContent = `SUB ${state.sub} / 2`;
|
||
if (state.sub === 1) {
|
||
ci.innerHTML = `// 预计 <span class="price">¥${(0.40 * selectedCount()).toFixed(2)}</span> / ${4 * selectedCount()} 张`;
|
||
const SVG_LEFT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>';
|
||
const SVG_RIGHT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
|
||
if (btnPrev) btnPrev.innerHTML = SVG_LEFT + ' 返回头图';
|
||
if (btnNext) btnNext.innerHTML = '下一步 ' + SVG_RIGHT + ' 生成上身';
|
||
} else {
|
||
ci.innerHTML = '// 已生成 4 张 / 模特 · 累计 ¥0.80';
|
||
const SVG_LEFT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>';
|
||
const SVG_RIGHT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
|
||
if (btnPrev) btnPrev.innerHTML = SVG_LEFT + ' 改模特';
|
||
if (btnNext) btnNext.innerHTML = '下一步 ' + SVG_RIGHT + ' 完成创建';
|
||
}
|
||
} else {
|
||
bm.textContent = `STEP ${state.step} / 4`;
|
||
if (state.step === 1) { ci.innerHTML = '// 填写信息 · 不消耗 token'; }
|
||
else if (state.step === 2) { ci.innerHTML = '// ~¥0.20 / 4 张候选'; }
|
||
else if (state.step === 4) { ci.innerHTML = '// 累计预估 <span class="price">¥1.00</span>'; }
|
||
const SVG_LEFT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>';
|
||
const SVG_RIGHT = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
|
||
const SVG_CHECK = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l5 5L20 6"/></svg>';
|
||
if (btnPrev) btnPrev.innerHTML = state.step === 1 ? '取消' : SVG_LEFT + ' 上一步';
|
||
if (btnNext) btnNext.innerHTML = state.step === 4 ? SVG_CHECK + ' 创建商品' : '下一步 ' + SVG_RIGHT;
|
||
}
|
||
}
|
||
|
||
function bindStepButtons() {
|
||
const btnPrev = document.getElementById('btn-prev-sub');
|
||
const btnNext = document.getElementById('btn-next-sub');
|
||
if (!btnPrev || !btnNext) return;
|
||
|
||
btnPrev.onclick = () => {
|
||
if (state.step === 3 && state.sub === 2) showSub(1);
|
||
else if (state.step === 1) location.href = 'products.html';
|
||
else showStep(state.step - 1);
|
||
};
|
||
btnNext.onclick = () => {
|
||
if (state.step === 3 && state.sub === 1) showSub(2);
|
||
else if (state.step === 4) alert('商品已创建!');
|
||
else showStep(state.step + 1);
|
||
};
|
||
}
|
||
|
||
// sub-tab click
|
||
document.querySelectorAll('.subtab').forEach(t => {
|
||
t.onclick = () => {
|
||
if (state.sub === 2 || +t.dataset.sub === 1 || selectedCount() > 0) {
|
||
showSub(+t.dataset.sub);
|
||
}
|
||
};
|
||
});
|
||
|
||
// sidebar step click
|
||
document.querySelectorAll('#ai-steps .step-item').forEach(s => {
|
||
s.onclick = () => {
|
||
if (s.classList.contains('locked')) return;
|
||
const n = +s.dataset.step;
|
||
if (n) showStep(n);
|
||
};
|
||
});
|
||
|
||
// sub-nav click
|
||
document.querySelectorAll('#sub-nav .substep-item').forEach(t => {
|
||
t.onclick = () => showSub(+t.dataset.sub);
|
||
});
|
||
|
||
// ============================================================
|
||
// MODEL CARDS · multi select + hover detail
|
||
// ============================================================
|
||
function selectedCount() {
|
||
return document.querySelectorAll('.model-card.selected').length;
|
||
}
|
||
function refreshCounts() {
|
||
const n = selectedCount();
|
||
const els = ['sel-count', 'sel-count-3', 'meta-count', 'sub1-meta'];
|
||
document.getElementById('sel-count').textContent = n;
|
||
document.getElementById('sel-count-3').textContent = n;
|
||
document.getElementById('meta-count').textContent = n;
|
||
document.getElementById('sub1-meta').textContent = `${n} / 50+`;
|
||
if (state.step === 3 && state.sub === 1) updateBottom();
|
||
}
|
||
|
||
document.querySelectorAll('.model-card').forEach(c => {
|
||
c.onclick = () => {
|
||
c.classList.toggle('selected');
|
||
refreshCounts();
|
||
// also update selected-list in right panel
|
||
refreshSelectedList();
|
||
};
|
||
c.onmouseenter = () => {
|
||
const name = c.dataset.name;
|
||
const base = c.dataset.base;
|
||
const role = c.dataset.role;
|
||
const tags = (c.dataset.tags || '').split(',');
|
||
hoverModel(name, base, role, tags);
|
||
};
|
||
});
|
||
|
||
function hoverModel(name, base, role, tags) {
|
||
const hd = document.getElementById('hover-detail');
|
||
hd.querySelector('.hd-name').textContent = name;
|
||
const baseTags = base.split(' · ');
|
||
const all = baseTags.concat([role]).concat(tags).filter(Boolean);
|
||
hd.querySelector('#hd-tags').innerHTML = all.map(t => `<span>${t}</span>`).join('');
|
||
}
|
||
|
||
function refreshSelectedList() {
|
||
const list = document.getElementById('selected-list');
|
||
const sels = document.querySelectorAll('.model-card.selected');
|
||
if (sels.length === 0) {
|
||
list.innerHTML = '<div class="sel-empty">// 未选模特 · 在中间网格点选</div>';
|
||
return;
|
||
}
|
||
list.innerHTML = Array.from(sels).map(c => {
|
||
const id = c.dataset.id;
|
||
const name = c.dataset.name;
|
||
const role = c.dataset.role;
|
||
const initial = name.charAt(0);
|
||
return `
|
||
<div class="sel-row" data-id="${id}">
|
||
<div class="placeholder"><span class="ph-frame">${initial}</span></div>
|
||
<div class="nm">${name} · ${role}</div>
|
||
<div class="x-btn" title="移除" onclick="removeModel('${id}')">
|
||
<svg width="12" height="12" viewBox="0 0 16 16"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
}
|
||
|
||
window.removeModel = function(id) {
|
||
document.querySelector(`.model-card[data-id="${id}"]`)?.classList.remove('selected');
|
||
refreshCounts();
|
||
refreshSelectedList();
|
||
};
|
||
|
||
// ============================================================
|
||
// BIG CARD (head + wear) select
|
||
// ============================================================
|
||
document.querySelectorAll('#head-grid .big-card').forEach(c => {
|
||
c.onclick = () => {
|
||
document.querySelectorAll('#head-grid .big-card').forEach(x => x.classList.remove('selected'));
|
||
c.classList.add('selected');
|
||
};
|
||
});
|
||
document.querySelectorAll('#wear-grid .big-card').forEach(c => {
|
||
c.onclick = () => c.classList.toggle('selected');
|
||
});
|
||
|
||
// ============================================================
|
||
// REGEN
|
||
// ============================================================
|
||
function regen(t) {
|
||
const el = document.querySelector(t === 'head' ? '#head-grid' : '#wear-grid');
|
||
if (!el) return;
|
||
el.style.opacity = .35;
|
||
Shell.toast('正在重新生成', t === 'head' ? '~¥0.20 / 4 张' : '~¥0.40 / 4 张');
|
||
setTimeout(() => { el.style.opacity = 1; }, 800);
|
||
}
|
||
|
||
// ============================================================
|
||
// INIT
|
||
// ============================================================
|
||
bindStepButtons();
|
||
updateBottom();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|