优化视频复刻和商品内容大小
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
max-width: none;
|
||||
box-sizing: border-box;
|
||||
margin: -24px -28px -60px;
|
||||
padding: 52px clamp(40px, 3.2vw, 68px) 48px;
|
||||
padding: 28px clamp(24px, 2.2vw, 40px) 36px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -25,33 +25,32 @@
|
||||
}
|
||||
|
||||
.library-page .lib-head {
|
||||
min-height: 76px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 30px;
|
||||
margin-bottom: 34px;
|
||||
gap: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.library-page .lib-head h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 32px;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4px;
|
||||
font-size: 22px;
|
||||
line-height: 1.25;
|
||||
font-weight: 600;
|
||||
letter-spacing: -.02em;
|
||||
color: var(--accent-black);
|
||||
}
|
||||
.library-page .lib-head p { margin: 0; color: var(--lib-muted); font-size: 15px; }
|
||||
.library-page .lib-head p { margin: 0; color: var(--lib-muted); font-size: 13px; }
|
||||
.library-page .lib-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
|
||||
.library-page .lib-ghost {
|
||||
min-width: 132px;
|
||||
height: 46px;
|
||||
padding: 0 18px;
|
||||
min-width: 108px;
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 9px;
|
||||
border: 1px solid rgba(28, 34, 43, 0.12);
|
||||
border-radius: 11px;
|
||||
border-radius: 8px;
|
||||
color: var(--accent-black);
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
font: inherit;
|
||||
@@ -59,17 +58,17 @@
|
||||
}
|
||||
.library-page .lib-ghost:hover { background: rgba(34, 42, 54, 0.10); }
|
||||
.library-page .lib-ghost.is-on { color: #fff; border-color: var(--lib-black); background: var(--lib-black); }
|
||||
.library-page .lib-ghost svg { width: 19px; height: 19px; }
|
||||
.library-page .lib-ghost svg { width: 16px; height: 16px; }
|
||||
|
||||
.library-page .lib-toolbar {
|
||||
min-height: 66px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
padding: 10px 14px;
|
||||
gap: 12px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(34, 42, 54, 0.09);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: var(--lib-wash);
|
||||
box-shadow: 0 7px 17px rgba(20, 27, 38, 0.07);
|
||||
}
|
||||
@@ -207,21 +206,21 @@
|
||||
.library-page .lib-select-option:hover { background: #e8f2ff; }
|
||||
.library-page .lib-select-option.selected { color: var(--klein); background: #f3f7ff; font-weight: 600; }
|
||||
|
||||
.library-page .lib-section { margin: 32px 0 17px; }
|
||||
.library-page .lib-section h2 { margin: 0; font-size: 21px; font-weight: 600; color: var(--accent-black); }
|
||||
.library-page .lib-section { margin: 22px 0 12px; }
|
||||
.library-page .lib-section h2 { margin: 0; font-size: 16px; font-weight: 600; color: var(--accent-black); }
|
||||
.library-page .lib-section p { margin: 5px 0 0; color: var(--lib-muted); font-size: 13px; }
|
||||
|
||||
.library-page .lib-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.library-page .lib-card {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--lib-line);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
box-shadow: var(--lib-shadow);
|
||||
cursor: pointer;
|
||||
@@ -235,7 +234,7 @@
|
||||
}
|
||||
.library-page .lib-thumb {
|
||||
position: relative;
|
||||
aspect-ratio: 1 / 0.78;
|
||||
aspect-ratio: 4 / 3;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -253,10 +252,10 @@
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
.library-page .lib-meta { padding: 15px 16px 17px; }
|
||||
.library-page .lib-meta { padding: 10px 12px 12px; }
|
||||
.library-page .lib-meta h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
margin: 0 0 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-black);
|
||||
overflow: hidden;
|
||||
@@ -276,14 +275,14 @@
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 13px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.library-page .lib-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
padding: 0 9px;
|
||||
border-radius: 7px;
|
||||
height: 22px;
|
||||
padding: 0 7px;
|
||||
border-radius: 6px;
|
||||
color: #4c5360;
|
||||
background: rgba(34, 42, 54, 0.08);
|
||||
font-size: 11px;
|
||||
@@ -430,15 +429,12 @@
|
||||
.library-page .lib-sel-confirm:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
.library-page .list-pager { margin-top: 20px; }
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
.library-page .lib-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.library-page .lib-sel-bar { left: 50%; }
|
||||
.library-page .lib-toolbar { flex-wrap: wrap; }
|
||||
}
|
||||
@media (max-width: 860px) {
|
||||
.library-page .lib-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.library-page .lib-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
|
||||
.library-page .lib-search { width: 100%; }
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
max-width: none;
|
||||
box-sizing: border-box;
|
||||
margin: -24px -28px -60px;
|
||||
padding: 52px clamp(40px, 3.2vw, 68px) 48px;
|
||||
padding: 28px clamp(24px, 2.2vw, 40px) 36px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -24,22 +24,21 @@
|
||||
}
|
||||
|
||||
.models-page .ml-head {
|
||||
min-height: 76px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 30px;
|
||||
margin-bottom: 34px;
|
||||
gap: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.models-page .ml-head h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 32px;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4px;
|
||||
font-size: 22px;
|
||||
line-height: 1.25;
|
||||
font-weight: 600;
|
||||
letter-spacing: -.02em;
|
||||
color: var(--accent-black);
|
||||
}
|
||||
.models-page .ml-head p { margin: 0; color: var(--ml-muted); font-size: 15px; }
|
||||
.models-page .ml-head p { margin: 0; color: var(--ml-muted); font-size: 13px; }
|
||||
.models-page .ml-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
|
||||
.models-page .ml-ghost,
|
||||
.models-page .ml-primary {
|
||||
@@ -52,73 +51,73 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
.models-page .ml-ghost {
|
||||
min-width: 132px;
|
||||
height: 46px;
|
||||
padding: 0 18px;
|
||||
min-width: 108px;
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(28, 34, 43, 0.12);
|
||||
border-radius: 11px;
|
||||
border-radius: 8px;
|
||||
color: var(--accent-black);
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
}
|
||||
.models-page .ml-ghost:hover { background: rgba(34, 42, 54, 0.10); }
|
||||
.models-page .ml-ghost.is-on { color: #fff; border-color: var(--ml-black); background: var(--ml-black); }
|
||||
.models-page .ml-primary {
|
||||
min-width: 158px;
|
||||
height: 50px;
|
||||
padding: 0 22px;
|
||||
border-radius: 11px;
|
||||
min-width: 124px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
background: var(--klein);
|
||||
box-shadow: 0 9px 18px rgba(0, 47, 167, 0.18);
|
||||
box-shadow: 0 6px 14px rgba(0, 47, 167, 0.18);
|
||||
}
|
||||
.models-page .ml-primary:hover { transform: translateY(-2px); background: var(--klein-hover); }
|
||||
.models-page .ml-primary:disabled { opacity: 0.38; transform: none; box-shadow: none; cursor: not-allowed; }
|
||||
.models-page .ml-ghost svg,
|
||||
.models-page .ml-primary svg { width: 19px; height: 19px; }
|
||||
.models-page .ml-primary svg { width: 16px; height: 16px; }
|
||||
|
||||
.models-page .ml-toolbar {
|
||||
min-height: 66px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
padding: 10px 14px;
|
||||
gap: 12px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(34, 42, 54, 0.09);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: var(--ml-wash);
|
||||
box-shadow: 0 7px 17px rgba(20, 27, 38, 0.07);
|
||||
}
|
||||
.models-page .ml-seg { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.models-page .ml-seg-btn {
|
||||
height: 38px;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 8px;
|
||||
color: #50555d;
|
||||
background: transparent;
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.models-page .ml-seg-btn:hover { color: var(--accent-black); }
|
||||
.models-page .ml-seg-btn.active { color: #fff; background: var(--ml-black); }
|
||||
.models-page .ml-note { color: var(--ml-muted); font-size: 12px; }
|
||||
|
||||
.models-page .ml-section { margin: 32px 0 17px; }
|
||||
.models-page .ml-section h2 { margin: 0; font-size: 21px; font-weight: 600; color: var(--accent-black); }
|
||||
.models-page .ml-section { margin: 22px 0 12px; }
|
||||
.models-page .ml-section h2 { margin: 0; font-size: 16px; font-weight: 600; color: var(--accent-black); }
|
||||
.models-page .ml-section p { margin: 5px 0 0; color: var(--ml-muted); font-size: 13px; }
|
||||
|
||||
.models-page .ml-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.models-page .ml-card {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--ml-line);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
box-shadow: var(--ml-shadow);
|
||||
cursor: pointer;
|
||||
@@ -147,10 +146,10 @@
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.models-page .ml-meta { padding: 12px 13px 14px; }
|
||||
.models-page .ml-meta { padding: 8px 10px 10px; }
|
||||
.models-page .ml-meta h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
margin: 0 0 3px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -162,14 +161,14 @@
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 13px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.models-page .ml-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
padding: 0 9px;
|
||||
border-radius: 7px;
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
border-radius: 6px;
|
||||
color: #4c5360;
|
||||
background: rgba(34, 42, 54, 0.08);
|
||||
font-size: 11px;
|
||||
@@ -274,14 +273,11 @@
|
||||
.models-page .ml-sel-confirm { color: #fff; background: #002fa7; }
|
||||
.models-page .ml-sel-confirm:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
.models-page .ml-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.models-page .ml-sel-bar { left: 50%; }
|
||||
}
|
||||
@media (max-width: 860px) {
|
||||
.models-page .ml-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.models-page .ml-grid { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); }
|
||||
}
|
||||
|
||||
/* 模特详情弹窗:参考视频项目角色详情结构 */
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
max-width: none;
|
||||
box-sizing: border-box;
|
||||
margin: -24px -28px -60px;
|
||||
padding: 52px clamp(40px, 3.2vw, 68px) 48px;
|
||||
padding: 28px clamp(24px, 2.2vw, 40px) 36px;
|
||||
|
||||
.pl-inner {
|
||||
width: min(1560px, 100%);
|
||||
@@ -20,17 +20,16 @@
|
||||
}
|
||||
|
||||
.pl-head {
|
||||
min-height: 76px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 30px;
|
||||
margin-bottom: 34px;
|
||||
gap: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.pl-head h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 32px;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4px;
|
||||
font-size: 22px;
|
||||
line-height: 1.25;
|
||||
font-weight: 600;
|
||||
letter-spacing: -.02em;
|
||||
color: var(--accent-black);
|
||||
@@ -38,7 +37,7 @@
|
||||
.pl-head p {
|
||||
margin: 0;
|
||||
color: var(--pl-muted);
|
||||
font-size: 15px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.pl-actions {
|
||||
display: flex;
|
||||
@@ -58,11 +57,11 @@
|
||||
transition: transform 180ms ease, background-color 180ms ease, box-shadow 180ms ease;
|
||||
}
|
||||
.pl-ghost {
|
||||
min-width: 132px;
|
||||
height: 46px;
|
||||
padding: 0 18px;
|
||||
min-width: 108px;
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(28, 34, 43, 0.12);
|
||||
border-radius: 11px;
|
||||
border-radius: 8px;
|
||||
color: var(--accent-black);
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
}
|
||||
@@ -73,36 +72,36 @@
|
||||
background: var(--pl-black);
|
||||
}
|
||||
.pl-primary {
|
||||
min-width: 158px;
|
||||
height: 50px;
|
||||
padding: 0 22px;
|
||||
border-radius: 11px;
|
||||
min-width: 124px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
background: var(--klein);
|
||||
box-shadow: 0 9px 18px rgba(0, 47, 167, 0.18);
|
||||
box-shadow: 0 6px 14px rgba(0, 47, 167, 0.18);
|
||||
}
|
||||
.pl-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
background: var(--klein-hover);
|
||||
}
|
||||
.pl-ghost svg,
|
||||
.pl-primary svg { width: 19px; height: 19px; flex: 0 0 auto; }
|
||||
.pl-primary svg { width: 16px; height: 16px; flex: 0 0 auto; }
|
||||
|
||||
.pl-toolbar {
|
||||
min-height: 66px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
padding: 10px 14px;
|
||||
gap: 12px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(34, 42, 54, 0.09);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: var(--pl-wash);
|
||||
box-shadow: 0 7px 17px rgba(20, 27, 38, 0.07);
|
||||
}
|
||||
.pl-search {
|
||||
width: 250px;
|
||||
height: 40px;
|
||||
width: 220px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -140,7 +139,7 @@
|
||||
.pl-select[data-key="cat"] { width: 168px; flex-basis: 168px; }
|
||||
.pl-select-trigger {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -225,7 +224,7 @@
|
||||
.pl-select-option.selected .pl-cat-count { color: var(--klein); }
|
||||
|
||||
.pl-clear {
|
||||
height: 40px;
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
@@ -243,8 +242,8 @@
|
||||
gap: 8px;
|
||||
}
|
||||
.pl-view-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -264,11 +263,11 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 22px;
|
||||
margin: 32px 0 17px;
|
||||
margin: 22px 0 12px;
|
||||
}
|
||||
.pl-section h2 {
|
||||
margin: 0;
|
||||
font-size: 21px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-black);
|
||||
}
|
||||
@@ -280,8 +279,8 @@
|
||||
|
||||
.pl-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.pl-card {
|
||||
position: relative;
|
||||
@@ -290,7 +289,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--pl-line);
|
||||
border-radius: 13px;
|
||||
border-radius: 10px;
|
||||
background: rgba(34, 42, 54, 0.05);
|
||||
box-shadow: var(--pl-shadow);
|
||||
cursor: pointer;
|
||||
@@ -308,7 +307,7 @@
|
||||
|
||||
.pl-thumb {
|
||||
position: relative;
|
||||
aspect-ratio: 1 / 0.78;
|
||||
aspect-ratio: 4 / 3;
|
||||
overflow: hidden;
|
||||
background: #e6e7ea;
|
||||
border: 0;
|
||||
@@ -332,10 +331,10 @@
|
||||
}
|
||||
.pl-thumb .ph-frame { display: none; }
|
||||
|
||||
.pl-meta { padding: 15px 16px 17px; }
|
||||
.pl-meta { padding: 10px 12px 12px; }
|
||||
.pl-meta h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
margin: 0 0 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-black);
|
||||
overflow: hidden;
|
||||
@@ -355,15 +354,15 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-top: 13px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pl-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
padding: 0 9px;
|
||||
height: 22px;
|
||||
padding: 0 7px;
|
||||
border: 0;
|
||||
border-radius: 7px;
|
||||
border-radius: 6px;
|
||||
color: #4c5360;
|
||||
background: rgba(34, 42, 54, 0.08);
|
||||
font: inherit;
|
||||
@@ -406,18 +405,18 @@
|
||||
.pl-grid.list { grid-template-columns: 1fr; }
|
||||
.pl-grid.list .pl-card {
|
||||
display: grid;
|
||||
grid-template-columns: 190px minmax(0, 1fr);
|
||||
grid-template-columns: 148px minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
}
|
||||
.pl-grid.list .pl-thumb {
|
||||
width: 190px;
|
||||
height: 130px;
|
||||
width: 148px;
|
||||
height: 100px;
|
||||
aspect-ratio: auto;
|
||||
}
|
||||
.pl-grid.list .pl-meta {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
padding: 18px 22px;
|
||||
padding: 12px 16px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -462,14 +461,11 @@
|
||||
.list-pager { margin-top: 20px; }
|
||||
}
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
.products-page .pl-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.products-page { margin: -28px -24px -48px; }
|
||||
}
|
||||
@media (max-width: 860px) {
|
||||
.products-page .pl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.products-page .pl-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
|
||||
.products-page .pl-toolbar { flex-wrap: wrap; }
|
||||
.products-page .pl-search { width: 100%; }
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [page, setPage] = useState(1);
|
||||
const [tab, setTab] = useState("");
|
||||
const [selected, setSelected] = useState<Set<string>>(new Set());
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [preview, setPreview] = useState<{ src: string; name: string } | null>(null);
|
||||
|
||||
@@ -42,6 +43,11 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
if (res.results.length === 0 && res.count > 0 && page > 1) { setPage((p) => Math.max(1, p - 1)); return; }
|
||||
setAssets(res.results);
|
||||
setCount(res.count);
|
||||
if (!silent) setSelected(new Set());
|
||||
else {
|
||||
const ids = new Set(res.results.map((a) => a.id));
|
||||
setSelected((s) => new Set([...s].filter((id) => ids.has(id))));
|
||||
}
|
||||
} catch {
|
||||
if (!silent) notify("error", "加载审核队列失败");
|
||||
} finally {
|
||||
@@ -71,15 +77,27 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
};
|
||||
}, [load]);
|
||||
|
||||
async function retry(id: string) {
|
||||
if (busy) return;
|
||||
function toggleOne(id: string) {
|
||||
setSelected((s) => {
|
||||
const next = new Set(s);
|
||||
if (next.has(id)) next.delete(id); else next.add(id);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
function toggleAll() {
|
||||
setSelected((s) => (s.size === assets.length ? new Set() : new Set(assets.map((a) => a.id))));
|
||||
}
|
||||
|
||||
async function submit(ids: string[]) {
|
||||
if (busy || ids.length === 0) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
await adminApi.submitReviews([id]);
|
||||
notify("success", "已重新送审");
|
||||
const res = await adminApi.submitReviews(ids);
|
||||
notify("success", `已提交 ${res.submitted} 个资产送审`);
|
||||
setSelected(new Set());
|
||||
await load({ silent: true });
|
||||
} catch {
|
||||
notify("error", "重试失败");
|
||||
notify("error", "送审失败");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -113,7 +131,7 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
<div className="page-head">
|
||||
<div>
|
||||
<h1>资产审核</h1>
|
||||
<div className="sub"><span className="mono">{count} 个真人资产</span> · 未送审会自动提交火山审核,无需手动点送审</div>
|
||||
<div className="sub"><span className="mono">{count} 个真人资产</span> · 未送审会自动提交火山审核,也可勾选批量送审</div>
|
||||
</div>
|
||||
<div className="actions">
|
||||
<button className="btn" type="button" disabled={busy} onClick={() => void poll()}>
|
||||
@@ -139,6 +157,7 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
<table className="t admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="col-check"><input type="checkbox" checked={selected.size === assets.length && assets.length > 0} onChange={toggleAll} aria-label="全选" /></th>
|
||||
<th>预览</th>
|
||||
<th>团队</th>
|
||||
<th>名称</th>
|
||||
@@ -149,6 +168,7 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
<tbody>
|
||||
{assets.map((a) => (
|
||||
<tr key={a.id}>
|
||||
<td className="col-check"><input type="checkbox" checked={selected.has(a.id)} onChange={() => toggleOne(a.id)} aria-label="选择" /></td>
|
||||
<td>
|
||||
{a.preview_url
|
||||
? (
|
||||
@@ -171,11 +191,11 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
{a.review_status === "failed" && a.review_error && <span className="admin-review-err mono" title={a.review_error}>!</span>}
|
||||
</td>
|
||||
<td className="col-actions">
|
||||
{a.review_status === "failed" ? (
|
||||
<button className="btn btn-sm btn-ghost" type="button" disabled={busy} onClick={() => void retry(a.id)}>
|
||||
重试
|
||||
{(a.review_status === "failed" || a.review_status === "") && (
|
||||
<button className="btn btn-sm btn-ghost" type="button" disabled={busy} onClick={() => void submit([a.id])}>
|
||||
{a.review_status === "failed" ? "重试" : "送审"}
|
||||
</button>
|
||||
) : null}
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -185,6 +205,14 @@ export function AdminReviewsPage({ notify }: { notify: Notify }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selected.size > 0 && (
|
||||
<div className="admin-bulk-bar" role="toolbar" aria-label="批量送审">
|
||||
<span className="admin-bulk-count">已选 {selected.size} 个</span>
|
||||
<button className="btn btn-sm" type="button" onClick={() => setSelected(new Set())}>取消</button>
|
||||
<button className="btn btn-sm btn-primary" type="button" disabled={busy} onClick={() => void submit([...selected])}>批量送审</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<MediaLightbox open={!!preview} src={preview?.src || ""} kind="image" name={preview?.name} close={() => setPreview(null)} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -54,7 +54,7 @@ const REPLACE_MODE_COPY = {
|
||||
targetLabel: "商品",
|
||||
targetStep: "2. 选择自己的商品",
|
||||
videoEmpty: "系统会先把参考视频拆成分镜稿,再照着它换成你的商品",
|
||||
videoReady: "视频已就绪,将先提炼分镜稿再复刻",
|
||||
videoReady: "视频已就绪,将先提炼分镜稿再逐镜复刻",
|
||||
libraryTitle: "从商品库选择",
|
||||
libraryEmpty: "选择已创建的商品",
|
||||
temporaryTitle: "临时上传素材",
|
||||
@@ -62,9 +62,9 @@ const REPLACE_MODE_COPY = {
|
||||
temporaryNoun: "商品图",
|
||||
temporaryFallback: "临时商品素材",
|
||||
generatingTitle: "正在进行商品复刻",
|
||||
generatingCopy: "正在照着分镜稿还原镜头,并换上你的商品",
|
||||
generatingCopy: "正在按分镜一镜一镜还原,并换上你的商品",
|
||||
reviewingTitle: "正在审核参考素材",
|
||||
reviewingCopy: "真人视频需先通过合规审核,通过后自动开始复刻",
|
||||
reviewingCopy: "参考图需先通过合规审核,通过后自动开始复刻",
|
||||
digestingTitle: "正在提炼参考视频",
|
||||
digestingCopy: "逐镜拆解景别、运镜、节奏与口播,拆完自动开始复刻",
|
||||
resultTitle: "商品复刻已完成",
|
||||
@@ -80,8 +80,8 @@ const REPLACE_MODE_COPY = {
|
||||
modeLabel: "角色复刻",
|
||||
targetLabel: "角色",
|
||||
targetStep: "2. 选择自己的角色",
|
||||
videoEmpty: "系统将自动识别需要替换的原片角色",
|
||||
videoReady: "视频已就绪,将自动识别原片角色",
|
||||
videoEmpty: "系统会先把参考视频拆成分镜稿,再照着它换成你的角色",
|
||||
videoReady: "视频已就绪,将先提炼分镜稿再逐镜复刻",
|
||||
libraryTitle: "从人物库选择",
|
||||
libraryEmpty: "选择已创建的人物",
|
||||
temporaryTitle: "临时上传角色",
|
||||
@@ -89,9 +89,9 @@ const REPLACE_MODE_COPY = {
|
||||
temporaryNoun: "角色参考图",
|
||||
temporaryFallback: "临时角色素材",
|
||||
generatingTitle: "正在进行角色复刻",
|
||||
generatingCopy: "正在匹配角色外观、表情与原片动作",
|
||||
generatingCopy: "正在按分镜一镜一镜还原,并换上你的角色",
|
||||
reviewingTitle: "正在审核参考素材",
|
||||
reviewingCopy: "真人视频需先通过合规审核,通过后自动开始复刻",
|
||||
reviewingCopy: "参考图需先通过合规审核,通过后自动开始复刻",
|
||||
digestingTitle: "正在提炼参考视频",
|
||||
digestingCopy: "逐镜拆解景别、运镜、节奏与口播,拆完自动开始复刻",
|
||||
resultTitle: "角色复刻已完成",
|
||||
@@ -365,10 +365,6 @@ export function VideoReplacePage({
|
||||
resolution: "720p",
|
||||
duration: outputDuration,
|
||||
refs: [
|
||||
// 商品复刻的参考视频只用来提炼分镜稿,不会发给火山,算进估价会让显示积分高于实际扣费。
|
||||
...(videoRef && replaceMode === "character"
|
||||
? [{ type: "video", duration: videoRef.duration || videoMeta.duration }]
|
||||
: []),
|
||||
...((source === "library"
|
||||
? Array.from({ length: Math.max(1, libraryImageCount) }, () => ({ type: "image" as const }))
|
||||
: (tempFiles.length ? tempFiles : tempAssetRefs)
|
||||
@@ -379,10 +375,14 @@ export function VideoReplacePage({
|
||||
// 上传参考视频不算「正在复刻」:右侧面板要保持待命,只在上传区自己显示进度。
|
||||
// 生成按钮不会因此被误点 —— videoReady 要等 asset_id 回来才为真。
|
||||
const generating = Boolean(job && isInFlight(job.status)) || submitting;
|
||||
const digesting = Boolean(job && isDigesting(job));
|
||||
// 商品复刻提交后先进拆解态;审核态是角色复刻专有(参考视频直传火山才需要送审)。
|
||||
const reviewing = !digesting && (submitting || Boolean(job && (job.review_stage === "reviewing" || job.status === "created")));
|
||||
const digesting = Boolean((job && isDigesting(job)) || (submitting && !job));
|
||||
const reviewing = !digesting && Boolean(job && (job.review_stage === "reviewing" || job.status === "created"));
|
||||
const hasResult = Boolean(job && job.status === "succeeded" && job.video_url);
|
||||
const shotTotal = Number(job?.shot_total || 0);
|
||||
const shotIndex = Number(job?.shot_index || 0);
|
||||
const generatingCopy = shotTotal > 1 && shotIndex > 0
|
||||
? `正在生成第 ${shotIndex}/${shotTotal} 镜,逐镜还原后再拼成完整片子`
|
||||
: copy.generatingCopy;
|
||||
const resultCopy = hasResult && job ? REPLACE_MODE_COPY[modeFromTask(job)] : copy;
|
||||
const panelClass = [
|
||||
"video-result-panel replace-result-panel",
|
||||
@@ -392,7 +392,7 @@ export function VideoReplacePage({
|
||||
hasResult ? "has-result" : "",
|
||||
].filter(Boolean).join(" ");
|
||||
const generateLabel = generating
|
||||
? (digesting ? "正在提炼参考视频…" : reviewing ? "正在审核素材…" : "正在复刻…")
|
||||
? (digesting ? "正在提炼参考视频…" : reviewing ? "正在审核素材…" : (shotTotal > 1 && shotIndex > 0 ? `正在复刻 ${shotIndex}/${shotTotal} 镜…` : "正在复刻…"))
|
||||
: hasResult
|
||||
? `再次${copy.modeLabel} · 消耗 ${points} 积分`
|
||||
: `开始${copy.modeLabel} · 消耗 ${points} 积分`;
|
||||
@@ -1121,7 +1121,7 @@ export function VideoReplacePage({
|
||||
<span className="replace-generating-product">{replaceMode === "character" ? <UserRound /> : <Package />}</span>
|
||||
</div>
|
||||
<strong>{digesting ? copy.digestingTitle : reviewing ? copy.reviewingTitle : copy.generatingTitle}</strong>
|
||||
<span>{digesting ? copy.digestingCopy : reviewing ? copy.reviewingCopy : copy.generatingCopy}</span>
|
||||
<span>{digesting ? copy.digestingCopy : reviewing ? copy.reviewingCopy : generatingCopy}</span>
|
||||
<div className="replace-generating-bar" aria-hidden="true"><span /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -627,12 +627,14 @@ export type FreeVideoTask = {
|
||||
product_id?: string;
|
||||
model_id?: string;
|
||||
review_stage?: "reviewing" | "";
|
||||
// 商品复刻专有:参考视频先被提炼成分镜稿,再连商品图一起交给 Seedance。
|
||||
// 商品复刻:参考视频先被提炼成分镜稿,再一镜一次交给 Seedance。
|
||||
digest_stage?: "digesting" | "";
|
||||
digest_text?: string;
|
||||
digest_shots?: number;
|
||||
digest_source_name?: string;
|
||||
digest_source?: FreeVideoRef | null;
|
||||
shot_index?: number;
|
||||
shot_total?: number;
|
||||
references: FreeVideoRef[];
|
||||
estimated_tokens: number;
|
||||
actual_tokens: number;
|
||||
|
||||
Reference in New Issue
Block a user