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>
This commit is contained in:
@@ -0,0 +1,393 @@
|
||||
# 流·Studio 设计规范
|
||||
|
||||
> **风格代号:** Restraint(克制)· Firecrawl-inspired
|
||||
> **适用范围:** 流·Studio 全产品(工作台 / 项目 / 商品库 / 流水线 / 资产库 / 编辑器 / 账户)
|
||||
> **最后更新:** 2026-05-14
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计哲学
|
||||
|
||||
**一句话:** 不是 SaaS,不是工具站,而是「一台精密设备的工作面板」。
|
||||
|
||||
**三条铁律:**
|
||||
|
||||
1. **克制大于装饰** — 留白 > 容器 > 内容,大量空气感,绝不堆砌
|
||||
2. **单色锚点** — 全场只有一种 accent(橙色),且只用于 CTA / 关键状态 / 强调单词
|
||||
3. **结构清晰可见** — 用 1px 边框、网格背景、"+" 准星、mono 标签暴露"图纸感",而不是用阴影/渐变去隐藏结构
|
||||
|
||||
**避免的"AI 味":**
|
||||
- 渐变铺面、玻璃拟态、彩色阴影
|
||||
- 多色 emoji 图标
|
||||
- 圆角无差别(全部 8px / 16px)
|
||||
- 卡片浮在背景上的"贴纸感"
|
||||
- 装饰盖过内容(场记板、霓虹灯、丝绒幕布这种主题公园式视觉)
|
||||
|
||||
---
|
||||
|
||||
## 2. 色彩系统
|
||||
|
||||
### 2.1 中性色(主体 95%)
|
||||
|
||||
| Token | Hex | 用途 |
|
||||
| ------------- | --------- | ------------------------------- |
|
||||
| `--bg` | `#FAF9F5` | 页面底色 · 米白(暖调,不是纯白) |
|
||||
| `--bg-soft` | `#F4F2EC` | hover 态底色 / mono 标签背景 |
|
||||
| `--card` | `#FFFFFF` | 卡片 / 容器底色 |
|
||||
| `--border` | `#E9E5DB` | 主分隔线 · 1px 实线 |
|
||||
| `--border-soft` | `#EFEBE0` | 次级边框 / 缩略图边框 |
|
||||
|
||||
### 2.2 文字层级(4 层)
|
||||
|
||||
| Token | Hex | 权重 / 用途 |
|
||||
| ---------- | --------- | ----------------------------- |
|
||||
| `--ink` | `#15140F` | 一级 · 标题 / 数值 / 主文本 |
|
||||
| `--ink-2` | `#5A584F` | 二级 · 描述 / 次要按钮文字 |
|
||||
| `--ink-3` | `#9C988C` | 三级 · 标签 / 辅助说明 / 占位 |
|
||||
| `--ink-4` | `#C8C4B8` | 四级 · ASCII 散点 / 极弱装饰 |
|
||||
|
||||
### 2.3 主题色(只用于关键点)
|
||||
|
||||
| Token | Hex | 用途 |
|
||||
| ----------------- | --------- | --------------------------------- |
|
||||
| `--orange` | `#E55B26` | **唯一 accent** · 主 CTA / active 态 / 关键单词强调 |
|
||||
| `--orange-soft` | `#FAE8DC` | 主色边框软化 / pill 边框 |
|
||||
| `--orange-tint` | `#FFF4ED` | 主色背景软化 / active nav 底色 |
|
||||
|
||||
### 2.4 状态色(仅状态使用,克制)
|
||||
|
||||
| 含义 | Token | Hex | 配套底色 | 配套边框 |
|
||||
| -------- | ---------- | --------- | ---------- | -------- |
|
||||
| 成功 | `--green` | `#3F6B3F` | `#EAF2EA` | `#D5E5D5` |
|
||||
| 失败/告警 | `--red` | `#B33A2A` | `#FBEBE7` | `#F2D6CE` |
|
||||
|
||||
> **注:** 没有蓝、紫、黄、青等「企业 dashboard 五彩斑斓色」。状态用色仅在 pill / 进度条段位上出现,绝不用作大面积背景。
|
||||
|
||||
---
|
||||
|
||||
## 3. 字体系统
|
||||
|
||||
### 3.1 字体族
|
||||
|
||||
| 用途 | 字体 |
|
||||
| ---------- | --------------------------------- |
|
||||
| 正文 / UI | `Inter, 'PingFang SC', 'Microsoft YaHei', sans-serif` |
|
||||
| Mono 装饰 | `'JetBrains Mono', monospace` |
|
||||
|
||||
> Mono 字体不是用来写代码的,是用来做**装饰元素**:`[ 200 OK ]` 标签、时间戳 `// 05.14`、小注释 `/v2`、deltas `↑ 本月 +3` 等。
|
||||
|
||||
### 3.2 字号 / 字重 / 行高
|
||||
|
||||
| 角色 | 字号 | 字重 | 字距 | 用途 |
|
||||
| ------------- | ------- | ---- | ---------- | ---------------- |
|
||||
| H1 欢迎语 | 26 px | 600 | -0.018em | 页面主标题 |
|
||||
| 数值 (KPI) | 30 px | 600 | -0.02em | 统计大数字 |
|
||||
| H2 区块标题 | 15 px | 600 | -0.01em | "最近项目""快捷入口" |
|
||||
| 卡片标题 | 13.5 px | 600 | normal | 项目名 / 商品名 |
|
||||
| 正文 / nav | 13.5 px | 500 | normal | 默认 |
|
||||
| 按钮 | 13 px | 500/600 | normal | btn / pill-btn |
|
||||
| 描述次级 | 12.5 px | 400 | normal | meta / 描述行 |
|
||||
| Pill 文字 | 11.5 px | 500 | normal | 状态徽标 |
|
||||
| Mono 标签 | 10.5–11.5 px | 400/500 | 0.04em (letter-spacing) | `[ STATUS ]` |
|
||||
| Mono 散点 | 8.5 px | 400 | 0.04em | 背景 ASCII 装饰 |
|
||||
|
||||
**行高:** 默认 `1.5`,标题 `1.2`,提示文本 `1.6–1.7`。
|
||||
|
||||
**关键属性:** 数值类必须加 `font-variant-numeric: tabular-nums`,保证等宽对齐。
|
||||
|
||||
---
|
||||
|
||||
## 4. 圆角规则(最关键的一条)
|
||||
|
||||
> **核心原则:结构性容器 = 0 圆角 · 交互元素 = 适度圆角 · 状态徽标 = 完全圆**
|
||||
|
||||
| 元素类型 | 圆角值 | 例子 |
|
||||
| -------------------- | ---------- | ------------------------------- |
|
||||
| 大卡片 / 区块容器 | **0 px** | `.stats` `.list-card` `.shortcut` `.tip` `.modal` |
|
||||
| 缩略图 / 画面占位 | **0 px** | `.thumb` `.ic` |
|
||||
| 按钮 / 输入框 / icon-btn | **9 px** | `.btn` `.pill-btn` `.icon-btn` `.search` |
|
||||
| nav 项 | **7 px** | `nav a` |
|
||||
| 头像 / 小色块 | **6 px** | `.av` `.team .p` |
|
||||
| Mono 标签 / badge | **3–4 px** | `.kbd` `.badge` |
|
||||
| Pill 状态徽标 / dot | **999 px** | `.pill` `.dot` |
|
||||
| 进度条段位 | **2–3 px** | `.prog span` `.bar` |
|
||||
|
||||
**为什么这样定:** 大容器走硬边像「图纸框」,有工程感;按钮走中圆角保留可点击的友好感;状态徽标走全圆形成强对比,视觉跳脱出来。
|
||||
|
||||
---
|
||||
|
||||
## 5. 边框 / 阴影 / 描边
|
||||
|
||||
### 5.1 边框
|
||||
|
||||
- **统一规格:** `1px solid var(--border)`
|
||||
- 次级:`1px solid var(--border-soft)`
|
||||
- **不允许 2px / 3px 实线**(粗边破坏轻盈感)
|
||||
- 虚线仅用于 `.tip` 提示框:`1px dashed var(--border)`
|
||||
|
||||
### 5.2 阴影
|
||||
|
||||
- **默认无阴影**(关键!)
|
||||
- 唯一例外:Toast 浮层 `box-shadow: 0 4px 20px rgba(21,20,15,0.06)`
|
||||
- 弹窗用半透明遮罩 `rgba(21,20,15,0.42)` 拉开层级,**不靠 shadow**
|
||||
|
||||
### 5.3 容器四角"+" 注册标(签名元素)
|
||||
|
||||
所有重要区块容器的四角放 `+` 字符,模拟印刷套版准星:
|
||||
|
||||
```css
|
||||
.stats::before,.stats::after { content:'+'; position:absolute; color:var(--ink-3); font-family:'JetBrains Mono',monospace; font-size:13px; }
|
||||
.stats::before { top:-8px; left:-8px }
|
||||
.stats::after { bottom:-8px; right:-8px }
|
||||
/* 另外两角用额外 .corner-tr / .corner-bl span 补齐 */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 间距 / 栅格
|
||||
|
||||
**基础栅格:** 4 px
|
||||
|
||||
**常用间距:** 4 / 6 / 8 / 12 / 14 / 16 / 18 / 22 / 24 / 36 / 48 / 56
|
||||
|
||||
**主区块布局:**
|
||||
- 侧边栏宽度:`248px`
|
||||
- 主内容 padding:`48px 56px 56px`
|
||||
- 内容最大宽度:`1480px`
|
||||
- 区块之间垂直间距:`36px`(welcome → stats → grid2)
|
||||
- 卡片网格间距:`24px`(主区) / `12px`(子区)
|
||||
- 卡片内 padding:`22px 24px`(stats) / `14px 18px`(列表行) / `14px`(快捷入口)
|
||||
|
||||
---
|
||||
|
||||
## 7. 背景:制图纸网格
|
||||
|
||||
### 7.1 三层叠加
|
||||
|
||||
```
|
||||
图层 1(最上):主交叉点 "+" 准星 — 240×240 重复
|
||||
图层 2(中间):子交叉点小圆点 — 60×60 重复
|
||||
图层 3(最下):虚线网格 — 240×240 重复(stroke-dasharray: 1.5 4)
|
||||
```
|
||||
|
||||
**配色:**
|
||||
- "+" 准星:`#B8B3A4`(stroke 1px)
|
||||
- 小点:`#CFCABB`(r=0.9)
|
||||
- 虚线:`#E2DED2`(stroke 1px)
|
||||
|
||||
### 7.2 视觉聚焦遮罩
|
||||
|
||||
整个网格背景用径向遮罩,中心实、四周淡:
|
||||
|
||||
```css
|
||||
mask-image: radial-gradient(ellipse 95% 80% at 50% 35%, #000 25%, transparent 95%);
|
||||
```
|
||||
|
||||
效果:网格在屏幕中心隐约可见,边缘消失,不抢内容。
|
||||
|
||||
### 7.3 装饰散点(Mono ASCII)
|
||||
|
||||
主区域 4 个固定位置撒 ASCII 散点,模拟手稿污点 / 颗粒感:
|
||||
|
||||
```
|
||||
· · +
|
||||
· +XX+
|
||||
+XXXX·
|
||||
+X·
|
||||
```
|
||||
|
||||
字号 8.5px / 颜色 `--ink-4` / 透明度 0.8 / `pointer-events:none`。
|
||||
|
||||
### 7.4 边角 Mono 标签
|
||||
|
||||
主区域 4 个角各放一个 Mono 标签,样例:
|
||||
|
||||
```
|
||||
左上 [ 200 OK ] 右上 [ /v2 ]
|
||||
左下 [ .MP4 · 9:16 ] 右下 [ STUDIO ]
|
||||
```
|
||||
|
||||
字号 10.5px / 颜色 `--ink-3` / 字距 0.06em。
|
||||
|
||||
> **作用:** 让页面看起来像「在某个开发环境 / 调试视图里」,而不是普通官网。这是区分 AI 味和高级感的关键。
|
||||
|
||||
---
|
||||
|
||||
## 8. 组件规范
|
||||
|
||||
### 8.1 按钮
|
||||
|
||||
**3 种且仅 3 种:**
|
||||
|
||||
| 类型 | 背景 | 文字 | 边框 | 圆角 | 字重 |
|
||||
| --------- | ---------- | ---------- | ---------- | ---- | ---- |
|
||||
| `.btn` 默认 | `--card` 白 | `--ink` | `--border` | 9px | 500 |
|
||||
| `.btn-primary` 主 CTA | `--orange` | 白 | `--orange` | 9px | 600 |
|
||||
| `.btn-sm` 小按钮 | 同上,padding `5px 11px` / fontsize 12px | | | | |
|
||||
|
||||
Hover:默认 → `--bg-soft`;主按钮 → `#D04E1F`(橙色加深一档)。
|
||||
|
||||
### 8.2 Pill(状态徽标)
|
||||
|
||||
```
|
||||
[ ● 状态文字 ] 完全圆角 / 1px 边框 / 11.5px 字号 / 4px 10px padding
|
||||
```
|
||||
|
||||
| 状态 | 文字色 | 底色 | 边框 |
|
||||
| ---- | ----------- | --------- | ----------- |
|
||||
| info | `--orange` | `--orange-tint` | `--orange-soft` |
|
||||
| ok | `--green` | `#EAF2EA` | `#D5E5D5` |
|
||||
| err | `--red` | `#FBEBE7` | `#F2D6CE` |
|
||||
|
||||
每个 pill 前置 6px 圆点(`.dot`,颜色继承文字色)。
|
||||
|
||||
### 8.3 输入框 / 搜索
|
||||
|
||||
- 背景白 / 1px 边框 / 9px 圆角 / 9px 12px padding
|
||||
- 占位字色 `--ink-3`
|
||||
- 聚焦态可选橙色边框,但克制使用
|
||||
- 右侧带 mono `⌘K` 提示徽标
|
||||
|
||||
### 8.4 KPI 统计行(`.stats`)
|
||||
|
||||
- 1 行 4 格,共用一个 1px 边框容器,无 gap
|
||||
- 列与列之间用 `border-right: 1px` 分隔(最后一列去掉)
|
||||
- 容器四角加 "+" 注册标
|
||||
- 每格结构:label + badge → 大数字(30px) → delta / progress
|
||||
|
||||
### 8.5 列表(`.recent`)
|
||||
|
||||
```
|
||||
[缩略图 54×70] [标题 + meta] [进度条 5 段] [pill] [按钮]
|
||||
```
|
||||
|
||||
- grid 5 列:`54px 1fr auto auto auto`
|
||||
- 行高 padding `14px 18px`
|
||||
- 行间 1px 分隔线,最后行去掉
|
||||
- 整行 hover → `--bg-soft`
|
||||
|
||||
### 8.6 进度条段位(流水线 5 阶段专用)
|
||||
|
||||
5 个 `18×5px` 小段,3px 间距,每段独立颜色:
|
||||
|
||||
| 状态 | 颜色 |
|
||||
| ---- | ------------- |
|
||||
| 未开始 | `--bg-soft` |
|
||||
| 已完成 | `--ink-2` |
|
||||
| 当前 | `--orange` |
|
||||
| 失败 | `--red` |
|
||||
|
||||
### 8.7 快捷入口卡(`.shortcut`)
|
||||
|
||||
- 白底 / 1px 边框 / 0 圆角 / 14px padding
|
||||
- 左侧 30×30 橙色 tint 图标块(0 圆角)
|
||||
- 右侧:标题(13px 600)+ Mono 描述(11.5px,`--ink-3`)
|
||||
- Hover → `--bg-soft`
|
||||
|
||||
### 8.8 提示框(`.tip`)
|
||||
|
||||
- 白底 / **1px 虚线**边框(`dashed`)/ 0 圆角
|
||||
- 加粗标题独立一行 + 正文
|
||||
- 内联代码用 `.mono` 类:橙色文字 + 橙色 tint 底 + 3px 圆角
|
||||
|
||||
### 8.9 Toast
|
||||
|
||||
- 右下角 24px 偏移 / 白底 / 1px 边框 / 0 圆角
|
||||
- 唯一允许阴影的元素:`0 4px 20px rgba(21,20,15,0.06)`
|
||||
- 进入动画:`translateX(420px → 0)`,缓动 `cubic-bezier(0.34, 1.56, 0.64, 1)`,300ms
|
||||
- 自动消失:2400ms
|
||||
- 内容结构:左侧 24×24 橙色 tint 图标 + 右侧标题 + Mono 副文本(`[ 200 OK ]`)
|
||||
|
||||
### 8.10 弹窗(Modal)
|
||||
|
||||
- 居中,460–480px 宽,白底,**0 圆角**
|
||||
- 四角加 "+" 准星(同 stats 容器规则)
|
||||
- 遮罩 `rgba(21,20,15,0.42)`,带 `backdrop-filter: blur(8px)`
|
||||
- 进入动画:`scale(0.96 → 1)`,250ms 弹性缓动
|
||||
- 三段结构:
|
||||
- **Header**:36px 橙色 tint 图标 + 标题 + Mono 副标
|
||||
- **Body**:13px / `--ink-2` / 行高 1.7
|
||||
- **Footer**:右对齐两个按钮(取消 + 主 CTA)
|
||||
- ESC 关闭 / 点击遮罩关闭
|
||||
|
||||
---
|
||||
|
||||
## 9. 微观细节(决定"高级感"的)
|
||||
|
||||
### 9.1 标签全部包方括号
|
||||
|
||||
不要写 `200 OK`,要写 `[ 200 OK ]`(方括号 + 单空格)。
|
||||
不要写 `MP4 9:16`,要写 `[ .MP4 · 9:16 ]`(中点分隔)。
|
||||
路径用 `/v2`、`/sidebar collapse` 这种伪命令。
|
||||
|
||||
### 9.2 注释样式时间
|
||||
|
||||
`// 05.14 · 周五` —— 双斜杠开头,模拟代码注释。
|
||||
|
||||
### 9.3 数值后缀
|
||||
|
||||
`¥327` 主体大字 + `.40` 小字次级(`<small>`),让数字主体更突出。
|
||||
|
||||
### 9.4 强调单词上色
|
||||
|
||||
正文里 `<b style="color:var(--ink)">3 个项目</b>` —— 让具体数值/名词比周围文字更深一档(不是变橙,只是变更黑)。**橙色只留给 CTA。**
|
||||
|
||||
### 9.5 ASCII 字符做装饰
|
||||
|
||||
进度箭头:`↑ 本月 +3` `↓ -1.2%` 用 Unicode 箭头。
|
||||
中文标点 + 英文单位混排:`本月 +3` `较上月 +33%` 中间用半角空格分隔。
|
||||
|
||||
### 9.6 链接式"更多"按钮
|
||||
|
||||
"查看全部"不要写成按钮,写成 `[ ALL · 12 ] →`(Mono 标签 + 箭头),颜色 `--ink-3`,hover 橙色。
|
||||
|
||||
### 9.7 缩略图不放图,放比例
|
||||
|
||||
`9:16` 这种 Mono 字符占位,模拟「未渲染图片的草稿状态」,呼应工程感。
|
||||
|
||||
---
|
||||
|
||||
## 10. Don't List(绝对禁止)
|
||||
|
||||
- ❌ 渐变背景(只有 hero 区可考虑,但首选纯色)
|
||||
- ❌ 玻璃拟态(`backdrop-filter` 只用于 modal 遮罩,不用于卡片)
|
||||
- ❌ 彩色 emoji 图标(用 SVG line icon,1.8px stroke)
|
||||
- ❌ 多个 accent 色(全场只有橙色一个 accent)
|
||||
- ❌ 大圆角容器(>=10px 的卡片直接判错)
|
||||
- ❌ 投影 / 内阴影 / 文字阴影
|
||||
- ❌ 鲜艳的状态色(避免荧光绿、电光蓝、霓虹粉这类)
|
||||
- ❌ 居中对齐大段正文(全部左对齐)
|
||||
- ❌ 把装饰当主角(场记板、丝绒、霓虹灯都不要)
|
||||
- ❌ 无意义的微动效(hover 旋转、缩放、彩虹流光等)
|
||||
|
||||
---
|
||||
|
||||
## 11. Sass / CSS Token 速查
|
||||
|
||||
```css
|
||||
:root{
|
||||
/* 中性 */
|
||||
--bg:#FAF9F5; --bg-soft:#F4F2EC; --card:#FFFFFF;
|
||||
--border:#E9E5DB; --border-soft:#EFEBE0;
|
||||
|
||||
/* 文字 4 层 */
|
||||
--ink:#15140F; --ink-2:#5A584F; --ink-3:#9C988C; --ink-4:#C8C4B8;
|
||||
|
||||
/* 主色 */
|
||||
--orange:#E55B26; --orange-soft:#FAE8DC; --orange-tint:#FFF4ED;
|
||||
--orange-hover:#D04E1F;
|
||||
|
||||
/* 状态 */
|
||||
--green:#3F6B3F; --green-bg:#EAF2EA; --green-bd:#D5E5D5;
|
||||
--red:#B33A2A; --red-bg:#FBEBE7; --red-bd:#F2D6CE;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 参考与来源
|
||||
|
||||
- **视觉灵感:** [Firecrawl](https://www.firecrawl.dev/)(克制 / 单橙 accent / mono 装饰)
|
||||
- **结构灵感:** Linear / Stripe Dashboard(信息密度 / 1px 分层)
|
||||
- **图纸感来源:** 印刷套版准星 + 老 Unix 终端
|
||||
- **当前实现:** [demo.html](demo.html) · [restraint-2.html](_design_src/untitled/project/directions/v3/restraint-2.html)
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<!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>
|
||||
.acc-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 24px; align-items: start; }
|
||||
.balance-banner {
|
||||
background: var(--ink);
|
||||
color: #FFF;
|
||||
padding: 28px 32px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
border: 1px solid var(--ink);
|
||||
}
|
||||
.balance-banner::before, .balance-banner::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; }
|
||||
.balance-banner::before { top: -8px; left: -8px; }
|
||||
.balance-banner::after { bottom: -8px; right: -8px; }
|
||||
.balance-banner .corner-tr, .balance-banner .corner-bl { position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; }
|
||||
.balance-banner .corner-tr { top: -8px; right: -8px; }
|
||||
.balance-banner .corner-bl { bottom: -8px; left: -8px; }
|
||||
.balance-banner .lbl { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: rgba(255,255,255,.55); letter-spacing: .04em; }
|
||||
.balance-banner .v { font-size: 42px; font-weight: 700; letter-spacing: -.018em; margin-top: 8px; font-variant-numeric: tabular-nums; }
|
||||
.balance-banner .meta { font-size: 12.5px; color: rgba(255,255,255,.5); margin-top: 8px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.balance-banner .actions { display: flex; gap: 8px; margin-top: 18px; }
|
||||
.balance-banner .btn { background: #FFF; color: var(--ink); border-color: #FFF; }
|
||||
.balance-banner .btn:hover { background: var(--bg); }
|
||||
.balance-banner .btn-ghost { background: transparent; color: #FFF; border: 1px solid rgba(255,255,255,.25); }
|
||||
.balance-banner .btn-ghost:hover { background: rgba(255,255,255,.1); color: #FFF; }
|
||||
|
||||
.recharge-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 12px; }
|
||||
.recharge-card { border: 1px solid var(--border); padding: 18px; text-align: center; cursor: pointer; background: var(--card); position: relative; }
|
||||
.recharge-card:hover { background: var(--bg-soft); }
|
||||
.recharge-card.selected { border-color: var(--orange); background: var(--orange-tint); }
|
||||
.recharge-card.selected::before, .recharge-card.selected::after { content: '+'; position: absolute; color: var(--orange); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||||
.recharge-card.selected::before { top: -7px; left: -7px; }
|
||||
.recharge-card.selected::after { bottom: -7px; right: -7px; }
|
||||
.recharge-card .amt { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
|
||||
.recharge-card .gift { font-size: 11px; color: var(--ink-3); margin-top: 4px; font-family: 'JetBrains Mono', monospace; }
|
||||
.recharge-card .gift.bonus { color: var(--green); font-weight: 600; }
|
||||
.recharge-card .ribbon { position: absolute; top: -8px; right: 8px; font-family: 'JetBrains Mono', monospace; font-size: 9.5px; padding: 1px 6px; background: var(--orange); color: #FFF; letter-spacing: .04em; font-weight: 600; }
|
||||
|
||||
.pane { background: var(--card); border: 1px solid var(--border); padding: 20px; margin-bottom: 16px; }
|
||||
.pane h3 { font-size: 14px; font-weight: 600; margin-bottom: 14px; }
|
||||
.pane h3 + .desc { font-size: 12px; color: var(--ink-3); margin-top: -10px; margin-bottom: 14px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
|
||||
.bills .neg { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 500; }
|
||||
.bills .pos { color: var(--green); font-variant-numeric: tabular-nums; font-weight: 500; }
|
||||
.bills .ref { color: var(--ink-3); font-size: 11px; font-family: 'JetBrains Mono', monospace; }
|
||||
|
||||
.usage-line { display: flex; justify-content: space-between; padding: 6px 0; font-size: 13px; }
|
||||
.usage-line .v { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 600; }
|
||||
.usage-bar { height: 4px; background: var(--bg-soft); border-radius: 2px; margin: 6px 0 12px; overflow: hidden; }
|
||||
.usage-bar > span { display: block; height: 100%; }
|
||||
|
||||
.rule-list { font-size: 12.5px; color: var(--ink-2); line-height: 1.7; }
|
||||
.rule-list strong { color: var(--ink); font-weight: 600; }
|
||||
.rule-list .mono { font-family: 'JetBrains Mono', monospace; color: var(--orange); background: var(--orange-tint); padding: 1px 5px; font-size: 11.5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>账户</h1>
|
||||
<div class="sub"><span class="mono">// 余额 · 充值 · 消费明细</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="acc-grid">
|
||||
<div>
|
||||
<div class="balance-banner">
|
||||
<span class="corner-tr">+</span><span class="corner-bl">+</span>
|
||||
<div class="lbl">[ CURRENT BALANCE ]</div>
|
||||
<div class="v">¥327.40</div>
|
||||
<div class="meta">// 本月已消费 ¥162.60 · 可使用约 32 个项目</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-lg" onclick="Shell.toast('充值', '/billing/topup')">充值</button>
|
||||
<button class="btn btn-ghost btn-lg" onclick="Shell.toast('提取明细', '/billing/export')">提取消费明细</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pane">
|
||||
<h3>快速充值</h3>
|
||||
<div class="desc">// 充值后立刻到账,可开发票</div>
|
||||
<div class="recharge-row">
|
||||
<div class="recharge-card" onclick="Shell.toast('选择 ¥100')"><div class="amt">¥100</div><div class="gift">无赠送</div></div>
|
||||
<div class="recharge-card selected"><span class="ribbon">推荐</span><div class="amt">¥500</div><div class="gift bonus">+ ¥30 赠送</div></div>
|
||||
<div class="recharge-card" onclick="Shell.toast('选择 ¥1000')"><div class="amt">¥1000</div><div class="gift bonus">+ ¥80 赠送</div></div>
|
||||
<div class="recharge-card" onclick="Shell.toast('选择 ¥3000')"><div class="amt">¥3000</div><div class="gift bonus">+ ¥300 赠送</div></div>
|
||||
</div>
|
||||
<div style="display:flex; gap:10px; margin-top:14px;">
|
||||
<input class="input" placeholder="自定义金额(最低 ¥50)" style="flex:1;">
|
||||
<button class="btn btn-primary" onclick="Shell.toast('微信支付', '¥500 · WechatPay')">微信支付 ¥500</button>
|
||||
<button class="btn" onclick="Shell.toast('支付宝', '¥500 · Alipay')">支付宝</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; align-items:baseline; margin-bottom:12px;">
|
||||
<h2 style="font-size:15px; font-weight:600;">消费明细</h2>
|
||||
<span class="spacer"></span>
|
||||
<div class="hstack">
|
||||
<button class="chip" style="height:28px; font-size:12px;">近 30 天</button>
|
||||
<button class="chip" style="height:28px; font-size:12px;">导出</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="t bills">
|
||||
<thead>
|
||||
<tr><th>时间</th><th>项目 / 类型</th><th>详情</th><th style="text-align:right;">金额</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.09 14:08</td><td>补水面膜 · v3</td><td class="muted">故事板 image-2 · 1 次</td><td style="text-align:right;" class="neg">-¥0.45</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.09 14:02</td><td>补水面膜 · v3</td><td class="muted">脚本 LLM · 2.4k tokens</td><td style="text-align:right;" class="neg">-¥0.04</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.09 13:38</td><td>补水面膜 · v3</td><td class="muted">基础资产 · 5 张图</td><td style="text-align:right;" class="neg">-¥1.05</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.08 18:21</td><td>透真防晒 · 通勤对比</td><td class="muted">视频片段 · 6 镜</td><td style="text-align:right;" class="neg">-¥1.20</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.08 11:02</td><td>充值</td><td class="muted">微信支付 · <span class="ref">TX2024050811021Z</span></td><td style="text-align:right;" class="pos">+¥500.00</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.07 20:14</td><td>蓝牙耳机 · 开箱</td><td class="muted">视频片段 · 5 镜(1 镜重跑不扣)</td><td style="text-align:right;" class="neg">-¥0.94</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.07 15:48</td><td>咖啡冻干 · 早八</td><td class="muted">故事板生成失败 · <span style="color:var(--ink-3);">不扣费</span></td><td style="text-align:right;" class="muted-2">¥0.00</td></tr>
|
||||
<tr><td class="muted-2 mono" style="font-size:11.5px;">05.06 10:30</td><td>瑜伽裤 · 通勤穿搭</td><td class="muted">项目导出 · 1 次</td><td style="text-align:right;" class="neg">-¥3.20</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pane">
|
||||
<h3>本月消费分布</h3>
|
||||
<div class="usage-line"><span>视频片段(Seedance)</span><span class="v">¥98.40</span></div>
|
||||
<div class="usage-bar"><span style="width:60%; background:var(--orange);"></span></div>
|
||||
|
||||
<div class="usage-line"><span>故事板(image-2)</span><span class="v">¥36.00</span></div>
|
||||
<div class="usage-bar"><span style="width:22%; background:var(--green);"></span></div>
|
||||
|
||||
<div class="usage-line"><span>基础资产</span><span class="v">¥21.00</span></div>
|
||||
<div class="usage-bar"><span style="width:13%; background:var(--ink-2);"></span></div>
|
||||
|
||||
<div class="usage-line"><span>脚本 LLM</span><span class="v">¥7.20</span></div>
|
||||
<div class="usage-bar"><span style="width:5%; background:var(--ink-3);"></span></div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="usage-line" style="font-weight:600;"><span>合计</span><span class="v">¥162.60</span></div>
|
||||
</div>
|
||||
|
||||
<div class="pane">
|
||||
<h3>扣费规则</h3>
|
||||
<div class="rule-list">
|
||||
<strong>① 失败不扣</strong>:模型超时、内容审核拦截、生成异常一律不扣费。<br>
|
||||
<strong>② 用户重跑不扣首次</strong>:第一次重跑保留原扣费,第二次起按次结算。<br>
|
||||
<strong>③ 仅在你点击 <span class="mono">[ 确认通过 ]</span> 时入账</strong>。<br>
|
||||
<strong>④ 导出不再扣费</strong>,所有 token 已在过程中结算。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pane">
|
||||
<h3>开发票</h3>
|
||||
<div style="font-size:12.5px; color:var(--ink-2); margin-bottom:10px;">本月可开发票额度:<strong style="color:var(--ink);">¥162.60</strong></div>
|
||||
<button class="btn" style="width:100%;" onclick="Shell.toast('申请发票', '/billing/invoice')">申请发票</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>Shell.render({ active: 'account', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '账户' }] });</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,546 @@
|
||||
/* ============================================================
|
||||
流·Studio · Restraint 风格(Firecrawl-inspired)
|
||||
适用所有页面 · 米白底 + 单橙 accent + 0 圆角硬边容器
|
||||
============================================================ */
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--bg: #FAF9F5;
|
||||
--bg-soft: #F4F2EC;
|
||||
--card: #FFFFFF;
|
||||
--border: #E9E5DB;
|
||||
--border-soft: #EFEBE0;
|
||||
--ink: #15140F;
|
||||
--ink-2: #5A584F;
|
||||
--ink-3: #9C988C;
|
||||
--ink-4: #C8C4B8;
|
||||
--orange: #E55B26;
|
||||
--orange-soft: #FAE8DC;
|
||||
--orange-tint: #FFF4ED;
|
||||
--orange-hover: #D04E1F;
|
||||
--green: #3F6B3F;
|
||||
--green-bg: #EAF2EA;
|
||||
--green-bd: #D5E5D5;
|
||||
--red: #B33A2A;
|
||||
--red-bg: #FBEBE7;
|
||||
--red-bd: #F2D6CE;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font: inherit; cursor: pointer; border: 0; background: none; color: inherit; }
|
||||
input, textarea, select { font: inherit; color: inherit; outline: none; }
|
||||
img, svg, video { display: block; max-width: 100%; }
|
||||
|
||||
.num, .tnum { font-variant-numeric: tabular-nums; }
|
||||
.mono { font-family: 'JetBrains Mono', monospace; }
|
||||
.muted { color: var(--ink-2); }
|
||||
.muted-2 { color: var(--ink-3); }
|
||||
.spacer { flex: 1; }
|
||||
.hstack { display: flex; align-items: center; gap: 8px; }
|
||||
.vstack { display: flex; flex-direction: column; gap: 8px; }
|
||||
.divider { height: 1px; background: var(--border); margin: 14px 0; }
|
||||
|
||||
/* ─── App shell ─── */
|
||||
.app { display: grid; grid-template-columns: 248px 1fr; min-height: 100vh; }
|
||||
|
||||
/* ─── Sidebar ─── */
|
||||
aside.sidebar {
|
||||
padding: 18px 14px;
|
||||
border-right: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px 14px; }
|
||||
.flame { width: 22px; height: 22px; color: var(--orange); }
|
||||
.flame svg { width: 100%; height: 100%; }
|
||||
.brand .name { font-weight: 700; font-size: 18px; letter-spacing: -.012em; }
|
||||
.search-box {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 9px 12px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 14px;
|
||||
cursor: text;
|
||||
}
|
||||
.search-box svg { width: 14px; height: 14px; }
|
||||
.search-box input { flex: 1; border: 0; background: transparent; font-size: 13px; color: var(--ink); }
|
||||
.search-box input::placeholder { color: var(--ink-3); }
|
||||
.search-box .kbd {
|
||||
margin-left: auto;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
background: var(--bg-soft);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-soft);
|
||||
}
|
||||
.nav-section { font-size: 10.5px; color: var(--ink-3); padding: 14px 12px 6px; letter-spacing: .08em; text-transform: uppercase; font-weight: 600; }
|
||||
nav { display: flex; flex-direction: column; gap: 1px; }
|
||||
nav a {
|
||||
display: flex; align-items: center; gap: 11px;
|
||||
padding: 8px 12px;
|
||||
color: var(--ink-2);
|
||||
font-size: 13.5px; font-weight: 500;
|
||||
border-radius: 7px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
nav a:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
nav a.active { background: var(--orange-tint); color: var(--orange); }
|
||||
nav a svg { width: 14px; height: 14px; opacity: .85; }
|
||||
nav a.active svg { opacity: 1; }
|
||||
nav a .pill-mini {
|
||||
margin-left: auto;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 9.5px; font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
background: var(--card);
|
||||
color: var(--ink-3);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
nav a.disabled { color: var(--ink-4); cursor: not-allowed; }
|
||||
nav a.disabled:hover { background: transparent; color: var(--ink-4); }
|
||||
.aside-foot { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
|
||||
.user {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.user:hover { background: var(--bg-soft); }
|
||||
.user .av {
|
||||
width: 24px; height: 24px;
|
||||
border-radius: 6px;
|
||||
background: var(--ink);
|
||||
color: #FFF;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 600; font-size: 11px;
|
||||
}
|
||||
.user .em { font-size: 12.5px; }
|
||||
|
||||
/* ─── Main + grid background ─── */
|
||||
main { position: relative; overflow: hidden; background: var(--bg); }
|
||||
.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'/><path d='M-5 240 L5 240 M0 235 L0 245'/><path d='M235 240 L245 240 M240 235 L240 245'/></g></svg>"),
|
||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='60' height='60'><circle cx='60' cy='60' r='0.9' fill='%23CFCABB'/></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, 60px 60px, 240px 240px;
|
||||
mask-image: radial-gradient(ellipse 95% 80% at 50% 35%, #000 25%, transparent 95%);
|
||||
-webkit-mask-image: radial-gradient(ellipse 95% 80% at 50% 35%, #000 25%, transparent 95%);
|
||||
}
|
||||
.scatter { position: absolute; font-family: 'JetBrains Mono', monospace; font-size: 8.5px; line-height: 1.05; color: var(--ink-4); white-space: pre; pointer-events: none; opacity: .8; letter-spacing: .04em; }
|
||||
.tag-corner { position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 10.5px; letter-spacing: .06em; pointer-events: none; opacity: .85; z-index: 1; }
|
||||
.sq-mark { position: absolute; width: 5px; height: 5px; background: var(--ink-3); opacity: .55; pointer-events: none; }
|
||||
|
||||
/* ─── Topbar ─── */
|
||||
.topbar {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
position: relative; z-index: 2;
|
||||
}
|
||||
.crumbs { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-3); }
|
||||
.crumbs .sep { color: var(--ink-4); }
|
||||
.crumbs .here { color: var(--ink); font-weight: 500; }
|
||||
.crumbs a:hover { color: var(--ink); }
|
||||
.topbar .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||||
.balance-chip {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 6px 12px 6px 10px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
.balance-chip:hover { background: var(--bg-soft); }
|
||||
.balance-chip strong { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||
.balance-chip svg { width: 13px; height: 13px; color: var(--orange); }
|
||||
.icon-btn {
|
||||
width: 36px; height: 36px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9px;
|
||||
color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
.icon-btn:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
.icon-btn svg { width: 15px; height: 15px; }
|
||||
.icon-btn .dot-noti { position: absolute; top: 8px; right: 9px; width: 7px; height: 7px; border-radius: 50%; background: var(--orange); border: 1.5px solid var(--card); }
|
||||
|
||||
/* ─── Content ─── */
|
||||
.content { padding: 36px 48px 60px; position: relative; z-index: 1; max-width: 1480px; }
|
||||
.page-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 28px; gap: 16px; flex-wrap: wrap; }
|
||||
.page-head h1 { font-size: 26px; font-weight: 600; letter-spacing: -.018em; line-height: 1.2; }
|
||||
.page-head .sub { font-size: 13.5px; color: var(--ink-2); margin-top: 6px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.page-head .sub .mono { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--ink-3); }
|
||||
.page-head .actions { display: flex; gap: 8px; align-items: center; }
|
||||
|
||||
.section-h { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; }
|
||||
.section-h h2 { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
|
||||
.section-h .more { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; cursor: pointer; }
|
||||
.section-h .more:hover { color: var(--orange); }
|
||||
|
||||
/* ─── Buttons ─── */
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 9px;
|
||||
font-size: 13px; font-weight: 500;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
font-family: inherit;
|
||||
}
|
||||
.btn:hover { background: var(--bg-soft); }
|
||||
.btn:disabled, .btn.disabled { color: var(--ink-3); background: var(--bg-soft); cursor: not-allowed; }
|
||||
.btn svg { width: 13px; height: 13px; }
|
||||
.btn-primary {
|
||||
background: var(--orange);
|
||||
color: #FFF;
|
||||
border-color: var(--orange);
|
||||
font-weight: 600;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
.btn-primary:hover { background: var(--orange-hover); border-color: var(--orange-hover); }
|
||||
.btn-primary:disabled { background: var(--bg-soft); color: var(--ink-3); border-color: var(--border); }
|
||||
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
|
||||
.btn-ghost:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
.btn-sm { padding: 5px 11px; font-size: 12px; border-radius: 7px; }
|
||||
.btn-lg { padding: 10px 18px; font-size: 13.5px; }
|
||||
|
||||
/* ─── Pills ─── */
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 11.5px; font-weight: 500;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
color: var(--ink-2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill.info { background: var(--orange-tint); color: var(--orange); border-color: var(--orange-soft); }
|
||||
.pill.ok { background: var(--green-bg); color: var(--green); border-color: var(--green-bd); }
|
||||
.pill.err { background: var(--red-bg); color: var(--red); border-color: var(--red-bd); }
|
||||
.pill.neutral { background: var(--bg-soft); color: var(--ink-2); border-color: var(--border); }
|
||||
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
||||
|
||||
/* ─── Cards / containers (0 圆角硬边) ─── */
|
||||
.card-hard {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
}
|
||||
.card-hard.with-corners::before, .card-hard.with-corners::after,
|
||||
.with-corners .corner-tr, .with-corners .corner-bl {
|
||||
position: absolute;
|
||||
color: var(--ink-3);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 13px; line-height: 1;
|
||||
}
|
||||
.card-hard.with-corners::before { content: '+'; top: -8px; left: -8px; }
|
||||
.card-hard.with-corners::after { content: '+'; bottom: -8px; right: -8px; }
|
||||
.with-corners .corner-tr { top: -8px; right: -8px; }
|
||||
.with-corners .corner-bl { bottom: -8px; left: -8px; }
|
||||
|
||||
/* ─── Stats (KPI bar) ─── */
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.stat {
|
||||
padding: 22px 24px;
|
||||
border-right: 1px solid var(--border);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
.stat:hover { background: var(--bg-soft); }
|
||||
.stat:last-child { border-right: 0; }
|
||||
.stat .lbl { font-size: 12.5px; color: var(--ink-3); font-weight: 500; display: flex; align-items: center; gap: 6px; }
|
||||
.stat .lbl .badge {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 9.5px; color: var(--ink-3);
|
||||
background: var(--bg-soft);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-soft);
|
||||
}
|
||||
.stat .v { font-size: 30px; font-weight: 600; letter-spacing: -.02em; line-height: 1; margin-top: 14px; font-variant-numeric: tabular-nums; }
|
||||
.stat .v small { font-size: 14px; color: var(--ink-3); font-weight: 500; margin-left: 2px; }
|
||||
.stat .delta { font-family: 'JetBrains Mono', monospace; font-size: 11px; margin-top: 8px; color: var(--ink-3); letter-spacing: .02em; }
|
||||
.stat .delta.up { color: var(--green); }
|
||||
.stat .bar { height: 5px; background: var(--bg-soft); border-radius: 3px; margin-top: 12px; overflow: hidden; }
|
||||
.stat .bar > span { display: block; height: 100%; background: var(--orange); border-radius: 3px; }
|
||||
.stat .sub { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-top: 8px; letter-spacing: .02em; }
|
||||
|
||||
/* ─── Form fields ─── */
|
||||
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
|
||||
.field-label { font-size: 12.5px; font-weight: 500; color: var(--ink); }
|
||||
.field-label .req { color: var(--red); margin-left: 2px; }
|
||||
.field-hint { font-size: 12px; color: var(--ink-3); }
|
||||
.input, .textarea, .select {
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9px;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
color: var(--ink);
|
||||
}
|
||||
.input:focus, .textarea:focus, .select:focus { border-color: var(--orange); box-shadow: 0 0 0 3px rgba(229, 91, 38, 0.10); }
|
||||
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }
|
||||
.textarea { height: auto; min-height: 80px; padding: 10px 12px; line-height: 1.55; resize: vertical; }
|
||||
.select { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16' fill='none'><path d='M4 6l4 4 4-4' stroke='%239C988C' stroke-width='1.4'/></svg>"); background-repeat: no-repeat; background-position: right 10px center; padding-right: 30px; }
|
||||
|
||||
/* ─── Tabs ─── */
|
||||
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
|
||||
.tab { padding: 9px 14px; font-size: 13px; color: var(--ink-2); border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; font-weight: 500; user-select: none; }
|
||||
.tab:hover { color: var(--ink); }
|
||||
.tab.active { color: var(--orange); border-bottom-color: var(--orange); }
|
||||
.tab .count { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-left: 6px; padding: 1px 6px; background: var(--bg-soft); border-radius: 4px; }
|
||||
.tab.active .count { background: var(--orange-tint); color: var(--orange); }
|
||||
|
||||
/* ─── Filter chips ─── */
|
||||
.chip {
|
||||
height: 32px; padding: 0 12px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
border-radius: 8px;
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
.chip:hover { background: var(--bg-soft); }
|
||||
.chip.active { border-color: var(--orange); color: var(--orange); background: var(--orange-tint); }
|
||||
.chip svg { width: 12px; height: 12px; }
|
||||
|
||||
/* ─── Toolbar ─── */
|
||||
.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.toolbar .search-inline {
|
||||
position: relative; flex: 1; max-width: 320px;
|
||||
}
|
||||
.toolbar .search-inline svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ink-3); width: 14px; height: 14px; }
|
||||
.toolbar .search-inline input { padding-left: 34px; }
|
||||
|
||||
/* ─── Progress (5 段流水线) ─── */
|
||||
.prog { display: flex; gap: 3px; }
|
||||
.prog span { width: 18px; height: 5px; border-radius: 2px; background: var(--bg-soft); }
|
||||
.prog span.done { background: var(--ink-2); }
|
||||
.prog span.cur { background: var(--orange); }
|
||||
.prog span.fail { background: var(--red); }
|
||||
|
||||
/* ─── Table ─── */
|
||||
table.t {
|
||||
width: 100%; border-collapse: collapse;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
table.t thead th {
|
||||
text-align: left;
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
color: var(--ink-3);
|
||||
padding: 12px 14px;
|
||||
background: var(--bg-soft);
|
||||
border-bottom: 1px solid var(--border);
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
table.t tbody td {
|
||||
padding: 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 13px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
table.t tbody tr:last-child td { border-bottom: 0; }
|
||||
table.t tbody tr { cursor: pointer; }
|
||||
table.t tbody tr:hover { background: var(--bg-soft); }
|
||||
|
||||
/* ─── Placeholder thumb ─── */
|
||||
.placeholder {
|
||||
background:
|
||||
repeating-linear-gradient(135deg, rgba(0,0,0,0.025) 0 1px, transparent 1px 12px),
|
||||
var(--bg-soft);
|
||||
border: 1px solid var(--border-soft);
|
||||
display: grid; place-items: center;
|
||||
color: var(--ink-3);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10px;
|
||||
letter-spacing: .04em;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
}
|
||||
.placeholder .ph-frame {
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid var(--border);
|
||||
padding: 3px 8px;
|
||||
font-size: 10.5px;
|
||||
color: var(--ink-2);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ─── Toast ─── */
|
||||
.toast {
|
||||
position: fixed; bottom: 24px; right: 24px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
padding: 12px 16px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
box-shadow: 0 4px 20px rgba(21, 20, 15, 0.06);
|
||||
transform: translateX(420px);
|
||||
transition: transform .3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
z-index: 1000;
|
||||
min-width: 240px;
|
||||
}
|
||||
.toast.show { transform: translateX(0); }
|
||||
.toast .ic-t {
|
||||
width: 24px; height: 24px;
|
||||
background: var(--orange-tint);
|
||||
color: var(--orange);
|
||||
border: 1px solid var(--orange-soft);
|
||||
display: grid; place-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.toast .ic-t svg { width: 12px; height: 12px; }
|
||||
.toast .txt { font-size: 12.5px; color: var(--ink); }
|
||||
.toast .txt .mono { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); display: block; margin-top: 2px; letter-spacing: .02em; }
|
||||
|
||||
/* ─── Modal ─── */
|
||||
.modal-bg {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(21, 20, 15, .42);
|
||||
display: none; align-items: center; justify-content: center;
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
.modal-bg.show { display: flex; opacity: 1; }
|
||||
.modal {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
max-width: 480px; width: 90%;
|
||||
position: relative;
|
||||
transform: scale(.96);
|
||||
transition: transform .25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
.modal-bg.show .modal { transform: scale(1); }
|
||||
.modal::before, .modal::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; line-height: 1; }
|
||||
.modal::before { top: -8px; left: -8px; }
|
||||
.modal::after { bottom: -8px; right: -8px; }
|
||||
.modal .corner-tr, .modal .corner-bl { position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; line-height: 1; }
|
||||
.modal .corner-tr { top: -8px; right: -8px; }
|
||||
.modal .corner-bl { bottom: -8px; left: -8px; }
|
||||
.modal-h { padding: 20px 24px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
|
||||
.modal-h .ic-m { width: 36px; height: 36px; background: var(--orange-tint); color: var(--orange); border: 1px solid var(--orange-soft); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.modal-h .ic-m svg { width: 17px; height: 17px; }
|
||||
.modal-h .ti { font-size: 15px; font-weight: 600; }
|
||||
.modal-h .ti span { display: block; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); font-weight: 400; margin-top: 3px; letter-spacing: .02em; }
|
||||
.modal-b { padding: 18px 24px; font-size: 13px; color: var(--ink-2); line-height: 1.7; }
|
||||
.modal-b .mono-acc { font-family: 'JetBrains Mono', monospace; color: var(--orange); background: var(--orange-tint); padding: 1px 5px; font-size: 11.5px; border-radius: 3px; }
|
||||
.modal-f { padding: 14px 24px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }
|
||||
|
||||
/* ─── Drawer ─── */
|
||||
.drawer-bg {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(21, 20, 15, .32);
|
||||
display: none;
|
||||
z-index: 90;
|
||||
}
|
||||
.drawer-bg.show { display: block; }
|
||||
.drawer {
|
||||
position: fixed; right: 0; top: 0; bottom: 0;
|
||||
width: 540px; max-width: 100vw;
|
||||
background: var(--card);
|
||||
border-left: 1px solid var(--border);
|
||||
z-index: 95;
|
||||
transform: translateX(100%);
|
||||
transition: transform .25s cubic-bezier(.32, .72, 0, 1);
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.drawer.show { transform: translateX(0); }
|
||||
.drawer-h {
|
||||
padding: 18px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.drawer-h h3 { font-size: 16px; font-weight: 600; }
|
||||
.drawer-h .x {
|
||||
margin-left: auto; width: 28px; height: 28px;
|
||||
border-radius: 6px;
|
||||
display: grid; place-items: center;
|
||||
color: var(--ink-2); cursor: pointer;
|
||||
}
|
||||
.drawer-h .x:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
.drawer-b { padding: 20px 24px; overflow-y: auto; flex: 1; }
|
||||
.drawer-f {
|
||||
padding: 14px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex; gap: 8px; justify-content: flex-end;
|
||||
background: var(--bg-soft);
|
||||
}
|
||||
|
||||
/* ─── Scrollbar ─── */
|
||||
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }
|
||||
|
||||
/* ─── Responsive ─── */
|
||||
@media (max-width: 1100px) {
|
||||
.app { grid-template-columns: 1fr; }
|
||||
aside.sidebar { display: none; }
|
||||
.stats { grid-template-columns: repeat(2, 1fr); }
|
||||
.stat:nth-child(2) { border-right: 0; }
|
||||
.stat:nth-child(1), .stat:nth-child(2) { border-bottom: 1px solid var(--border); }
|
||||
.content { padding: 24px; }
|
||||
}
|
||||
|
||||
/* ─── Spinner ─── */
|
||||
.spinner {
|
||||
width: 18px; height: 18px;
|
||||
border: 2px solid var(--border);
|
||||
border-top-color: var(--orange);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ============================================================
|
||||
流·Studio · Shell renderer
|
||||
渲染 sidebar / topbar / 网格背景装饰 / Toast / Modal helpers
|
||||
每个页面调用 Shell.render({ active, crumbs, balance, topActions })
|
||||
============================================================ */
|
||||
|
||||
const NAV = [
|
||||
{
|
||||
id: 'dashboard', label: '工作台', href: 'index.html',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M3 12 12 3l9 9"/><path d="M5 10v10h14V10"/></svg>'
|
||||
},
|
||||
{
|
||||
id: 'products', label: '商品库', href: 'products.html', badge: '12',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="m3 7 9-4 9 4-9 4-9-4z"/><path d="m3 12 9 4 9-4M3 17l9 4 9-4"/></svg>'
|
||||
},
|
||||
{
|
||||
id: 'projects', label: '视频项目', href: 'projects.html',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="16"/><path d="M7 4v16M16 4v16M3 9h18M3 15h18"/></svg>'
|
||||
},
|
||||
{
|
||||
id: 'library', label: '资产库', href: 'library.html',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="6" height="16"/><rect x="11" y="4" width="4" height="16"/><rect x="17" y="6" width="4" height="14"/></svg>'
|
||||
},
|
||||
{
|
||||
id: 'account', label: '账户', href: 'account.html',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18M16 14h2"/></svg>'
|
||||
},
|
||||
{
|
||||
id: 'settings', label: '设置', href: '#',
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8 2 2 0 0 1-2.8 2.8 1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5 2 2 0 0 1-4 0 1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3 2 2 0 0 1-2.8-2.8 1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1 2 2 0 0 1 0-4 1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8 2 2 0 0 1 2.8-2.8 1.7 1.7 0 0 0 1.8.3 1.7 1.7 0 0 0 1-1.5 2 2 0 0 1 4 0 1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3 2 2 0 0 1 2.8 2.8 1.7 1.7 0 0 0-.3 1.8 1.7 1.7 0 0 0 1.5 1 2 2 0 0 1 0 4 1.7 1.7 0 0 0-1.5 1.1Z"/></svg>'
|
||||
}
|
||||
];
|
||||
|
||||
const TEAM_NAV = {
|
||||
label: '团队', icon:
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="9" cy="8" r="3"/><circle cx="17" cy="9" r="2.5"/><path d="M3 19c0-3 2.7-5 6-5s6 2 6 5M14 19c.5-2.4 2.4-4 5-4 .8 0 1.5.2 2 .5"/></svg>',
|
||||
badge: 'V1.5'
|
||||
};
|
||||
|
||||
window.Shell = {
|
||||
render({ active = '', crumbs = [], balance = '¥327.40', topActions = '' } = {}) {
|
||||
const navHtml = NAV.map(n => `
|
||||
<a href="${n.href}" class="${active === n.id ? 'active' : ''}">
|
||||
${n.icon}
|
||||
<span>${n.label}</span>
|
||||
${n.badge ? `<span class="pill-mini">${n.badge}</span>` : ''}
|
||||
</a>
|
||||
`).join('');
|
||||
|
||||
const sidebar = `
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<div class="flame"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2c1 3 4 5 4 9a4 4 0 0 1-4 4 4 4 0 0 1-4-4 5 5 0 0 1 1.5-3.5C10.5 6 11.5 4 12 2zm-1 13c0 2 1 3 1 5 1-1 3-2 3-5 0-1.5-1-2-2-3-1 1-2 2-2 3z"/></svg></div>
|
||||
<div><div class="name">流·Studio</div></div>
|
||||
</div>
|
||||
<div class="search-box" onclick="document.getElementById('global-search').focus()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input id="global-search" placeholder="搜索"/>
|
||||
<span class="kbd">⌘K</span>
|
||||
</div>
|
||||
<div class="nav-section">主要</div>
|
||||
<nav>${navHtml}</nav>
|
||||
<div class="nav-section">协作</div>
|
||||
<nav>
|
||||
<a class="disabled" title="V1.5 上线,敬请期待">
|
||||
${TEAM_NAV.icon}
|
||||
<span>${TEAM_NAV.label}</span>
|
||||
<span class="pill-mini">${TEAM_NAV.badge}</span>
|
||||
</a>
|
||||
</nav>
|
||||
<div class="aside-foot">
|
||||
<div class="user" onclick="Shell.toast('账户菜单', 'li@shop.com')">
|
||||
<div class="av">李</div>
|
||||
<div class="em">小李的店</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
`;
|
||||
|
||||
const crumbHtml = crumbs.length ? `
|
||||
<div class="crumbs">
|
||||
${crumbs.map((c, i) => {
|
||||
const last = i === crumbs.length - 1;
|
||||
const sep = i > 0 ? '<span class="sep">/</span>' : '';
|
||||
if (last) return `${sep}<span class="here">${c.label}</span>`;
|
||||
return `${sep}<a href="${c.href || '#'}">${c.label}</a>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
` : '';
|
||||
|
||||
const topbar = `
|
||||
<header class="topbar">
|
||||
${crumbHtml}
|
||||
<div class="right">
|
||||
<span class="balance-chip" onclick="location.href='account.html'">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 7v10M9 10h4a1.5 1.5 0 0 1 0 3h-3a1.5 1.5 0 0 0 0 3h4"/></svg>
|
||||
余额 <strong>${balance}</strong>
|
||||
</span>
|
||||
<button class="icon-btn" onclick="Shell.toast('通知中心', '3 条未读')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9M10 21a2 2 0 0 0 4 0"/></svg>
|
||||
<span class="dot-noti"></span>
|
||||
</button>
|
||||
${topActions}
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
|
||||
const decorations = `
|
||||
<div class="grid-bg"></div>
|
||||
<pre class="scatter" style="top:96px;left:280px"> · · +
|
||||
· +XX+
|
||||
+XXXX·
|
||||
+X· </pre>
|
||||
<pre class="scatter" style="top:340px;right:96px">+ · ·
|
||||
XX· ·
|
||||
·XXXX·+
|
||||
·++· </pre>
|
||||
<pre class="scatter" style="bottom:160px;left:42%"> · +
|
||||
+·XX·
|
||||
·X+ ·
|
||||
· </pre>
|
||||
<pre class="scatter" style="top:580px;left:60px"> +X·
|
||||
·XX·
|
||||
+·X·+</pre>
|
||||
<span class="sq-mark" style="top:238px;left:478px"></span>
|
||||
<span class="sq-mark" style="top:478px;left:1198px"></span>
|
||||
<span class="sq-mark" style="bottom:300px;left:238px"></span>
|
||||
<span class="sq-mark" style="top:718px;right:240px"></span>
|
||||
<span class="tag-corner" style="top:158px;left:34px">[ 200 OK ]</span>
|
||||
<span class="tag-corner" style="top:158px;right:34px">[ /v2 ]</span>
|
||||
<span class="tag-corner" style="bottom:36px;left:34px">[ .MP4 · 9:16 ]</span>
|
||||
<span class="tag-corner" style="bottom:36px;right:34px">[ STUDIO ]</span>
|
||||
`;
|
||||
|
||||
const toastHtml = `
|
||||
<div class="toast" id="__toast">
|
||||
<div class="ic-t"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div>
|
||||
<div class="txt" id="__toast-txt">操作成功<span class="mono">[ 200 OK ]</span></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const app = document.createElement('div');
|
||||
app.className = 'app';
|
||||
app.innerHTML = sidebar + `<main>${decorations}${topbar}<div class="content" id="page-content"></div></main>`;
|
||||
|
||||
const src = document.getElementById('page');
|
||||
document.body.prepend(app);
|
||||
if (src) {
|
||||
document.getElementById('page-content').innerHTML = src.innerHTML;
|
||||
src.remove();
|
||||
}
|
||||
document.body.insertAdjacentHTML('beforeend', toastHtml);
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
document.getElementById('global-search')?.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
toast(text, mono) {
|
||||
const t = document.getElementById('__toast');
|
||||
const txt = document.getElementById('__toast-txt');
|
||||
if (!t || !txt) return;
|
||||
txt.innerHTML = text + (mono ? `<span class="mono">[ ${mono} ]</span>` : '');
|
||||
t.classList.add('show');
|
||||
clearTimeout(this._tt);
|
||||
this._tt = setTimeout(() => t.classList.remove('show'), 2400);
|
||||
},
|
||||
|
||||
openModal(id) { document.getElementById(id)?.classList.add('show'); },
|
||||
closeModal(id) { document.getElementById(id)?.classList.remove('show'); },
|
||||
openDrawer(id) {
|
||||
document.getElementById(id)?.classList.add('show');
|
||||
document.getElementById(id + '-bg')?.classList.add('show');
|
||||
},
|
||||
closeDrawer(id) {
|
||||
document.getElementById(id)?.classList.remove('show');
|
||||
document.getElementById(id + '-bg')?.classList.remove('show');
|
||||
}
|
||||
};
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<!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>
|
||||
.dash-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 24px; align-items: start; }
|
||||
.recent-row { display: grid; grid-template-columns: 54px 1fr auto auto auto; align-items: center; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); cursor: pointer; }
|
||||
.recent-row:last-child { border-bottom: 0; }
|
||||
.recent-row:hover { background: var(--bg-soft); }
|
||||
.recent-row .thumb { width: 54px; height: 70px; }
|
||||
.recent-meta .name { font-weight: 600; font-size: 13.5px; color: var(--ink); }
|
||||
.recent-meta .sub { font-size: 12px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .01em; }
|
||||
.shortcuts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.shortcut { background: var(--card); border: 1px solid var(--border); padding: 14px; display: flex; align-items: flex-start; gap: 11px; cursor: pointer; }
|
||||
.shortcut:hover { background: var(--bg-soft); }
|
||||
.shortcut .ic { width: 30px; height: 30px; background: var(--orange-tint); color: var(--orange); display: grid; place-items: center; border: 1px solid var(--orange-soft); flex-shrink: 0; }
|
||||
.shortcut .ic svg { width: 15px; height: 15px; }
|
||||
.shortcut .t { font-size: 13px; font-weight: 600; }
|
||||
.shortcut .d { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; font-family: 'JetBrains Mono', monospace; letter-spacing: .01em; }
|
||||
.tip { background: var(--card); border: 1px dashed var(--border); padding: 14px 16px; font-size: 12.5px; color: var(--ink-2); line-height: 1.6; }
|
||||
.tip strong { color: var(--ink); font-weight: 600; display: block; margin-bottom: 4px; }
|
||||
.tip .mono { font-family: 'JetBrains Mono', monospace; color: var(--orange); background: var(--orange-tint); padding: 1px 5px; border-radius: 3px; font-size: 11.5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>欢迎回来,小李</h1>
|
||||
<div class="sub">
|
||||
<span class="mono">// 05.14 · 周三</span>
|
||||
<span>·</span>
|
||||
<span>你有 <b style="color:var(--ink)">3 个项目</b> 正在进行中</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn" href="products.html">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
|
||||
新建商品
|
||||
</a>
|
||||
<a class="btn btn-primary btn-lg" href="projects-new.html">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="M12 5v14M5 12h14"/></svg>
|
||||
新建项目
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats with-corners">
|
||||
<span class="corner-tr">+</span><span class="corner-bl">+</span>
|
||||
<a class="stat" href="projects.html">
|
||||
<div class="lbl">总项目 <span class="badge">ALL</span></div>
|
||||
<div class="v">12</div>
|
||||
<div class="delta up">↑ 本月 +3</div>
|
||||
</a>
|
||||
<a class="stat" href="projects.html">
|
||||
<div class="lbl">进行中 <span class="badge">WIP</span></div>
|
||||
<div class="v">3</div>
|
||||
<div class="delta">2 个待审核</div>
|
||||
</a>
|
||||
<a class="stat" href="projects.html">
|
||||
<div class="lbl">本月成片 <span class="badge">DONE</span></div>
|
||||
<div class="v">8</div>
|
||||
<div class="delta up">↑ 较上月 +33%</div>
|
||||
</a>
|
||||
<a class="stat" href="account.html">
|
||||
<div class="lbl">余额 <span class="badge">¥</span></div>
|
||||
<div class="v">¥327<small>.40</small></div>
|
||||
<div class="bar"><span style="width:33%"></span></div>
|
||||
<div class="sub">已用 ¥162.60 / ¥500</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dash-grid">
|
||||
<div>
|
||||
<div class="section-h">
|
||||
<h2>最近项目</h2>
|
||||
<a class="more" href="projects.html">[ ALL · 12 ] →</a>
|
||||
</div>
|
||||
<div class="card-hard">
|
||||
<a class="recent-row" href="pipeline.html#stage-3">
|
||||
<div class="placeholder thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div class="recent-meta">
|
||||
<div class="name">补水面膜 · 痛点种草</div>
|
||||
<div class="sub">补水面膜 / AI 全生 / 6 镜</div>
|
||||
</div>
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="cur"></span><span></span><span></span></div>
|
||||
<span class="pill info"><span class="dot"></span>故事板 待确认</span>
|
||||
<span class="btn btn-sm">继续</span>
|
||||
</a>
|
||||
<a class="recent-row" href="pipeline.html#stage-5">
|
||||
<div class="placeholder thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div class="recent-meta">
|
||||
<div class="name">蓝牙耳机 · 开箱测评</div>
|
||||
<div class="sub">南卡 Lite Pro / 自带脚本 / 5 镜</div>
|
||||
</div>
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="pill ok"><span class="dot"></span>已完成</span>
|
||||
<span class="btn btn-sm">打开</span>
|
||||
</a>
|
||||
<a class="recent-row" href="pipeline.html#stage-2">
|
||||
<div class="placeholder thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div class="recent-meta">
|
||||
<div class="name">速食牛肉面 · 一句话主题</div>
|
||||
<div class="sub">滋啦速食 / 一句话 / 4 镜</div>
|
||||
</div>
|
||||
<div class="prog"><span class="done"></span><span class="cur"></span><span></span><span></span><span></span></div>
|
||||
<span class="pill info"><span class="dot"></span>资产生成中</span>
|
||||
<span class="btn btn-sm">继续</span>
|
||||
</a>
|
||||
<a class="recent-row" href="pipeline.html#stage-4">
|
||||
<div class="placeholder thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div class="recent-meta">
|
||||
<div class="name">防晒霜 · 对比展示</div>
|
||||
<div class="sub">透真防晒 / AI 全生 / 6 镜</div>
|
||||
</div>
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="cur"></span><span></span></div>
|
||||
<span class="pill info"><span class="dot"></span>视频生成 4/6</span>
|
||||
<span class="btn btn-sm">继续</span>
|
||||
</a>
|
||||
<a class="recent-row" href="pipeline.html#stage-3">
|
||||
<div class="placeholder thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div class="recent-meta">
|
||||
<div class="name">咖啡冻干粉 · 剧情带货</div>
|
||||
<div class="sub">三顿半同款 / 一句话 / 5 镜</div>
|
||||
</div>
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="fail"></span><span></span><span></span></div>
|
||||
<span class="pill err"><span class="dot"></span>故事板失败</span>
|
||||
<span class="btn btn-sm">查看</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:24px">
|
||||
<div>
|
||||
<div class="section-h"><h2>快捷入口</h2><span class="more">[ /shortcuts ]</span></div>
|
||||
<div class="shortcuts">
|
||||
<a class="shortcut" href="products.html">
|
||||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="m3 7 9-4 9 4-9 4-9-4z"/><path d="m3 12 9 4 9-4"/></svg></div>
|
||||
<div><div class="t">商品库</div><div class="d">12 SKU</div></div>
|
||||
</a>
|
||||
<a class="shortcut" href="library.html">
|
||||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="6" height="16"/><rect x="11" y="4" width="4" height="16"/><rect x="17" y="6" width="4" height="14"/></svg></div>
|
||||
<div><div class="t">资产库</div><div class="d">人 8 · 景 14 · 片 8</div></div>
|
||||
</a>
|
||||
<a class="shortcut" href="account.html">
|
||||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M12 7v10M9 10h4a1.5 1.5 0 0 1 0 3h-3a1.5 1.5 0 0 0 0 3h4"/></svg></div>
|
||||
<div><div class="t">充值</div><div class="d">¥327.40</div></div>
|
||||
</a>
|
||||
<a class="shortcut" href="projects.html">
|
||||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="16"/><path d="M7 4v16M16 4v16M3 9h18M3 15h18"/></svg></div>
|
||||
<div><div class="t">所有项目</div><div class="d">12 个</div></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-h"><h2>提示</h2><span class="more">[ FAQ ]</span></div>
|
||||
<div class="tip">
|
||||
<strong>扣费规则</strong>
|
||||
生成失败、超时、用户重跑 — 均不扣费。仅在你点 <span class="mono">[ 确认通过 ]</span> 时按 token 实际结算。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>Shell.render({ active: 'dashboard', crumbs: [{ label: '工作台' }] });</script>
|
||||
</body>
|
||||
</html>
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
<!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>
|
||||
.asset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; }
|
||||
.asset-card { background: var(--card); border: 1px solid var(--border); cursor: pointer; transition: background .15s; }
|
||||
.asset-card:hover { background: var(--bg-soft); border-color: var(--ink-3); }
|
||||
.asset-thumb { aspect-ratio: 1; }
|
||||
.asset-card.video .asset-thumb { aspect-ratio: 9/16; max-height: 280px; }
|
||||
.asset-body { padding: 10px 12px; }
|
||||
.asset-name { font-size: 13px; font-weight: 600; }
|
||||
.asset-meta { font-size: 11px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>资产库</h1>
|
||||
<div class="sub"><span class="mono">// 跨项目复用 · 人 8 · 景 14 · 商 12 · 片 8</span></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn" onclick="Shell.toast('上传资产', '/library/upload')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
|
||||
上传资产
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active">人物 <span class="count">8</span></div>
|
||||
<div class="tab">场景 <span class="count">14</span></div>
|
||||
<div class="tab">商品图 <span class="count">12</span></div>
|
||||
<div class="tab">成片 <span class="count">8</span></div>
|
||||
<div class="tab">我的上传 <span class="count">3</span></div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="search-inline">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input class="input" placeholder="搜索人物特征">
|
||||
</div>
|
||||
<button class="chip active">全部 <span class="mono" style="opacity:.7">8</span></button>
|
||||
<button class="chip">女性</button>
|
||||
<button class="chip">男性</button>
|
||||
<button class="chip">25-30 岁</button>
|
||||
<button class="chip">都市白领</button>
|
||||
<span class="spacer"></span>
|
||||
<button class="chip">最近使用 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="asset-grid">
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '林夕')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">林夕 · 都市白领</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">林夕</div>
|
||||
<div class="asset-meta">女 · 25-30 · 用过 4 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '阿楠')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">阿楠 · 同事女</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">阿楠</div>
|
||||
<div class="asset-meta">女 · 25-30 · 用过 2 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '小七')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">小七 · 学生女</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">小七</div>
|
||||
<div class="asset-meta">女 · 18-22 · 用过 3 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '阿杰')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">阿杰 · 通勤男</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">阿杰</div>
|
||||
<div class="asset-meta">男 · 28-35 · 用过 2 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '王姐')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">妈妈 · 居家</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">妈妈 · 王姐</div>
|
||||
<div class="asset-meta">女 · 38-45 · 用过 1 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '阿强')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">阿强 · 健身男</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">阿强</div>
|
||||
<div class="asset-meta">男 · 22-28 · 用过 2 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '小苏')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">研究生 · 文艺女</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">小苏</div>
|
||||
<div class="asset-meta">女 · 22-26 · 用过 1 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" onclick="Shell.toast('查看资产', '闺蜜组合')">
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">闺蜜组合 · 双人</span></div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">闺蜜组合</div>
|
||||
<div class="asset-meta">双人 · 25-30 · 用过 1 次</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>Shell.render({ active: 'library', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '资产库' }] });</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,495 @@
|
||||
<!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>
|
||||
body { background: var(--bg); padding: 40px 24px 80px; }
|
||||
.wrap { max-width: 1200px; margin: 0 auto; }
|
||||
|
||||
.head { margin-bottom: 36px; padding-bottom: 24px; border-bottom: 1px solid var(--border); position: relative; }
|
||||
.head::before { content: '+'; position: absolute; top: -8px; left: -8px; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||||
.head h1 { font-size: 28px; font-weight: 700; letter-spacing: -.018em; }
|
||||
.head .sub { font-size: 13.5px; color: var(--ink-2); margin-top: 8px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.head .sub .mono { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
.ctx-box {
|
||||
background: var(--card); border: 1px solid var(--border); padding: 18px 22px;
|
||||
margin-bottom: 28px; position: relative;
|
||||
}
|
||||
.ctx-box::before, .ctx-box::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||||
.ctx-box::before { top: -7px; left: -7px; }
|
||||
.ctx-box::after { bottom: -7px; right: -7px; }
|
||||
.ctx-box h3 { font-size: 13px; font-weight: 600; margin-bottom: 8px; }
|
||||
.ctx-box p { font-size: 12.5px; color: var(--ink-2); line-height: 1.7; }
|
||||
.ctx-box .flow-line {
|
||||
margin-top: 14px;
|
||||
padding: 12px 14px;
|
||||
background: var(--bg-soft);
|
||||
border: 1px solid var(--border-soft);
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 11.5px;
|
||||
color: var(--ink-2); letter-spacing: .02em;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.ctx-box .flow-line .arrow { color: var(--orange); margin: 0 6px; }
|
||||
|
||||
.versions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
|
||||
.ver-card {
|
||||
background: var(--card); border: 1px solid var(--border);
|
||||
display: flex; flex-direction: column;
|
||||
text-decoration: none; color: var(--ink);
|
||||
transition: border-color .15s;
|
||||
position: relative;
|
||||
}
|
||||
.ver-card:hover { border-color: var(--orange); }
|
||||
.ver-card:hover::before, .ver-card:hover::after { color: var(--orange); }
|
||||
.ver-card::before, .ver-card::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; transition: color .15s; }
|
||||
.ver-card::before { top: -7px; left: -7px; }
|
||||
.ver-card::after { bottom: -7px; right: -7px; }
|
||||
|
||||
.ver-preview {
|
||||
aspect-ratio: 4/3;
|
||||
background:
|
||||
repeating-linear-gradient(135deg, rgba(0,0,0,0.02) 0 1px, transparent 1px 12px),
|
||||
var(--bg-soft);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: relative;
|
||||
padding: 14px;
|
||||
}
|
||||
.ver-preview .mock { position: absolute; inset: 14px; background: var(--card); border: 1px solid var(--border); display: flex; flex-direction: column; }
|
||||
|
||||
/* Mock A: stepper */
|
||||
.mock-A-top { padding: 8px; border-bottom: 1px solid var(--border); display: flex; justify-content: center; gap: 8px; }
|
||||
.mock-A-top span { width: 12px; height: 12px; background: var(--bg-soft); border: 1px solid var(--border); }
|
||||
.mock-A-top span:nth-child(2) { background: var(--orange); border-color: var(--orange); }
|
||||
.mock-A-body { flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 10px; }
|
||||
.mock-A-body div { background: var(--bg-soft); border: 1px solid var(--border); }
|
||||
.mock-A-body div.sel { background: var(--orange-tint); border-color: var(--orange); }
|
||||
.mock-A-foot { padding: 6px 10px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; }
|
||||
.mock-A-foot span { width: 28px; height: 8px; background: var(--bg-soft); border: 1px solid var(--border); }
|
||||
.mock-A-foot span.primary { background: var(--orange); border-color: var(--orange); }
|
||||
|
||||
/* Mock B: accordion */
|
||||
.mock-B-top { padding: 6px 8px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 4px; }
|
||||
.mock-B-top .bar { flex: 1; height: 3px; background: var(--bg-soft); }
|
||||
.mock-B-top .bar span { display: block; width: 50%; height: 100%; background: var(--orange); }
|
||||
.mock-B-rows { flex: 1; display: flex; flex-direction: column; padding: 6px; gap: 4px; }
|
||||
.mock-B-row { padding: 6px 8px; background: var(--bg-soft); border: 1px solid var(--border); display: flex; align-items: center; gap: 6px; }
|
||||
.mock-B-row.done .num { background: var(--ink); color: #FFF; }
|
||||
.mock-B-row.active { background: var(--orange-tint); border-color: var(--orange); padding: 8px; flex: 1; }
|
||||
.mock-B-row .num { width: 14px; height: 14px; background: var(--card); border: 1px solid var(--border); font-size: 8px; display: grid; place-items: center; font-family: 'JetBrains Mono', monospace; }
|
||||
.mock-B-row.active .num { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.mock-B-row .lbl { flex: 1; height: 6px; background: var(--card); border: 1px solid var(--border); }
|
||||
|
||||
/* Mock C: workbench */
|
||||
.mock-C-top { padding: 6px 8px; border-bottom: 1px solid var(--border); }
|
||||
.mock-C-top .ti { width: 60%; height: 6px; background: var(--bg-soft); }
|
||||
.mock-C-body { flex: 1; display: grid; grid-template-columns: 36px 1fr 50px; gap: 0; }
|
||||
.mock-C-side { background: var(--bg-soft); border-right: 1px solid var(--border); padding: 6px; display: flex; flex-direction: column; gap: 4px; }
|
||||
.mock-C-side div { width: 100%; height: 8px; background: var(--card); border: 1px solid var(--border); }
|
||||
.mock-C-side div.act { background: var(--orange); border-color: var(--orange); }
|
||||
.mock-C-canvas { padding: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
|
||||
.mock-C-canvas div { background: var(--bg-soft); border: 1px solid var(--border); }
|
||||
.mock-C-canvas div.sel { background: var(--orange-tint); border-color: var(--orange); }
|
||||
.mock-C-controls { background: var(--bg-soft); border-left: 1px solid var(--border); padding: 6px; display: flex; flex-direction: column; gap: 4px; }
|
||||
.mock-C-controls div { background: var(--card); border: 1px solid var(--border); height: 14px; }
|
||||
|
||||
.ver-info { padding: 18px 20px; }
|
||||
.ver-info .badge {
|
||||
display: inline-block;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 10.5px;
|
||||
color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange-soft);
|
||||
padding: 2px 8px; letter-spacing: .04em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ver-info h2 { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
|
||||
.ver-info .tagline { font-size: 12.5px; color: var(--ink-2); line-height: 1.6; margin-bottom: 14px; }
|
||||
.ver-info ul { list-style: none; padding: 0; }
|
||||
.ver-info ul li { font-size: 11.5px; color: var(--ink-2); padding: 5px 0; display: flex; gap: 8px; align-items: flex-start; }
|
||||
.ver-info ul li::before { content: ''; width: 5px; height: 5px; background: var(--orange); border-radius: 50%; margin-top: 7px; flex-shrink: 0; }
|
||||
.ver-info .cta { margin-top: 16px; }
|
||||
|
||||
.ver-info .pros, .ver-info .cons { margin-top: 12px; padding: 10px 12px; font-size: 11.5px; line-height: 1.6; }
|
||||
.ver-info .pros { background: var(--green-bg); border: 1px solid var(--green-bd); color: var(--green); }
|
||||
.ver-info .cons { background: var(--bg-soft); border: 1px solid var(--border); color: var(--ink-2); }
|
||||
.ver-info .pros strong, .ver-info .cons strong { font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .04em; display: block; margin-bottom: 3px; }
|
||||
|
||||
.compare-table {
|
||||
margin-top: 36px;
|
||||
background: var(--card); border: 1px solid var(--border);
|
||||
position: relative;
|
||||
}
|
||||
.compare-table::before, .compare-table::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||||
.compare-table::before { top: -7px; left: -7px; }
|
||||
.compare-table::after { bottom: -7px; right: -7px; }
|
||||
.compare-table h3 { padding: 14px 18px; border-bottom: 1px solid var(--border); font-size: 14px; font-weight: 600; }
|
||||
table.cmp { width: 100%; border-collapse: collapse; }
|
||||
table.cmp th { text-align: left; padding: 12px 18px; background: var(--bg-soft); font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .04em; text-transform: uppercase; font-weight: 600; border-bottom: 1px solid var(--border); }
|
||||
table.cmp td { padding: 14px 18px; border-bottom: 1px solid var(--border); font-size: 12.5px; vertical-align: top; }
|
||||
table.cmp tr:last-child td { border-bottom: 0; }
|
||||
table.cmp .row-h { font-weight: 600; color: var(--ink); }
|
||||
table.cmp .stars { color: var(--orange); font-weight: 700; }
|
||||
table.cmp .small { font-size: 11px; color: var(--ink-3); display: block; margin-top: 2px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<div class="head">
|
||||
<h1>新建商品 · 三版交互方案对比</h1>
|
||||
<div class="sub">
|
||||
<span class="mono">// 流·Studio · AI 生成模式</span>
|
||||
<span>·</span>
|
||||
<span>三个独立 HTML,选定方案后再合并到 <a href="../products.html" style="color:var(--orange);">products.html</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctx-box">
|
||||
<h3>需求背景</h3>
|
||||
<p>商家可能<strong>没有现成的商品图</strong>。需要在原有"上传图片"的基础上,新增一种「AI 生成」模式:商家上传一张随手拍的原图,AI 生成 4 张满意的头图(可重跑),选 1 张;然后选 AI 模特模板,生成 4 张模特上身参考图(可重跑),勾选满意的。</p>
|
||||
<div class="flow-line">
|
||||
[ 上传原图 + 信息 ] <span class="arrow">→</span> [ AI 生成头图 · 4 选 1 ] <span class="arrow">→</span> [ 选模特 + 生成上身图 · 4 选 N ] <span class="arrow">→</span> [ 预览 · 创建 ]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="versions">
|
||||
|
||||
<!-- ===== 方案 A ===== -->
|
||||
<a class="ver-card" href="mockup-A.html">
|
||||
<div class="ver-preview">
|
||||
<div class="mock">
|
||||
<div class="mock-A-top"><span></span><span></span><span></span><span></span></div>
|
||||
<div class="mock-A-body">
|
||||
<div class="sel"></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="mock-A-foot"><span></span><span class="primary"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ver-info">
|
||||
<span class="badge">[ A · WIZARD ]</span>
|
||||
<h2>分步向导</h2>
|
||||
<p class="tagline">全屏 4 步,顶部 stepper,底部固定工具栏。每步只关注一件事,前进/后退清晰。</p>
|
||||
<ul>
|
||||
<li>跟「新建项目向导」一脉相承,商家熟悉</li>
|
||||
<li>每步沉浸式,不被其他信息干扰</li>
|
||||
<li>4 张候选图大网格 + 居中显示</li>
|
||||
</ul>
|
||||
<div class="pros">
|
||||
<strong>// PROS</strong>
|
||||
最经典最不容易迷路。新手友好,学习成本最低。
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>// CONS</strong>
|
||||
不能轻松看到"前面已经选了什么",回头改要点上一步。
|
||||
</div>
|
||||
<div class="cta"><span class="btn btn-primary">查看 Demo →</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- ===== 方案 B ===== -->
|
||||
<a class="ver-card" href="mockup-B.html">
|
||||
<div class="ver-preview">
|
||||
<div class="mock">
|
||||
<div class="mock-B-top"><div class="bar"><span></span></div></div>
|
||||
<div class="mock-B-rows">
|
||||
<div class="mock-B-row done"><span class="num">✓</span><span class="lbl"></span></div>
|
||||
<div class="mock-B-row active"><span class="num"></span><span class="lbl"></span></div>
|
||||
<div class="mock-B-row"><span class="num"></span><span class="lbl"></span></div>
|
||||
<div class="mock-B-row"><span class="num"></span><span class="lbl"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ver-info">
|
||||
<span class="badge">[ B · ACCORDION ]</span>
|
||||
<h2>单页折叠流</h2>
|
||||
<p class="tagline">右侧抽屉,4 步从上到下流式排列。已完成的折叠成"摘要 + 编辑"按钮,当前步展开。</p>
|
||||
<ul>
|
||||
<li>能看到完整流程上下文</li>
|
||||
<li>顶部模式切换:[我有商品图 / AI 生成] 一目了然</li>
|
||||
<li>已选过的随时可以回去改,不丢失</li>
|
||||
</ul>
|
||||
<div class="pros">
|
||||
<strong>// PROS</strong>
|
||||
最完整,适合反复对比和回溯。能跟原有"我有图"模式天然兼容。
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>// CONS</strong>
|
||||
页面信息密度略高,初次接触可能有"信息过载"感。
|
||||
</div>
|
||||
<div class="cta"><span class="btn btn-primary">查看 Demo →</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- ===== 方案 C ===== -->
|
||||
<a class="ver-card" href="mockup-C.html">
|
||||
<div class="ver-preview">
|
||||
<div class="mock">
|
||||
<div class="mock-C-top"><div class="ti"></div></div>
|
||||
<div class="mock-C-body">
|
||||
<div class="mock-C-side">
|
||||
<div></div><div class="act"></div><div></div><div></div>
|
||||
</div>
|
||||
<div class="mock-C-canvas">
|
||||
<div class="sel"></div><div></div>
|
||||
<div></div><div></div>
|
||||
</div>
|
||||
<div class="mock-C-controls">
|
||||
<div></div><div></div><div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ver-info">
|
||||
<span class="badge">[ C · WORKBENCH ]</span>
|
||||
<h2>双栏工作台</h2>
|
||||
<p class="tagline">全屏覆盖。左侧步骤导航 + 已选素材,中央大预览图(超大网格),右侧操作面板 + 重新生成参数。</p>
|
||||
<ul>
|
||||
<li>视觉空间最大,选图体验最爽</li>
|
||||
<li>右侧可调"背景风格 / 氛围"等参数后再重跑</li>
|
||||
<li>左侧实时显示已选素材缩略图</li>
|
||||
</ul>
|
||||
<div class="pros">
|
||||
<strong>// PROS</strong>
|
||||
专业感最强,选图最清晰。参考 Pebblely / Booth AI 的成熟方案。
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>// CONS</strong>
|
||||
覆盖整个屏幕,跟原有抽屉式新建商品形态差异大。学习成本略高。
|
||||
</div>
|
||||
<div class="cta"><span class="btn btn-primary">查看 Demo →</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="compare-table">
|
||||
<h3>横向对比</h3>
|
||||
<table class="cmp">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 22%;">维度</th>
|
||||
<th>A · 分步向导</th>
|
||||
<th>B · 单页折叠流</th>
|
||||
<th>C · 双栏工作台</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="row-h">傻瓜程度</td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">最简单 · 一次只看一件事</span></td>
|
||||
<td><span class="stars">★★★★</span><span class="small">略复杂 · 但全程可见</span></td>
|
||||
<td><span class="stars">★★★</span><span class="small">需要适应工作台布局</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">选图视觉空间</td>
|
||||
<td><span class="stars">★★★★</span><span class="small">大,但有页面边距</span></td>
|
||||
<td><span class="stars">★★★</span><span class="small">中等,抽屉宽度限制</span></td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">最大 · 参考 Pebblely</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">回头修改</td>
|
||||
<td><span class="stars">★★★</span><span class="small">点上一步 / 顶部 stepper</span></td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">点已完成的"编辑"即可</span></td>
|
||||
<td><span class="stars">★★★★</span><span class="small">点左侧 step 切换</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">"我有图 / AI 生成"双模式融合</td>
|
||||
<td>需要在第 1 步加分支选择</td>
|
||||
<td><strong>顶部就有切换</strong>,无缝</td>
|
||||
<td>需要在顶部或左侧加切换</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">改原 products.html 的成本</td>
|
||||
<td>中 · 替换抽屉 → 全屏向导</td>
|
||||
<td><strong>低 · 抽屉宽度加大,内部改</strong></td>
|
||||
<td>高 · 完全替换交互形态</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">参考产品</td>
|
||||
<td>稿定 AI 商品图 · 万相实验室</td>
|
||||
<td>Notion 引导 · Linear 流程</td>
|
||||
<td>Pebblely · Booth AI · 即梦</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 36px; padding: 18px 22px; background: var(--orange-tint); border: 1px solid var(--orange-soft); position: relative;">
|
||||
<div style="font-family:'JetBrains Mono',monospace; font-size:10.5px; color:var(--orange); letter-spacing:.04em; margin-bottom:6px;">// MY RECOMMENDATION</div>
|
||||
<p style="font-size:13px; color:var(--ink); line-height:1.7;">
|
||||
<strong>建议优先选 B(单页折叠流)</strong> ——
|
||||
用户说优先「最简单傻瓜」,但 A 的弱点是「我有图 / AI 生成」两条路要分支处理,流程会割裂;
|
||||
B 在顶部直接放 [我有商品图 / AI 生成] 模式切换,**两种模式共用一套抽屉框架**,改起来也最省事。
|
||||
C 视觉最爽但跟现有抽屉式新建商品差距太大,**留作 V1.5 升级方向**。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ============= 第二轮:C 方案的"模特挑选"细化 ============= -->
|
||||
<div style="margin-top:48px; padding-bottom:24px; border-bottom:1px solid var(--border); position:relative;">
|
||||
<div style="font-family:'JetBrains Mono',monospace; font-size:10.5px; color:var(--ink-3); letter-spacing:.08em; margin-bottom:8px;">// ROUND 2 · MODEL PICKER REFINEMENT</div>
|
||||
<h2 style="font-size:22px; font-weight:700; letter-spacing:-.012em;">C 方案 · 模特挑选交互细化</h2>
|
||||
<p style="font-size:13px; color:var(--ink-2); margin-top:8px; line-height:1.6;">
|
||||
用户已选 C 方案。模特库规模 <strong>50+</strong> ,需要更重的展示空间(立绘网格 + 悬浮三视图 + 多筛选 + 多选)。
|
||||
下面两版对比:把"选模特"独立成一个 step,还是放在原 Step 3 内部用子 Tab 切换。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="versions" style="margin-top:24px; grid-template-columns: repeat(2, 1fr);">
|
||||
|
||||
<!-- ===== Step 1 ===== -->
|
||||
<a class="ver-card" href="mockup-C-step1.html">
|
||||
<div class="ver-preview">
|
||||
<div class="mock">
|
||||
<div class="mock-C-top"><div class="ti"></div></div>
|
||||
<div class="mock-C-body">
|
||||
<div class="mock-C-side">
|
||||
<div></div><div></div><div class="act"></div><div></div><div></div>
|
||||
</div>
|
||||
<div class="mock-C-canvas" style="grid-template-columns:repeat(3,1fr); grid-template-rows:repeat(2,1fr);">
|
||||
<div class="sel"></div><div></div><div></div>
|
||||
<div></div><div class="sel"></div><div></div>
|
||||
</div>
|
||||
<div class="mock-C-controls">
|
||||
<div></div><div style="height:24px;"></div><div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ver-info">
|
||||
<span class="badge">[ C-STEP1 · 5 STEPS ]</span>
|
||||
<h2>方案 1 · 独立 Step</h2>
|
||||
<p class="tagline">把「选模特」独立成第 3 步,流程从 4 步变 5 步。模特库占满整个 canvas,有完整筛选空间。</p>
|
||||
<ul>
|
||||
<li>左侧 sidebar 出现 NEW 标记,5 步清晰可见</li>
|
||||
<li>顶部搜索 + 7-8 个 filter chips,多选无压力</li>
|
||||
<li>50+ 模特滚动加载,适合大型素材库</li>
|
||||
</ul>
|
||||
<div class="pros">
|
||||
<strong>// PROS</strong>
|
||||
每步只做一件事,模特库展示空间最大,筛选/多选最从容。适合 50+ 大库。
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>// CONS</strong>
|
||||
流程多一步,跟之前敲定的"4 步"轻量节奏冲突。"选模特→看效果"被切成两步,要点下一步才看到结果。
|
||||
</div>
|
||||
<div class="cta"><span class="btn btn-primary">查看 Demo →</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- ===== Step 3 (sub-tab) ===== -->
|
||||
<a class="ver-card" href="mockup-C-step3.html">
|
||||
<div class="ver-preview">
|
||||
<div class="mock">
|
||||
<div class="mock-C-top"><div class="ti"></div></div>
|
||||
<div class="mock-C-body">
|
||||
<div class="mock-C-side">
|
||||
<div></div><div></div><div class="act"></div><div></div>
|
||||
</div>
|
||||
<div style="display:flex; flex-direction:column; padding:0;">
|
||||
<div style="display:flex; gap:0; border-bottom:1px solid var(--border); padding:4px 6px; background:var(--bg-soft);">
|
||||
<div style="height:6px; flex:1; background:var(--orange); margin-right:2px;"></div>
|
||||
<div style="height:6px; flex:1; background:var(--bg-soft); border:1px solid var(--border);"></div>
|
||||
</div>
|
||||
<div class="mock-C-canvas" style="grid-template-columns:repeat(3,1fr); grid-template-rows:repeat(2,1fr); flex:1;">
|
||||
<div class="sel"></div><div></div><div></div>
|
||||
<div></div><div class="sel"></div><div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mock-C-controls">
|
||||
<div></div><div style="height:24px;"></div><div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ver-info">
|
||||
<span class="badge">[ C-STEP3 · 4 STEPS + SUB-TAB ]</span>
|
||||
<h2>方案 3 · 子步骤分屏</h2>
|
||||
<p class="tagline">保持 4 步主流程,但 Step 3 内部分两个子 Tab:① 挑选模特 → ② 生成上身图。在同一个 Step 里递进。</p>
|
||||
<ul>
|
||||
<li>主 sidebar 4 个 step 不变,只在 active step 下展开 2 个子项</li>
|
||||
<li>顶部 canvas 上方的子 Tab 条:① → ② 视觉递进</li>
|
||||
<li>选完模特点"下一步"自动跳子 Tab 并触发生成</li>
|
||||
<li>子 Tab 可来回切换,改模特立刻重新生成</li>
|
||||
</ul>
|
||||
<div class="pros">
|
||||
<strong>// PROS</strong>
|
||||
流程不变多。模特挑选 + 看效果在同一个 Step 内,有"递进"感。改模特 → 看新效果链路最短。
|
||||
</div>
|
||||
<div class="cons">
|
||||
<strong>// CONS</strong>
|
||||
子 Tab 比单 step 略复杂一点。主 Step 3 名字"模特上身图"略含糊(其实包含两件事)。
|
||||
</div>
|
||||
<div class="cta"><span class="btn btn-primary">查看 Demo →</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="compare-table" style="margin-top:36px;">
|
||||
<h3>横向对比 · C 方案模特挑选</h3>
|
||||
<table class="cmp">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 24%;">维度</th>
|
||||
<th>方案 1 · 独立 Step</th>
|
||||
<th>方案 3 · 子步骤分屏</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="row-h">主流程步数</td>
|
||||
<td>5 步</td>
|
||||
<td><strong>4 步(子 Tab 内嵌)</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">模特库展示空间</td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">整个 canvas 都给模特库</span></td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">同样整个 canvas,只少一条 24 px 子 Tab 条</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">"选模特 → 看上身图效果"链路</td>
|
||||
<td>选 → 下一步 → 等生成 → 看效果(3 步)</td>
|
||||
<td><strong>选 → 切子 Tab → 看效果(2 步,可反复切换)</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">改模特(已生成上身图后)</td>
|
||||
<td>"上一步" 回 Step 3 → 改 → 再 "下一步" 回 Step 4</td>
|
||||
<td><strong>点子 Tab ① → 改 → 子 Tab ② 自动重新生成</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">学习成本</td>
|
||||
<td><span class="stars">★★★★★</span><span class="small">最熟悉 · 跟其他 step 一致</span></td>
|
||||
<td><span class="stars">★★★★</span><span class="small">需要适应"主步骤里有子步骤"的概念</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">侧边栏复杂度</td>
|
||||
<td>5 个 step,均匀</td>
|
||||
<td>4 个 step + Step 3 下展开 2 个子项(可折叠)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="row-h">参考产品</td>
|
||||
<td>Adobe Express 模板挑选 · 阿里万相</td>
|
||||
<td>Figma 工作区子模式 · Photoshop Workspace · 即梦的 Tab 切换</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 24px; padding: 18px 22px; background: var(--orange-tint); border: 1px solid var(--orange-soft); position: relative;">
|
||||
<div style="font-family:'JetBrains Mono',monospace; font-size:10.5px; color:var(--orange); letter-spacing:.04em; margin-bottom:6px;">// MY RECOMMENDATION FOR ROUND 2</div>
|
||||
<p style="font-size:13px; color:var(--ink); line-height:1.7;">
|
||||
<strong>建议选方案 3(子步骤分屏)</strong> ——
|
||||
模特库展示空间一样大(都是整个 canvas),但方案 3 把"挑模特→看上身图效果"做成可来回切换的子 Tab,**改模特时链路最短**(适合反复试不同模特对比效果);
|
||||
流程数也保持 4 步,跟原 C 节奏一致。
|
||||
方案 1 唯一胜出的场景是"用户根本不想改"的一次性流程,但电商商家选模特通常会反复试。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,471 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,440 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>方案 B · 单页折叠流 · 新建商品(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); }
|
||||
|
||||
/* Drawer wrapper - simulating right-side drawer */
|
||||
.drawer-shell {
|
||||
background: var(--card);
|
||||
border-left: 1px solid var(--border);
|
||||
width: 920px; max-width: 100vw;
|
||||
margin-left: auto;
|
||||
min-height: calc(100vh - 60px);
|
||||
display: flex; flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.drawer-shell::before, .drawer-shell::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||||
.drawer-shell::before { top: -7px; left: -7px; }
|
||||
.drawer-shell::after { bottom: -7px; right: -7px; }
|
||||
|
||||
.dr-head {
|
||||
padding: 18px 28px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
}
|
||||
.dr-head h2 { font-size: 17px; font-weight: 700; }
|
||||
.dr-head .mode-pill {
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 10.5px;
|
||||
color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange-soft);
|
||||
padding: 2px 8px; letter-spacing: .04em;
|
||||
}
|
||||
.dr-head .x { margin-left: auto; width: 28px; height: 28px; display: grid; place-items: center; cursor: pointer; color: var(--ink-2); }
|
||||
.dr-head .x:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
|
||||
/* Mode toggle at top */
|
||||
.mode-toggle {
|
||||
padding: 14px 28px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-soft);
|
||||
}
|
||||
.mode-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.mode-card {
|
||||
padding: 14px 16px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.mode-card:hover { background: var(--bg); border-color: var(--ink-3); }
|
||||
.mode-card.active { border-color: var(--orange); background: var(--orange-tint); }
|
||||
.mode-card .ic-md { width: 36px; height: 36px; background: var(--card); border: 1px solid var(--border); display: grid; place-items: center; color: var(--orange); flex-shrink: 0; }
|
||||
.mode-card.active .ic-md { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.mode-card .t { font-size: 13px; font-weight: 600; }
|
||||
.mode-card .d { font-size: 11px; color: var(--ink-3); margin-top: 2px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
|
||||
/* Top progress bar */
|
||||
.progress-strip {
|
||||
padding: 16px 28px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
}
|
||||
.progress-strip .lbl { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; min-width: 70px; }
|
||||
.progress-strip .bar { flex: 1; height: 4px; background: var(--bg-soft); }
|
||||
.progress-strip .bar > span { display: block; height: 100%; background: var(--orange); transition: width .3s; }
|
||||
.progress-strip .pct { font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700; color: var(--orange); }
|
||||
|
||||
/* Body - vertical accordion sections */
|
||||
.dr-body { flex: 1; overflow-y: auto; }
|
||||
|
||||
.acc-section {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.acc-section:last-child { border-bottom: 0; }
|
||||
|
||||
.acc-head {
|
||||
padding: 18px 28px;
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
.acc-head:hover { background: var(--bg-soft); }
|
||||
.acc-section.locked .acc-head { cursor: not-allowed; opacity: .55; }
|
||||
.acc-section.locked .acc-head:hover { background: transparent; }
|
||||
.acc-num {
|
||||
width: 26px; height: 26px;
|
||||
border: 1px solid var(--border); background: var(--card);
|
||||
color: var(--ink-3);
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700;
|
||||
display: grid; place-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.acc-section.done .acc-num { background: var(--ink); color: #FFF; border-color: var(--ink); }
|
||||
.acc-section.active .acc-num { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.acc-meta { flex: 1; }
|
||||
.acc-meta h3 { font-size: 14px; font-weight: 600; }
|
||||
.acc-meta .sm { font-size: 11.5px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.acc-section.done .acc-meta .sm { color: var(--ink-2); font-family: 'Inter', sans-serif; letter-spacing: 0; }
|
||||
.acc-summary {
|
||||
display: none;
|
||||
align-items: center; gap: 8px;
|
||||
padding: 6px 12px;
|
||||
background: var(--green-bg); color: var(--green); border: 1px solid var(--green-bd);
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
}
|
||||
.acc-section.done .acc-summary { display: inline-flex; }
|
||||
.acc-section.done .acc-toggle { display: none; }
|
||||
.acc-toggle {
|
||||
padding: 4px 10px;
|
||||
background: var(--bg-soft); border: 1px solid var(--border);
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
.acc-section.active .acc-toggle { display: none; }
|
||||
.acc-section.locked .acc-toggle { display: none; }
|
||||
|
||||
.acc-body {
|
||||
padding: 0 28px 28px;
|
||||
display: none;
|
||||
}
|
||||
.acc-section.active .acc-body { display: block; animation: fade .2s ease; }
|
||||
@keyframes fade { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.acc-section.done .acc-body-summary {
|
||||
display: block;
|
||||
padding: 0 28px 16px 68px;
|
||||
}
|
||||
.acc-section:not(.done) .acc-body-summary { display: none; }
|
||||
.acc-body-summary { display: none; }
|
||||
.preview-row { display: flex; gap: 8px; align-items: center; }
|
||||
.preview-row .placeholder { width: 40px; height: 40px; }
|
||||
|
||||
/* Step contents */
|
||||
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.upload-mini {
|
||||
aspect-ratio: 4/5;
|
||||
background: var(--bg-soft);
|
||||
border: 1.5px dashed var(--border);
|
||||
display: grid; place-items: center;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
.upload-mini.has-file { border-style: solid; padding: 0; background: var(--bg-soft); border-color: var(--orange); }
|
||||
.upload-mini.has-file .placeholder { width: 100%; height: 100%; }
|
||||
.upload-mini .ic-cam { width: 36px; height: 36px; border: 1px solid currentColor; display: grid; place-items: center; margin: 0 auto 10px; }
|
||||
|
||||
.s1-row { display: grid; grid-template-columns: 200px 1fr; gap: 24px; }
|
||||
|
||||
.gen-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
|
||||
.gen-card { aspect-ratio: 1; background: var(--card); border: 1px solid var(--border); cursor: pointer; position: relative; 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: 6px; right: 6px;
|
||||
width: 22px; height: 22px; background: var(--orange); color: #FFF;
|
||||
display: grid; place-items: center; font-weight: 700; font-size: 12px;
|
||||
}
|
||||
.gen-card .badge {
|
||||
position: absolute; bottom: 6px; left: 6px;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 10px;
|
||||
background: rgba(255,255,255,.95); color: var(--ink-2);
|
||||
border: 1px solid var(--border);
|
||||
padding: 1px 5px; letter-spacing: .04em;
|
||||
}
|
||||
|
||||
.regen-line { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
|
||||
.regen-line .info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
.model-row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-bottom: 18px; }
|
||||
.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: 5px; right: 5px; width: 18px; height: 18px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 10px; }
|
||||
.model-card .lbl-bottom { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(255,255,255,.95); padding: 3px 6px; font-size: 10px; color: var(--ink-2); font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; text-align: center; }
|
||||
.model-card .placeholder { width: 100%; height: 100%; }
|
||||
|
||||
.sub-h { display: flex; align-items: center; justify-content: space-between; margin: 14px 0 10px; }
|
||||
.sub-h h4 { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
|
||||
.sub-h .info { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
.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; }
|
||||
|
||||
/* Footer */
|
||||
.dr-foot {
|
||||
padding: 14px 28px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-soft);
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
position: sticky; bottom: 0;
|
||||
}
|
||||
.dr-foot .info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="demo-bar">
|
||||
<a href="index.html" class="btn btn-ghost btn-sm">← 返回方案对比</a>
|
||||
<span class="ti">方案 B · 单页折叠流(Accordion Flow)</span>
|
||||
<span class="desc">// 全流程在一页,选过的折叠摘要,可随时回去改 · 适合反复对比</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-shell">
|
||||
|
||||
<header class="dr-head">
|
||||
<h2>新建商品</h2>
|
||||
<span class="mode-pill">[ AI 生成模式 ]</span>
|
||||
<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>
|
||||
|
||||
<!-- Mode select at top -->
|
||||
<div class="mode-toggle">
|
||||
<div class="mode-grid">
|
||||
<div class="mode-card">
|
||||
<div class="ic-md"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg></div>
|
||||
<div><div class="t">我有商品图</div><div class="d">// upload-only · 直接上传图片</div></div>
|
||||
</div>
|
||||
<div class="mode-card active">
|
||||
<div class="ic-md"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><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></div>
|
||||
<div><div class="t">AI 帮我生成图</div><div class="d">// 上传 1 张原图,AI 出 4 选 1 + 模特上身</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-strip">
|
||||
<span class="lbl">// PROGRESS</span>
|
||||
<div class="bar"><span style="width:50%" id="bar"></span></div>
|
||||
<span class="pct" id="pct">2 / 4</span>
|
||||
</div>
|
||||
|
||||
<div class="dr-body">
|
||||
|
||||
<!-- ============= 1 · 商品信息 (DONE - collapsed) ============= -->
|
||||
<section class="acc-section done" data-step="1">
|
||||
<div class="acc-head">
|
||||
<div class="acc-num">✓</div>
|
||||
<div class="acc-meta">
|
||||
<h3>商品信息 + 上传原图</h3>
|
||||
<div class="sm">透真玻尿酸补水面膜 · 美妆个护 · ¥39.9 · 已上传 1 张原图</div>
|
||||
</div>
|
||||
<span class="acc-summary">✓ 已完成</span>
|
||||
<button class="acc-toggle">编辑</button>
|
||||
</div>
|
||||
<div class="acc-body-summary">
|
||||
<div class="preview-row">
|
||||
<div class="placeholder"><span class="ph-frame">原图</span></div>
|
||||
<div style="font-size:12px; color:var(--ink-3); font-family:'JetBrains Mono',monospace;">// 已记录 · 点击右侧"编辑"修改</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= 2 · 生成头图 (ACTIVE) ============= -->
|
||||
<section class="acc-section active" data-step="2">
|
||||
<div class="acc-head">
|
||||
<div class="acc-num">2</div>
|
||||
<div class="acc-meta">
|
||||
<h3>生成头图 · 选 1 张</h3>
|
||||
<div class="sm">// AI 已生成 4 张候选 · 点击你最满意的那张</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="acc-body">
|
||||
<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</span></div>
|
||||
<div class="gen-card" data-id="h2"><div class="placeholder"><span class="ph-frame">候选 B · 木纹背景</span></div><span class="badge">B</span></div>
|
||||
<div class="gen-card" data-id="h3"><div class="placeholder"><span class="ph-frame">候选 C · 浅米石材</span></div><span class="badge">C</span></div>
|
||||
<div class="gen-card" data-id="h4"><div class="placeholder"><span class="ph-frame">候选 D · 暖光氛围</span></div><span class="badge">D</span></div>
|
||||
</div>
|
||||
<div class="regen-line">
|
||||
<span class="info">// ~¥0.20 / 4 张 · 不满意不扣费</span>
|
||||
<div class="hstack">
|
||||
<button class="btn btn-sm" onclick="regen('head')">↻ 全部重新生成</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="confirmStep(2)">确认这张 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= 3 · 模特上身 (LOCKED until step 2 done) ============= -->
|
||||
<section class="acc-section locked" data-step="3">
|
||||
<div class="acc-head">
|
||||
<div class="acc-num">3</div>
|
||||
<div class="acc-meta">
|
||||
<h3>选模特 · 生成上身图</h3>
|
||||
<div class="sm">// 完成上一步后开启</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="acc-body">
|
||||
<div class="sub-h">
|
||||
<h4>① 选一个 AI 模特</h4>
|
||||
<span class="info">// 12 个模板 · 1 选</span>
|
||||
</div>
|
||||
<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 class="sub-h">
|
||||
<h4>② 选你要的上身图(可多选)</h4>
|
||||
<span class="info">// 已选 2 / 4</span>
|
||||
</div>
|
||||
<div class="gen-grid" 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-line">
|
||||
<span class="info">// ~¥0.40 / 4 张</span>
|
||||
<div class="hstack">
|
||||
<button class="btn btn-sm" onclick="regen('wear')">↻ 重新生成上身图</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="confirmStep(3)">下一步 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= 4 · 完成 ============= -->
|
||||
<section class="acc-section locked" data-step="4">
|
||||
<div class="acc-head">
|
||||
<div class="acc-num">4</div>
|
||||
<div class="acc-meta">
|
||||
<h3>预览 · 创建</h3>
|
||||
<div class="sm">// 完成上一步后开启</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="acc-body">
|
||||
<p style="font-size:13px; color:var(--ink-2); margin-bottom:14px;">检查信息无误后,点击下方「创建商品」。</p>
|
||||
<div class="preview-row" style="gap:12px;">
|
||||
<div class="placeholder" style="width:120px; height:150px;"><span class="ph-frame">头图 A</span></div>
|
||||
<div class="placeholder" style="width:120px; height:150px;"><span class="ph-frame">上身 A</span></div>
|
||||
<div class="placeholder" style="width:120px; height:150px;"><span class="ph-frame">上身 C</span></div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg" style="margin-top:18px; width:100%;" onclick="alert('✓ 商品已创建!')">✓ 创建商品</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="dr-foot">
|
||||
<span class="info" id="foot">// STEP 2 / 4 · 选头图</span>
|
||||
<div class="hstack">
|
||||
<button class="btn">取消</button>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const TOTAL = 4;
|
||||
let activeStep = 2;
|
||||
|
||||
function refreshUI() {
|
||||
const sections = document.querySelectorAll('.acc-section');
|
||||
sections.forEach((s, i) => {
|
||||
const n = i + 1;
|
||||
s.classList.remove('active', 'locked');
|
||||
if (n < activeStep) s.classList.add('done');
|
||||
else s.classList.remove('done');
|
||||
if (n === activeStep) s.classList.add('active');
|
||||
else if (n > activeStep) s.classList.add('locked');
|
||||
});
|
||||
const pct = Math.round(((activeStep - 1) / TOTAL) * 100);
|
||||
document.getElementById('bar').style.width = pct + '%';
|
||||
document.getElementById('pct').textContent = `${activeStep - 1} / ${TOTAL}`;
|
||||
document.getElementById('foot').textContent = `// STEP ${activeStep} / ${TOTAL}`;
|
||||
}
|
||||
|
||||
function confirmStep(n) {
|
||||
activeStep = n + 1;
|
||||
refreshUI();
|
||||
const next = document.querySelector(`[data-step="${activeStep}"]`);
|
||||
if (next) next.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
// Click "编辑" on done sections
|
||||
document.querySelectorAll('.acc-section.done .acc-toggle').forEach(b => {
|
||||
b.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
const sec = b.closest('.acc-section');
|
||||
const n = +sec.dataset.step;
|
||||
activeStep = n;
|
||||
refreshUI();
|
||||
sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
};
|
||||
});
|
||||
|
||||
// Click on done section header to expand
|
||||
document.querySelectorAll('.acc-section.done .acc-head').forEach(h => {
|
||||
h.onclick = () => {
|
||||
const sec = h.closest('.acc-section');
|
||||
const n = +sec.dataset.step;
|
||||
activeStep = n;
|
||||
refreshUI();
|
||||
};
|
||||
});
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
refreshUI();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,383 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>方案 1 · 独立 Step · 模特挑选 · 流·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>
|
||||
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); }
|
||||
|
||||
/* Workbench - full screen */
|
||||
.wb { height: calc(100vh - 60px); display: grid; grid-template-rows: 56px 1fr; background: var(--bg); }
|
||||
|
||||
/* Top bar */
|
||||
.wb-top { padding: 0 24px; background: var(--card); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 18px; }
|
||||
.wb-top .ti { font-size: 15px; font-weight: 700; }
|
||||
.wb-top .ti span { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange-soft); padding: 2px 6px; margin-left: 8px; letter-spacing: .04em; }
|
||||
.wb-top .meta-line { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.wb-top .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||||
.wb-top .x { width: 32px; height: 32px; display: grid; place-items: center; cursor: pointer; color: var(--ink-2); border: 1px solid var(--border); }
|
||||
.wb-top .x:hover { color: var(--orange); border-color: var(--orange); }
|
||||
|
||||
/* Main split */
|
||||
.wb-main { display: grid; grid-template-columns: 280px 1fr 360px; min-height: 0; }
|
||||
|
||||
/* Left sidebar - 5 steps */
|
||||
.wb-side { border-right: 1px solid var(--border); background: var(--bg); 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; }
|
||||
.step-item:hover { background: var(--bg-soft); }
|
||||
.step-item.active { background: var(--orange-tint); border-left-color: var(--orange); }
|
||||
.step-item .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; flex-shrink: 0; }
|
||||
.step-item.done .num { background: var(--ink); color: #FFF; border-color: var(--ink); }
|
||||
.step-item.active .num { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.step-item .info { flex: 1; min-width: 0; }
|
||||
.step-item .ti2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
|
||||
.step-item.active .ti2 { color: var(--ink); }
|
||||
.step-item .sub { font-size: 11px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.step-item.done .sub { color: var(--ink-2); font-family: 'Inter', sans-serif; letter-spacing: 0; }
|
||||
.step-item.new { position: relative; }
|
||||
.step-item.new::after { content: 'NEW'; position: absolute; top: 14px; right: 16px; font-family: 'JetBrains Mono', monospace; font-size: 9px; padding: 1px 5px; background: var(--orange); color: #FFF; letter-spacing: .04em; font-weight: 700; }
|
||||
|
||||
.side-divider { height: 1px; background: var(--border); margin: 14px 20px; }
|
||||
.side-section-h { padding: 4px 20px 8px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; }
|
||||
|
||||
/* Center canvas */
|
||||
.wb-canvas { background: var(--bg); overflow-y: auto; padding: 28px 32px; display: flex; flex-direction: column; min-width: 0; }
|
||||
.canvas-h { margin-bottom: 18px; display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.canvas-h h2 { font-size: 22px; font-weight: 700; letter-spacing: -.012em; }
|
||||
.canvas-h p { font-size: 13px; color: var(--ink-2); margin-top: 4px; }
|
||||
.canvas-h .step-tag { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); padding: 3px 8px; background: var(--bg-soft); border: 1px solid var(--border-soft); letter-spacing: .04em; flex-shrink: 0; }
|
||||
|
||||
/* Search + filter toolbar */
|
||||
.filter-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; flex-wrap: wrap; }
|
||||
.search-input { position: relative; width: 280px; }
|
||||
.search-input svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-3); width: 14px; height: 14px; }
|
||||
.search-input input { padding-left: 32px; height: 30px; font-size: 12.5px; background: var(--card); border: 1px solid var(--border); color: var(--ink); outline: none; width: 100%; }
|
||||
.filter-chip { height: 30px; padding: 0 10px; border: 1px solid var(--border); background: var(--card); font-size: 12px; color: var(--ink-2); display: inline-flex; align-items: center; gap: 4px; cursor: pointer; font-family: inherit; }
|
||||
.filter-chip:hover { background: var(--bg-soft); }
|
||||
.filter-chip.active { border-color: var(--orange); color: var(--orange); background: var(--orange-tint); font-weight: 600; }
|
||||
.filter-chip svg { width: 11px; height: 11px; }
|
||||
.results-meta { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
/* Model grid */
|
||||
.model-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; }
|
||||
@media (max-width: 1400px) { .model-grid { grid-template-columns: repeat(5, 1fr); } }
|
||||
@media (max-width: 1200px) { .model-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
|
||||
.model-card {
|
||||
aspect-ratio: 3/4;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.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: 22px; height: 22px; background: var(--orange); color: #FFF;
|
||||
display: grid; place-items: center; font-weight: 700; font-size: 12px; z-index: 2;
|
||||
}
|
||||
.model-card .placeholder { width: 100%; height: 100%; }
|
||||
.model-card .lbl-bottom {
|
||||
position: absolute; bottom: 0; left: 0; right: 0;
|
||||
background: rgba(255,255,255,.95); border-top: 1px solid var(--border);
|
||||
padding: 6px 8px;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
}
|
||||
.model-card .lbl-bottom .nm { font-size: 12px; font-weight: 600; color: var(--ink); }
|
||||
.model-card .lbl-bottom .tags {
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 9.5px;
|
||||
color: var(--ink-3); letter-spacing: .02em;
|
||||
}
|
||||
.model-card .pose-tag {
|
||||
position: absolute; top: 6px; left: 6px;
|
||||
background: rgba(0,0,0,.55); color: #FFF;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 9px;
|
||||
padding: 1px 5px; letter-spacing: .04em;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Right controls - hover detail */
|
||||
.wb-controls { border-left: 1px solid var(--border); background: var(--bg); overflow-y: auto; padding: 22px 22px 100px; display: flex; flex-direction: column; position: relative; }
|
||||
.ctrl-section { margin-bottom: 22px; }
|
||||
.ctrl-section h3 { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; margin-bottom: 10px; }
|
||||
|
||||
/* Hover detail card */
|
||||
.hover-detail { background: var(--card); border: 1px solid var(--border); padding: 14px; }
|
||||
.hover-detail .hd-name { font-size: 15px; font-weight: 700; }
|
||||
.hover-detail .hd-tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
|
||||
.hover-detail .hd-tags span { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--ink-2); background: var(--bg-soft); border: 1px solid var(--border-soft); padding: 1px 5px; letter-spacing: .02em; }
|
||||
.hover-detail .hd-views { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 12px; }
|
||||
.hover-detail .hd-views .placeholder { aspect-ratio: 3/4; }
|
||||
.hover-detail .hd-meta { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 11.5px; color: var(--ink-2); }
|
||||
.hover-detail .hd-meta div { display: flex; gap: 8px; }
|
||||
.hover-detail .hd-meta .k { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); width: 56px; letter-spacing: .04em; }
|
||||
|
||||
/* Selected list */
|
||||
.selected-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.sel-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--card); border: 1px solid var(--border); }
|
||||
.sel-row .placeholder { width: 28px; height: 36px; flex-shrink: 0; }
|
||||
.sel-row .nm { flex: 1; font-size: 12px; font-weight: 600; }
|
||||
.sel-row .x-btn { width: 18px; height: 18px; display: grid; place-items: center; cursor: pointer; color: var(--ink-3); }
|
||||
.sel-row .x-btn:hover { color: var(--red); }
|
||||
.sel-empty { padding: 14px 12px; text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); border: 1px dashed var(--border); letter-spacing: .02em; }
|
||||
|
||||
/* Tip */
|
||||
.tip-mini { padding: 10px 12px; background: var(--orange-tint); border: 1px solid var(--orange-soft); font-size: 11.5px; color: var(--ink-2); line-height: 1.5; }
|
||||
.tip-mini strong { color: var(--orange); display: block; font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .04em; margin-bottom: 4px; }
|
||||
|
||||
/* Bottom bar */
|
||||
.wb-bottom { position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 22px; background: var(--card); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
|
||||
.wb-bottom .row1 { display: flex; align-items: center; justify-content: space-between; }
|
||||
.cost-info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.cost-info .price { color: var(--orange); font-weight: 700; }
|
||||
|
||||
/* Banner: 5-step indicator */
|
||||
.step-count-banner { padding: 8px 14px; background: var(--orange-tint); border: 1px solid var(--orange-soft); margin-bottom: 16px; display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: var(--ink-2); }
|
||||
.step-count-banner .ic { width: 16px; height: 16px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="demo-bar">
|
||||
<a href="index.html" class="btn btn-ghost btn-sm">← 返回方案对比</a>
|
||||
<span class="ti">方案 1 · 独立 Step(5 步流程)</span>
|
||||
<span class="desc">// "选模特" 独立成第 3 步,canvas 充分展示模特库 · 适合 50+ 大型素材库</span>
|
||||
</div>
|
||||
|
||||
<div class="wb">
|
||||
<header class="wb-top">
|
||||
<div class="ti">透真玻尿酸补水面膜 <span>[ AI 生成模式 ]</span></div>
|
||||
<div class="meta-line">// 美妆个护 · ¥39.9 · 已选头图 A · WHITE-BG</div>
|
||||
<div class="right">
|
||||
<button class="btn btn-sm">保存草稿</button>
|
||||
<div class="x"><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>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="wb-main">
|
||||
|
||||
<!-- Left: 5 steps -->
|
||||
<aside class="wb-side">
|
||||
<div class="side-section-h">// CREATE FLOW · 5 STEPS</div>
|
||||
<div class="step-item done"><div class="num">✓</div><div class="info"><div class="ti2">商品信息</div><div class="sub">名称、品类、价格、卖点</div></div></div>
|
||||
<div class="step-item done"><div class="num">✓</div><div class="info"><div class="ti2">生成头图</div><div class="sub">已选 1 / 4 · 候选 A</div></div></div>
|
||||
<div class="step-item active new"><div class="num">3</div><div class="info"><div class="ti2">挑选模特</div><div class="sub">// 50+ 模特库 · 单选/多选</div></div></div>
|
||||
<div class="step-item"><div class="num">4</div><div class="info"><div class="ti2">生成上身图</div><div class="sub">// 4 选 N · 多选保留</div></div></div>
|
||||
<div class="step-item"><div class="num">5</div><div class="info"><div class="ti2">完成创建</div><div class="sub">// 预览 · 提交</div></div></div>
|
||||
|
||||
<div class="side-divider"></div>
|
||||
|
||||
<div class="side-section-h">// SELECTED ASSETS</div>
|
||||
<div style="padding: 0 20px;">
|
||||
<div style="font-size:11.5px; color:var(--ink-3); margin-bottom:6px; font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">原图 · 1 张</div>
|
||||
<div class="placeholder" style="width:100%; aspect-ratio: 4/5; margin-bottom:12px;"><span class="ph-frame">补水面膜<br>1200×1500</span></div>
|
||||
<div style="font-size:11.5px; color:var(--ink-3); margin-bottom:6px; font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">头图 · 候选 A</div>
|
||||
<div class="placeholder" style="width:100%; aspect-ratio: 1; margin-bottom:12px;"><span class="ph-frame">A · 白底简约</span></div>
|
||||
<div style="font-size:11.5px; color:var(--ink-3); font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">模特 · 已选 <span style="color:var(--orange); font-weight:700;" id="sel-count-side">2</span> / 50+</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Center canvas: model picker -->
|
||||
<main class="wb-canvas">
|
||||
<div class="canvas-h">
|
||||
<div>
|
||||
<h2>挑选模特</h2>
|
||||
<p>从 AI 模特库选 1 个或多个模特,统一白 T + 白短裤立绘。悬浮看详情和三视图。</p>
|
||||
</div>
|
||||
<span class="step-tag">// STEP 3 / 5 · MODEL SELECT</span>
|
||||
</div>
|
||||
|
||||
<div class="step-count-banner">
|
||||
<span class="ic">i</span>
|
||||
独立成步的好处:筛选 + 搜索 + 多选都有完整空间,适合 50+ 模特库。代价是流程多一步。
|
||||
</div>
|
||||
|
||||
<div class="filter-bar">
|
||||
<div class="search-input">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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:'JetBrains Mono',monospace; 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">健身</button>
|
||||
<button class="filter-chip">+ 更多筛选 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
<span class="results-meta">// 12 / 52 · 已选 <span id="sel-count" style="color:var(--orange); font-weight:700;">2</span></span>
|
||||
</div>
|
||||
|
||||
<div class="model-grid" id="model-grid">
|
||||
<div class="model-card selected" data-id="m1" onmouseenter="hoverModel('林夕','女 · 25-30','都市白领',['温柔','日常','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" onmouseenter="hoverModel('阿楠','女 · 25-30','姐妹/同事',['精致','干练','短发'])">
|
||||
<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" onmouseenter="hoverModel('小七','女 · 18-22','学生/Z世代',['青春','元气','校园'])">
|
||||
<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" onmouseenter="hoverModel('王姐','女 · 38-45','妈妈/居家',['亲和','成熟','温暖'])">
|
||||
<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" onmouseenter="hoverModel('阿杰','男 · 28-35','通勤男',['商务','干练','西装'])">
|
||||
<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" onmouseenter="hoverModel('阿强','男 · 22-28','健身男',['阳光','运动','肌肉'])">
|
||||
<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" onmouseenter="hoverModel('小苏','女 · 22-26','文艺研究生',['书卷气','静谧','文艺'])">
|
||||
<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" onmouseenter="hoverModel('文文','女 · 30-38','母亲',['温柔','母性','温暖'])">
|
||||
<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" onmouseenter="hoverModel('老王','男 · 45-55','大叔',['沉稳','成熟','商务'])">
|
||||
<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" onmouseenter="hoverModel('萌萌','女 · 18-22','Z世代',['可爱','潮流','二次元'])">
|
||||
<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" onmouseenter="hoverModel('Leo','男 · 30-38','健身教练',['专业','力量','健身房'])">
|
||||
<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" onmouseenter="hoverModel('双人组合','双人 · 25-30','闺蜜',['亲密','日常','搭档'])">
|
||||
<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 style="margin-top: 18px; padding: 12px 16px; background: var(--card); border: 1px dashed var(--border); text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--ink-3); letter-spacing: .02em;">
|
||||
// 还有 40 个模特 · 滚动加载 / 用上方筛选缩小范围
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Right: hover detail + selected list -->
|
||||
<aside class="wb-controls">
|
||||
<div class="ctrl-section">
|
||||
<h3>// 当前悬浮 · DETAIL</h3>
|
||||
<div class="hover-detail" id="hover-detail">
|
||||
<div class="hd-name">林夕</div>
|
||||
<div class="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 style="color:var(--orange); font-weight:700;" 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="移除">✕</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="移除">✕</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctrl-section">
|
||||
<h3>// TIP</h3>
|
||||
<div class="tip-mini">
|
||||
<strong>多选会怎样?</strong>
|
||||
每选一个模特,下一步会为其各自生成 4 张上身图。已选 2 个 = 8 张候选,可多选保留。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wb-bottom">
|
||||
<div class="row1">
|
||||
<span class="cost-info">// 预计 <span class="price">¥0.80</span> / 8 张</span>
|
||||
<span class="cost-info">3 / 5</span>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<button class="btn" style="flex:1;">← 上一步</button>
|
||||
<button class="btn btn-primary" style="flex:1;">下一步 → 生成上身图</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// multi select
|
||||
document.querySelectorAll('.model-card').forEach(c => {
|
||||
c.onclick = () => {
|
||||
c.classList.toggle('selected');
|
||||
updateCount();
|
||||
};
|
||||
});
|
||||
|
||||
function updateCount() {
|
||||
const n = document.querySelectorAll('.model-card.selected').length;
|
||||
document.getElementById('sel-count').textContent = n;
|
||||
document.getElementById('sel-count-3').textContent = n;
|
||||
document.getElementById('sel-count-side').textContent = n;
|
||||
}
|
||||
|
||||
function hoverModel(name, base, role, tags) {
|
||||
const hd = document.getElementById('hover-detail');
|
||||
hd.querySelector('.hd-name').textContent = name;
|
||||
hd.querySelector('.hd-tags').innerHTML = (base.split(' · ')).concat([role]).concat(tags).map(t => `<span>${t}</span>`).join('');
|
||||
}
|
||||
|
||||
document.querySelectorAll('.x-btn').forEach(b => {
|
||||
b.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
const row = b.closest('.sel-row');
|
||||
const id = row.dataset.id;
|
||||
document.querySelector(`.model-card[data-id="${id}"]`)?.classList.remove('selected');
|
||||
row.remove();
|
||||
updateCount();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,530 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>方案 3 · 子步骤分屏 · 模特挑选 · 流·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>
|
||||
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); }
|
||||
|
||||
.wb { height: calc(100vh - 60px); display: grid; grid-template-rows: 56px 1fr; background: var(--bg); }
|
||||
|
||||
.wb-top { padding: 0 24px; background: var(--card); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 18px; }
|
||||
.wb-top .ti { font-size: 15px; font-weight: 700; }
|
||||
.wb-top .ti span { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange-soft); padding: 2px 6px; margin-left: 8px; letter-spacing: .04em; }
|
||||
.wb-top .meta-line { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.wb-top .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||||
.wb-top .x { width: 32px; height: 32px; display: grid; place-items: center; cursor: pointer; color: var(--ink-2); border: 1px solid var(--border); }
|
||||
.wb-top .x:hover { color: var(--orange); border-color: var(--orange); }
|
||||
|
||||
.wb-main { display: grid; grid-template-columns: 280px 1fr 360px; min-height: 0; }
|
||||
|
||||
/* Sidebar - 4 steps with sub-items */
|
||||
.wb-side { border-right: 1px solid var(--border); background: var(--bg); 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; }
|
||||
.step-item:hover { background: var(--bg-soft); }
|
||||
.step-item.active { background: var(--orange-tint); border-left-color: var(--orange); }
|
||||
.step-item .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; flex-shrink: 0; }
|
||||
.step-item.done .num { background: var(--ink); color: #FFF; border-color: var(--ink); }
|
||||
.step-item.active .num { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.step-item .info { flex: 1; min-width: 0; }
|
||||
.step-item .ti2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
|
||||
.step-item.active .ti2 { color: var(--ink); }
|
||||
.step-item .sub { font-size: 11px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
|
||||
/* Sub items under active step */
|
||||
.substep-list { padding: 4px 20px 8px 56px; }
|
||||
.substep-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 7px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 12px; color: var(--ink-3);
|
||||
border-left: 2px solid var(--border);
|
||||
margin-left: -4px;
|
||||
}
|
||||
.substep-item:hover { color: var(--ink-2); border-left-color: var(--ink-3); }
|
||||
.substep-item.active { color: var(--orange); border-left-color: var(--orange); font-weight: 600; }
|
||||
.substep-item.done { color: var(--ink-2); }
|
||||
.substep-item .num-mini {
|
||||
width: 16px; height: 16px;
|
||||
border: 1px solid currentColor;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 9px; font-weight: 700;
|
||||
display: grid; place-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.substep-item.active .num-mini, .substep-item.done .num-mini { background: currentColor; color: #FFF; }
|
||||
.substep-item.done .num-mini { background: var(--ink); border-color: var(--ink); }
|
||||
|
||||
.side-divider { height: 1px; background: var(--border); margin: 14px 20px; }
|
||||
.side-section-h { padding: 4px 20px 8px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; }
|
||||
|
||||
/* Center canvas */
|
||||
.wb-canvas { background: var(--bg); overflow-y: auto; padding: 0; display: flex; flex-direction: column; min-width: 0; }
|
||||
|
||||
/* Sub-tab strip on top */
|
||||
.subtab-strip {
|
||||
padding: 0 32px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
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(--ink-2);
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
cursor: pointer;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.subtab:hover { color: var(--ink); }
|
||||
.subtab.active { color: var(--orange); border-bottom-color: var(--orange); font-weight: 600; }
|
||||
.subtab.disabled { color: var(--ink-3); cursor: not-allowed; opacity: .55; }
|
||||
.subtab .num-circ {
|
||||
width: 20px; height: 20px;
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 50%;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700;
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
.subtab.done .num-circ { background: var(--ink); color: #FFF; border-color: var(--ink); }
|
||||
.subtab.active .num-circ { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.subtab .arrow { color: var(--ink-3); margin: 0 4px; }
|
||||
.subtab-meta { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
/* Canvas inner */
|
||||
.canvas-inner { padding: 24px 32px 40px; flex: 1; }
|
||||
.canvas-h { margin-bottom: 18px; display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.canvas-h h2 { font-size: 22px; font-weight: 700; letter-spacing: -.012em; }
|
||||
.canvas-h p { font-size: 13px; color: var(--ink-2); margin-top: 4px; }
|
||||
.canvas-h .step-tag { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); padding: 3px 8px; background: var(--bg-soft); border: 1px solid var(--border-soft); letter-spacing: .04em; flex-shrink: 0; }
|
||||
|
||||
.filter-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; flex-wrap: wrap; }
|
||||
.search-input { position: relative; width: 280px; }
|
||||
.search-input svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-3); width: 14px; height: 14px; }
|
||||
.search-input input { padding-left: 32px; height: 30px; font-size: 12.5px; background: var(--card); border: 1px solid var(--border); color: var(--ink); outline: none; width: 100%; }
|
||||
.filter-chip { height: 30px; padding: 0 10px; border: 1px solid var(--border); background: var(--card); font-size: 12px; color: var(--ink-2); display: inline-flex; align-items: center; gap: 4px; cursor: pointer; font-family: inherit; }
|
||||
.filter-chip:hover { background: var(--bg-soft); }
|
||||
.filter-chip.active { border-color: var(--orange); color: var(--orange); background: var(--orange-tint); font-weight: 600; }
|
||||
.filter-chip svg { width: 11px; height: 11px; }
|
||||
.results-meta { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
|
||||
.model-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; }
|
||||
@media (max-width: 1400px) { .model-grid { grid-template-columns: repeat(5, 1fr); } }
|
||||
@media (max-width: 1200px) { .model-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
|
||||
.model-card { aspect-ratio: 3/4; background: var(--card); border: 1px solid var(--border); cursor: pointer; position: relative; overflow: hidden; }
|
||||
.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: 22px; height: 22px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 12px; z-index: 2; }
|
||||
.model-card .placeholder { width: 100%; height: 100%; }
|
||||
.model-card .lbl-bottom { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(255,255,255,.95); border-top: 1px solid var(--border); padding: 6px 8px; display: flex; flex-direction: column; gap: 2px; }
|
||||
.model-card .lbl-bottom .nm { font-size: 12px; font-weight: 600; color: var(--ink); }
|
||||
.model-card .lbl-bottom .tags { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; color: var(--ink-3); letter-spacing: .02em; }
|
||||
.model-card .pose-tag { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,.55); color: #FFF; font-family: 'JetBrains Mono', monospace; font-size: 9px; padding: 1px 5px; letter-spacing: .04em; z-index: 1; }
|
||||
|
||||
/* Wear (sub-tab 2) */
|
||||
.wear-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; max-width: 760px; }
|
||||
.wear-card { aspect-ratio: 1; background: var(--card); border: 1px solid var(--border); cursor: pointer; position: relative; overflow: hidden; }
|
||||
.wear-card:hover { border-color: var(--ink-3); }
|
||||
.wear-card .placeholder { width: 100%; height: 100%; }
|
||||
.wear-card.selected { border: 3px solid var(--orange); }
|
||||
.wear-card.selected::after { content: '✓'; position: absolute; top: 12px; right: 12px; width: 32px; height: 32px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 16px; }
|
||||
.wear-card .corner-info { position: absolute; bottom: 12px; left: 12px; background: rgba(255,255,255,.94); border: 1px solid var(--border); padding: 4px 10px; font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .04em; color: var(--ink-2); }
|
||||
|
||||
/* Right panel */
|
||||
.wb-controls { border-left: 1px solid var(--border); background: var(--bg); overflow-y: auto; padding: 22px 22px 100px; display: flex; flex-direction: column; position: relative; }
|
||||
.ctrl-section { margin-bottom: 22px; }
|
||||
.ctrl-section h3 { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; margin-bottom: 10px; }
|
||||
|
||||
.hover-detail { background: var(--card); border: 1px solid var(--border); padding: 14px; }
|
||||
.hover-detail .hd-name { font-size: 15px; font-weight: 700; }
|
||||
.hover-detail .hd-tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
|
||||
.hover-detail .hd-tags span { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--ink-2); background: var(--bg-soft); border: 1px solid var(--border-soft); padding: 1px 5px; letter-spacing: .02em; }
|
||||
.hover-detail .hd-views { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 12px; }
|
||||
.hover-detail .hd-views .placeholder { aspect-ratio: 3/4; }
|
||||
.hover-detail .hd-meta { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 11.5px; color: var(--ink-2); }
|
||||
.hover-detail .hd-meta div { display: flex; gap: 8px; }
|
||||
.hover-detail .hd-meta .k { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); width: 56px; letter-spacing: .04em; }
|
||||
|
||||
.selected-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.sel-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--card); border: 1px solid var(--border); }
|
||||
.sel-row .placeholder { width: 28px; height: 36px; flex-shrink: 0; }
|
||||
.sel-row .nm { flex: 1; font-size: 12px; font-weight: 600; }
|
||||
.sel-row .x-btn { width: 18px; height: 18px; display: grid; place-items: center; cursor: pointer; color: var(--ink-3); }
|
||||
.sel-row .x-btn:hover { color: var(--red); }
|
||||
|
||||
.tip-mini { padding: 10px 12px; background: var(--orange-tint); border: 1px solid var(--orange-soft); font-size: 11.5px; color: var(--ink-2); line-height: 1.5; }
|
||||
.tip-mini strong { color: var(--orange); display: block; font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .04em; margin-bottom: 4px; }
|
||||
|
||||
.wb-bottom { position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 22px; background: var(--card); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
|
||||
.wb-bottom .row1 { display: flex; align-items: center; justify-content: space-between; }
|
||||
.cost-info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.cost-info .price { color: var(--orange); font-weight: 700; }
|
||||
|
||||
/* Step pane switching */
|
||||
.sub-pane { display: none; }
|
||||
.sub-pane.active { display: block; animation: fade .2s ease; }
|
||||
@keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="demo-bar">
|
||||
<a href="index.html" class="btn btn-ghost btn-sm">← 返回方案对比</a>
|
||||
<span class="ti">方案 3 · 子步骤分屏(4 步主流程,Step 3 内部 2 个子 Tab)</span>
|
||||
<span class="desc">// 顶部子 Tab 切换:挑选模特 → 生成上身图 · 在同一步骤内递进</span>
|
||||
</div>
|
||||
|
||||
<div class="wb">
|
||||
<header class="wb-top">
|
||||
<div class="ti">透真玻尿酸补水面膜 <span>[ AI 生成模式 ]</span></div>
|
||||
<div class="meta-line">// 美妆个护 · ¥39.9 · 已选头图 A · WHITE-BG</div>
|
||||
<div class="right">
|
||||
<button class="btn btn-sm">保存草稿</button>
|
||||
<div class="x"><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>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="wb-main">
|
||||
|
||||
<!-- Left: 4 main steps + sub items under step 3 -->
|
||||
<aside class="wb-side">
|
||||
<div class="side-section-h">// CREATE FLOW · 4 STEPS</div>
|
||||
<div class="step-item done"><div class="num">✓</div><div class="info"><div class="ti2">商品信息</div><div class="sub">名称、品类、价格、卖点</div></div></div>
|
||||
<div class="step-item done"><div class="num">✓</div><div class="info"><div class="ti2">生成头图</div><div class="sub">已选 1 / 4 · 候选 A</div></div></div>
|
||||
<div class="step-item active">
|
||||
<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:'JetBrains Mono',monospace; font-size:10px; opacity:.7;">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:'JetBrains Mono',monospace; font-size:10px; opacity:.6;">待开始</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item"><div class="num">4</div><div class="info"><div class="ti2">完成创建</div><div class="sub">// 预览 · 提交</div></div></div>
|
||||
|
||||
<div class="side-divider"></div>
|
||||
|
||||
<div class="side-section-h">// SELECTED ASSETS</div>
|
||||
<div style="padding: 0 20px;">
|
||||
<div style="font-size:11.5px; color:var(--ink-3); margin-bottom:6px; font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">原图 · 1 张</div>
|
||||
<div class="placeholder" style="width:100%; aspect-ratio: 4/5; margin-bottom:12px;"><span class="ph-frame">补水面膜<br>1200×1500</span></div>
|
||||
<div style="font-size:11.5px; color:var(--ink-3); margin-bottom:6px; font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">头图 · 候选 A</div>
|
||||
<div class="placeholder" style="width:100%; aspect-ratio: 1; margin-bottom:12px;"><span class="ph-frame">A · 白底简约</span></div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Center canvas: with sub-tab strip on top -->
|
||||
<main class="wb-canvas">
|
||||
|
||||
<!-- Sub-tab strip -->
|
||||
<div class="subtab-strip">
|
||||
<div class="subtab active" data-sub="1">
|
||||
<span class="num-circ">①</span>
|
||||
挑选模特
|
||||
</div>
|
||||
<span class="arrow" style="color:var(--ink-3); padding: 0 6px;">→</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 style="color:var(--orange); font-weight:700;" id="meta-count">2</span></span>
|
||||
</div>
|
||||
|
||||
<div class="canvas-inner">
|
||||
|
||||
<!-- ============= SUB 1 · 挑选模特 ============= -->
|
||||
<section class="sub-pane active" data-pane="1">
|
||||
<div class="canvas-h">
|
||||
<div>
|
||||
<h2>挑选模特 · 子步骤 ①</h2>
|
||||
<p>从 AI 模特库选 1 个或多个,统一白 T + 白短裤立绘。选好后点右下「下一步 → 生成上身图」自动跳到 ②。</p>
|
||||
</div>
|
||||
<span class="step-tag">// STEP 3 / 4 · SUB 1</span>
|
||||
</div>
|
||||
|
||||
<div class="filter-bar">
|
||||
<div class="search-input">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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:'JetBrains Mono',monospace; 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.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
<span class="results-meta">// 12 / 52 · 已选 <span id="sel-count" style="color:var(--orange); font-weight:700;">2</span></span>
|
||||
</div>
|
||||
|
||||
<div class="model-grid" id="model-grid">
|
||||
<div class="model-card selected" data-id="m1" onmouseenter="hoverModel('林夕','女 · 25-30','都市白领',['温柔','日常','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" onmouseenter="hoverModel('阿楠','女 · 25-30','姐妹/同事',['精致','干练','短发'])">
|
||||
<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" onmouseenter="hoverModel('小七','女 · 18-22','学生/Z世代',['青春','元气','校园'])">
|
||||
<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" onmouseenter="hoverModel('王姐','女 · 38-45','妈妈/居家',['亲和','成熟','温暖'])">
|
||||
<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" onmouseenter="hoverModel('阿杰','男 · 28-35','通勤男',['商务','干练','西装'])">
|
||||
<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" onmouseenter="hoverModel('阿强','男 · 22-28','健身男',['阳光','运动','肌肉'])">
|
||||
<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" onmouseenter="hoverModel('小苏','女 · 22-26','文艺研究生',['书卷气','静谧','文艺'])">
|
||||
<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" onmouseenter="hoverModel('文文','女 · 30-38','母亲',['温柔','母性','温暖'])">
|
||||
<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" onmouseenter="hoverModel('老王','男 · 45-55','大叔',['沉稳','成熟','商务'])">
|
||||
<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" onmouseenter="hoverModel('萌萌','女 · 18-22','Z世代',['可爱','潮流','二次元'])">
|
||||
<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" onmouseenter="hoverModel('Leo','男 · 30-38','健身教练',['专业','力量','健身房'])">
|
||||
<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" onmouseenter="hoverModel('双人组合','双人 · 25-30','闺蜜',['亲密','日常','搭档'])">
|
||||
<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 style="margin-top: 18px; padding: 12px 16px; background: var(--card); border: 1px dashed var(--border); text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--ink-3); letter-spacing: .02em;">
|
||||
// 还有 40 个模特 · 滚动加载 / 用上方筛选缩小范围
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= SUB 2 · 生成上身图 ============= -->
|
||||
<section class="sub-pane" data-pane="2">
|
||||
<div class="canvas-h">
|
||||
<div>
|
||||
<h2>生成上身图 · 子步骤 ②</h2>
|
||||
<p>已为你选的每个模特生成 4 张上身图,可多选保留。可点 ← 回到「① 挑选模特」改人。</p>
|
||||
</div>
|
||||
<span class="step-tag">// STEP 3 / 4 · SUB 2</span>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-bottom:14px; padding:10px 14px; background: var(--card); border: 1px solid var(--border);">
|
||||
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); letter-spacing:.02em;">// 当前模特:</span>
|
||||
<button class="filter-chip active" style="height: 26px;">林夕 · 都市白领</button>
|
||||
<button class="filter-chip" style="height: 26px;">小七 · 学生</button>
|
||||
<span style="margin-left:auto; font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); letter-spacing:.02em;">// 切换模特看不同上身效果</span>
|
||||
</div>
|
||||
|
||||
<div class="wear-grid">
|
||||
<div class="wear-card selected"><div class="placeholder"><span class="ph-frame">上身 A · 持物半身</span></div><span class="corner-info">[ A · 持物半身 ]</span></div>
|
||||
<div class="wear-card"><div class="placeholder"><span class="ph-frame">上身 B · 敷面膜中</span></div><span class="corner-info">[ B · 敷面膜中 ]</span></div>
|
||||
<div class="wear-card selected"><div class="placeholder"><span class="ph-frame">上身 C · 镜前自拍</span></div><span class="corner-info">[ C · 镜前自拍 ]</span></div>
|
||||
<div class="wear-card"><div class="placeholder"><span class="ph-frame">上身 D · 床边特写</span></div><span class="corner-info">[ D · 床边特写 ]</span></div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 18px; display: flex; align-items: center; gap: 12px;">
|
||||
<button class="btn btn-sm">↻ 全部重新生成(¥0.40)</button>
|
||||
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); letter-spacing:.02em;">// 不满意不扣费 · 已选 2 / 4 张</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Right controls -->
|
||||
<aside class="wb-controls">
|
||||
<div class="ctrl-section">
|
||||
<h3>// 当前悬浮 · DETAIL</h3>
|
||||
<div class="hover-detail" id="hover-detail">
|
||||
<div class="hd-name">林夕</div>
|
||||
<div class="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 style="color:var(--orange); font-weight:700;" 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="移除">✕</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="移除">✕</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctrl-section">
|
||||
<h3>// TIP</h3>
|
||||
<div class="tip-mini">
|
||||
<strong>子步骤的好处</strong>
|
||||
挑模特 → 生上身在同一个 Step 内推进,左侧 sidebar 不变多。Sub Tab 可来回切换不丢已选。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wb-bottom">
|
||||
<div class="row1">
|
||||
<span class="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">
|
||||
<button class="btn" style="flex:1;" id="btn-prev-sub">← 上一步</button>
|
||||
<button class="btn btn-primary" style="flex:1;" id="btn-next-sub">下一步 → 生成上身</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let curSub = 1;
|
||||
|
||||
function switchSub(n) {
|
||||
curSub = n;
|
||||
document.querySelectorAll('.sub-pane').forEach(p => p.classList.remove('active'));
|
||||
document.querySelector(`[data-pane="${n}"]`).classList.add('active');
|
||||
|
||||
// sub tabs in canvas
|
||||
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');
|
||||
});
|
||||
|
||||
// sub items in sidebar
|
||||
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 style="color:var(--orange); font-weight:700;">2</span>'
|
||||
: '// SUB-STEP 2 / 2 · 上身图 4 选 N';
|
||||
document.getElementById('bottom-meta').textContent = `SUB ${n} / 2`;
|
||||
document.getElementById('btn-next-sub').textContent = n === 1 ? '下一步 → 生成上身' : '下一步 → 完成创建';
|
||||
|
||||
document.querySelector('.wb-canvas').scrollTop = 0;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.subtab').forEach(t => {
|
||||
t.onclick = () => {
|
||||
if (t.classList.contains('disabled')) {
|
||||
const n = +t.dataset.sub;
|
||||
// allow click after step 1 satisfied
|
||||
switchSub(n);
|
||||
} else {
|
||||
switchSub(+t.dataset.sub);
|
||||
}
|
||||
};
|
||||
});
|
||||
document.querySelectorAll('.substep-item').forEach(t => {
|
||||
t.onclick = () => switchSub(+t.dataset.sub);
|
||||
});
|
||||
document.getElementById('btn-next-sub').onclick = () => {
|
||||
if (curSub === 1) switchSub(2);
|
||||
else alert('→ 进入 Step 4 完成创建');
|
||||
};
|
||||
document.getElementById('btn-prev-sub').onclick = () => {
|
||||
if (curSub === 2) switchSub(1);
|
||||
else alert('← 返回 Step 2 头图');
|
||||
};
|
||||
|
||||
// multi select
|
||||
document.querySelectorAll('.model-card').forEach(c => {
|
||||
c.onclick = () => {
|
||||
c.classList.toggle('selected');
|
||||
updateCount();
|
||||
};
|
||||
});
|
||||
function updateCount() {
|
||||
const n = document.querySelectorAll('.model-card.selected').length;
|
||||
document.getElementById('sel-count').textContent = n;
|
||||
document.getElementById('sel-count-3').textContent = n;
|
||||
document.getElementById('meta-count').textContent = n;
|
||||
}
|
||||
function hoverModel(name, base, role, tags) {
|
||||
const hd = document.getElementById('hover-detail');
|
||||
hd.querySelector('.hd-name').textContent = name;
|
||||
hd.querySelector('.hd-tags').innerHTML = (base.split(' · ')).concat([role]).concat(tags).map(t => `<span>${t}</span>`).join('');
|
||||
}
|
||||
|
||||
// wear cards multi select
|
||||
document.querySelectorAll('.wear-card').forEach(c => {
|
||||
c.onclick = () => c.classList.toggle('selected');
|
||||
});
|
||||
|
||||
document.querySelectorAll('.x-btn').forEach(b => {
|
||||
b.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
const row = b.closest('.sel-row');
|
||||
const id = row.dataset.id;
|
||||
document.querySelector(`.model-card[data-id="${id}"]`)?.classList.remove('selected');
|
||||
row.remove();
|
||||
updateCount();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,476 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>方案 C · 双栏工作台 · 新建商品(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); }
|
||||
|
||||
/* Workbench - full screen */
|
||||
.wb {
|
||||
height: calc(100vh - 60px);
|
||||
display: grid;
|
||||
grid-template-rows: 56px 1fr;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* Top bar */
|
||||
.wb-top {
|
||||
padding: 0 24px;
|
||||
background: var(--card);
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center; gap: 18px;
|
||||
}
|
||||
.wb-top .ti { font-size: 15px; font-weight: 700; }
|
||||
.wb-top .ti span { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--orange); background: var(--orange-tint); border: 1px solid var(--orange-soft); padding: 2px 6px; margin-left: 8px; letter-spacing: .04em; }
|
||||
.wb-top .meta-line { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.wb-top .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||||
.wb-top .x { width: 32px; height: 32px; display: grid; place-items: center; cursor: pointer; color: var(--ink-2); border: 1px solid var(--border); }
|
||||
.wb-top .x:hover { color: var(--orange); border-color: var(--orange); }
|
||||
|
||||
/* Main split */
|
||||
.wb-main { display: grid; grid-template-columns: 280px 1fr 340px; min-height: 0; }
|
||||
|
||||
/* Left sidebar - steps */
|
||||
.wb-side {
|
||||
border-right: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
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;
|
||||
}
|
||||
.step-item:hover { background: var(--bg-soft); }
|
||||
.step-item.active { background: var(--orange-tint); border-left-color: var(--orange); }
|
||||
.step-item .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;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.step-item.done .num { background: var(--ink); color: #FFF; border-color: var(--ink); }
|
||||
.step-item.active .num { background: var(--orange); color: #FFF; border-color: var(--orange); }
|
||||
.step-item .info { flex: 1; min-width: 0; }
|
||||
.step-item .ti2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
|
||||
.step-item.active .ti2 { color: var(--ink); }
|
||||
.step-item .sub { font-size: 11px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; line-height: 1.5; }
|
||||
.step-item.done .sub { color: var(--ink-2); font-family: 'Inter', sans-serif; letter-spacing: 0; }
|
||||
|
||||
.side-divider { height: 1px; background: var(--border); margin: 14px 20px; }
|
||||
.side-section-h { padding: 4px 20px 8px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; }
|
||||
|
||||
/* Center canvas - big preview */
|
||||
.wb-canvas {
|
||||
background: var(--bg);
|
||||
overflow-y: auto;
|
||||
padding: 32px;
|
||||
display: flex; flex-direction: column;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
.canvas-h {
|
||||
margin-bottom: 24px;
|
||||
display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
|
||||
}
|
||||
.canvas-h h2 { font-size: 22px; font-weight: 700; letter-spacing: -.012em; }
|
||||
.canvas-h p { font-size: 13px; color: var(--ink-2); margin-top: 4px; }
|
||||
.canvas-h .step-tag { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); padding: 3px 8px; background: var(--bg-soft); border: 1px solid var(--border-soft); letter-spacing: .04em; flex-shrink: 0; }
|
||||
|
||||
/* Center grid - bigger images */
|
||||
.big-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; max-width: 720px; }
|
||||
.big-card {
|
||||
aspect-ratio: 1;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.big-card:hover { border-color: var(--ink-3); }
|
||||
.big-card .placeholder { width: 100%; height: 100%; }
|
||||
.big-card.selected { border: 3px solid var(--orange); }
|
||||
.big-card.selected::after {
|
||||
content: '✓'; position: absolute; top: 12px; right: 12px;
|
||||
width: 32px; height: 32px; background: var(--orange); color: #FFF;
|
||||
display: grid; place-items: center; font-weight: 700; font-size: 16px;
|
||||
}
|
||||
.big-card .corner-info {
|
||||
position: absolute; bottom: 12px; left: 12px;
|
||||
background: rgba(255,255,255,.94);
|
||||
border: 1px solid var(--border);
|
||||
padding: 4px 10px;
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 11px;
|
||||
letter-spacing: .04em; color: var(--ink-2);
|
||||
}
|
||||
|
||||
/* Right panel - controls */
|
||||
.wb-controls {
|
||||
border-left: 1px solid var(--border);
|
||||
background: var(--bg);
|
||||
overflow-y: auto;
|
||||
padding: 22px 22px 100px;
|
||||
display: flex; flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.ctrl-section { margin-bottom: 24px; }
|
||||
.ctrl-section h3 {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10.5px;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ctrl-card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
padding: 14px;
|
||||
font-size: 12.5px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.ctrl-card strong { color: var(--ink); font-weight: 600; }
|
||||
.selected-mini { display: flex; gap: 6px; margin-top: 8px; }
|
||||
.selected-mini .placeholder { width: 36px; height: 36px; }
|
||||
.selected-mini .placeholder.empty { background: transparent; border: 1px dashed var(--border); }
|
||||
|
||||
/* Model row in controls */
|
||||
.model-row-side { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||||
.model-card-side { aspect-ratio: 3/4; background: var(--card); border: 1px solid var(--border); cursor: pointer; position: relative; }
|
||||
.model-card-side:hover { border-color: var(--ink-3); }
|
||||
.model-card-side.selected { border: 2px solid var(--orange); }
|
||||
.model-card-side.selected::after { content: '✓'; position: absolute; top: 4px; right: 4px; width: 16px; height: 16px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 9px; }
|
||||
.model-card-side .placeholder { width: 100%; height: 100%; }
|
||||
.model-card-side .lbl { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(255,255,255,.95); padding: 2px 4px; font-size: 9.5px; color: var(--ink-2); font-family: 'JetBrains Mono', monospace; text-align: center; letter-spacing: .02em; }
|
||||
|
||||
/* Bottom bar */
|
||||
.wb-bottom {
|
||||
position: absolute; left: 0; right: 0; bottom: 0;
|
||||
padding: 14px 22px;
|
||||
background: var(--card);
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.wb-bottom .row1 { display: flex; align-items: center; justify-content: space-between; }
|
||||
.cost-info { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.cost-info .price { color: var(--orange); font-weight: 700; }
|
||||
|
||||
/* Step 1 form (used when step 1 active) */
|
||||
.form-card { background: var(--card); border: 1px solid var(--border); padding: 22px; max-width: 720px; }
|
||||
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.upload-c {
|
||||
aspect-ratio: 4/5;
|
||||
background: var(--card);
|
||||
border: 1.5px dashed var(--border);
|
||||
display: grid; place-items: center;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: var(--ink-2);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.upload-c.has-file { border-style: solid; padding: 0; border-color: var(--orange); background: var(--bg-soft); }
|
||||
.upload-c.has-file .placeholder { width: 100%; height: 100%; }
|
||||
.upload-c .ic-cam { width: 40px; height: 40px; border: 1px solid currentColor; display: grid; place-items: center; margin: 0 auto 12px; }
|
||||
|
||||
.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; }
|
||||
|
||||
/* Tip box */
|
||||
.tip-mini { padding: 10px 12px; background: var(--orange-tint); border: 1px solid var(--orange-soft); font-size: 11.5px; color: var(--ink-2); margin-bottom: 18px; line-height: 1.5; }
|
||||
.tip-mini strong { color: var(--orange); display: block; font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .04em; margin-bottom: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="demo-bar">
|
||||
<a href="index.html" class="btn btn-ghost btn-sm">← 返回方案对比</a>
|
||||
<span class="ti">方案 C · 双栏工作台(Canvas + Sidebar)</span>
|
||||
<span class="desc">// 全屏工作台,左侧导航 + 中间大预览 + 右侧操作 · 视觉空间最大</span>
|
||||
</div>
|
||||
|
||||
<div class="wb">
|
||||
|
||||
<!-- Top -->
|
||||
<header class="wb-top">
|
||||
<div class="ti">透真玻尿酸补水面膜 <span>[ AI 生成模式 ]</span></div>
|
||||
<div class="meta-line">// 美妆个护 · ¥39.9 · 已上传原图</div>
|
||||
<div class="right">
|
||||
<button class="btn btn-sm">保存草稿</button>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="wb-main">
|
||||
|
||||
<!-- Left sidebar -->
|
||||
<aside class="wb-side">
|
||||
<div class="side-section-h">// CREATE FLOW</div>
|
||||
<div class="step-item done" data-step="1">
|
||||
<div class="num">✓</div>
|
||||
<div class="info">
|
||||
<div class="ti2">商品信息</div>
|
||||
<div class="sub">名称、品类、价格、卖点</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item active" data-step="2">
|
||||
<div class="num">2</div>
|
||||
<div class="info">
|
||||
<div class="ti2">生成头图</div>
|
||||
<div class="sub">// AI 生成 · 4 选 1</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item" data-step="3">
|
||||
<div class="num">3</div>
|
||||
<div class="info">
|
||||
<div class="ti2">模特上身图</div>
|
||||
<div class="sub">// 选模特 + 4 选 N</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item" data-step="4">
|
||||
<div class="num">4</div>
|
||||
<div class="info">
|
||||
<div class="ti2">完成创建</div>
|
||||
<div class="sub">// 预览 · 提交</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="side-divider"></div>
|
||||
|
||||
<div class="side-section-h">// SELECTED ASSETS</div>
|
||||
<div style="padding: 0 20px;">
|
||||
<div style="font-size:11.5px; color:var(--ink-3); margin-bottom:8px; font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">原图 · 1 张</div>
|
||||
<div class="placeholder" style="width:100%; aspect-ratio: 4/5; margin-bottom:14px;"><span class="ph-frame">补水面膜<br>1200×1500</span></div>
|
||||
<div style="font-size:11.5px; color:var(--ink-3); font-family:'JetBrains Mono',monospace; letter-spacing:.02em;">头图 · 待选</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Center canvas (changes per step) -->
|
||||
<main class="wb-canvas" id="canvas">
|
||||
|
||||
<!-- STEP 2 default view: 生成头图 -->
|
||||
<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>
|
||||
</main>
|
||||
|
||||
<!-- Right controls -->
|
||||
<aside class="wb-controls">
|
||||
<div class="ctrl-section">
|
||||
<h3>// 当前选中</h3>
|
||||
<div class="ctrl-card">
|
||||
<strong>候选 A · 白底简约</strong>
|
||||
<div style="margin-top:6px; color:var(--ink-3); font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:.02em;">1920×1920 · 适合主图</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctrl-section">
|
||||
<h3>// 重新生成参数</h3>
|
||||
<div class="field" style="margin-bottom:10px;">
|
||||
<label class="field-label" style="font-size:11.5px;">背景风格</label>
|
||||
<select class="select" style="height:32px; font-size:12px;">
|
||||
<option>纯白底(电商主图)</option>
|
||||
<option>木纹/石材</option>
|
||||
<option>居家场景</option>
|
||||
<option>自定义</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom:10px;">
|
||||
<label class="field-label" style="font-size:11.5px;">画面氛围</label>
|
||||
<select class="select" style="height:32px; font-size:12px;">
|
||||
<option>明亮简洁</option>
|
||||
<option>暖光氛围</option>
|
||||
<option>冷调高级</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn" style="width:100%; margin-top:6px;">↻ 用以上参数重新生成</button>
|
||||
</div>
|
||||
|
||||
<div class="ctrl-section">
|
||||
<h3>// 提示</h3>
|
||||
<div class="tip-mini">
|
||||
<strong>不满意不扣费</strong>
|
||||
重新生成 4 张约 ¥0.20。已生成共 4 张,确认后才扣费。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wb-bottom">
|
||||
<div class="row1">
|
||||
<span class="cost-info">// 累计 <span class="price">¥0.20</span></span>
|
||||
<span class="cost-info">2 / 4</span>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<button class="btn" style="flex:1;">← 上一步</button>
|
||||
<button class="btn btn-primary" style="flex:1;" id="next-btn">下一步 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// single select for head
|
||||
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');
|
||||
};
|
||||
});
|
||||
|
||||
// Step navigation
|
||||
document.querySelectorAll('.step-item').forEach(item => {
|
||||
item.onclick = () => {
|
||||
const step = +item.dataset.step;
|
||||
switchStep(step);
|
||||
};
|
||||
});
|
||||
|
||||
document.getElementById('next-btn').onclick = () => {
|
||||
const cur = document.querySelector('.step-item.active');
|
||||
const n = +cur.dataset.step;
|
||||
if (n < 4) switchStep(n + 1);
|
||||
else alert('✓ 商品已创建!');
|
||||
};
|
||||
|
||||
function switchStep(n) {
|
||||
document.querySelectorAll('.step-item').forEach((s, i) => {
|
||||
s.classList.remove('active', 'done');
|
||||
if (i + 1 < n) s.classList.add('done');
|
||||
if (i + 1 === n) s.classList.add('active');
|
||||
});
|
||||
renderCanvas(n);
|
||||
}
|
||||
|
||||
function renderCanvas(n) {
|
||||
const c = document.getElementById('canvas');
|
||||
if (n === 1) {
|
||||
c.innerHTML = `
|
||||
<div class="canvas-h">
|
||||
<div><h2>填写商品信息 · 上传原图</h2><p>上传一张你拍的商品图(任何角度都行,AI 会优化),并填写商品基本信息。</p></div>
|
||||
<span class="step-tag">// STEP 1 / 4 · INFO + UPLOAD</span>
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns: 280px 1fr; gap:24px; max-width: 920px;">
|
||||
<div>
|
||||
<div class="upload-c has-file"><div class="placeholder"><span class="ph-frame">补水面膜.jpg<br>1200×1500</span></div></div>
|
||||
<div style="font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-3); text-align:center; letter-spacing:.02em;">// 已上传 · 点击重选</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></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>
|
||||
`;
|
||||
} else if (n === 2) {
|
||||
c.innerHTML = `
|
||||
<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 ]</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 ]</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 ]</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 ]</span></div>
|
||||
</div>
|
||||
`;
|
||||
bindBig('#head-grid');
|
||||
} else if (n === 3) {
|
||||
c.innerHTML = `
|
||||
<div class="canvas-h">
|
||||
<div><h2>选模特,生成上身图</h2><p>选好模特后,AI 会让 ta 持你刚选的商品图生成 4 张参考图,可多选。</p></div>
|
||||
<span class="step-tag">// STEP 3 / 4 · MODEL + WEAR</span>
|
||||
</div>
|
||||
<div class="big-grid" id="wear-grid" style="grid-template-columns: repeat(2,1fr);">
|
||||
<div class="big-card selected" data-id="w1"><div class="placeholder"><span class="ph-frame">上身 A · 持物半身</span></div><span class="corner-info">[ A · 持物半身 ]</span></div>
|
||||
<div class="big-card" data-id="w2"><div class="placeholder"><span class="ph-frame">上身 B · 敷面膜中</span></div><span class="corner-info">[ B · 敷面膜中 ]</span></div>
|
||||
<div class="big-card selected" data-id="w3"><div class="placeholder"><span class="ph-frame">上身 C · 镜前自拍</span></div><span class="corner-info">[ C · 镜前自拍 ]</span></div>
|
||||
<div class="big-card" data-id="w4"><div class="placeholder"><span class="ph-frame">上身 D · 床边特写</span></div><span class="corner-info">[ D · 床边特写 ]</span></div>
|
||||
</div>
|
||||
`;
|
||||
bindMulti('#wear-grid');
|
||||
} else if (n === 4) {
|
||||
c.innerHTML = `
|
||||
<div class="canvas-h">
|
||||
<div><h2>预览 · 创建</h2><p>检查下面的素材和信息无误后,点击右下「创建商品」。</p></div>
|
||||
<span class="step-tag">// STEP 4 / 4 · CONFIRM</span>
|
||||
</div>
|
||||
<div style="background:var(--card); border:1px solid var(--border); padding:32px; max-width: 760px;">
|
||||
<h3 style="font-size:20px; font-weight:700;">透真玻尿酸补水面膜</h3>
|
||||
<div style="font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--ink-3); margin-top:4px; letter-spacing:.02em;">// 美妆个护 · ¥39.9 · AI 生成 · 模特: 林夕</div>
|
||||
<div style="display:grid; grid-template-columns: repeat(4,1fr); gap:10px; margin-top:18px;">
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">原图</span></div>
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">头图 A</span></div>
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">上身 A</span></div>
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">上身 C</span></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function bindBig(sel) {
|
||||
document.querySelectorAll(sel + ' .big-card').forEach(c => {
|
||||
c.onclick = () => {
|
||||
document.querySelectorAll(sel + ' .big-card').forEach(x => x.classList.remove('selected'));
|
||||
c.classList.add('selected');
|
||||
};
|
||||
});
|
||||
}
|
||||
function bindMulti(sel) {
|
||||
document.querySelectorAll(sel + ' .big-card').forEach(c => {
|
||||
c.onclick = () => c.classList.toggle('selected');
|
||||
});
|
||||
}
|
||||
|
||||
bindBig('#head-grid');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,783 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>补水面膜 · v3 · 流水线 · 流·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>
|
||||
/* ─── Project header ─── */
|
||||
.proj-head { display: flex; justify-content: space-between; gap: 16px; margin-bottom: 22px; align-items: flex-start; }
|
||||
.proj-head h1 { font-size: 20px; font-weight: 700; letter-spacing: -.012em; }
|
||||
|
||||
/* ─── Stepper ─── */
|
||||
.stepper { display: flex; align-items: center; gap: 0; margin-bottom: 28px; padding: 14px 18px; background: var(--card); border: 1px solid var(--border); position: relative; }
|
||||
.stepper::before, .stepper::after { content: '+'; position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; line-height: 1; }
|
||||
.stepper::before { top: -8px; left: -8px; }
|
||||
.stepper::after { bottom: -8px; right: -8px; }
|
||||
.stepper .corner-tr, .stepper .corner-bl { position: absolute; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; font-size: 13px; }
|
||||
.stepper .corner-tr { top: -8px; right: -8px; }
|
||||
.stepper .corner-bl { bottom: -8px; left: -8px; }
|
||||
.stage-step { display: flex; align-items: center; gap: 10px; padding: 6px 0; cursor: pointer; user-select: none; }
|
||||
.stage-step .num { width: 26px; height: 26px; display: grid; place-items: center; font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 600; border: 1px solid var(--border); background: var(--card); color: var(--ink-3); flex-shrink: 0; }
|
||||
.stage-step.done .num { background: var(--ink); border-color: var(--ink); color: #FFF; }
|
||||
.stage-step.active .num { background: var(--orange); border-color: var(--orange); color: #FFF; }
|
||||
.stage-step.locked { opacity: .5; cursor: not-allowed; }
|
||||
.stage-step .lbl { font-size: 13px; font-weight: 500; color: var(--ink-2); }
|
||||
.stage-step.active .lbl { color: var(--ink); font-weight: 600; }
|
||||
.stage-step .st { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-left: 4px; padding: 1px 6px; background: var(--bg-soft); border: 1px solid var(--border-soft); letter-spacing: .04em; }
|
||||
.stage-step.active .st { background: var(--orange-tint); color: var(--orange); border-color: var(--orange-soft); }
|
||||
.stage-line { flex: 1; height: 1px; background: var(--border); margin: 0 14px; min-width: 30px; }
|
||||
.stage-line.done { background: var(--ink); }
|
||||
|
||||
/* ─── Stage panes ─── */
|
||||
.stage { display: none; }
|
||||
.stage.active { display: block; }
|
||||
|
||||
/* Common pane */
|
||||
.pane { background: var(--card); border: 1px solid var(--border); }
|
||||
.pane-h { display: flex; align-items: center; gap: 8px; padding: 14px 18px; border-bottom: 1px solid var(--border); }
|
||||
.pane-h strong { font-size: 14px; font-weight: 600; }
|
||||
|
||||
/* Stage foot */
|
||||
.stage-foot { display: flex; justify-content: space-between; align-items: center; padding: 18px 0 0; margin-top: 18px; border-top: 1px solid var(--border); }
|
||||
.stage-foot .info { font-size: 12.5px; color: var(--ink-2); }
|
||||
.stage-foot .info .mono { font-family: 'JetBrains Mono', monospace; color: var(--ink-3); font-size: 11.5px; letter-spacing: .02em; }
|
||||
|
||||
/* === STAGE 1 · 脚本 === */
|
||||
.stage-script { display: grid; grid-template-columns: 1fr 1.2fr; gap: 16px; min-height: 560px; }
|
||||
|
||||
.chat-pane { display: flex; flex-direction: column; }
|
||||
.chat-body { padding: 16px 18px; flex: 1; overflow-y: auto; max-height: 460px; display: flex; flex-direction: column; gap: 14px; }
|
||||
.msg .bubble { max-width: 90%; padding: 10px 14px; font-size: 13px; line-height: 1.6; border: 1px solid var(--border); }
|
||||
.msg.ai .bubble { background: var(--card); }
|
||||
.msg.user { display: flex; flex-direction: column; align-items: flex-end; }
|
||||
.msg.user .bubble { background: var(--orange-tint); color: var(--ink); border-color: var(--orange-soft); }
|
||||
.msg .time { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-top: 4px; letter-spacing: .02em; }
|
||||
.msg .actions { display: flex; gap: 6px; margin-top: 6px; }
|
||||
.ai-avatar { width: 26px; height: 26px; background: var(--orange); color: #FFF; display: grid; place-items: center; font-size: 11px; font-weight: 700; border: 1px solid var(--orange); }
|
||||
.del { text-decoration: line-through; color: var(--ink-3); }
|
||||
.ins { background: var(--green-bg); color: var(--green); padding: 0 3px; }
|
||||
.chat-input { padding: 14px 18px; border-top: 1px solid var(--border); }
|
||||
|
||||
.shot-list { display: flex; flex-direction: column; }
|
||||
.shots-body { padding: 12px 16px; flex: 1; overflow-y: auto; max-height: 540px; display: flex; flex-direction: column; gap: 10px; }
|
||||
.shot-card { background: var(--bg); border: 1px solid var(--border); padding: 12px 14px; }
|
||||
.shot-card.highlight { border-color: var(--orange); background: var(--orange-tint); }
|
||||
.shot-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
||||
.shot-num { width: 22px; height: 22px; background: var(--ink); color: #FFF; display: grid; place-items: center; font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; }
|
||||
.shot-time { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); padding: 2px 6px; background: var(--card); border: 1px solid var(--border); }
|
||||
.shot-row { display: grid; grid-template-columns: 36px 1fr; gap: 8px; padding: 4px 0; }
|
||||
.shot-k { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); padding-top: 2px; letter-spacing: .04em; }
|
||||
.shot-v { font-size: 12.5px; color: var(--ink); line-height: 1.55; }
|
||||
.icon-mini-btn { width: 24px; height: 24px; display: grid; place-items: center; color: var(--ink-3); background: var(--card); border: 1px solid var(--border); cursor: pointer; font-size: 14px; }
|
||||
.icon-mini-btn:hover { color: var(--orange); border-color: var(--orange); }
|
||||
|
||||
/* === STAGE 2 · 基础资产 === */
|
||||
.stage-assets { display: grid; grid-template-columns: 200px 1fr; gap: 24px; }
|
||||
.asset-side .ttab { padding: 10px 12px; font-size: 13px; cursor: pointer; display: flex; align-items: center; gap: 8px; border: 1px solid transparent; }
|
||||
.asset-side .ttab:hover { background: var(--bg-soft); }
|
||||
.asset-side .ttab.active { background: var(--orange-tint); color: var(--orange); border-color: var(--orange-soft); font-weight: 600; }
|
||||
.asset-side .ttab .num { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ink-3); margin-left: auto; }
|
||||
.asset-side .ttab.active .num { color: var(--orange); }
|
||||
.asset-side .info { font-size: 12px; color: var(--ink-3); padding: 14px 12px; line-height: 1.6; margin-top: 14px; border-top: 1px solid var(--border); }
|
||||
.asset-side .info strong { color: var(--ink-2); display: block; }
|
||||
.asset-side .info .mono { font-family: 'JetBrains Mono', monospace; }
|
||||
|
||||
.asset-grid-2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
|
||||
.asset-card-2 { background: var(--card); border: 1px solid var(--border); }
|
||||
.asset-card-2 .thumb-2 { aspect-ratio: 1; }
|
||||
.asset-card-2 .body-2 { padding: 12px 14px; }
|
||||
.prompt-box { background: var(--bg); border: 1px solid var(--border); padding: 8px 10px; font-size: 12px; color: var(--ink-2); margin-top: 8px; line-height: 1.55; font-family: 'JetBrains Mono', monospace; letter-spacing: .01em; }
|
||||
.fail-icon { width: 28px; height: 28px; background: var(--red); color: #FFF; display: grid; place-items: center; font-weight: 700; font-size: 16px; }
|
||||
|
||||
/* === STAGE 3 · 故事板 === */
|
||||
.stage-storyboard { display: grid; grid-template-columns: 1.7fr 1fr; gap: 16px; align-items: start; }
|
||||
.sb-canvas { background: var(--card); border: 1px solid var(--border); }
|
||||
.sb-row { display: grid; grid-template-columns: 60px 1fr 1fr; gap: 0; border-bottom: 1px solid var(--border); }
|
||||
.sb-row:last-child { border-bottom: 0; }
|
||||
.sb-row.head { background: var(--bg-soft); font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .04em; text-transform: uppercase; }
|
||||
.sb-row.head > div { padding: 10px 14px; }
|
||||
.sb-num { padding: 14px; font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 700; color: var(--ink); border-right: 1px solid var(--border); }
|
||||
.sb-num .t { display: block; font-size: 10.5px; color: var(--ink-3); font-weight: 400; margin-top: 4px; letter-spacing: .02em; }
|
||||
.sb-img { padding: 10px; border-right: 1px solid var(--border); }
|
||||
.sb-img .placeholder { aspect-ratio: 16/9; }
|
||||
.sb-text { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
|
||||
.sb-text .meta { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .04em; }
|
||||
.sb-text .dialog { font-size: 12.5px; color: var(--ink); line-height: 1.55; }
|
||||
.sb-text .sfx { font-size: 11.5px; color: var(--ink-3); }
|
||||
|
||||
.sb-side .pane { padding: 18px; }
|
||||
.prompt-edit { background: var(--bg); border: 1px solid var(--border); padding: 10px 12px; font-family: 'JetBrains Mono', monospace; font-size: 11.5px; line-height: 1.7; color: var(--ink-2); white-space: pre-wrap; min-height: 200px; outline: none; letter-spacing: .01em; }
|
||||
.prompt-edit:focus { border-color: var(--orange); box-shadow: 0 0 0 3px rgba(229, 91, 38, 0.10); }
|
||||
.asset-tag { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; background: var(--bg-soft); border: 1px solid var(--border); font-size: 11.5px; }
|
||||
.asset-tag .dotc { width: 14px; height: 14px; background: var(--card); border: 1px solid var(--border); }
|
||||
|
||||
/* === STAGE 4 · 视频片段 === */
|
||||
.queue-bar { display: flex; align-items: center; gap: 16px; padding: 14px 18px; background: var(--card); border: 1px solid var(--border); margin-bottom: 18px; }
|
||||
.queue-bar .bar-wrap { flex: 1; height: 6px; background: var(--bg-soft); overflow: hidden; }
|
||||
.queue-bar .bar-wrap > span { display: block; height: 100%; background: var(--orange); }
|
||||
|
||||
.video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
|
||||
.video-card { background: var(--card); border: 1px solid var(--border); }
|
||||
.video-thumb { aspect-ratio: 9/16; max-height: 320px; position: relative; }
|
||||
.video-thumb .play { position: absolute; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,0.05); cursor: pointer; opacity: 0; transition: opacity .15s; }
|
||||
.video-thumb:hover .play { opacity: 1; }
|
||||
.video-thumb .btn-play { width: 36px; height: 36px; background: rgba(0,0,0,.7); color: #FFF; border-radius: 50%; display: grid; place-items: center; }
|
||||
.video-card .body { padding: 10px 12px; }
|
||||
|
||||
/* === STAGE 5 · 编辑器 === */
|
||||
.editor { display: grid; grid-template-columns: 1fr 280px; grid-template-rows: 1fr auto; gap: 0; height: 580px; background: var(--card); border: 1px solid var(--border); }
|
||||
.editor-preview { padding: 16px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); display: flex; flex-direction: column; gap: 12px; }
|
||||
.editor-preview .canvas { flex: 1; aspect-ratio: 9/16; max-height: 380px; margin: 0 auto; background:
|
||||
repeating-linear-gradient(135deg, rgba(0,0,0,0.03) 0 1px, transparent 1px 12px),
|
||||
var(--bg-soft);
|
||||
border: 1px solid var(--border);
|
||||
display: grid; place-items: center;
|
||||
color: var(--ink-3);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12px; }
|
||||
.editor-preview .controls { display: flex; align-items: center; gap: 8px; justify-content: center; }
|
||||
.ctl-btn { width: 32px; height: 32px; border: 1px solid var(--border); background: var(--card); color: var(--ink-2); display: grid; place-items: center; cursor: pointer; }
|
||||
.ctl-btn:hover { color: var(--orange); border-color: var(--orange); }
|
||||
|
||||
.editor-props { padding: 16px; border-bottom: 1px solid var(--border); overflow-y: auto; }
|
||||
.props-tabs { display: flex; gap: 0; margin-bottom: 14px; border-bottom: 1px solid var(--border); }
|
||||
.props-tabs > div { padding: 8px 12px; font-size: 12.5px; color: var(--ink-2); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
|
||||
.props-tabs > div.active { color: var(--orange); border-bottom-color: var(--orange); font-weight: 600; }
|
||||
.style-swatch { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
||||
.swatch-card { padding: 10px; border: 1px solid var(--border); cursor: pointer; }
|
||||
.swatch-card:hover { background: var(--bg-soft); }
|
||||
.swatch-card.selected { border-color: var(--orange); background: var(--orange-tint); }
|
||||
.swatch-card .demo { font-size: 12px; padding: 6px 8px; background: var(--card); border: 1px solid var(--border); margin-bottom: 4px; text-align: center; }
|
||||
.swatch-card .demo.b { background: #000; color: #FFF; font-family: serif; }
|
||||
.swatch-card .demo.c { color: var(--orange); -webkit-text-stroke: 0.5px var(--ink); }
|
||||
.swatch-card .demo.d { background: #FFC700; color: var(--ink); font-weight: 700; }
|
||||
.swatch-card .nm { font-size: 11px; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.props-row { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 12.5px; }
|
||||
.props-row:last-child { border-bottom: 0; }
|
||||
.props-row .k { color: var(--ink-3); flex: 1; font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .02em; }
|
||||
.input-mini { width: 90px; padding: 4px 8px; height: 26px; font-size: 12px; border-radius: 0; background: var(--card); border: 1px solid var(--border); }
|
||||
|
||||
.timeline { grid-column: 1 / -1; padding: 14px 16px; background: var(--bg); }
|
||||
.tl-toolbar { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
|
||||
.tl-ruler { display: grid; grid-template-columns: 80px 1fr; align-items: center; padding: 4px 0; font-size: 10.5px; }
|
||||
.tl-ruler .l { font-family: 'JetBrains Mono', monospace; color: var(--ink-3); padding-left: 4px; }
|
||||
.tl-ruler .ticks { display: flex; justify-content: space-between; font-family: 'JetBrains Mono', monospace; color: var(--ink-3); padding: 0 4px; letter-spacing: .04em; }
|
||||
.tl-track { display: grid; grid-template-columns: 80px 1fr; align-items: center; gap: 0; padding: 6px 0; }
|
||||
.tl-track .label { font-size: 11.5px; color: var(--ink-2); display: flex; align-items: center; gap: 6px; padding-left: 4px; }
|
||||
.tl-track .label .dot { width: 8px; height: 8px; }
|
||||
.tl-track .lane { display: flex; gap: 2px; height: 30px; position: relative; }
|
||||
.clip { padding: 0 8px; font-size: 11px; display: flex; align-items: center; cursor: pointer; overflow: hidden; white-space: nowrap; user-select: none; }
|
||||
.clip.video { background: rgba(229, 91, 38, 0.12); border: 1px solid rgba(229, 91, 38, 0.3); color: var(--orange); }
|
||||
.clip.video.selected { background: var(--orange); color: #FFF; border-color: var(--orange-hover); }
|
||||
.clip.subtitle { background: rgba(63, 107, 63, 0.10); border: 1px solid rgba(63, 107, 63, 0.3); color: var(--green); }
|
||||
.clip.bgm { background: rgba(245, 165, 36, 0.12); border: 1px solid rgba(245, 165, 36, 0.3); color: #B8651A; }
|
||||
.clip .num { font-family: 'JetBrains Mono', monospace; font-weight: 700; margin-right: 6px; opacity: .7; }
|
||||
.playhead { position: absolute; top: -16px; bottom: -54px; width: 1px; background: var(--orange); pointer-events: none; }
|
||||
.playhead::before { content: ''; position: absolute; top: -2px; left: -4px; width: 9px; height: 9px; background: var(--orange); transform: rotate(45deg); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<!-- Project header -->
|
||||
<div class="proj-head">
|
||||
<div style="display:flex; gap:14px; align-items:center;">
|
||||
<div class="placeholder" style="width:42px;height:54px;"><span class="ph-frame">9:16</span></div>
|
||||
<div>
|
||||
<div style="display:flex; gap:8px; align-items:center;">
|
||||
<h1>补水面膜 · 痛点种草 · v3</h1>
|
||||
<span class="pill info"><span class="dot"></span>进行中</span>
|
||||
</div>
|
||||
<div class="muted-2 mono" style="font-size:11.5px; margin-top:4px; letter-spacing:.02em;">// 透真补水面膜 · AI 全生 · 6 镜 · 0-15s · 9:16</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('分享', '/projects/p3/share')">分享</button>
|
||||
<button class="btn btn-sm" onclick="Shell.toast('已复制项目', '补水面膜 · v4')">复制项目</button>
|
||||
<button class="btn btn-sm" onclick="Shell.toast('归档项目', '/projects/p3/archive')">归档</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stage stepper -->
|
||||
<div class="stepper">
|
||||
<span class="corner-tr">+</span><span class="corner-bl">+</span>
|
||||
<a class="stage-step done" data-stage="1" href="#stage-1"><div class="num">1</div><div class="lbl">脚本</div><div class="st">已确认</div></a>
|
||||
<div class="stage-line done"></div>
|
||||
<a class="stage-step done" data-stage="2" href="#stage-2"><div class="num">2</div><div class="lbl">基础资产</div><div class="st">已确认</div></a>
|
||||
<div class="stage-line done"></div>
|
||||
<a class="stage-step active" data-stage="3" href="#stage-3"><div class="num">3</div><div class="lbl">故事板</div><div class="st">待确认</div></a>
|
||||
<div class="stage-line"></div>
|
||||
<a class="stage-step" data-stage="4" href="#stage-4"><div class="num">4</div><div class="lbl">视频片段</div><div class="st">未开始</div></a>
|
||||
<div class="stage-line"></div>
|
||||
<a class="stage-step" data-stage="5" href="#stage-5"><div class="num">5</div><div class="lbl">拼接导出</div><div class="st">未开始</div></a>
|
||||
</div>
|
||||
|
||||
<!-- ============= STAGE 1 · 脚本 ============= -->
|
||||
<section class="stage" data-stage-pane="1">
|
||||
<div class="stage-script">
|
||||
<div class="pane chat-pane">
|
||||
<div class="pane-h">
|
||||
<div class="ai-avatar">AI</div>
|
||||
<strong>脚本助手</strong>
|
||||
<span class="muted-2 mono" style="font-size:11px;">· GPT-4o</span>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已清空对话')">清空对话</button>
|
||||
</div>
|
||||
<div class="chat-body">
|
||||
<div class="msg ai">
|
||||
<div style="display:flex; gap:10px; align-items:flex-start;">
|
||||
<div class="ai-avatar" style="margin-top:2px;">AI</div>
|
||||
<div class="bubble">根据 <strong>透真补水面膜</strong> 和"痛点种草"风格,我先生成了一版 6 镜的脚本,主线是"加班党的深夜急救"。你可以直接编辑右侧的镜头,或者让我重写某一镜。</div>
|
||||
</div>
|
||||
<div class="time" style="margin-left:36px;">14:02</div>
|
||||
</div>
|
||||
<div class="msg user">
|
||||
<div class="bubble">第 4 镜对白太硬了,能不能更口语化?</div>
|
||||
<div class="time">14:05</div>
|
||||
</div>
|
||||
<div class="msg ai">
|
||||
<div style="display:flex; gap:10px; align-items:flex-start;">
|
||||
<div class="ai-avatar" style="margin-top:2px;">AI</div>
|
||||
<div>
|
||||
<div class="bubble">把 "<span class="del">补水力极强,锁水持久</span>" 改成 "<span class="ins">真的,敷完第二天起来脸是软的,不是绷着的</span>",更像真实分享。已替换右侧第 4 镜,确认要的话点 [接受]。</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-sm" onclick="Shell.toast('已接受改动', 'shot-4')">接受</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('再来一版')">再来一版</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time" style="margin-left:36px;">14:05</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<textarea class="textarea" placeholder="对脚本的修改诉求 · 比如:让第 5 镜更夸张一点、整体加 1 镜结尾……" rows="2"></textarea>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm">↻ 整体重写</button>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-primary" onclick="Shell.toast('已发送', 'POST /chat')">发送 ⌘↵</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pane shot-list">
|
||||
<div class="pane-h">
|
||||
<strong>镜头脚本</strong>
|
||||
<span class="muted-2 mono" style="font-size:11px;">· 6 镜 · 0-15s</span>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已加一镜')">+ 加一镜</button>
|
||||
</div>
|
||||
<div class="shots-body">
|
||||
<div class="shot-card">
|
||||
<div class="shot-head"><div class="shot-num">1</div><div class="shot-time">0-2s</div><span class="spacer"></span><button class="icon-mini-btn" title="重写">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">深夜的办公桌,电脑屏幕亮着,女主对着镜子叹气,皮肤干燥起皮特写。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">(叹气)"加班三天,脸已经不能看了……"</div></div>
|
||||
</div>
|
||||
<div class="shot-card">
|
||||
<div class="shot-head"><div class="shot-num">2</div><div class="shot-time">2-5s</div><span class="spacer"></span><button class="icon-mini-btn">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">女主从抽屉拿出补水面膜,包装特写,光线柔和。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">"还好我有这个 —— 透真玻尿酸面膜。"</div></div>
|
||||
</div>
|
||||
<div class="shot-card">
|
||||
<div class="shot-head"><div class="shot-num">3</div><div class="shot-time">5-8s</div><span class="spacer"></span><button class="icon-mini-btn">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">面膜布展开,30g 精华液从布上滴落特写,慢镜头。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">"30g 精华液,一片顶三片的量。"</div></div>
|
||||
</div>
|
||||
<div class="shot-card highlight">
|
||||
<div class="shot-head"><div class="shot-num">4</div><div class="shot-time">8-11s</div><span class="pill info" style="margin-left:6px;"><span class="dot"></span>刚改</span><span class="spacer"></span><button class="icon-mini-btn">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">女主敷面膜,闭眼平躺,灯光暖。床头闹钟显示 23:41。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">"真的,敷完第二天起来脸是软的,不是绷着的。"</div></div>
|
||||
</div>
|
||||
<div class="shot-card">
|
||||
<div class="shot-head"><div class="shot-num">5</div><div class="shot-time">11-13s</div><span class="spacer"></span><button class="icon-mini-btn">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">第二天早上,女主对镜化妆,皮肤透亮,状态饱满。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">"早上化妆都能看出来,不假吹。"</div></div>
|
||||
</div>
|
||||
<div class="shot-card">
|
||||
<div class="shot-head"><div class="shot-num">6</div><div class="shot-time">13-15s</div><span class="spacer"></span><button class="icon-mini-btn">↻</button></div>
|
||||
<div class="shot-row"><span class="shot-k">画面</span><div class="shot-v">面膜产品大图,价格标签 "5 片装 ¥39.9",购物车浮动按钮。</div></div>
|
||||
<div class="shot-row"><span class="shot-k">对白</span><div class="shot-v">"618 五片才 39.9,囤起来。"</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-foot">
|
||||
<div class="info"><span class="mono">[ LLM 用量 ~2.4k tokens · ¥0.04 ]</span></div>
|
||||
<div class="hstack">
|
||||
<button class="btn" onclick="Shell.toast('重新生成', 'POST /script/regen')">↻ 重新生成全部</button>
|
||||
<button class="btn btn-primary btn-lg" onclick="location.hash='#stage-2'">确认脚本,进入下一步 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= STAGE 2 · 基础资产 ============= -->
|
||||
<section class="stage" data-stage-pane="2">
|
||||
<div class="stage-assets">
|
||||
<div class="asset-side">
|
||||
<div class="ttab active"><span>人物</span><span class="num">2/2</span></div>
|
||||
<div class="ttab"><span>场景</span><span class="num">3/3</span></div>
|
||||
<div class="ttab"><span>商品</span><span class="num">3 张</span></div>
|
||||
<div class="info">
|
||||
基础资产是后续故事板的素材。生成后可以单独修改提示词重跑,或上传你已有的图替换。
|
||||
<br><br>
|
||||
<strong class="mono">// 人物 +¥0.20/张</strong>
|
||||
<strong class="mono">// 场景 +¥0.15/张</strong>
|
||||
<span style="color:var(--ink-3);">商品图无成本(直接复用商品库)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="hstack" style="margin-bottom:14px;">
|
||||
<h3 style="font-size:15px; font-weight:600;">人物 · 2 个</h3>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm">从我的资产库选</button>
|
||||
<button class="btn btn-sm" onclick="Shell.toast('+ 新增人物')">+ 新增人物</button>
|
||||
</div>
|
||||
|
||||
<div class="asset-grid-2">
|
||||
<div class="asset-card-2">
|
||||
<div class="placeholder thumb-2"><span class="ph-frame">林夕 · 都市白领</span></div>
|
||||
<div class="body-2">
|
||||
<div class="hstack"><strong style="font-size:13.5px;">主角 · 林夕</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>已确认</span></div>
|
||||
<div class="prompt-box">25-30 岁都市白领,长发,穿宽松米色家居服,温柔但带点疲倦感,肤色偏黄/略干。</div>
|
||||
<div class="hstack" style="margin-top:10px;">
|
||||
<button class="btn btn-ghost btn-sm">改提示词</button>
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm">⤓ 上传替换</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="asset-card-2">
|
||||
<div class="placeholder thumb-2">
|
||||
<div style="display:flex; flex-direction:column; gap:8px; align-items:center;">
|
||||
<div class="spinner"></div>
|
||||
<span class="ph-frame">生成中 · 约 8s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body-2">
|
||||
<div class="hstack"><strong style="font-size:13.5px;">朋友/同事 · 阿楠</strong><span class="spacer"></span><span class="pill info"><span class="dot"></span>生成中</span></div>
|
||||
<div class="prompt-box">25-30 岁同龄女性,短发,穿白色衬衫,妆容精致皮肤好,作为对比。</div>
|
||||
<div class="hstack" style="margin-top:10px;">
|
||||
<button class="btn btn-ghost btn-sm" disabled>改提示词</button>
|
||||
<button class="btn btn-ghost btn-sm" disabled>↻ 重跑</button>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm" disabled>⤓ 上传替换</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hstack" style="margin:28px 0 14px;">
|
||||
<h3 style="font-size:15px; font-weight:600;">场景 · 3 个</h3>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm">从我的资产库选</button>
|
||||
<button class="btn btn-sm">+ 新增场景</button>
|
||||
</div>
|
||||
|
||||
<div class="asset-grid-2">
|
||||
<div class="asset-card-2">
|
||||
<div class="placeholder thumb-2"><span class="ph-frame">深夜办公桌</span></div>
|
||||
<div class="body-2">
|
||||
<div class="hstack"><strong style="font-size:13.5px;">深夜办公桌</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>已确认</span></div>
|
||||
<div class="prompt-box">深夜居家办公环境,木质书桌,台灯暖光,电脑屏幕亮着,背景虚化。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card-2">
|
||||
<div class="placeholder thumb-2"><span class="ph-frame">床头特写</span></div>
|
||||
<div class="body-2">
|
||||
<div class="hstack"><strong style="font-size:13.5px;">卧室床头</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>已确认</span></div>
|
||||
<div class="prompt-box">米白色床品,木质床头柜,闹钟显示晚间时间,氛围温柔安静。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card-2">
|
||||
<div class="placeholder thumb-2">
|
||||
<div style="display:flex; flex-direction:column; gap:6px; align-items:center;">
|
||||
<div class="fail-icon">!</div>
|
||||
<span class="ph-frame">生成失败</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body-2">
|
||||
<div class="hstack"><strong style="font-size:13.5px;">通勤地铁</strong><span class="spacer"></span><span class="pill err"><span class="dot"></span>失败</span></div>
|
||||
<div class="prompt-box">早高峰地铁车厢,光线偏冷,年轻通勤族,氛围紧张。</div>
|
||||
<div class="muted-2" style="font-size:12px; margin-top:6px;">⚠ 提示词被安全审核拦截,请调整后重试(不扣费)</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-sm">改提示词</button>
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-foot">
|
||||
<div class="info"><span class="mono">[ 已确认 ¥0.85 · 待生成 ¥0.20 · 失败 ¥0(不扣) ]</span></div>
|
||||
<div class="hstack">
|
||||
<button class="btn" onclick="location.hash='#stage-1'">← 返回脚本</button>
|
||||
<button class="btn btn-primary btn-lg" disabled>1 个资产生成中 · 等待</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= STAGE 3 · 故事板 ============= -->
|
||||
<section class="stage active" data-stage-pane="3">
|
||||
<div class="stage-storyboard">
|
||||
<div class="sb-canvas">
|
||||
<div class="sb-row head">
|
||||
<div>镜号</div><div>画面</div><div>机位 / 对白 / 音效</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">1<span class="t">0-2s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">深夜办公桌 · 女主对镜叹气 · 暖光</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">中景 / 固定机位</div>
|
||||
<div class="dialog">"加班三天,脸已经不能看了……"</div>
|
||||
<div class="sfx">SFX:键盘声 + 远处空调嗡鸣</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">2<span class="t">2-5s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">面膜包装特写 · 抽屉光线柔和</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">特写 / 缓推</div>
|
||||
<div class="dialog">"还好我有这个 —— 透真玻尿酸面膜。"</div>
|
||||
<div class="sfx">SFX:抽屉滑动声</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">3<span class="t">5-8s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">面膜布展开 · 30g 精华滴落 · 慢动作</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">微距 / 慢镜头</div>
|
||||
<div class="dialog">"30g 精华液,一片顶三片的量。"</div>
|
||||
<div class="sfx">SFX:水滴慢速回弹</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">4<span class="t">8-11s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">女主敷面膜平躺 · 闹钟 23:41</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">中近景 / 固定</div>
|
||||
<div class="dialog">"敷完第二天起来脸是软的,不是绷着的。"</div>
|
||||
<div class="sfx">SFX:呼吸声 + 窗外风声</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">5<span class="t">11-13s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">早晨化妆台 · 女主对镜上妆 · 透亮</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">中景 / 固定</div>
|
||||
<div class="dialog">"早上化妆都能看出来,不假吹。"</div>
|
||||
<div class="sfx">SFX:化妆刷轻扫声</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-row">
|
||||
<div class="sb-num">6<span class="t">13-15s</span></div>
|
||||
<div class="sb-img"><div class="placeholder"><span class="ph-frame">产品大图 · 价格 5片¥39.9 · 购物车</span></div></div>
|
||||
<div class="sb-text">
|
||||
<div class="meta">产品定格 / 静止</div>
|
||||
<div class="dialog">"618 五片才 39.9,囤起来。"</div>
|
||||
<div class="sfx">SFX:清脆叮咚音效</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sb-side">
|
||||
<div class="pane">
|
||||
<div class="hstack" style="margin-bottom:10px;">
|
||||
<strong style="font-size:14px;">故事板</strong>
|
||||
<span class="spacer"></span>
|
||||
<span class="pill ok"><span class="dot"></span>已生成</span>
|
||||
</div>
|
||||
<div class="muted-2" style="font-size:12px; line-height:1.55; margin-bottom:14px;">
|
||||
整张故事板由 image-2 一次性输出,包含画面 + 镜头说明。如需修改请编辑下方提示词整张重跑(不能局部改)。
|
||||
</div>
|
||||
|
||||
<div class="muted mono" style="font-size:11px; font-weight:500; margin-bottom:6px; letter-spacing:.04em;">// 视觉提示词</div>
|
||||
<div class="prompt-edit" contenteditable="true">风格:日系小清新短视频,暖色调,午夜→清晨光线变化。
|
||||
镜头列表(6 镜,0-15s):
|
||||
1. 中景 · 深夜办公桌女主对镜叹气
|
||||
2. 特写 · 面膜包装从抽屉拿出
|
||||
3. 微距 · 面膜布展开 + 精华液滴落慢镜
|
||||
4. 中近景 · 女主敷面膜平躺,床头闹钟 23:41
|
||||
5. 中景 · 早晨化妆台 + 女主透亮上妆
|
||||
6. 产品定格 · 面膜盒 + 价格标签 ¥39.9
|
||||
人物:林夕(参考 R1)
|
||||
场景:深夜办公桌(S1) + 卧室床头(S2)
|
||||
要求:表格化布局,每镜含画面 + 文字说明</div>
|
||||
|
||||
<div class="hstack" style="margin-top:12px;">
|
||||
<button class="btn btn-sm" onclick="Shell.toast('整张重跑', 'POST /storyboard/regen ¥0.45')">↻ 整张重跑</button>
|
||||
<span class="spacer"></span>
|
||||
<span class="muted-2 mono" style="font-size:11px;">~¥0.45</span>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="muted mono" style="font-size:11px; font-weight:500; margin-bottom:8px; letter-spacing:.04em;">// 绑定的资产</div>
|
||||
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
||||
<span class="asset-tag"><span class="dotc"></span>林夕(人物)</span>
|
||||
<span class="asset-tag"><span class="dotc"></span>深夜办公桌(场景)</span>
|
||||
<span class="asset-tag"><span class="dotc"></span>卧室床头(场景)</span>
|
||||
<span class="asset-tag"><span class="dotc"></span>面膜盒(商品)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-foot">
|
||||
<div class="info"><span class="mono">[ image-2 一次 ¥0.45 · 累计 ¥1.50 ]</span></div>
|
||||
<div class="hstack">
|
||||
<button class="btn" onclick="location.hash='#stage-2'">← 返回资产</button>
|
||||
<button class="btn btn-primary btn-lg" onclick="location.hash='#stage-4'">确认故事板,开始生成视频片段 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= STAGE 4 · 视频片段 ============= -->
|
||||
<section class="stage" data-stage-pane="4">
|
||||
<div class="queue-bar">
|
||||
<div>
|
||||
<div style="font-size:14px; font-weight:600;">视频生成中 · 4 / 6 完成</div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:3px; letter-spacing:.02em;">// 每镜 Seedance 调用 ~30s · 预计还需 1 分钟</div>
|
||||
</div>
|
||||
<div class="bar-wrap"><span style="width:67%"></span></div>
|
||||
<span class="muted mono" style="font-size:12px;">67%</span>
|
||||
<button class="btn btn-sm" onclick="Shell.toast('全部重跑', 'POST /video/regen-all')">↻ 全部重跑</button>
|
||||
</div>
|
||||
|
||||
<div class="video-grid">
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb">
|
||||
<span class="ph-frame">镜 1 · 0-2s</span>
|
||||
<div class="play"><div class="btn-play"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></div></div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 1 · 深夜办公桌</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>完成</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">2.0s · 1080×1920 · ¥0.18</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
<button class="btn btn-ghost btn-sm">⤓ 下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb">
|
||||
<span class="ph-frame">镜 2 · 2-5s</span>
|
||||
<div class="play"><div class="btn-play"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></div></div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 2 · 面膜包装</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>完成</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">3.0s · 1080×1920 · ¥0.22</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
<button class="btn btn-ghost btn-sm">⤓ 下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb">
|
||||
<span class="ph-frame">镜 3 · 5-8s</span>
|
||||
<div class="play"><div class="btn-play"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></div></div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 3 · 精华液微距</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>完成</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">3.0s · 1080×1920 · ¥0.22</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
<button class="btn btn-ghost btn-sm">⤓ 下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb">
|
||||
<span class="ph-frame">镜 4 · 8-11s</span>
|
||||
<div class="play"><div class="btn-play"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></div></div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 4 · 敷面膜平躺</strong><span class="spacer"></span><span class="pill ok"><span class="dot"></span>完成</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">3.0s · 1080×1920 · ¥0.22</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm">↻ 重跑</button>
|
||||
<button class="btn btn-ghost btn-sm">⤓ 下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb">
|
||||
<div style="display:flex; flex-direction:column; gap:8px; align-items:center;">
|
||||
<div class="spinner"></div>
|
||||
<span class="ph-frame">镜 5 · 生成中 18s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 5 · 化妆台</strong><span class="spacer"></span><span class="pill info"><span class="dot"></span>生成中</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">2.0s · 排队中 · ~¥0.18</div>
|
||||
<div class="hstack" style="margin-top:8px;">
|
||||
<button class="btn btn-ghost btn-sm" disabled>↻ 重跑</button>
|
||||
<button class="btn btn-ghost btn-sm" disabled>⤓ 下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card">
|
||||
<div class="placeholder video-thumb"><span class="ph-frame">镜 6 · 排队</span></div>
|
||||
<div class="body">
|
||||
<div class="hstack"><strong style="font-size:13px;">镜 6 · 产品定格</strong><span class="spacer"></span><span class="pill neutral"><span class="dot"></span>排队中</span></div>
|
||||
<div class="muted-2 mono" style="font-size:11px; margin-top:4px;">2.0s · 等待中 · ~¥0.18</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-foot">
|
||||
<div class="info"><span class="mono">[ 已确认 ¥0.84 · 待生成 ¥0.36 · 累计 ¥2.34 ]</span></div>
|
||||
<div class="hstack">
|
||||
<button class="btn" onclick="location.hash='#stage-3'">← 返回故事板</button>
|
||||
<button class="btn btn-primary btn-lg" disabled>2 镜生成中 · 等待</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============= STAGE 5 · 拼接编辑器 ============= -->
|
||||
<section class="stage" data-stage-pane="5">
|
||||
<div class="editor">
|
||||
<div class="editor-preview">
|
||||
<div class="canvas">9:16 预览 · 1080×1920</div>
|
||||
<div class="controls">
|
||||
<button class="ctl-btn" title="上一帧"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M3 3v10l4-5zM9 3v10l4-5z" fill="currentColor"/></svg></button>
|
||||
<button class="ctl-btn" title="播放" onclick="Shell.toast('播放', '00:08.42 / 00:15.00')"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M5 4l7 4-7 4z" fill="currentColor"/></svg></button>
|
||||
<button class="ctl-btn" title="下一帧"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M13 3v10l-4-5zM7 3v10l-4-5z" fill="currentColor"/></svg></button>
|
||||
<span class="muted mono" style="font-size:12px; margin-left:8px;">00:08.42 / 00:15.00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-props">
|
||||
<div class="props-tabs">
|
||||
<div class="active">字幕</div>
|
||||
<div>转场</div>
|
||||
<div>BGM</div>
|
||||
</div>
|
||||
<div class="muted mono" style="font-size:11px; font-weight:500; margin-bottom:8px; letter-spacing:.04em;">// 字幕样式</div>
|
||||
<div class="style-swatch">
|
||||
<div class="swatch-card selected"><div class="demo">真实分享</div><div class="nm">朴素白底</div></div>
|
||||
<div class="swatch-card"><div class="demo b">真实分享</div><div class="nm">影视黑底</div></div>
|
||||
<div class="swatch-card"><div class="demo c">真实分享</div><div class="nm">手写描边</div></div>
|
||||
<div class="swatch-card"><div class="demo d">真实分享</div><div class="nm">综艺暖黄</div></div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="muted mono" style="font-size:11px; font-weight:500; margin-bottom:8px; letter-spacing:.04em;">// 当前选中(镜 4)</div>
|
||||
<div class="props-row"><span class="k">起始</span><input class="input-mini" value="00:08.00"></div>
|
||||
<div class="props-row"><span class="k">时长</span><input class="input-mini" value="3.00s"></div>
|
||||
<div class="props-row"><span class="k">音量</span><input class="input-mini" value="100"></div>
|
||||
<div class="props-row"><span class="k">速度</span><input class="input-mini" value="1.0x"></div>
|
||||
<div class="props-row"><span class="k">入场</span><span class="mono" style="font-size:11.5px;">交叉淡化</span></div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="muted mono" style="font-size:11px; font-weight:500; margin-bottom:8px; letter-spacing:.04em;">// BGM</div>
|
||||
<div class="props-row" style="border-bottom:0;">
|
||||
<span style="font-size:12px; flex:1;">温柔治愈钢琴 · 0:42</span>
|
||||
<button class="btn btn-ghost btn-sm">替换</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline">
|
||||
<div class="tl-toolbar">
|
||||
<button class="btn btn-ghost btn-sm">↶</button>
|
||||
<button class="btn btn-ghost btn-sm">↷</button>
|
||||
<span class="muted-2" style="font-size:12px;">|</span>
|
||||
<button class="btn btn-ghost btn-sm">分割</button>
|
||||
<button class="btn btn-ghost btn-sm">复制</button>
|
||||
<button class="btn btn-ghost btn-sm">删除</button>
|
||||
<span class="spacer"></span>
|
||||
<span class="muted mono" style="font-size:11px;">缩放</span>
|
||||
<input type="range" min="50" max="200" value="100" style="width:120px;">
|
||||
</div>
|
||||
|
||||
<div class="tl-ruler">
|
||||
<div class="l">// time</div>
|
||||
<div class="ticks">
|
||||
<span>0s</span><span>2s</span><span>5s</span><span>8s</span><span>11s</span><span>13s</span><span>15s</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tl-track">
|
||||
<div class="label"><span class="dot" style="background:var(--orange);"></span>视频</div>
|
||||
<div class="lane">
|
||||
<div class="clip video" style="flex:2;"><span class="num">1</span> 深夜办公桌</div>
|
||||
<div class="clip video" style="flex:3;"><span class="num">2</span> 面膜包装</div>
|
||||
<div class="clip video" style="flex:3;"><span class="num">3</span> 精华液微距</div>
|
||||
<div class="clip video selected" style="flex:3;"><span class="num">4</span> 敷面膜平躺</div>
|
||||
<div class="clip video" style="flex:2;"><span class="num">5</span> 化妆台</div>
|
||||
<div class="clip video" style="flex:2;"><span class="num">6</span> 产品定格</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tl-track">
|
||||
<div class="label"><span class="dot" style="background:var(--green);"></span>字幕</div>
|
||||
<div class="lane" style="position:relative;">
|
||||
<div class="clip subtitle" style="flex:2;">加班三天 脸已经不能看了…</div>
|
||||
<div class="clip subtitle" style="flex:3;">还好我有这个 透真玻尿酸面膜</div>
|
||||
<div class="clip subtitle" style="flex:3;">30g 精华 一片顶三片</div>
|
||||
<div class="clip subtitle" style="flex:3;">敷完起来脸是软的</div>
|
||||
<div class="clip subtitle" style="flex:2;">化妆都能看出来</div>
|
||||
<div class="clip subtitle" style="flex:2;">5 片 ¥39.9 囤起来</div>
|
||||
<div class="playhead" style="left:56%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tl-track">
|
||||
<div class="label"><span class="dot" style="background:#B8651A;"></span>BGM</div>
|
||||
<div class="lane">
|
||||
<div class="clip bgm" style="flex:15;">温柔治愈钢琴 · 0:42(循环 1 次,淡入淡出)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-foot">
|
||||
<div class="info"><span class="mono">[ 合成预估 ~30s · 不消耗 token ]</span></div>
|
||||
<div class="hstack">
|
||||
<button class="btn" onclick="location.hash='#stage-4'">← 返回片段</button>
|
||||
<button class="btn" onclick="Shell.toast('已保存草稿', '/projects/p3/draft')">保存草稿</button>
|
||||
<button class="btn btn-primary btn-lg" onclick="Shell.toast('开始导出', 'POST /export · 1080P 9:16')">导出 MP4 · 1080P 9:16 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>
|
||||
Shell.render({
|
||||
active: 'projects',
|
||||
crumbs: [{ label: '工作台', href: 'index.html' }, { label: '视频项目', href: 'projects.html' }, { label: '补水面膜 · v3' }]
|
||||
});
|
||||
|
||||
// hash routing
|
||||
function activateStage(n) {
|
||||
document.querySelectorAll('.stage').forEach(s => s.classList.remove('active'));
|
||||
document.querySelector(`[data-stage-pane="${n}"]`)?.classList.add('active');
|
||||
|
||||
document.querySelectorAll('.stage-step').forEach(s => {
|
||||
s.classList.remove('active');
|
||||
if (+s.dataset.stage === +n) s.classList.add('active');
|
||||
});
|
||||
|
||||
// update top breadcrumb fragment indicator with shell.toast
|
||||
const stageNames = { 1:'脚本', 2:'基础资产', 3:'故事板', 4:'视频片段', 5:'拼接导出' };
|
||||
Shell.toast('进入 Stage ' + n + ' · ' + stageNames[n], 'pipeline#stage-' + n);
|
||||
}
|
||||
function readHash() {
|
||||
const m = location.hash.match(/stage-(\d)/);
|
||||
if (m) activateStage(+m[1]);
|
||||
}
|
||||
window.addEventListener('hashchange', readHash);
|
||||
readHash();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,191 @@
|
||||
<!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>
|
||||
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
|
||||
.product-card { background: var(--card); border: 1px solid var(--border); cursor: pointer; transition: background .15s; }
|
||||
.product-card:hover { background: var(--bg-soft); border-color: var(--ink-3); }
|
||||
.product-thumb { aspect-ratio: 1.4 / 1; }
|
||||
.product-body { padding: 14px; }
|
||||
.product-name { font-size: 14px; font-weight: 600; color: var(--ink); }
|
||||
.product-meta { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.product-tags { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
|
||||
.tag-chip { font-size: 11px; padding: 2px 8px; background: var(--bg-soft); color: var(--ink-2); border: 1px solid var(--border); }
|
||||
.product-card.add { border: 1px dashed var(--border); background: transparent; display: grid; place-items: center; min-height: 220px; color: var(--ink-2); gap: 8px; padding: 16px; }
|
||||
.product-card.add:hover { border-color: var(--orange); color: var(--orange); background: var(--orange-tint); }
|
||||
.product-card.add .plus-ic { width: 36px; height: 36px; border: 1px solid currentColor; display: grid; place-items: center; }
|
||||
.upload-zone { border: 1px dashed var(--border); padding: 24px; text-align: center; background: var(--bg-soft); color: var(--ink-2); font-size: 13px; }
|
||||
.upload-zone strong { color: var(--orange); font-weight: 600; }
|
||||
.upload-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 10px; }
|
||||
.upload-grid .placeholder { aspect-ratio: 1; }
|
||||
.bullet-list { list-style: none; }
|
||||
.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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>商品库</h1>
|
||||
<div class="sub"><span class="mono">// 12 SKU</span> · 商品信息会作为脚本和资产生成的素材</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" onclick="Shell.openDrawer('new-product')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="M12 5v14M5 12h14"/></svg>
|
||||
新建商品
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="search-inline">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input class="input" placeholder="搜索商品名称、品牌">
|
||||
</div>
|
||||
<button class="chip active">全部 <span class="mono" style="opacity:.7">12</span></button>
|
||||
<button class="chip">美妆个护</button>
|
||||
<button class="chip">数码 3C</button>
|
||||
<button class="chip">食品饮料</button>
|
||||
<button class="chip">服饰</button>
|
||||
<span class="spacer"></span>
|
||||
<button class="chip">最近添加 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="product-grid">
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '透真玻尿酸补水面膜')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">补水面膜 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">透真玻尿酸补水面膜</div>
|
||||
<div class="product-meta">美妆个护 · 3 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">熬夜党</span><span class="tag-chip">敏感肌</span><span class="tag-chip">¥39.9</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '南卡 Lite Pro')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">蓝牙耳机 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">南卡 Lite Pro 蓝牙耳机</div>
|
||||
<div class="product-meta">数码 3C · 5 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">通勤</span><span class="tag-chip">运动</span><span class="tag-chip">¥199</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '滋啦速食')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">速食牛肉面 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">滋啦速食牛肉面 · 6 桶装</div>
|
||||
<div class="product-meta">食品饮料 · 4 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">加班</span><span class="tag-chip">独居</span><span class="tag-chip">¥49.9</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '透真清透防晒')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">防晒霜 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">透真清透物理防晒霜</div>
|
||||
<div class="product-meta">美妆个护 · 4 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">SPF50</span><span class="tag-chip">通勤</span><span class="tag-chip">¥69</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '三顿半同款')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">咖啡冻干粉 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">三顿半同款冻干咖啡粉</div>
|
||||
<div class="product-meta">食品饮料 · 6 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">提神</span><span class="tag-chip">早八</span><span class="tag-chip">¥89/24 颗</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '小熊 4L 空气炸锅')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">空气炸锅 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">小熊 4L 可视空气炸锅</div>
|
||||
<div class="product-meta">家电 · 5 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">小户型</span><span class="tag-chip">健康</span><span class="tag-chip">¥159</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card" onclick="Shell.toast('打开商品', '露露同款瑜伽裤')">
|
||||
<div class="placeholder product-thumb"><span class="ph-frame">瑜伽裤 · 1200×800</span></div>
|
||||
<div class="product-body">
|
||||
<div class="product-name">露露同款裸感瑜伽裤</div>
|
||||
<div class="product-meta">服饰 · 8 张图</div>
|
||||
<div class="product-tags"><span class="tag-chip">健身房</span><span class="tag-chip">通勤</span><span class="tag-chip">¥119</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card add" onclick="Shell.openDrawer('new-product')">
|
||||
<div class="plus-ic"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 5v14M5 12h14"/></svg></div>
|
||||
<div style="font-size:13px; font-weight:500;">新建商品</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Drawer: 新建商品 -->
|
||||
<div class="drawer-bg" id="new-product-bg" onclick="Shell.closeDrawer('new-product')"></div>
|
||||
<aside class="drawer" id="new-product">
|
||||
<div class="drawer-h">
|
||||
<h3>新建商品</h3>
|
||||
<div class="x" onclick="Shell.closeDrawer('new-product')">
|
||||
<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>
|
||||
</div>
|
||||
<div class="drawer-b">
|
||||
<div class="field">
|
||||
<label class="field-label">商品名称<span class="req">*</span></label>
|
||||
<input class="input" value="透真玻尿酸补水面膜">
|
||||
</div>
|
||||
<div style="display:flex; gap:12px;">
|
||||
<div class="field" style="flex:1;">
|
||||
<label class="field-label">品类</label>
|
||||
<select class="select"><option>美妆个护</option><option>数码 3C</option><option>食品饮料</option><option>服饰</option></select>
|
||||
</div>
|
||||
<div class="field" style="flex:1;">
|
||||
<label class="field-label">参考价</label>
|
||||
<input class="input" value="39.9" placeholder="¥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(--ink-3); 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>
|
||||
<div class="field-hint">每条一行,3-5 条。脚本会优先围绕这些卖点展开。</div>
|
||||
<ul class="bullet-list" style="margin-top:8px;">
|
||||
<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:28px; 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 class="field">
|
||||
<label class="field-label">补充说明</label>
|
||||
<textarea class="textarea">日常 ¥69 ,618 活动 5 片装 ¥39.9 。建议睡前敷 15 分钟,可冷藏后使用,体感更好。</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-f">
|
||||
<button class="btn btn-ghost" onclick="Shell.closeDrawer('new-product')">取消</button>
|
||||
<button class="btn" onclick="Shell.toast('已保存草稿', '/products/draft')">保存草稿</button>
|
||||
<button class="btn btn-primary" onclick="Shell.closeDrawer('new-product'); Shell.toast('商品已创建', '透真补水面膜')">创建商品</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>Shell.render({ active: 'products', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '商品库' }] });</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,286 @@
|
||||
<!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>
|
||||
.wizard { display: grid; grid-template-columns: 200px minmax(0, 1fr) 300px; gap: 36px; align-items: start; max-width: 1400px; }
|
||||
@media (max-width: 1180px) { .wizard { grid-template-columns: 200px minmax(0, 1fr); } .wiz-preview { display: none; } }
|
||||
.steps { position: sticky; top: 24px; align-self: start; }
|
||||
.step { display: flex; gap: 12px; padding: 12px 0; position: relative; }
|
||||
.step:not(:last-child)::after { content: ''; position: absolute; left: 11px; top: 36px; width: 1px; height: calc(100% - 24px); background: var(--border); }
|
||||
.step .num { width: 24px; height: 24px; border: 1px solid var(--border); background: var(--card); display: grid; place-items: center; font-size: 11px; font-weight: 600; color: var(--ink-3); flex-shrink: 0; z-index: 1; font-family: 'JetBrains Mono', monospace; }
|
||||
.step.done .num { background: var(--ink); border-color: var(--ink); color: #FFF; }
|
||||
.step.active .num { background: var(--orange); border-color: var(--orange); color: #FFF; }
|
||||
.step .label { font-size: 13.5px; font-weight: 500; color: var(--ink-2); padding-top: 2px; }
|
||||
.step .desc { font-size: 11.5px; color: var(--ink-3); padding-top: 3px; line-height: 1.4; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.step.active .label { color: var(--ink); font-weight: 600; }
|
||||
.step.done .label { color: var(--ink-2); }
|
||||
.step.done:not(:last-child)::after { background: var(--ink); }
|
||||
|
||||
.wiz-pane { background: var(--card); border: 1px solid var(--border); padding: 22px 24px; margin-bottom: 14px; }
|
||||
.wiz-pane.active { padding: 26px 28px; position: relative; }
|
||||
.wiz-pane.active::before, .wiz-pane.active::after { content: ''; position: absolute; width: 14px; height: 14px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 21' fill='%239C988C'%3E%3Cpath d='M10.5 4C10.5 7.31371 7.81371 10 4.5 10H0.5V11H4.5C7.81371 11 10.5 13.6863 10.5 17V21H11.5V17C11.5 13.6863 14.1863 11 17.5 11H21.5V10H17.5C14.1863 10 11.5 7.31371 11.5 4V0H10.5V4Z'/%3E%3C/svg%3E") no-repeat center; background-size: contain; pointer-events: none; }
|
||||
.wiz-pane.active::before { top: -7px; left: -7px; }
|
||||
.wiz-pane.active::after { bottom: -7px; right: -7px; }
|
||||
.wiz-pane.collapsed { padding: 16px 20px; }
|
||||
.wiz-pane-h { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }
|
||||
.wiz-pane-h h3 { font-size: 14px; font-weight: 600; }
|
||||
.wiz-step-h h2 { font-size: 20px; font-weight: 600; letter-spacing: -.015em; }
|
||||
.wiz-step-h p { font-size: 13px; color: var(--ink-2); margin-top: 6px; }
|
||||
|
||||
.opt-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
||||
.opt-row.cols-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
.opt-row.cols-6 { grid-template-columns: repeat(3, 1fr); }
|
||||
@media (min-width: 1280px) { .opt-row.cols-6 { grid-template-columns: repeat(6, 1fr); } }
|
||||
.opt-card { border: 1px solid var(--border); padding: 14px; background: var(--card); cursor: pointer; position: relative; display: flex; flex-direction: column; min-width: 0; }
|
||||
.opt-card:hover { background: var(--bg-soft); }
|
||||
.opt-card.selected { border-color: var(--orange); background: var(--orange-tint); }
|
||||
.opt-card.selected::after { content: ''; position: absolute; top: 8px; right: 10px; width: 16px; height: 16px; background-color: var(--orange); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center; background-size: 10px 10px; }
|
||||
.opt-card h4 { font-size: 13px; font-weight: 600; }
|
||||
.opt-card .sub { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-top: 3px; letter-spacing: .02em; }
|
||||
.opt-card .note { font-size: 11.5px; color: var(--ink-2); margin-top: 6px; line-height: 1.5; }
|
||||
.opt-card .metric { margin-top: auto; padding-top: 10px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .02em; }
|
||||
.opt-card .metric .val { color: var(--ink); font-weight: 500; }
|
||||
.opt-card.selected .metric .val { color: var(--orange); }
|
||||
.opt-card .badge { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; padding: 1px 6px; background: var(--card); border: 1px solid var(--border); color: var(--ink-3); display: inline-block; margin-top: 8px; letter-spacing: .04em; align-self: flex-start; }
|
||||
.opt-card.selected .badge { color: var(--orange); border-color: var(--orange-soft); }
|
||||
|
||||
.theme-pill { display: inline-flex; gap: 4px; height: 28px; align-items: center; padding: 0 12px; border: 1px solid var(--border); border-radius: 999px; background: var(--card); font-size: 12.5px; cursor: pointer; color: var(--ink-2); }
|
||||
.theme-pill:hover { background: var(--bg-soft); }
|
||||
.theme-pill.active { background: var(--orange-tint); color: var(--orange); border-color: var(--orange-soft); font-weight: 600; }
|
||||
|
||||
.reco-bubble { position: relative; margin-top: 10px; padding: 10px 14px; background: var(--orange-tint); border: 1px solid var(--orange-soft); display: flex; align-items: center; gap: 12px; font-size: 12.5px; color: var(--ink); }
|
||||
.reco-bubble::before { content: ''; position: absolute; top: -5px; left: 28px; width: 9px; height: 9px; background: var(--orange-tint); border-left: 1px solid var(--orange-soft); border-top: 1px solid var(--orange-soft); transform: rotate(45deg); }
|
||||
.reco-bubble .ic { color: var(--orange); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; }
|
||||
.reco-bubble .ic svg, .reco-bubble .dismiss svg { display: block; }
|
||||
.reco-bubble .txt { flex: 1; line-height: 1.5; }
|
||||
.reco-bubble .txt strong { color: var(--orange); font-weight: 600; }
|
||||
.reco-bubble .txt .meta { display: block; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); margin-top: 2px; letter-spacing: .02em; }
|
||||
.reco-bubble .btn-apply { height: 26px; padding: 0 12px; background: var(--orange); color: #FFF; border: 1px solid var(--orange); font-size: 11.5px; font-weight: 600; cursor: pointer; flex-shrink: 0; }
|
||||
.reco-bubble .btn-apply:hover { background: var(--orange-d); }
|
||||
.reco-bubble .dismiss { background: transparent; color: var(--ink-3); border: 0; width: 24px; height: 24px; padding: 0; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
|
||||
.reco-bubble .dismiss:hover { color: var(--ink); }
|
||||
|
||||
.wiz-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border); }
|
||||
|
||||
/* preview panel */
|
||||
.wiz-preview { position: sticky; top: 24px; background: var(--card); border: 1px solid var(--border); padding: 18px; }
|
||||
.wiz-preview::before, .wiz-preview::after { content: ''; position: absolute; width: 14px; height: 14px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 21' fill='%239C988C'%3E%3Cpath d='M10.5 4C10.5 7.31371 7.81371 10 4.5 10H0.5V11H4.5C7.81371 11 10.5 13.6863 10.5 17V21H11.5V17C11.5 13.6863 14.1863 11 17.5 11H21.5V10H17.5C14.1863 10 11.5 7.31371 11.5 4V0H10.5V4Z'/%3E%3C/svg%3E") no-repeat center; background-size: contain; pointer-events: none; }
|
||||
.wiz-preview::before { top: -7px; left: -7px; }
|
||||
.wiz-preview::after { bottom: -7px; right: -7px; }
|
||||
.pv-h { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .08em; margin-bottom: 12px; text-transform: uppercase; display: flex; justify-content: space-between; }
|
||||
.pv-h .live { display: inline-flex; align-items: center; gap: 5px; color: var(--orange); }
|
||||
.pv-h .live::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--orange); animation: pulse 1.6s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
|
||||
.pv-title { font-size: 14px; font-weight: 600; line-height: 1.3; margin-bottom: 14px; }
|
||||
.pv-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--border); border: 1px solid var(--border); margin-bottom: 14px; }
|
||||
.pv-metric { padding: 10px 12px; background: var(--card); }
|
||||
.pv-metric .l { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; color: var(--ink-3); letter-spacing: .04em; text-transform: uppercase; }
|
||||
.pv-metric .v { font-size: 18px; font-weight: 600; margin-top: 3px; font-variant-numeric: tabular-nums; color: var(--ink); }
|
||||
.pv-metric .v small { font-size: 11px; color: var(--ink-3); font-weight: 500; }
|
||||
.pv-metric.accent .v { color: var(--orange); }
|
||||
.pv-section { margin-top: 14px; }
|
||||
.pv-section .lbl { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; color: var(--ink-3); letter-spacing: .06em; text-transform: uppercase; margin-bottom: 8px; }
|
||||
.pv-flow { display: flex; flex-wrap: wrap; gap: 4px 0; font-size: 11.5px; color: var(--ink-2); align-items: center; line-height: 1.7; }
|
||||
.pv-flow .node { padding: 2px 7px; background: var(--bg-soft); border: 1px solid var(--border-soft); color: var(--ink); font-weight: 500; }
|
||||
.pv-flow .arrow { color: var(--orange); margin: 0 5px; font-family: 'JetBrains Mono', monospace; }
|
||||
.pv-list { list-style: none; padding: 0; margin: 0; }
|
||||
.pv-list li { font-size: 11.5px; color: var(--ink-2); padding: 4px 0; display: flex; align-items: center; gap: 6px; }
|
||||
.pv-list li::before { content: ''; width: 11px; height: 11px; flex-shrink: 0; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E55B26' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l5 5L20 6'/%3E%3C/svg%3E") no-repeat center; background-size: contain; }
|
||||
.pv-foot { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border); font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); display: flex; justify-content: space-between; }
|
||||
.pv-foot strong { color: var(--ink); font-weight: 500; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>新建项目</h1>
|
||||
<div class="sub"><span class="mono">// 商品 → 脚本来源 → 配置 → 确认 · 4 步开始生成</span></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn btn-ghost" href="projects.html">退出</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard">
|
||||
<nav class="steps">
|
||||
<div class="step done"><div class="num">✓</div><div><div class="label">选择商品</div><div class="desc">透真补水面膜</div></div></div>
|
||||
<div class="step done"><div class="num">✓</div><div><div class="label">脚本来源</div><div class="desc">AI 全生 · 痛点种草</div></div></div>
|
||||
<div class="step active"><div class="num">3</div><div><div class="label">项目配置</div><div class="desc">名称 · 时长 · 风格</div></div></div>
|
||||
<div class="step"><div class="num">4</div><div><div class="label">确认与计费</div><div class="desc">预估 ¥3.20</div></div></div>
|
||||
</nav>
|
||||
|
||||
<div>
|
||||
<div class="wiz-pane collapsed">
|
||||
<div class="wiz-pane-h">
|
||||
<h3>第 1 步 · 选择商品</h3>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm">修改</button>
|
||||
</div>
|
||||
<div class="hstack" style="gap:12px;">
|
||||
<div class="placeholder" style="width:44px;height:56px;"><span class="ph-frame">主图</span></div>
|
||||
<div>
|
||||
<div style="font-weight:600; font-size:13.5px;">透真玻尿酸补水面膜</div>
|
||||
<div class="muted-2 mono" style="font-size:11.5px; margin-top:3px; letter-spacing:.02em;">美妆个护 · ¥39.9 · 3 张图 · 3 个卖点</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wiz-pane collapsed">
|
||||
<div class="wiz-pane-h">
|
||||
<h3>第 2 步 · 脚本来源</h3>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-ghost btn-sm">修改</button>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<span class="pill info"><span class="dot"></span>AI 全生</span>
|
||||
<span class="muted">主题:</span>
|
||||
<span style="font-size:13px;">熬夜党的急救面膜 · 痛点种草向</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wiz-pane active">
|
||||
<div class="wiz-step-h" style="margin-bottom:18px;">
|
||||
<h2>第 3 步 · 项目配置</h2>
|
||||
<p>这些设置会影响 LLM 生成脚本的方向,确认后会进入流水线第 1 步(脚本生成)。</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">项目名称<span class="req">*</span></label>
|
||||
<input class="input" value="补水面膜 · 痛点种草 · v3">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">视频时长<span class="req">*</span></label>
|
||||
<div class="opt-row cols-4">
|
||||
<div class="opt-card"><h4>0-10 秒</h4><div class="sub">3-4 镜</div><div class="note">黄金完播</div><div class="metric">完播 <span class="val">52%</span></div></div>
|
||||
<div class="opt-card selected"><h4>0-15 秒</h4><div class="sub">4-5 镜</div><div class="note">完播率最佳</div><div class="metric">完播 <span class="val">42%</span></div></div>
|
||||
<div class="opt-card"><h4>0-30 秒</h4><div class="sub">6-8 镜</div><div class="note">卖点详解</div><div class="metric">完播 <span class="val">32%</span></div></div>
|
||||
<div class="opt-card"><h4>0-60 秒</h4><div class="sub">10-12 镜</div><div class="note">故事化</div><div class="metric">完播 <span class="val">26%</span></div></div>
|
||||
</div>
|
||||
<div class="field-hint">数据来源:抖音同品类 TOP 视频均值 · 实际镜头数由 LLM 决定</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">脚本风格</label>
|
||||
<div class="opt-row">
|
||||
<div class="opt-card selected">
|
||||
<h4>痛点种草</h4>
|
||||
<div class="note">用户痛点切入,以「我懂你」的口吻引出产品。</div>
|
||||
<span class="badge">最常用</span>
|
||||
</div>
|
||||
<div class="opt-card">
|
||||
<h4>开箱测评</h4>
|
||||
<div class="note">朋友式分享,从开箱到使用感受娓娓道来。</div>
|
||||
</div>
|
||||
<div class="opt-card">
|
||||
<h4>对比展示</h4>
|
||||
<div class="note">「用前 vs 用后 / 同类 vs 本品」直观呈现。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">人物设定</label>
|
||||
<div class="opt-row cols-6">
|
||||
<div class="opt-card"><h4>都市白领女性</h4><div class="sub">25-30 岁</div><div class="metric"><span class="val">大盘消费力</span></div></div>
|
||||
<div class="opt-card"><h4>闺蜜种草</h4><div class="sub">邻家女孩</div><div class="metric"><span class="val">复购最高</span></div></div>
|
||||
<div class="opt-card"><h4>总裁亲选</h4><div class="sub">创始人 IP</div><div class="metric"><span class="val">30 万销额</span></div></div>
|
||||
<div class="opt-card"><h4>专业测评师</h4><div class="sub">垂类达人</div><div class="metric"><span class="val">互动 +30%</span></div></div>
|
||||
<div class="opt-card"><h4>实用宝妈</h4><div class="sub">家庭决策者</div><div class="metric"><span class="val">母婴/家清</span></div></div>
|
||||
<div class="opt-card selected"><h4>学生党</h4><div class="sub">Z 世代 18-24</div><div class="metric"><span class="val">平价快消</span></div></div>
|
||||
</div>
|
||||
|
||||
<div class="reco-bubble">
|
||||
<span class="ic">
|
||||
<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="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg>
|
||||
</span>
|
||||
<div class="txt">
|
||||
<span>抖音同人设 TOP 视频更常用 <strong>0-10 秒</strong> + <strong>对比展示</strong></span>
|
||||
<span class="meta">当前 0-15 秒 · 痛点种草 → 推荐换为学生党最优组合</span>
|
||||
</div>
|
||||
<button class="btn-apply">一键套用</button>
|
||||
<button class="dismiss" aria-label="忽略">
|
||||
<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 5l14 14M19 5L5 19"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">关键卖点(可勾选要重点突出的)</label>
|
||||
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
||||
<span class="theme-pill active">✓ 透明质酸 + B5</span>
|
||||
<span class="theme-pill active">✓ 30g 大容量精华</span>
|
||||
<span class="theme-pill">+ 0 香精 0 酒精</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wiz-foot">
|
||||
<button class="btn">← 上一步</button>
|
||||
<div class="hstack">
|
||||
<span class="muted-2 mono" style="font-size:11.5px; letter-spacing:.02em;">// 下一步:确认与计费</span>
|
||||
<a class="btn btn-primary btn-lg" href="pipeline.html#stage-1" onclick="Shell.toast('开始生成项目', 'pipeline#stage-1')">下一步 →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Live preview ── -->
|
||||
<aside class="wiz-preview">
|
||||
<div class="pv-h">
|
||||
<span>实时预估</span>
|
||||
<span class="live">LIVE</span>
|
||||
</div>
|
||||
<div class="pv-title">补水面膜 · 痛点种草 · v3</div>
|
||||
|
||||
<div class="pv-metrics">
|
||||
<div class="pv-metric"><div class="l">镜头</div><div class="v">4-5<small>镜</small></div></div>
|
||||
<div class="pv-metric accent"><div class="l">预估完播</div><div class="v">42<small>%</small></div></div>
|
||||
<div class="pv-metric"><div class="l">预估转化</div><div class="v">1.8<small>%</small></div></div>
|
||||
<div class="pv-metric"><div class="l">预估成本</div><div class="v">¥3.20</div></div>
|
||||
</div>
|
||||
|
||||
<div class="pv-section">
|
||||
<div class="lbl">人设 · 风格</div>
|
||||
<ul class="pv-list">
|
||||
<li>学生党 · Z 世代 18-24</li>
|
||||
<li>痛点种草 · 完播率最佳</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pv-section">
|
||||
<div class="lbl">脚本走向</div>
|
||||
<div class="pv-flow">
|
||||
<span style="display:inline-flex; align-items:center;"><span class="node">痛点</span><span class="arrow">→</span></span>
|
||||
<span style="display:inline-flex; align-items:center;"><span class="node">共鸣</span><span class="arrow">→</span></span>
|
||||
<span style="display:inline-flex; align-items:center;"><span class="node">产品</span><span class="arrow">→</span></span>
|
||||
<span style="display:inline-flex; align-items:center;"><span class="node">效果</span><span class="arrow">→</span></span>
|
||||
<span class="node">引导</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pv-section">
|
||||
<div class="lbl">突出卖点</div>
|
||||
<ul class="pv-list">
|
||||
<li>透明质酸 + B5</li>
|
||||
<li>30g 大容量精华</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pv-foot">
|
||||
<span>v3 · Restraint</span>
|
||||
<strong>就绪</strong>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>Shell.render({ active: 'projects', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '视频项目', href: 'projects.html' }, { label: '新建项目' }] });</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,521 @@
|
||||
<!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>
|
||||
/* ─── List view ─── */
|
||||
.proj-name-cell { display: flex; align-items: center; gap: 12px; }
|
||||
.proj-thumb { width: 40px; height: 52px; flex-shrink: 0; }
|
||||
.proj-name { font-weight: 600; color: var(--ink); font-size: 13.5px; }
|
||||
.proj-sub { font-size: 11.5px; color: var(--ink-3); margin-top: 3px; font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.row-action { display: flex; gap: 4px; visibility: hidden; }
|
||||
table.t tbody tr:hover .row-action { visibility: visible; }
|
||||
.row-action a { width: 28px; height: 28px; display: grid; place-items: center; color: var(--ink-2); }
|
||||
.row-action a:hover { background: var(--card); color: var(--orange); border: 1px solid var(--border); }
|
||||
|
||||
/* ─── View toggle ─── */
|
||||
.view-toggle { display: inline-flex; border: 1px solid var(--border); }
|
||||
.view-toggle button { padding: 6px 12px; background: var(--card); color: var(--ink-2); font-size: 12px; border-right: 1px solid var(--border); border-radius: 0; height: 32px; cursor: pointer; font-family: inherit; display: flex; align-items: center; gap: 5px; }
|
||||
.view-toggle button:last-child { border-right: 0; }
|
||||
.view-toggle button:hover { background: var(--bg-soft); color: var(--ink); }
|
||||
.view-toggle button.active { background: var(--orange-tint); color: var(--orange); font-weight: 600; }
|
||||
.view-toggle button svg { width: 13px; height: 13px; }
|
||||
|
||||
/* ─── Grid view ─── */
|
||||
.proj-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
|
||||
.proj-card { background: var(--card); border: 1px solid var(--border); cursor: pointer; transition: background .15s; display: flex; flex-direction: column; }
|
||||
.proj-card:hover { background: var(--bg-soft); border-color: var(--ink-3); }
|
||||
.proj-card .card-thumb { aspect-ratio: 9/16; max-height: 280px; }
|
||||
.proj-card .card-body { padding: 14px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
|
||||
.proj-card .card-name { font-size: 13.5px; font-weight: 600; color: var(--ink); line-height: 1.4; }
|
||||
.proj-card .card-sub { font-size: 11.5px; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
.proj-card .card-foot { display: flex; align-items: center; justify-content: space-between; padding-top: 10px; border-top: 1px solid var(--border); margin-top: auto; }
|
||||
.proj-card .card-time { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--ink-3); letter-spacing: .02em; }
|
||||
|
||||
/* ─── Empty state ─── */
|
||||
.empty-state {
|
||||
background: var(--card);
|
||||
border: 1px dashed var(--border);
|
||||
padding: 80px 40px;
|
||||
text-align: center;
|
||||
color: var(--ink-2);
|
||||
display: none;
|
||||
}
|
||||
.empty-state.show { display: block; }
|
||||
.empty-state .ic-empty {
|
||||
width: 48px; height: 48px;
|
||||
margin: 0 auto 14px;
|
||||
background: var(--bg-soft);
|
||||
border: 1px solid var(--border);
|
||||
display: grid; place-items: center;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.empty-state h3 { font-size: 14px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
|
||||
.empty-state p { font-size: 12.5px; color: var(--ink-3); font-family: 'JetBrains Mono', monospace; letter-spacing: .02em; }
|
||||
|
||||
/* ─── Result count ─── */
|
||||
.result-meta {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--ink-3);
|
||||
margin-bottom: 14px;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
.result-meta .count { color: var(--orange); font-weight: 600; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>视频项目</h1>
|
||||
<div class="sub"><span class="mono">// 12 个 · 3 进行中 · 8 完成 · 1 失败</span></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn btn-primary btn-lg" href="projects-new.html">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="M12 5v14M5 12h14"/></svg>
|
||||
新建项目
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" id="status-tabs">
|
||||
<div class="tab active" data-filter="all">全部 <span class="count">12</span></div>
|
||||
<div class="tab" data-filter="wip">进行中 <span class="count">3</span></div>
|
||||
<div class="tab" data-filter="done">已完成 <span class="count">8</span></div>
|
||||
<div class="tab" data-filter="fail">失败 <span class="count">1</span></div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="search-inline">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input class="input" id="search-input" placeholder="搜索项目名称、商品">
|
||||
</div>
|
||||
<button class="chip" onclick="Shell.toast('商品筛选', '/filter/product')">商品 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
<button class="chip" onclick="Shell.toast('脚本来源筛选', '/filter/source')">脚本来源 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
<button class="chip" onclick="Shell.toast('时间筛选', '/filter/date')">创建时间 <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6l4 4 4-4"/></svg></button>
|
||||
<span class="spacer"></span>
|
||||
<div class="view-toggle">
|
||||
<button id="view-grid" data-view="grid">
|
||||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="2" width="5" height="5"/><rect x="9" y="2" width="5" height="5"/><rect x="2" y="9" width="5" height="5"/><rect x="9" y="9" width="5" height="5"/></svg>
|
||||
网格
|
||||
</button>
|
||||
<button id="view-list" class="active" data-view="list">
|
||||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M2 4h12M2 8h12M2 12h12"/></svg>
|
||||
列表
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-meta" id="result-meta">// 显示 <span class="count">12</span> / 12 个项目</div>
|
||||
|
||||
<!-- ============= LIST VIEW ============= -->
|
||||
<div id="list-view">
|
||||
<table class="t">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:32%">项目</th>
|
||||
<th>商品</th>
|
||||
<th>脚本来源</th>
|
||||
<th style="width:200px">进度</th>
|
||||
<th>状态</th>
|
||||
<th style="width:120px">更新于</th>
|
||||
<th style="width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="list-tbody">
|
||||
<tr data-status="wip" data-name="补水面膜 痛点种草" onclick="location.href='pipeline.html#stage-3'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">补水面膜 · 痛点种草 · v3</div><div class="proj-sub">6 镜 · 0-15s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>透真补水面膜</td>
|
||||
<td><span class="muted">AI 全生</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="cur"></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">3/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill info"><span class="dot"></span>故事板 待确认</span></td>
|
||||
<td class="muted-2">12 分钟前</td>
|
||||
<td>
|
||||
<div class="row-action">
|
||||
<a href="pipeline.html#stage-3" onclick="event.stopPropagation()" title="继续"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></a>
|
||||
<a onclick="event.stopPropagation();Shell.toast('更多操作')"><svg width="14" height="14" viewBox="0 0 16 16"><circle cx="3" cy="8" r="1.2" fill="currentColor"/><circle cx="8" cy="8" r="1.2" fill="currentColor"/><circle cx="13" cy="8" r="1.2" fill="currentColor"/></svg></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-status="wip" data-name="速食牛肉面 加班治愈" onclick="location.href='pipeline.html#stage-2'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">速食牛肉面 · 加班治愈</div><div class="proj-sub">4 镜 · 0-12s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>滋啦速食 · 6 桶装</td>
|
||||
<td><span class="muted">一句话主题</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="cur"></span><span></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">2/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill info"><span class="dot"></span>资产生成中</span></td>
|
||||
<td class="muted-2">37 分钟前</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="wip" data-name="透真防晒 通勤对比" onclick="location.href='pipeline.html#stage-4'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">透真防晒 · 通勤对比</div><div class="proj-sub">6 镜 · 0-18s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>透真清透防晒霜</td>
|
||||
<td><span class="muted">AI 全生</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="cur"></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">4/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill info"><span class="dot"></span>视频生成 4/6</span></td>
|
||||
<td class="muted-2">2 小时前</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="fail" data-name="咖啡冻干 早八剧情" onclick="location.href='pipeline.html#stage-3'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">咖啡冻干 · 早八剧情</div><div class="proj-sub">5 镜 · 0-15s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>三顿半同款冻干</td>
|
||||
<td><span class="muted">一句话主题</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="fail"></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">3/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill err"><span class="dot"></span>故事板生成失败</span></td>
|
||||
<td class="muted-2">昨天 18:42</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="done" data-name="蓝牙耳机 开箱测评" onclick="location.href='pipeline.html#stage-5'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">蓝牙耳机 · 开箱测评</div><div class="proj-sub">5 镜 · 0-15s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>南卡 Lite Pro</td>
|
||||
<td><span class="muted">自带脚本</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">5/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill ok"><span class="dot"></span>已完成</span></td>
|
||||
<td class="muted-2">5 月 7 日</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="done" data-name="瑜伽裤 通勤穿搭" onclick="location.href='pipeline.html#stage-5'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">瑜伽裤 · 通勤穿搭</div><div class="proj-sub">5 镜 · 0-15s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>露露同款瑜伽裤</td>
|
||||
<td><span class="muted">AI 全生</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">5/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill ok"><span class="dot"></span>已完成</span></td>
|
||||
<td class="muted-2">5 月 6 日</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="done" data-name="空气炸锅 小户型" onclick="location.href='pipeline.html#stage-5'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">空气炸锅 · 小户型</div><div class="proj-sub">4 镜 · 0-12s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>小熊 4L 空气炸锅</td>
|
||||
<td><span class="muted">一句话主题</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">5/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill ok"><span class="dot"></span>已完成</span></td>
|
||||
<td class="muted-2">5 月 4 日</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr data-status="archived" data-name="补水面膜 痛点种草 v1" onclick="location.href='pipeline.html#stage-5'">
|
||||
<td>
|
||||
<div class="proj-name-cell">
|
||||
<div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div>
|
||||
<div><div class="proj-name">补水面膜 · 痛点种草 · v1</div><div class="proj-sub">6 镜 · 0-15s</div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>透真补水面膜</td>
|
||||
<td><span class="muted">AI 全生</span></td>
|
||||
<td>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:11px;">5/5</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill neutral"><span class="dot"></span>已归档</span></td>
|
||||
<td class="muted-2">4 月 28 日</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- ============= GRID VIEW ============= -->
|
||||
<div id="grid-view" style="display:none;">
|
||||
<div class="proj-grid" id="grid-body">
|
||||
<div class="proj-card" data-status="wip" data-name="补水面膜 痛点种草" onclick="location.href='pipeline.html#stage-3'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 镜 3/6</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">补水面膜 · 痛点种草 · v3</div>
|
||||
<div class="card-sub" style="margin-top:4px;">透真补水面膜 · 6 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="cur"></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">3/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill info"><span class="dot"></span>故事板 待确认</span>
|
||||
<span class="card-time">12 分钟前</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="wip" data-name="速食牛肉面 加班治愈" onclick="location.href='pipeline.html#stage-2'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 镜 2/4</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">速食牛肉面 · 加班治愈</div>
|
||||
<div class="card-sub" style="margin-top:4px;">滋啦速食 · 4 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="cur"></span><span></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">2/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill info"><span class="dot"></span>资产生成中</span>
|
||||
<span class="card-time">37 分钟前</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="wip" data-name="透真防晒 通勤对比" onclick="location.href='pipeline.html#stage-4'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 镜 4/6</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">透真防晒 · 通勤对比</div>
|
||||
<div class="card-sub" style="margin-top:4px;">透真清透防晒霜 · 6 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="cur"></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">4/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill info"><span class="dot"></span>视频 4/6</span>
|
||||
<span class="card-time">2 小时前</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="fail" data-name="咖啡冻干 早八剧情" onclick="location.href='pipeline.html#stage-3'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 镜 3/5</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">咖啡冻干 · 早八剧情</div>
|
||||
<div class="card-sub" style="margin-top:4px;">三顿半同款 · 5 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="fail"></span><span></span><span></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">3/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill err"><span class="dot"></span>故事板失败</span>
|
||||
<span class="card-time">昨天 18:42</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="done" data-name="蓝牙耳机 开箱测评" onclick="location.href='pipeline.html#stage-5'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 5/5 ✓</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">蓝牙耳机 · 开箱测评</div>
|
||||
<div class="card-sub" style="margin-top:4px;">南卡 Lite Pro · 5 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">5/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill ok"><span class="dot"></span>已完成</span>
|
||||
<span class="card-time">5 月 7 日</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="done" data-name="瑜伽裤 通勤穿搭" onclick="location.href='pipeline.html#stage-5'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 5/5 ✓</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">瑜伽裤 · 通勤穿搭</div>
|
||||
<div class="card-sub" style="margin-top:4px;">露露同款 · 5 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">5/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill ok"><span class="dot"></span>已完成</span>
|
||||
<span class="card-time">5 月 6 日</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="done" data-name="空气炸锅 小户型" onclick="location.href='pipeline.html#stage-5'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 5/5 ✓</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">空气炸锅 · 小户型</div>
|
||||
<div class="card-sub" style="margin-top:4px;">小熊 4L · 4 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">5/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill ok"><span class="dot"></span>已完成</span>
|
||||
<span class="card-time">5 月 4 日</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="proj-card" data-status="archived" data-name="补水面膜 痛点种草 v1" onclick="location.href='pipeline.html#stage-5'">
|
||||
<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 5/5 ✓</span></div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="card-name">补水面膜 · 痛点种草 · v1</div>
|
||||
<div class="card-sub" style="margin-top:4px;">透真补水面膜 · 6 镜</div>
|
||||
</div>
|
||||
<div class="hstack">
|
||||
<div class="prog"><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span><span class="done"></span></div>
|
||||
<span class="muted-2 mono" style="font-size:10.5px;">5/5</span>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<span class="pill neutral"><span class="dot"></span>已归档</span>
|
||||
<span class="card-time">4 月 28 日</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div class="empty-state" id="empty">
|
||||
<div class="ic-empty">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
</div>
|
||||
<h3>没有匹配的项目</h3>
|
||||
<p>// 试试切换 tab 或修改搜索词</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="assets/shell.js"></script>
|
||||
<script>
|
||||
Shell.render({ active: 'projects', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '视频项目' }] });
|
||||
|
||||
// ============== Tab + search filter + view toggle ==============
|
||||
const state = { filter: 'all', view: 'list', search: '' };
|
||||
const TOTAL = 8; // 实际渲染的样本数
|
||||
|
||||
function applyFilter() {
|
||||
const isList = state.view === 'list';
|
||||
document.getElementById('list-view').style.display = isList ? '' : 'none';
|
||||
document.getElementById('grid-view').style.display = isList ? 'none' : '';
|
||||
|
||||
const items = document.querySelectorAll(isList ? '#list-tbody tr' : '.proj-card');
|
||||
let visible = 0;
|
||||
items.forEach(el => {
|
||||
const status = el.dataset.status || '';
|
||||
const name = (el.dataset.name || '').toLowerCase();
|
||||
const matchFilter = state.filter === 'all' || status.split(' ').includes(state.filter);
|
||||
const matchSearch = !state.search || name.includes(state.search.toLowerCase());
|
||||
const show = matchFilter && matchSearch;
|
||||
el.style.display = show ? '' : 'none';
|
||||
if (show) visible++;
|
||||
});
|
||||
|
||||
// empty state
|
||||
const empty = document.getElementById('empty');
|
||||
if (visible === 0) {
|
||||
empty.classList.add('show');
|
||||
document.getElementById('list-view').style.display = 'none';
|
||||
document.getElementById('grid-view').style.display = 'none';
|
||||
} else {
|
||||
empty.classList.remove('show');
|
||||
}
|
||||
|
||||
// result meta
|
||||
document.getElementById('result-meta').innerHTML = `// 显示 <span class="count">${visible}</span> / ${TOTAL} 个项目`;
|
||||
}
|
||||
|
||||
// Tab clicks
|
||||
document.querySelectorAll('#status-tabs .tab').forEach(t => {
|
||||
t.addEventListener('click', () => {
|
||||
document.querySelectorAll('#status-tabs .tab').forEach(x => x.classList.remove('active'));
|
||||
t.classList.add('active');
|
||||
state.filter = t.dataset.filter;
|
||||
applyFilter();
|
||||
Shell.toast('筛选: ' + t.textContent.trim().split(' ')[0], 'filter=' + state.filter);
|
||||
});
|
||||
});
|
||||
|
||||
// View toggle
|
||||
document.querySelectorAll('.view-toggle button').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
document.querySelectorAll('.view-toggle button').forEach(x => x.classList.remove('active'));
|
||||
b.classList.add('active');
|
||||
state.view = b.dataset.view;
|
||||
applyFilter();
|
||||
Shell.toast('视图切换: ' + (state.view === 'list' ? '列表' : '网格'), 'view=' + state.view);
|
||||
});
|
||||
});
|
||||
|
||||
// Search
|
||||
document.getElementById('search-input').addEventListener('input', e => {
|
||||
state.search = e.target.value.trim();
|
||||
applyFilter();
|
||||
});
|
||||
|
||||
applyFilter();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user