AirShelf/v2/product-detail.html
UI 设计 e293aa43be
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6s
feat(v2): 添加 V2.1 设计稿目录 · 团队/设置页 · pipeline 多项 mock 优化
2026-05-21 16:18:28 +08:00

1360 lines
52 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<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>
/* ─── 顶部 标题 + 状态 ─── */
.pd-title {
display: flex; align-items: center; gap: 12px;
margin-bottom: 22px;
}
.pd-title h1 {
font-size: 24px; font-weight: 600;
letter-spacing: -.015em;
color: var(--accent-black);
line-height: 1.25;
}
.pd-title .status {
display: inline-flex; align-items: center; gap: 4px;
padding: 3px 10px;
background: var(--accent-emerald-bg, #e6f4ec);
color: var(--accent-emerald, #1f8a51);
border: 1px solid var(--accent-emerald-bd, #c4e3d1);
border-radius: var(--r-sm);
font-size: 11.5px;
font-weight: 500;
}
/* ─── 商品信息(含图片) + 快速操作(辅助) · 3 : 2 两栏 · 高度对齐 ─── */
.pd-overview {
display: grid;
grid-template-columns: 3fr 2fr;
gap: 16px;
margin-bottom: 24px;
align-items: stretch;
}
.pd-overview .ov-card { height: 100%; box-sizing: border-box; }
.pd-overview .ov-card {
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
padding: 20px 22px;
min-width: 0;
position: relative;
}
/* 编辑按钮 · 放在 .ov-h 标题行右侧 (flex item, 不再 absolute) */
.pd-overview .ov-h { align-items: center; }
.ov-edit {
display: inline-flex; align-items: center; gap: 5px;
height: 28px;
padding: 0 12px;
background: var(--surface);
border: 1px solid var(--black-alpha-12);
border-radius: var(--r-sm);
color: var(--black-alpha-72);
font-size: 12px;
font-family: inherit;
cursor: pointer;
white-space: nowrap;
transition: border-color var(--t-base), color var(--t-base), background var(--t-base);
}
.ov-edit-single { margin-left: auto; }
.ov-edit:hover {
border-color: var(--heat-40);
color: var(--heat);
background: var(--heat-12);
}
.ov-edit svg { width: 12px; height: 12px; }
.ov-edit.primary {
background: var(--heat);
color: var(--accent-white);
border-color: var(--heat);
white-space: nowrap;
}
.ov-edit.primary:hover { filter: brightness(1.05); background: var(--heat); color: var(--accent-white); }
/* 编辑模式按钮组 (重置 + 取消 + 保存) */
.ov-edit-group {
display: none;
align-items: center;
gap: 6px;
margin-left: auto;
}
.ov-card.editing .ov-edit-single { display: none; }
.ov-card.editing .ov-edit-group { display: inline-flex; }
/* 字段 view ↔ edit 状态切换 */
.v-edit { display: none; }
.ov-card.editing .v-static { display: none; }
.ov-card.editing .v-edit { display: block; }
/* 输入控件 · 对齐新建表单 V2.1 规范 */
.v-input,
.v-select {
width: 100%;
max-width: 100%;
height: 38px;
border: 1px solid var(--black-alpha-12);
border-radius: var(--r-md);
padding: 0 14px;
font-size: 13.5px;
color: var(--accent-black);
background: var(--background-lighter);
font-family: inherit;
outline: none;
transition: border-color var(--t-base), box-shadow var(--t-base);
}
.v-input:focus,
.v-select:focus {
border-color: var(--heat-40);
box-shadow: inset 0 0 0 1px var(--heat-40);
}
/* 编辑模式下 · 核心卖点 bullet-list (与新建表单完全一致) */
.v-bullet-list {
list-style: none;
padding: 0; margin: 0;
}
.v-bullet-list .bl-item,
.v-bullet-list .bl-add {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px;
background: var(--background-lighter);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
margin-bottom: 6px;
font-size: 13.5px;
}
.v-bullet-list .bl-add { background: transparent; border-style: dashed; }
.v-bullet-list .bl-add:focus-within { border-color: var(--heat-40); background: var(--surface); }
.v-bullet-list .num {
width: 22px; height: 22px;
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-sm);
font-family: var(--font-mono);
font-size: 11px;
color: var(--heat);
font-weight: 700;
display: grid; place-items: center;
flex-shrink: 0;
}
.v-bullet-list .bl-add .num {
background: transparent;
color: var(--heat);
border-color: var(--heat-40);
}
.v-bullet-list .bl-text { flex: 1; color: var(--accent-black); }
.v-bullet-list .bl-input {
flex: 1;
background: transparent; border: 0; outline: none;
font-size: 13.5px;
color: var(--accent-black);
font-family: inherit;
}
.v-bullet-list .bl-input::placeholder { color: var(--black-alpha-48); }
.v-bullet-list .bl-x {
width: 22px; height: 22px;
color: var(--black-alpha-48);
cursor: pointer;
display: grid; place-items: center;
border-radius: var(--r-sm);
transition: color var(--t-base), background var(--t-base);
}
.v-bullet-list .bl-x:hover { color: var(--accent-crimson, #c43d3d); background: var(--crimson-bg, #fdebea); }
.v-bullet-list .bl-x svg { width: 11px; height: 11px; }
/* 编辑模式下,商品图片显示一个 [+ 上传] 占位 */
.img-upload {
display: none;
aspect-ratio: 1;
border: 1.5px dashed var(--black-alpha-24);
border-radius: var(--r-sm);
cursor: pointer;
place-items: center;
color: var(--black-alpha-48);
background: var(--background-lighter);
transition: border-color var(--t-base), color var(--t-base);
}
.img-upload:hover { border-color: var(--heat); color: var(--heat); }
.img-upload svg { width: 18px; height: 18px; }
.ov-card.editing .img-upload { display: grid; }
.ov-card.editing .ov-images-sub .thumb { cursor: pointer; }
.ov-card.editing .ov-images-sub .thumb::after {
content: '×';
position: absolute;
top: 4px; right: 4px;
width: 18px; height: 18px;
background: rgba(0,0,0,.7);
color: var(--accent-white);
border-radius: 50%;
display: grid; place-items: center;
font-size: 13px;
line-height: 1;
}
.ov-images-sub .thumb { position: relative; }
.pd-overview .ov-h {
display: flex; align-items: baseline; gap: 8px;
margin-bottom: 14px;
}
.pd-overview .ov-h .ti {
font-size: 14px; font-weight: 600;
color: var(--accent-black);
}
.pd-overview .ov-h .ct {
font-family: var(--font-mono);
font-size: 11.5px;
color: var(--black-alpha-48);
letter-spacing: .02em;
}
.pd-overview .ov-h .more {
margin-left: auto;
font-size: 12px;
color: var(--heat);
cursor: pointer;
}
.pd-overview .ov-h .more:hover { text-decoration: underline; }
/* 商品信息卡片内 · 上信息 / 下图片 (堆叠, 图片铺满卡片) */
.ov-main-grid {
display: flex;
flex-direction: column;
gap: 18px;
}
.ov-main-grid > .ov-images-sub {
padding-top: 18px;
border-top: 1px solid var(--border-faint);
}
.ov-info .row {
display: flex; gap: 12px;
margin-bottom: 10px;
font-size: 13px;
}
.ov-info .row:last-child { margin-bottom: 0; }
.ov-info .k {
width: 64px;
flex-shrink: 0;
color: var(--black-alpha-48);
font-size: 12.5px;
}
.ov-info .v {
flex: 1; min-width: 0;
color: var(--accent-black);
line-height: 1.6;
}
.ov-info .v .bullet { display: block; }
.ov-info .v .bullet::before {
content: '·';
color: var(--heat);
margin-right: 6px;
font-weight: 700;
}
/* 商品图片 · 卡片内子 section */
.ov-images-sub .sub-h {
display: flex; align-items: baseline; gap: 6px;
margin-bottom: 10px;
}
.ov-images-sub .sub-h .ti {
font-size: 12.5px; font-weight: 500;
color: var(--black-alpha-72);
}
.ov-images-sub .sub-h .ct {
font-family: var(--font-mono);
font-size: 11px;
color: var(--black-alpha-48);
letter-spacing: .02em;
}
.ov-images-sub .sub-h .more {
margin-left: auto;
font-size: 11.5px;
color: var(--heat);
cursor: pointer;
}
.ov-images-sub .sub-h .more:hover { text-decoration: underline; }
.ov-images-sub .grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
gap: 8px;
}
.ov-images-sub .thumb {
aspect-ratio: 1;
border-radius: var(--r-sm);
overflow: hidden;
cursor: pointer;
}
.ov-images-sub .thumb img { width: 100%; height: 100%; object-fit: cover; }
/* 快速操作 · 2 段:图片生成(3 等比 CTA)+ 视频生成(1 CTA) */
.ov-actions { display: flex; flex-direction: column; }
.ov-actions .qa-section { margin-bottom: 14px; }
.ov-actions .qa-section:last-child { margin-bottom: 0; }
.ov-actions .qa-section-h {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--black-alpha-48);
letter-spacing: .06em;
text-transform: uppercase;
margin-bottom: 8px;
}
.ov-actions .qa-row-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.ov-actions .qa-row-1 .qa-item { width: 100%; }
.qa-item {
display: flex; flex-direction: column; align-items: center; gap: 8px;
padding: 14px 10px;
background: var(--background-lighter);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
cursor: pointer;
font-size: 12.5px;
color: var(--accent-black);
text-align: center;
transition: border-color var(--t-base), background var(--t-base), color var(--t-base);
}
.qa-item:hover { border-color: var(--heat); background: var(--heat-12); color: var(--heat); }
.qa-item .ic {
width: 32px; height: 32px;
display: grid; place-items: center;
color: var(--heat);
flex-shrink: 0;
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-sm);
}
.qa-item:hover .ic { border-color: var(--heat-20); background: var(--surface); }
.qa-item .ic svg { width: 16px; height: 16px; }
.qa-item.primary {
background: var(--heat);
color: var(--accent-white);
border-color: var(--heat);
}
.qa-item.primary .ic { background: rgba(255,255,255,.16); color: var(--accent-white); border-color: rgba(255,255,255,.24); }
.qa-item.primary:hover { color: var(--accent-white); box-shadow: var(--shadow-cta-hover); }
/* 状态 pill 三态(通过/不通过/归档) */
.asset-card .meta .pill.pass {
background: var(--accent-emerald-bg, #e6f4ec);
color: var(--accent-emerald, #1f8a51);
border: 1px solid var(--accent-emerald-bd, #c4e3d1);
cursor: pointer;
}
.asset-card .meta .pill.fail {
background: var(--crimson-bg, #fdebea);
color: var(--accent-crimson, #c43d3d);
border: 1px solid var(--crimson-bd, #f5c2bf);
cursor: pointer;
}
.asset-card .meta .pill.archive {
background: var(--background-lighter);
color: var(--black-alpha-56);
border: 1px solid var(--border-faint);
cursor: pointer;
}
/* ─── Tabs ─── */
.pd-tabs {
display: flex; gap: 4px;
border-bottom: 1px solid var(--border-faint);
margin-bottom: 18px;
}
.pd-tabs .tab {
padding: 10px 14px;
font-size: 13.5px;
color: var(--black-alpha-56);
background: transparent;
border: 0;
border-bottom: 2px solid transparent;
cursor: pointer;
font-family: inherit;
font-weight: 500;
transition: color var(--t-base), border-color var(--t-base);
}
.pd-tabs .tab:hover { color: var(--accent-black); }
.pd-tabs .tab.active {
color: var(--accent-black);
border-bottom-color: var(--heat);
font-weight: 600;
}
.tab-pane { display: none; }
.tab-pane.active { display: block; }
/* ─── AI 素材 工具栏 ─── */
.pd-toolbar {
display: flex; align-items: center; gap: 10px;
margin-bottom: 14px;
flex-wrap: wrap;
}
.pd-toolbar .total {
font-size: 14px; font-weight: 600;
color: var(--accent-black);
}
.pd-toolbar .total .ct {
font-family: var(--font-mono);
font-size: 11.5px;
color: var(--black-alpha-48);
letter-spacing: .02em;
margin-left: 4px;
font-weight: 500;
}
.pd-toolbar .filter {
display: inline-flex; align-items: center; gap: 4px;
height: 30px;
padding: 0 10px;
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-sm);
cursor: pointer;
font-size: 12.5px;
color: var(--black-alpha-72);
}
.pd-toolbar .filter svg { width: 10px; height: 10px; opacity: .6; }
.pd-toolbar .right { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.pd-toolbar .view-tog {
display: inline-flex;
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-sm);
padding: 2px;
}
.pd-toolbar .view-tog button {
width: 28px; height: 26px;
display: grid; place-items: center;
border: 0;
background: transparent;
color: var(--black-alpha-48);
cursor: pointer;
border-radius: 4px;
}
.pd-toolbar .view-tog button.active {
background: var(--accent-black);
color: var(--accent-white);
}
.pd-toolbar .view-tog button svg { width: 13px; height: 13px; }
/* ─── AI 素材 网格 ─── */
.asset-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 12px;
}
.asset-card {
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
overflow: hidden;
cursor: pointer;
transition: border-color var(--t-base), transform var(--t-fast);
}
.asset-card:hover { border-color: var(--black-alpha-24); transform: translateY(-1px); }
.asset-card .thumb {
aspect-ratio: 3/4;
position: relative;
overflow: hidden;
}
.asset-card .thumb .type-pill {
position: absolute; top: 8px; left: 8px;
padding: 3px 8px;
background: rgba(0,0,0,.65);
color: var(--accent-white);
border-radius: var(--r-sm);
font-size: 11px;
font-weight: 500;
backdrop-filter: blur(4px);
}
.asset-card .meta {
padding: 10px 12px;
display: flex; align-items: center; gap: 8px;
}
.asset-card .meta .pill {
padding: 2px 8px;
background: var(--accent-emerald-bg, #e6f4ec);
color: var(--accent-emerald, #1f8a51);
border: 1px solid var(--accent-emerald-bd, #c4e3d1);
border-radius: var(--r-sm);
font-size: 10.5px;
font-weight: 500;
}
.asset-card .meta .date {
margin-left: auto;
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--black-alpha-48);
letter-spacing: .02em;
}
.pd-more {
text-align: center;
padding: 18px 0 32px;
}
.pd-more button {
height: 32px;
padding: 0 18px;
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
color: var(--black-alpha-72);
font-size: 12.5px;
font-family: inherit;
cursor: pointer;
}
.pd-more button:hover { border-color: var(--heat-40); color: var(--heat); }
/* ─── 任务记录 · 表格 ─── */
.task-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 18px;
}
.task-stat {
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
padding: 14px 18px;
}
.task-stat .lbl {
font-family: var(--font-mono);
font-size: 11px;
color: var(--black-alpha-48);
letter-spacing: .04em;
margin-bottom: 6px;
}
.task-stat .v {
font-size: 22px; font-weight: 600;
color: var(--accent-black);
letter-spacing: -.01em;
}
.task-stat .v small {
font-size: 13px;
color: var(--black-alpha-48);
font-weight: 400;
margin-left: 4px;
}
.task-stat.ok .v { color: var(--accent-emerald, #1f8a51); }
.task-stat.gen .v { color: var(--heat); }
.task-stat.err .v { color: var(--accent-crimson, #c43d3d); }
.task-table {
background: var(--surface);
border: 1px solid var(--border-faint);
border-radius: var(--r-md);
overflow: hidden;
}
.task-row {
display: grid;
grid-template-columns: 36px 1.8fr 0.7fr 1fr 1.1fr 1.1fr 0.7fr 100px;
align-items: center;
gap: 12px;
padding: 12px 18px;
border-bottom: 1px solid var(--border-faint);
font-size: 13px;
}
.task-row:last-child { border-bottom: 0; }
.task-row.head {
background: var(--background-lighter);
font-family: var(--font-mono);
font-size: 11px;
color: var(--black-alpha-48);
letter-spacing: .04em;
font-weight: 500;
padding: 10px 18px;
}
.task-row .ph {
width: 36px; height: 36px;
border-radius: var(--r-sm);
flex-shrink: 0;
}
.task-row .nm {
color: var(--accent-black);
font-weight: 500;
display: flex; align-items: center; gap: 8px;
}
.task-row .nm .id-mono {
font-family: var(--font-mono);
font-size: 11px;
color: var(--black-alpha-48);
font-weight: 400;
}
.task-row .qty { color: var(--black-alpha-72); font-family: var(--font-mono); }
.task-row .time {
color: var(--black-alpha-72);
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: .01em;
}
.task-row .dur {
color: var(--black-alpha-56);
font-family: var(--font-mono);
font-size: 12px;
}
.task-row .pill {
display: inline-flex; align-items: center; gap: 5px;
padding: 3px 9px;
border-radius: var(--r-sm);
font-size: 11.5px;
font-weight: 500;
width: fit-content;
}
.task-row .pill .dot {
width: 6px; height: 6px;
border-radius: 50%;
}
.task-row .pill.ok {
background: var(--accent-emerald-bg, #e6f4ec);
color: var(--accent-emerald, #1f8a51);
border: 1px solid var(--accent-emerald-bd, #c4e3d1);
}
.task-row .pill.ok .dot { background: var(--accent-emerald, #1f8a51); }
.task-row .pill.gen {
background: var(--heat-12);
color: var(--heat);
border: 1px solid var(--heat-20);
}
.task-row .pill.gen .dot { background: var(--heat); animation: pulse 1.6s ease-in-out infinite; }
.task-row .pill.err {
background: var(--crimson-bg, #fdebea);
color: var(--accent-crimson, #c43d3d);
border: 1px solid var(--crimson-bd, #f5c2bf);
}
.task-row .pill.err .dot { background: var(--accent-crimson, #c43d3d); }
.task-row .pill.wait {
background: var(--background-lighter);
color: var(--black-alpha-56);
border: 1px solid var(--border-faint);
}
.task-row .pill.wait .dot { background: var(--black-alpha-32); }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .3; }
}
.task-row .status-cell { display: flex; flex-direction: column; gap: 4px; }
.task-row .progress {
width: 100%; height: 3px;
background: var(--black-alpha-12);
border-radius: 2px;
overflow: hidden;
}
.task-row .progress > span {
display: block;
height: 100%;
background: var(--heat);
}
.task-row .ops {
display: inline-flex; gap: 4px;
justify-self: end;
}
.task-row .ops button {
padding: 4px 10px;
height: 26px;
background: transparent;
border: 1px solid var(--border-faint);
border-radius: var(--r-sm);
color: var(--black-alpha-72);
font-size: 11.5px;
font-family: inherit;
cursor: pointer;
transition: border-color var(--t-base), color var(--t-base);
}
.task-row .ops button:hover { border-color: var(--heat-40); color: var(--heat); }
.task-row .ops button.danger:hover { border-color: var(--crimson-bd, #f5c2bf); color: var(--accent-crimson, #c43d3d); }
@media (max-width: 1100px) {
.pd-overview { grid-template-columns: 1fr; }
.ov-actions .qa-grid {
display: grid !important;
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 900px) {
.ov-actions .qa-grid { grid-template-columns: 1fr 1fr; }
.task-stats { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div id="page">
<!-- 顶部 标题 + 状态 -->
<div class="pd-title">
<h1 id="pd-name">补水保湿精华液</h1>
</div>
<!-- 商品信息(含图片) + 快速操作 · 主辅两栏 -->
<div class="pd-overview">
<div class="ov-card ov-main" id="ov-main-card">
<div class="ov-h">
<span class="ti">商品信息</span>
<!-- view 模式: 单个 [编辑信息] -->
<button class="ov-edit ov-edit-single" type="button" id="ov-edit-btn" title="编辑商品信息">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
编辑信息
</button>
<!-- edit 模式: [重置] [取消] [保存] -->
<div class="ov-edit-group">
<button class="ov-edit" type="button" id="ov-reset-btn" title="重置为修改前">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg>
重置
</button>
<button class="ov-edit" type="button" id="ov-cancel-btn">取消</button>
<button class="ov-edit primary" type="button" id="ov-save-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l5 5L20 6"/></svg>
保存
</button>
</div>
</div>
<div class="ov-main-grid">
<div class="ov-info">
<div class="row" data-field="name">
<div class="k">商品名称</div>
<div class="v">
<span class="v-static">补水保湿精华液</span>
<input class="v-edit v-input" type="text" value="补水保湿精华液" maxlength="100">
</div>
</div>
<div class="row" data-field="cat">
<div class="k">品类</div>
<div class="v">
<span class="v-static">美妆个护 / 精华液</span>
<select class="v-edit v-select">
<option>美妆个护 / 精华液</option>
<option>美妆个护</option>
<option>服饰内衣</option>
<option>食品饮料</option>
<option>家居家电</option>
<option>数码 3C</option>
<option>个护清洁</option>
<option>运动户外</option>
<option>母婴亲子</option>
</select>
</div>
</div>
<div class="row" data-field="target">
<div class="k">目标人群</div>
<div class="v">
<span class="v-static">22-32 岁女性、敏感肌、办公室通勤</span>
<input class="v-edit v-input" type="text" value="22-32 岁女性、敏感肌、办公室通勤">
</div>
</div>
<div class="row" data-field="bullets">
<div class="k">核心卖点</div>
<div class="v">
<div class="v-static">
<span class="bullet">透明质酸 + B5,敷完不黏不闷</span>
<span class="bullet">30g 大容量精华液</span>
<span class="bullet">0 香精 0 酒精,敏感肌可用</span>
</div>
<ul class="v-edit v-bullet-list" id="v-bullets-list">
<!-- li.bl-item × N + li.bl-add 由 JS 在进入编辑模式时渲染 -->
</ul>
</div>
</div>
</div>
<div class="ov-images-sub">
<div class="sub-h">
<span class="ti">商品图片</span>
<span class="ct">(6)</span>
</div>
<div class="grid" id="ov-images-grid">
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="thumb placeholder"><span class="ph-frame">1:1</span></div>
<div class="img-upload" id="ov-img-add" title="上传图片">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
</div>
</div>
</div>
</div>
</div>
<div class="ov-card ov-actions">
<div class="ov-h"><span class="ti">快速操作</span></div>
<div class="qa-section">
<div class="qa-section-h">// 图片生成</div>
<div class="qa-row-3">
<div class="qa-item" data-go="model-photo" role="button" tabindex="0">
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21v-2a4 4 0 014-4h8a4 4 0 014 4v2"/></svg></span>
模特上身图
</div>
<div class="qa-item" data-go="platform-cover" role="button" tabindex="0">
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 3v18"/></svg></span>
平台套图
</div>
<div class="qa-item" data-go="image-optimize" role="button" tabindex="0">
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M3 12h18M5 5l14 14M5 19l14-14"/></svg></span>
图片优化
</div>
</div>
</div>
<div class="qa-section">
<div class="qa-section-h">// 视频生成</div>
<div class="qa-row-1">
<div class="qa-item primary" data-go="projects-new" role="button" tabindex="0">
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="14" height="12" rx="2"/><path d="M16 10l6-3v10l-6-3z"/></svg></span>
生成视频
</div>
</div>
</div>
</div>
</div>
<!-- Tabs -->
<div class="pd-tabs">
<button class="tab active" type="button" data-tab="assets">AI 生成素材</button>
<button class="tab" type="button" data-tab="videos">视频项目</button>
<button class="tab" type="button" data-tab="tasks">任务记录</button>
</div>
<!-- ===== AI 生成素材 ===== -->
<div class="tab-pane active" data-pane="assets">
<div class="pd-toolbar">
<div class="total">全部 AI 素材 <span class="ct">(32)</span></div>
<button class="filter" type="button">
全部类型
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<button class="filter" type="button">
全部状态
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<div class="right">
<div class="view-tog">
<button type="button" class="active" title="网格视图">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
</button>
<button type="button" title="列表视图">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
</button>
</div>
<button class="filter" type="button">
最新生成
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
</div>
</div>
<div class="asset-grid">
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">模特上身图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">模特上身图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">模特上身图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill fail" data-status="fail" title="点击切换状态">不通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">模特上身图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">模特上身图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill archive" data-status="archive" title="点击切换状态">归档</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">平台套图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">平台套图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">平台套图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill fail" data-status="fail" title="点击切换状态">不通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">平台套图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill archive" data-status="archive" title="点击切换状态">归档</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">平台套图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">三视图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 15:30</span></div></div>
<div class="asset-card"><div class="thumb placeholder"><span class="type-pill">三视图</span><span class="ph-frame">3:4</span></div><div class="meta"><span class="pill archive" data-status="archive" title="点击切换状态">归档</span><span class="date">2026-05-19 15:30</span></div></div>
</div>
<div class="pd-more"><button type="button">加载更多</button></div>
</div>
<!-- ===== 视频项目 ===== -->
<div class="tab-pane" data-pane="videos">
<div class="pd-toolbar">
<div class="total">该商品视频项目 <span class="ct">(4)</span></div>
<button class="filter" type="button">
全部状态
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<div class="right">
<button class="filter" type="button">
最新导出
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
</div>
</div>
<div class="asset-grid">
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v3</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-20 12:08</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v2</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 10:24</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">熬夜急救 · v1</span></div><div class="meta"><span class="pill archive" data-status="archive" title="点击切换状态">归档</span><span class="date">2026-05-18 21:42</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v1</span></div><div class="meta"><span class="pill fail" data-status="fail" title="点击切换状态">不通过</span><span class="date">2026-05-17 16:00</span></div></div>
</div>
<div class="pd-more"><button type="button">加载更多</button></div>
</div>
<!-- ===== 任务记录 ===== -->
<div class="tab-pane" data-pane="tasks">
<!-- 顶部统计概览 -->
<div class="task-stats">
<div class="task-stat">
<div class="lbl">// TOTAL</div>
<div class="v">12 <small>个任务</small></div>
</div>
<div class="task-stat ok">
<div class="lbl">// SUCCESS</div>
<div class="v">9</div>
</div>
<div class="task-stat gen">
<div class="lbl">// RUNNING</div>
<div class="v">2</div>
</div>
<div class="task-stat err">
<div class="lbl">// FAILED</div>
<div class="v">1</div>
</div>
</div>
<!-- 工具栏 -->
<div class="pd-toolbar">
<div class="total">任务记录 <span class="ct">(12)</span></div>
<button class="filter" type="button">
全部类型
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<button class="filter" type="button">
全部状态
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<button class="filter" type="button">
近 7 天
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<div class="right">
<button class="filter" type="button">
提交时间倒序
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
</div>
</div>
<!-- 任务表格 -->
<div class="task-table">
<div class="task-row head">
<span></span>
<span>任务 / 编号</span>
<span>数量</span>
<span>状态</span>
<span>提交时间</span>
<span>完成时间</span>
<span>耗时</span>
<span style="justify-self:end">操作</span>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">视频素材 <span class="id-mono">// T-2026-0519-0007</span></div>
<div class="qty">1 个</div>
<div class="status-cell">
<span class="pill gen"><span class="dot"></span>生成中 60%</span>
<span class="progress"><span style="width:60%"></span></span>
</div>
<div class="time">2026-05-19 16:00</div>
<div class="time"></div>
<div class="dur"></div>
<div class="ops">
<button type="button">查看</button>
<button type="button" class="danger">取消</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">模特上身图 <span class="id-mono">// T-2026-0519-0006</span></div>
<div class="qty">3 张</div>
<div class="status-cell">
<span class="pill wait"><span class="dot"></span>排队中</span>
</div>
<div class="time">2026-05-19 15:58</div>
<div class="time"></div>
<div class="dur"></div>
<div class="ops">
<button type="button">查看</button>
<button type="button" class="danger">取消</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">模特上身图 <span class="id-mono">// T-2026-0519-0005</span></div>
<div class="qty">5 张</div>
<div class="status-cell">
<span class="pill ok"><span class="dot"></span>已完成</span>
</div>
<div class="time">2026-05-19 15:30</div>
<div class="time">2026-05-19 15:32</div>
<div class="dur">2m 14s</div>
<div class="ops">
<button type="button">查看</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">平台套图 <span class="id-mono">// T-2026-0519-0004</span></div>
<div class="qty">4 张</div>
<div class="status-cell">
<span class="pill ok"><span class="dot"></span>已完成</span>
</div>
<div class="time">2026-05-19 14:20</div>
<div class="time">2026-05-19 14:23</div>
<div class="dur">3m 02s</div>
<div class="ops">
<button type="button">查看</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">模特上身图 <span class="id-mono">// T-2026-0519-0003</span></div>
<div class="qty">4 张</div>
<div class="status-cell">
<span class="pill ok"><span class="dot"></span>已完成</span>
</div>
<div class="time">2026-05-19 13:10</div>
<div class="time">2026-05-19 13:13</div>
<div class="dur">2m 50s</div>
<div class="ops">
<button type="button">查看</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">三视图 <span class="id-mono">// T-2026-0519-0002</span></div>
<div class="qty">3 张</div>
<div class="status-cell">
<span class="pill err"><span class="dot"></span>失败</span>
</div>
<div class="time">2026-05-19 12:00</div>
<div class="time">2026-05-19 12:01</div>
<div class="dur">30s</div>
<div class="ops">
<button type="button">重试</button>
<button type="button">日志</button>
</div>
</div>
<div class="task-row">
<div class="ph placeholder"></div>
<div class="nm">平台套图 <span class="id-mono">// T-2026-0518-0001</span></div>
<div class="qty">6 张</div>
<div class="status-cell">
<span class="pill ok"><span class="dot"></span>已完成</span>
</div>
<div class="time">2026-05-18 18:42</div>
<div class="time">2026-05-18 18:46</div>
<div class="dur">4m 10s</div>
<div class="ops">
<button type="button">查看</button>
</div>
</div>
</div>
<div class="pd-more"><button type="button">加载更多</button></div>
</div>
</div>
<script src="assets/shell.js"></script>
<script>
// 从 URL ?product= 读出商品名,注入 crumb / h1 / 商品名字段
const _urlProductName = (function () {
try {
const q = new URLSearchParams(location.search);
const v = q.get('product') || q.get('name');
return v ? decodeURIComponent(v) : '';
} catch (e) { return ''; }
})();
const _productDisplayName = _urlProductName || '补水保湿精华液';
Shell.render({
active: 'products',
crumbs: [
{ label: '工作台', href: 'index.html' },
{ label: '商品库', href: 'products.html' },
{ label: _productDisplayName }
]
});
if (_urlProductName) {
const h1 = document.getElementById('pd-name');
if (h1) h1.textContent = _urlProductName;
const nameRow = document.querySelector('[data-field="name"] .v-static');
if (nameRow) nameRow.textContent = _urlProductName;
const nameInput = document.querySelector('[data-field="name"] .v-input');
if (nameInput) nameInput.value = _urlProductName;
}
// 快速操作 · 跳转至对应工作台/wizard 并携带商品名
(function bindQuickActions() {
const productName = (document.querySelector('.pd-title h1, .pd-title, .ov-h .ti, h1') || {}).textContent || '';
const crumbName = (document.querySelector('.crumb-current') || {}).textContent
|| (document.querySelectorAll('.crumb-item').length ? document.querySelectorAll('.crumb-item')[document.querySelectorAll('.crumb-item').length-1].textContent : '')
|| '补水保湿精华液';
const name = (crumbName || productName || '').trim();
document.querySelectorAll('.qa-item[data-go]').forEach(item => {
item.style.cursor = 'pointer';
let go = item.dataset.go;
// 图片优化暂复用 model-photo.html?mode=tri (后端实际生成人物 / 商品三视图)
let url;
if (go === 'image-optimize') {
url = 'model-photo.html?mode=tri&t=' + Date.now() + '&product=' + encodeURIComponent(name);
} else {
url = go + '.html?t=' + Date.now() + '&product=' + encodeURIComponent(name);
}
item.addEventListener('click', () => { location.href = url; });
item.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); location.href = url; }
});
});
})();
// 任务记录 · 「查看」按钮 / 行点击 → 跳对应工作台
(function bindTaskRowJump() {
const TYPE_MAP = {
'模特上身图': 'model-photo.html',
'平台套图': 'platform-cover.html',
'视频素材': 'projects-new.html',
'视频': 'projects-new.html',
'三视图': 'model-photo.html?mode=tri',
};
const productName = (document.getElementById('pd-name')?.textContent || '').trim();
document.querySelectorAll('.task-table .task-row:not(.head)').forEach(row => {
const nameCell = row.querySelector('.nm');
if (!nameCell) return;
// 任务类型 = nm 的第一段文本(去掉 id-mono)
const type = nameCell.firstChild?.nodeValue?.trim() || '';
let url = TYPE_MAP[type];
if (!url) return;
url += (url.includes('?') ? '&' : '?') + 't=' + Date.now()
+ (productName ? '&product=' + encodeURIComponent(productName) : '');
row.style.cursor = 'pointer';
row.addEventListener('click', e => {
if (e.target.closest('.ops button')) return;
location.href = url;
});
// 「查看」按钮
row.querySelectorAll('.ops button').forEach(btn => {
if (btn.textContent.trim() === '查看') {
btn.addEventListener('click', e => {
e.stopPropagation();
location.href = url;
});
}
});
});
})();
// 状态 pill · 三态循环(通过 → 不通过 → 归档 → 通过)
(function bindStatusPills() {
const labels = { pass: '通过', fail: '不通过', archive: '归档' };
const order = ['pass', 'fail', 'archive'];
document.addEventListener('click', e => {
const pill = e.target.closest('.asset-card .meta .pill[data-status]');
if (!pill) return;
e.stopPropagation();
const cur = pill.dataset.status;
const next = order[(order.indexOf(cur) + 1) % order.length];
pill.dataset.status = next;
pill.classList.remove('pass', 'fail', 'archive');
pill.classList.add(next);
pill.textContent = labels[next];
Shell.toast('状态已更新', labels[next]);
});
})();
// Tab 切换
document.querySelectorAll('.pd-tabs .tab').forEach(t => {
t.onclick = () => {
document.querySelectorAll('.pd-tabs .tab').forEach(x => x.classList.remove('active'));
t.classList.add('active');
const target = t.dataset.tab;
document.querySelectorAll('.tab-pane').forEach(p => {
p.classList.toggle('active', p.dataset.pane === target);
});
};
});
// 视图切换
document.querySelectorAll('.view-tog button').forEach(b => {
b.onclick = () => {
const wrap = b.closest('.view-tog');
wrap.querySelectorAll('button').forEach(x => x.classList.remove('active'));
b.classList.add('active');
};
});
// 编辑商品信息 · 在卡片内 inline 切换 view ↔ edit
(function initEdit() {
const card = document.getElementById('ov-main-card');
const editBtn = document.getElementById('ov-edit-btn');
const cancelBtn = document.getElementById('ov-cancel-btn');
const saveBtn = document.getElementById('ov-save-btn');
if (!card || !editBtn || !cancelBtn || !saveBtn) return;
// 同时同步顶部 h1 标题 — 商品名称改动后,顶部大标题也跟着更新
const pdName = document.getElementById('pd-name');
function escapeHtml(s) {
return s.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
const X_SVG = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 4l8 8M12 4l-8 8"/></svg>';
// bullet-list 编辑模式 · 操作 helpers
function renumberBullets(list) {
[...list.querySelectorAll('.bl-item .num')].forEach((n, i) => n.textContent = i + 1);
}
function bindBulletX(x, list) {
x.addEventListener('click', () => {
x.closest('.bl-item').remove();
renumberBullets(list);
});
}
function makeBulletLi(text) {
const li = document.createElement('li');
li.className = 'bl-item';
const safe = (text || '').replace(/"/g, '&quot;');
li.innerHTML = `<span class="num"></span><input class="bl-input bl-edit" type="text" value="${safe}" placeholder="卖点内容"><span class="bl-x" title="删除">${X_SVG}</span>`;
return li;
}
function addBulletItem(list, text) {
const li = makeBulletLi(text);
list.querySelector('.bl-add').before(li);
bindBulletX(li.querySelector('.bl-x'), list);
renumberBullets(list);
}
function renderBulletEditor(list, items) {
list.innerHTML = '';
// 已有项 (每条都是 input 可直接修改)
items.forEach(text => list.appendChild(makeBulletLi(text)));
// 添加行
const addLi = document.createElement('li');
addLi.className = 'bl-add';
addLi.innerHTML = `<span class="num">+</span><input class="bl-input" placeholder="添加新卖点 · 回车确认">`;
list.appendChild(addLi);
// 绑定已有项的删除
list.querySelectorAll('.bl-item .bl-x').forEach(x => bindBulletX(x, list));
// 绑定回车追加
const addInput = addLi.querySelector('.bl-input');
addInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
const v = addInput.value.trim();
if (!v) return;
addBulletItem(list, v);
addInput.value = '';
addInput.focus();
}
});
renumberBullets(list);
}
// 进入编辑模式: 把当前静态值灌进 input
function enterEdit() {
card.querySelectorAll('[data-field]').forEach(row => {
const stat = row.querySelector('.v-static');
const inp = row.querySelector('.v-edit');
if (!stat || !inp) return;
if (row.dataset.field === 'bullets') {
const items = [...stat.querySelectorAll('.bullet')].map(b => b.textContent.trim());
renderBulletEditor(inp, items);
} else if (inp.tagName === 'SELECT') {
const cur = stat.textContent.trim();
[...inp.options].forEach((o, i) => { if (o.textContent.trim() === cur) inp.selectedIndex = i; });
} else {
inp.value = stat.textContent.trim();
}
});
card.classList.add('editing');
}
function exitEdit() {
card.classList.remove('editing');
}
function save() {
card.querySelectorAll('[data-field]').forEach(row => {
const stat = row.querySelector('.v-static');
const inp = row.querySelector('.v-edit');
if (!stat || !inp) return;
if (row.dataset.field === 'bullets') {
// 从 .bl-item .bl-edit input 读值 (允许修改已有条目)
const items = [...inp.querySelectorAll('.bl-item .bl-edit')]
.map(t => t.value.trim()).filter(Boolean);
stat.innerHTML = items.map(s => `<span class="bullet">${escapeHtml(s)}</span>`).join('');
} else {
const val = (inp.tagName === 'SELECT') ? inp.options[inp.selectedIndex].textContent : inp.value;
stat.textContent = val.trim();
if (row.dataset.field === 'name' && pdName) {
pdName.textContent = val.trim() || pdName.textContent;
}
}
});
card.classList.remove('editing');
Shell.toast('已保存', '商品信息已更新');
}
// 重置 · 在编辑模式下,把所有输入框回退到当前静态值 (相当于重新进入 edit)
function resetEdit() {
card.querySelectorAll('[data-field]').forEach(row => {
const stat = row.querySelector('.v-static');
const inp = row.querySelector('.v-edit');
if (!stat || !inp) return;
if (row.dataset.field === 'bullets') {
const items = [...stat.querySelectorAll('.bullet')].map(b => b.textContent.trim());
renderBulletEditor(inp, items);
} else if (inp.tagName === 'SELECT') {
const cur = stat.textContent.trim();
[...inp.options].forEach((o, i) => { if (o.textContent.trim() === cur) inp.selectedIndex = i; });
} else {
inp.value = stat.textContent.trim();
}
});
Shell.toast('已重置');
}
const resetBtn = document.getElementById('ov-reset-btn');
// 防御性: 先清空可能存在的 inline onclick, 再用 addEventListener 绑定
editBtn.onclick = null;
cancelBtn.onclick = null;
saveBtn.onclick = null;
if (resetBtn) resetBtn.onclick = null;
editBtn.addEventListener('click', (e) => { e.preventDefault(); enterEdit(); });
cancelBtn.addEventListener('click', (e) => { e.preventDefault(); exitEdit(); });
saveBtn.addEventListener('click', (e) => { e.preventDefault(); save(); });
if (resetBtn) resetBtn.addEventListener('click', (e) => { e.preventDefault(); resetEdit(); });
// 编辑模式下,点缩略图 → 删除
const grid = document.getElementById('ov-images-grid');
if (grid) {
grid.addEventListener('click', e => {
if (!card.classList.contains('editing')) return;
const thumb = e.target.closest('.thumb');
if (thumb && !thumb.classList.contains('img-upload')) {
thumb.remove();
}
});
// [+] 上传占位
const addBtn = document.getElementById('ov-img-add');
if (addBtn) addBtn.onclick = () => Shell.toast('上传图片', '请选择本地图片 (占位)');
}
})();
// 从 create 跳来时显示 toast
if (location.search.includes('id=new')) {
setTimeout(() => Shell.toast('商品已创建', '开始创建 AI 资产'), 200);
}
</script>
</body>
</html>