完善原型 mock 与交互细节
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 10s
152
电商AI平台/assets/mock-media.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
(function () {
|
||||||
|
const base = 'assets/mock/';
|
||||||
|
const media = {
|
||||||
|
products: {
|
||||||
|
mask: base + 'product-mask.png',
|
||||||
|
earbuds: base + 'product-earbuds.png',
|
||||||
|
noodle: base + 'product-noodle.png',
|
||||||
|
sunscreen: base + 'product-sunscreen.png',
|
||||||
|
coffee: base + 'product-coffee.png',
|
||||||
|
airFryer: base + 'product-air-fryer.png',
|
||||||
|
yoga: base + 'product-yoga-pants.png',
|
||||||
|
storage: base + 'product-storage.png'
|
||||||
|
},
|
||||||
|
covers: {
|
||||||
|
mask: base + 'cover-mask-v3.png',
|
||||||
|
maskFinal: base + 'cover-mask-final.png',
|
||||||
|
noodle: base + 'cover-noodle.png',
|
||||||
|
sunscreen: base + 'cover-sunscreen.png',
|
||||||
|
coffee: base + 'cover-coffee.png',
|
||||||
|
earbuds: base + 'cover-earbuds.png',
|
||||||
|
yoga: base + 'cover-yoga.png',
|
||||||
|
airFryer: base + 'cover-air-fryer.png'
|
||||||
|
},
|
||||||
|
people: {
|
||||||
|
linxi: base + 'person-linxi.png',
|
||||||
|
ajie: base + 'person-ajie.png',
|
||||||
|
aqiang: base + 'person-aqiang.png',
|
||||||
|
xiaosu: base + 'person-xiaosu.png'
|
||||||
|
},
|
||||||
|
scenes: {
|
||||||
|
bedroom: base + 'scene-bedroom.png',
|
||||||
|
bathroom: base + 'scene-bathroom.png',
|
||||||
|
living: base + 'scene-living.png',
|
||||||
|
kitchen: base + 'scene-kitchen.png',
|
||||||
|
office: base + 'scene-office.png',
|
||||||
|
cafe: base + 'scene-cafe.png',
|
||||||
|
street: base + 'scene-night-street.png',
|
||||||
|
tabletop: base + 'scene-tabletop.png'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function clean(text) {
|
||||||
|
return String(text || '').replace(/\s+/g, '').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function contextText(el) {
|
||||||
|
const card = el.closest('[data-name], [data-product], [data-project], [data-cat], [data-asset-kind], [data-scene-type]');
|
||||||
|
const bits = [
|
||||||
|
el.textContent,
|
||||||
|
card?.dataset.name,
|
||||||
|
card?.dataset.product,
|
||||||
|
card?.dataset.project,
|
||||||
|
card?.dataset.cat,
|
||||||
|
card?.dataset.assetKind,
|
||||||
|
card?.dataset.sceneType,
|
||||||
|
card?.querySelector('.product-name, .card-name, .asset-name, .prod-name, strong')?.textContent
|
||||||
|
];
|
||||||
|
return clean(bits.filter(Boolean).join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
function productFor(t) {
|
||||||
|
if (/蓝牙|耳机|earbud|南卡/.test(t)) return media.products.earbuds;
|
||||||
|
if (/速食|牛肉面|泡面|面条|noodle/.test(t)) return media.products.noodle;
|
||||||
|
if (/防晒|sunscreen/.test(t)) return media.products.sunscreen;
|
||||||
|
if (/咖啡|冻干|coffee/.test(t)) return media.products.coffee;
|
||||||
|
if (/空气炸锅|airfryer|小熊/.test(t)) return media.products.airFryer;
|
||||||
|
if (/瑜伽裤|露露|yoga/.test(t)) return media.products.yoga;
|
||||||
|
if (/收纳|storage|北欧/.test(t)) return media.products.storage;
|
||||||
|
if (/面膜|补水|玻尿酸|mask|透真/.test(t)) return media.products.mask;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function coverFor(t) {
|
||||||
|
if (/蓝牙|耳机|earbud|南卡/.test(t)) return media.covers.earbuds;
|
||||||
|
if (/速食|牛肉面|泡面|面条|noodle/.test(t)) return media.covers.noodle;
|
||||||
|
if (/防晒|sunscreen/.test(t)) return media.covers.sunscreen;
|
||||||
|
if (/咖啡|冻干|coffee/.test(t)) return media.covers.coffee;
|
||||||
|
if (/空气炸锅|airfryer|小熊/.test(t)) return media.covers.airFryer;
|
||||||
|
if (/瑜伽裤|露露|yoga/.test(t)) return media.covers.yoga;
|
||||||
|
if (/v1|final|已完成|5\/5|成片|敷面膜|化妆台/.test(t)) return media.covers.maskFinal;
|
||||||
|
if (/面膜|补水|玻尿酸|mask|透真|场1|场2|场3/.test(t)) return media.covers.mask;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function personFor(t) {
|
||||||
|
if (/阿杰|通勤男|男青年/.test(t)) return media.people.ajie;
|
||||||
|
if (/阿强|健身男|健身/.test(t)) return media.people.aqiang;
|
||||||
|
if (/小苏|文艺女|短发|阿楠|同事|小七|学生女|闺蜜|妈妈|王姐|豆豆/.test(t)) return media.people.xiaosu;
|
||||||
|
if (/林夕|主播|都市白领|主角|女性|女生/.test(t)) return media.people.linxi;
|
||||||
|
if (/小宇|李爷爷|男性|男/.test(t)) return media.people.ajie;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sceneFor(t) {
|
||||||
|
if (/卧室|床头|bedroom/.test(t)) return media.scenes.bedroom;
|
||||||
|
if (/浴室|梳妆台|bathroom/.test(t)) return media.scenes.bathroom;
|
||||||
|
if (/客厅|living/.test(t)) return media.scenes.living;
|
||||||
|
if (/厨房|中岛|kitchen/.test(t)) return media.scenes.kitchen;
|
||||||
|
if (/办公室|办公桌|会议室|office|深夜办公/.test(t)) return media.scenes.office;
|
||||||
|
if (/咖啡店|窗边|cafe/.test(t)) return media.scenes.cafe;
|
||||||
|
if (/街景|夜|street/.test(t)) return media.scenes.street;
|
||||||
|
if (/平台|套图|布景|tabletop/.test(t)) return media.scenes.tabletop;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageFor(el) {
|
||||||
|
if (el.classList.contains('missing') || el.querySelector('.spinner, .fail-icon')) return '';
|
||||||
|
const t = contextText(el);
|
||||||
|
const card = el.closest('.asset-card, .asset-card-2, .proj-card, .product-card, .video-card');
|
||||||
|
if (el.id === 'ed-canvas') return media.covers.maskFinal;
|
||||||
|
if (el.id === 'sb-main-img' || el.id === 'vd-main-img') return media.covers.mask;
|
||||||
|
if (el.matches('.video-thumb')) {
|
||||||
|
if (card?.dataset.videoId === 'v2') return media.products.mask;
|
||||||
|
if (card?.dataset.videoId === 'v3') return media.covers.maskFinal;
|
||||||
|
}
|
||||||
|
if (el.matches('.card-thumb, .proj-thumb, .video-thumb') || card?.classList.contains('video')) return coverFor(t);
|
||||||
|
if (el.matches('.product-thumb, .prod-thumb, .pl-thumb')) return productFor(t);
|
||||||
|
if (el.matches('.m-thumb')) return personFor(t);
|
||||||
|
if (el.matches('.thumb-2, .asset-thumb')) {
|
||||||
|
return personFor(t) || sceneFor(t) || productFor(t) || coverFor(t);
|
||||||
|
}
|
||||||
|
if (el.matches('.sb-scene-thumb, .sb-history-thumb, .vd-history-thumb')) return coverFor(t) || media.covers.mask;
|
||||||
|
return productFor(t) || personFor(t) || sceneFor(t) || coverFor(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyOne(el) {
|
||||||
|
if (!el || el.dataset.mockMediaApplied === '1') return;
|
||||||
|
const src = imageFor(el);
|
||||||
|
if (!src) return;
|
||||||
|
el.dataset.mockMediaApplied = '1';
|
||||||
|
el.classList.add('has-mock-media');
|
||||||
|
el.style.setProperty('--mock-media-url', `url("${src}")`);
|
||||||
|
el.style.backgroundImage = `url("${src}")`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply() {
|
||||||
|
document.querySelectorAll('.placeholder, #ed-canvas').forEach(applyOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
function boot() {
|
||||||
|
apply();
|
||||||
|
const mo = new MutationObserver(() => apply());
|
||||||
|
mo.observe(document.body, { childList: true, subtree: true });
|
||||||
|
window.MockMedia = { apply, media };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||||
|
} else {
|
||||||
|
boot();
|
||||||
|
}
|
||||||
|
})();
|
||||||
BIN
电商AI平台/assets/mock/cover-air-fryer.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
电商AI平台/assets/mock/cover-coffee.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
电商AI平台/assets/mock/cover-earbuds.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
电商AI平台/assets/mock/cover-mask-final.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
电商AI平台/assets/mock/cover-mask-v3.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
电商AI平台/assets/mock/cover-noodle.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
电商AI平台/assets/mock/cover-sunscreen.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
电商AI平台/assets/mock/cover-yoga.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
电商AI平台/assets/mock/person-ajie.png
Normal file
|
After Width: | Height: | Size: 678 KiB |
BIN
电商AI平台/assets/mock/person-aqiang.png
Normal file
|
After Width: | Height: | Size: 683 KiB |
BIN
电商AI平台/assets/mock/person-linxi.png
Normal file
|
After Width: | Height: | Size: 723 KiB |
BIN
电商AI平台/assets/mock/person-xiaosu.png
Normal file
|
After Width: | Height: | Size: 738 KiB |
BIN
电商AI平台/assets/mock/product-air-fryer.png
Normal file
|
After Width: | Height: | Size: 894 KiB |
BIN
电商AI平台/assets/mock/product-coffee.png
Normal file
|
After Width: | Height: | Size: 1011 KiB |
BIN
电商AI平台/assets/mock/product-earbuds.png
Normal file
|
After Width: | Height: | Size: 779 KiB |
BIN
电商AI平台/assets/mock/product-mask.png
Normal file
|
After Width: | Height: | Size: 888 KiB |
BIN
电商AI平台/assets/mock/product-noodle.png
Normal file
|
After Width: | Height: | Size: 997 KiB |
BIN
电商AI平台/assets/mock/product-storage.png
Normal file
|
After Width: | Height: | Size: 927 KiB |
BIN
电商AI平台/assets/mock/product-sunscreen.png
Normal file
|
After Width: | Height: | Size: 1008 KiB |
BIN
电商AI平台/assets/mock/product-yoga-pants.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
电商AI平台/assets/mock/scene-bathroom.png
Normal file
|
After Width: | Height: | Size: 782 KiB |
BIN
电商AI平台/assets/mock/scene-bedroom.png
Normal file
|
After Width: | Height: | Size: 800 KiB |
BIN
电商AI平台/assets/mock/scene-cafe.png
Normal file
|
After Width: | Height: | Size: 1001 KiB |
BIN
电商AI平台/assets/mock/scene-kitchen.png
Normal file
|
After Width: | Height: | Size: 930 KiB |
BIN
电商AI平台/assets/mock/scene-living.png
Normal file
|
After Width: | Height: | Size: 865 KiB |
BIN
电商AI平台/assets/mock/scene-night-street.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
电商AI平台/assets/mock/scene-office.png
Normal file
|
After Width: | Height: | Size: 902 KiB |
BIN
电商AI平台/assets/mock/scene-tabletop.png
Normal file
|
After Width: | Height: | Size: 643 KiB |
@ -758,7 +758,7 @@ main { position: relative; background: var(--background-base); min-width: 0; }
|
|||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border-faint);
|
border: 1px solid var(--border-faint);
|
||||||
border-radius: var(--r-md);
|
border-radius: var(--r-md);
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,.08);
|
box-shadow: var(--shadow-floating);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
display: none;
|
display: none;
|
||||||
@ -781,6 +781,271 @@ main { position: relative; background: var(--background-base); min-width: 0; }
|
|||||||
.chip-menu .mi.selected .mi-check { opacity: 1; }
|
.chip-menu .mi.selected .mi-check { opacity: 1; }
|
||||||
.chip-menu .mi-sep { height: 1px; background: var(--border-faint); margin: 4px 6px; }
|
.chip-menu .mi-sep { height: 1px; background: var(--border-faint); margin: 4px 6px; }
|
||||||
|
|
||||||
|
/* ─── Dropdown unification · 下拉框 / 菜单视觉收口 ─── */
|
||||||
|
select.select,
|
||||||
|
select.v-select,
|
||||||
|
select.nm-select,
|
||||||
|
select.duration-select,
|
||||||
|
.filter-bar select {
|
||||||
|
appearance: none !important;
|
||||||
|
height: 36px !important;
|
||||||
|
padding: 0 32px 0 14px !important;
|
||||||
|
background-color: var(--surface) !important;
|
||||||
|
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='%237a7a7a' stroke-width='1.4'/></svg>") !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
background-position: right 12px center !important;
|
||||||
|
border: 1px solid var(--black-alpha-12) !important;
|
||||||
|
border-radius: var(--r-md) !important;
|
||||||
|
color: var(--accent-black) !important;
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: 13.5px !important;
|
||||||
|
transition: border-color var(--t-base), box-shadow var(--t-base), background var(--t-base) !important;
|
||||||
|
}
|
||||||
|
select.select:hover,
|
||||||
|
select.v-select:hover,
|
||||||
|
select.nm-select:hover,
|
||||||
|
select.duration-select:hover,
|
||||||
|
.filter-bar select:hover {
|
||||||
|
border-color: var(--black-alpha-24) !important;
|
||||||
|
}
|
||||||
|
select.select:focus,
|
||||||
|
select.v-select:focus,
|
||||||
|
select.nm-select:focus,
|
||||||
|
select.duration-select:focus,
|
||||||
|
.filter-bar select:focus {
|
||||||
|
border-color: var(--heat-40) !important;
|
||||||
|
box-shadow: inset 0 0 0 1px var(--heat-40) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pd-toolbar .filter,
|
||||||
|
.pp-toolbar .pp-chip,
|
||||||
|
.mp-main-h .tb-chip,
|
||||||
|
.pc-main-h .tb-chip {
|
||||||
|
height: 36px !important;
|
||||||
|
padding: 0 14px !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
background: var(--surface) !important;
|
||||||
|
border: 1px solid var(--border-faint) !important;
|
||||||
|
border-radius: var(--r-md) !important;
|
||||||
|
color: var(--black-alpha-56) !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
transition: background var(--t-base), border-color var(--t-base), color var(--t-base) !important;
|
||||||
|
}
|
||||||
|
.pd-toolbar .filter:hover,
|
||||||
|
.pp-toolbar .pp-chip:hover,
|
||||||
|
.mp-main-h .tb-chip:hover,
|
||||||
|
.pc-main-h .tb-chip:hover {
|
||||||
|
background: var(--black-alpha-4) !important;
|
||||||
|
border-color: var(--black-alpha-24) !important;
|
||||||
|
color: var(--accent-black) !important;
|
||||||
|
}
|
||||||
|
.pd-toolbar .filter.open,
|
||||||
|
.pd-toolbar .filter.filtered,
|
||||||
|
.pp-toolbar .pp-chip.active,
|
||||||
|
.mp-main-h .tb-chip.active,
|
||||||
|
.pc-main-h .tb-chip.active {
|
||||||
|
background: var(--heat-12) !important;
|
||||||
|
border-color: var(--heat-40) !important;
|
||||||
|
color: var(--heat) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(.chip-menu, .filter-pop, .pp-menu, .tb-menu, .io-param-menu, .move-menu, .cell-more-menu, .msg-more-menu, .batch-more-menu) {
|
||||||
|
background: var(--surface) !important;
|
||||||
|
border: 1px solid var(--border-faint) !important;
|
||||||
|
border-radius: var(--r-md) !important;
|
||||||
|
box-shadow: var(--shadow-floating) !important;
|
||||||
|
padding: 4px !important;
|
||||||
|
}
|
||||||
|
:where(
|
||||||
|
.chip-menu .mi,
|
||||||
|
.filter-pop button,
|
||||||
|
.pp-menu .mi,
|
||||||
|
.tb-menu-item,
|
||||||
|
.io-param-menu .mi,
|
||||||
|
.move-menu .mv-item,
|
||||||
|
.cell-more-menu button,
|
||||||
|
.msg-more-menu button,
|
||||||
|
.batch-more-menu button
|
||||||
|
) {
|
||||||
|
min-height: 32px !important;
|
||||||
|
padding: 0 10px !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border: 0 !important;
|
||||||
|
border-radius: var(--r-sm) !important;
|
||||||
|
color: var(--accent-black) !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
text-align: left !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: background var(--t-base), color var(--t-base) !important;
|
||||||
|
}
|
||||||
|
:where(
|
||||||
|
.chip-menu .mi,
|
||||||
|
.filter-pop button,
|
||||||
|
.pp-menu .mi,
|
||||||
|
.tb-menu-item,
|
||||||
|
.io-param-menu .mi,
|
||||||
|
.move-menu .mv-item,
|
||||||
|
.cell-more-menu button,
|
||||||
|
.msg-more-menu button,
|
||||||
|
.batch-more-menu button
|
||||||
|
):hover {
|
||||||
|
background: var(--black-alpha-4) !important;
|
||||||
|
color: var(--accent-black) !important;
|
||||||
|
}
|
||||||
|
:where(
|
||||||
|
.chip-menu .mi.selected,
|
||||||
|
.filter-pop button.selected,
|
||||||
|
.pp-menu .mi.selected,
|
||||||
|
.tb-menu-item.active,
|
||||||
|
.io-param-menu .mi.selected
|
||||||
|
) {
|
||||||
|
background: var(--heat-12) !important;
|
||||||
|
color: var(--heat) !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
:where(.cell-more-menu button.danger:hover, .msg-more-menu button.danger:hover, .batch-more-menu button.danger:hover) {
|
||||||
|
background: var(--crimson-bg) !important;
|
||||||
|
color: var(--accent-crimson) !important;
|
||||||
|
}
|
||||||
|
:where(.tb-menu-empty) {
|
||||||
|
color: var(--black-alpha-48) !important;
|
||||||
|
font-family: var(--font-mono) !important;
|
||||||
|
font-size: 11.5px !important;
|
||||||
|
letter-spacing: .02em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs-select {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 126px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.rs-select.rs-select-fill { width: 100%; }
|
||||||
|
.rs-select.rs-select-filter { min-width: 126px; }
|
||||||
|
.rs-select > select[data-rs-select-bound="1"] {
|
||||||
|
position: absolute !important;
|
||||||
|
inset: 0 auto auto 0 !important;
|
||||||
|
width: 1px !important;
|
||||||
|
height: 1px !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
.rs-select-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 12px 0 14px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--black-alpha-12);
|
||||||
|
border-radius: var(--r-md);
|
||||||
|
color: var(--accent-black);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--t-base), border-color var(--t-base), box-shadow var(--t-base), color var(--t-base);
|
||||||
|
}
|
||||||
|
.rs-select-btn:hover {
|
||||||
|
background: var(--black-alpha-4);
|
||||||
|
border-color: var(--black-alpha-24);
|
||||||
|
}
|
||||||
|
.rs-select.open .rs-select-btn,
|
||||||
|
.rs-select-btn:focus-visible {
|
||||||
|
border-color: var(--heat-40);
|
||||||
|
box-shadow: inset 0 0 0 1px var(--heat-40);
|
||||||
|
}
|
||||||
|
.rs-select-btn[disabled] {
|
||||||
|
background: var(--black-alpha-5);
|
||||||
|
color: var(--black-alpha-32);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.rs-select-label {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.rs-select-btn svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
color: var(--black-alpha-56);
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: transform var(--t-base), color var(--t-base);
|
||||||
|
}
|
||||||
|
.rs-select.open .rs-select-btn svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
color: var(--heat);
|
||||||
|
}
|
||||||
|
.rs-select-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
width: max-content;
|
||||||
|
max-width: min(260px, calc(100vw - 24px));
|
||||||
|
max-height: 320px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: none;
|
||||||
|
z-index: 1600;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border-faint);
|
||||||
|
border-radius: var(--r-md);
|
||||||
|
box-shadow: var(--shadow-floating);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.rs-select-menu.align-right {
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.rs-select.open .rs-select-menu { display: block; }
|
||||||
|
.rs-select-option {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--r-sm);
|
||||||
|
color: var(--accent-black);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--t-base), color var(--t-base);
|
||||||
|
}
|
||||||
|
.rs-select-option:hover,
|
||||||
|
.rs-select-option.is-active {
|
||||||
|
background: var(--black-alpha-4);
|
||||||
|
}
|
||||||
|
.rs-select-option.selected {
|
||||||
|
background: var(--heat-12);
|
||||||
|
color: var(--heat);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.rs-select-option[disabled] {
|
||||||
|
color: var(--black-alpha-32);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.rs-select-option .mi-check {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
color: var(--heat);
|
||||||
|
opacity: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.rs-select-option.selected .mi-check { opacity: 1; }
|
||||||
|
|
||||||
/* ─── Clear-filters btn · 共享组件 ─── */
|
/* ─── Clear-filters btn · 共享组件 ─── */
|
||||||
.clear-filters {
|
.clear-filters {
|
||||||
height: 36px; padding: 0 12px;
|
height: 36px; padding: 0 12px;
|
||||||
@ -1272,6 +1537,25 @@ table.t tbody tr:hover { background: var(--black-alpha-4); }
|
|||||||
color: var(--black-alpha-56);
|
color: var(--black-alpha-56);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.placeholder.has-mock-media {
|
||||||
|
background-color: var(--background-lighter);
|
||||||
|
background-image: var(--mock-media-url);
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.placeholder.has-mock-media .ph-frame {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.placeholder.has-mock-media.mock-label .ph-frame {
|
||||||
|
position: absolute;
|
||||||
|
left: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
max-width: calc(100% - 16px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── Toast ─── */
|
/* ─── Toast ─── */
|
||||||
.toast {
|
.toast {
|
||||||
|
|||||||
@ -292,6 +292,144 @@ window.Shell = {
|
|||||||
document.getElementById('global-search')?.focus();
|
document.getElementById('global-search')?.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.enhanceSelects(document);
|
||||||
|
},
|
||||||
|
|
||||||
|
enhanceSelects(root = document) {
|
||||||
|
const scope = root || document;
|
||||||
|
const nodes = [];
|
||||||
|
if (scope.matches?.('select')) nodes.push(scope);
|
||||||
|
scope.querySelectorAll?.('select:not([data-rs-select-bound])').forEach(el => nodes.push(el));
|
||||||
|
const selects = [...new Set(nodes)].filter(select =>
|
||||||
|
select.tagName === 'SELECT' &&
|
||||||
|
!select.multiple &&
|
||||||
|
Number(select.size || 0) <= 1 &&
|
||||||
|
!select.closest('.rs-select') &&
|
||||||
|
select.dataset.nativeSelect !== 'true'
|
||||||
|
);
|
||||||
|
if (!selects.length) {
|
||||||
|
this._bindCustomSelectInfrastructure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, c => ({
|
||||||
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
||||||
|
})[c]);
|
||||||
|
const checkSvg = ShellIcon('check', { className: 'mi-check', size: 13 });
|
||||||
|
const caretSvg = ShellIcon('chevronDown', { size: 12 });
|
||||||
|
|
||||||
|
selects.forEach(select => {
|
||||||
|
const wrap = document.createElement('span');
|
||||||
|
wrap.className = 'rs-select';
|
||||||
|
if (select.classList.contains('v-edit')) wrap.classList.add('v-edit');
|
||||||
|
if (select.hidden) wrap.hidden = true;
|
||||||
|
if (
|
||||||
|
select.classList.contains('select') ||
|
||||||
|
select.classList.contains('v-select') ||
|
||||||
|
select.classList.contains('nm-select') ||
|
||||||
|
select.closest('.field') ||
|
||||||
|
select.closest('.form-row')
|
||||||
|
) wrap.classList.add('rs-select-fill');
|
||||||
|
if (select.closest('.filter-bar')) wrap.classList.add('rs-select-filter');
|
||||||
|
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.className = 'rs-select-btn';
|
||||||
|
btn.setAttribute('aria-haspopup', 'listbox');
|
||||||
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
|
|
||||||
|
const menu = document.createElement('div');
|
||||||
|
menu.className = 'rs-select-menu';
|
||||||
|
menu.setAttribute('role', 'listbox');
|
||||||
|
|
||||||
|
select.parentNode.insertBefore(wrap, select);
|
||||||
|
wrap.appendChild(select);
|
||||||
|
wrap.appendChild(btn);
|
||||||
|
wrap.appendChild(menu);
|
||||||
|
select.dataset.rsSelectBound = '1';
|
||||||
|
select.tabIndex = -1;
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
const selected = select.selectedOptions[0] || select.options[select.selectedIndex] || select.options[0];
|
||||||
|
btn.disabled = select.disabled;
|
||||||
|
btn.innerHTML = `<span class="rs-select-label">${esc(selected?.textContent || '')}</span>${caretSvg}`;
|
||||||
|
menu.innerHTML = [...select.options].map((opt, i) => `
|
||||||
|
<button class="rs-select-option${i === select.selectedIndex ? ' selected' : ''}" type="button" role="option" data-index="${i}" aria-selected="${i === select.selectedIndex ? 'true' : 'false'}" ${opt.disabled ? 'disabled' : ''}>
|
||||||
|
${checkSvg}
|
||||||
|
<span>${esc(opt.textContent)}</span>
|
||||||
|
</button>
|
||||||
|
`).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
btn.addEventListener('click', e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (select.disabled) return;
|
||||||
|
const willOpen = !wrap.classList.contains('open');
|
||||||
|
this.closeCustomSelects(wrap);
|
||||||
|
wrap.classList.toggle('open', willOpen);
|
||||||
|
btn.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
|
||||||
|
if (willOpen) {
|
||||||
|
menu.classList.remove('align-right');
|
||||||
|
const rect = menu.getBoundingClientRect();
|
||||||
|
if (rect.right > window.innerWidth - 12) menu.classList.add('align-right');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
btn.addEventListener('keydown', e => {
|
||||||
|
if (e.key !== 'Enter' && e.key !== ' ' && e.key !== 'ArrowDown') return;
|
||||||
|
e.preventDefault();
|
||||||
|
btn.click();
|
||||||
|
});
|
||||||
|
menu.addEventListener('click', e => {
|
||||||
|
const item = e.target.closest('.rs-select-option');
|
||||||
|
if (!item || item.disabled) return;
|
||||||
|
const idx = Number(item.dataset.index);
|
||||||
|
if (!Number.isNaN(idx) && select.selectedIndex !== idx) {
|
||||||
|
select.selectedIndex = idx;
|
||||||
|
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
}
|
||||||
|
sync();
|
||||||
|
this.closeCustomSelects();
|
||||||
|
btn.focus();
|
||||||
|
});
|
||||||
|
select.addEventListener('change', sync);
|
||||||
|
select._rsSelectSync = sync;
|
||||||
|
sync();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bindCustomSelectInfrastructure();
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshCustomSelects(root = document) {
|
||||||
|
root.querySelectorAll?.('select[data-rs-select-bound="1"]').forEach(select => {
|
||||||
|
if (typeof select._rsSelectSync === 'function') select._rsSelectSync();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeCustomSelects(except) {
|
||||||
|
document.querySelectorAll('.rs-select.open').forEach(wrap => {
|
||||||
|
if (except && wrap === except) return;
|
||||||
|
wrap.classList.remove('open');
|
||||||
|
wrap.querySelector('.rs-select-btn')?.setAttribute('aria-expanded', 'false');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_bindCustomSelectInfrastructure() {
|
||||||
|
if (this._rsSelectInfrastructureBound) return;
|
||||||
|
this._rsSelectInfrastructureBound = true;
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
if (!e.target.closest('.rs-select')) this.closeCustomSelects();
|
||||||
|
requestAnimationFrame(() => this.refreshCustomSelects(document));
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Escape') this.closeCustomSelects();
|
||||||
|
});
|
||||||
|
this._rsSelectObserver = new MutationObserver(records => {
|
||||||
|
if (!records.some(r => [...r.addedNodes].some(n =>
|
||||||
|
n.nodeType === 1 && (n.matches?.('select') || n.querySelector?.('select'))
|
||||||
|
))) return;
|
||||||
|
requestAnimationFrame(() => this.enhanceSelects(document));
|
||||||
|
});
|
||||||
|
if (document.body) this._rsSelectObserver.observe(document.body, { childList: true, subtree: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
// [DEPRECATED] 新建商品弹窗 · 已废弃,创建商品改为直跳 product-create.html
|
// [DEPRECATED] 新建商品弹窗 · 已废弃,创建商品改为直跳 product-create.html
|
||||||
@ -612,3 +750,12 @@ window.Shell = {
|
|||||||
this.unlockScroll();
|
this.unlockScroll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(function loadMockMedia() {
|
||||||
|
if (window.__AIRSHELF_DISABLE_MOCK_MEDIA__) return;
|
||||||
|
if (document.querySelector('script[data-mock-media]')) return;
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'assets/mock-media.js?v=2026052703';
|
||||||
|
script.dataset.mockMedia = '1';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
})();
|
||||||
|
|||||||
@ -164,7 +164,7 @@
|
|||||||
/* 镜头脚本 | 拖拽分隔条 | 脚本助手 三列布局 · 分隔条可拖动 */
|
/* 镜头脚本 | 拖拽分隔条 | 脚本助手 三列布局 · 分隔条可拖动 */
|
||||||
.content--fh-flat .stage[data-stage-pane="1"].active > .stage-script {
|
.content--fh-flat .stage[data-stage-pane="1"].active > .stage-script {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
grid-template-columns: minmax(0, 1fr) 6px var(--chat-w, 360px);
|
grid-template-columns: minmax(0, 1fr) 6px var(--chat-w, 520px);
|
||||||
}
|
}
|
||||||
/* 拖拽 gutter · 默认 1px 灰线,hover/拖中加重 · 鼠标 col-resize */
|
/* 拖拽 gutter · 默认 1px 灰线,hover/拖中加重 · 鼠标 col-resize */
|
||||||
.stage-script-gutter {
|
.stage-script-gutter {
|
||||||
@ -2518,12 +2518,12 @@ document.getElementById('page-title').textContent = PROJECT_TITLE + ' · 流水
|
|||||||
topbar.appendChild(anchor); // position: absolute · 自动锚定 topbar 中部
|
topbar.appendChild(anchor); // position: absolute · 自动锚定 topbar 中部
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/* ─── Stage 1 拖拽分隔条 · 控制脚本助手宽度 · clamp 在 [280, 600] ─── */
|
/* ─── Stage 1 拖拽分隔条 · 控制脚本助手宽度 · clamp 在 [380, 680] ─── */
|
||||||
(function _setupStageScriptGutter() {
|
(function _setupStageScriptGutter() {
|
||||||
const gutter = document.getElementById('stage-script-gutter');
|
const gutter = document.getElementById('stage-script-gutter');
|
||||||
const grid = document.querySelector('.stage-script');
|
const grid = document.querySelector('.stage-script');
|
||||||
if (!gutter || !grid) return;
|
if (!gutter || !grid) return;
|
||||||
const MIN = 320, MAX = 600;
|
const MIN = 380, MAX = 680;
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
gutter.addEventListener('mousedown', (e) => {
|
gutter.addEventListener('mousedown', (e) => {
|
||||||
dragging = true;
|
dragging = true;
|
||||||
|
|||||||
@ -1446,12 +1446,18 @@
|
|||||||
const pill = e.target.closest('.asset-card .meta .pill[data-status]');
|
const pill = e.target.closest('.asset-card .meta .pill[data-status]');
|
||||||
if (!pill) return;
|
if (!pill) return;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
if (pill.closest('.asset-card[data-tri-version]')) {
|
||||||
|
window.ProductDetailFilters?.applyAssets?.();
|
||||||
|
Shell.toast('三视图状态由采用版本决定', '请在三视图面板选择「采用此版本」');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const cur = pill.dataset.status;
|
const cur = pill.dataset.status;
|
||||||
const next = order[(order.indexOf(cur) + 1) % order.length];
|
const next = order[(order.indexOf(cur) + 1) % order.length];
|
||||||
pill.dataset.status = next;
|
pill.dataset.status = next;
|
||||||
pill.classList.remove('pass', 'fail', 'archive');
|
pill.classList.remove('pass', 'fail', 'archive');
|
||||||
pill.classList.add(next);
|
pill.classList.add(next);
|
||||||
pill.textContent = labels[next];
|
pill.textContent = labels[next];
|
||||||
|
window.ProductDetailFilters?.applyAssets?.();
|
||||||
Shell.toast('状态已更新', labels[next]);
|
Shell.toast('状态已更新', labels[next]);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
@ -1721,6 +1727,16 @@
|
|||||||
if (more) more.style.display = '';
|
if (more) more.style.display = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.ProductDetailFilters = {
|
||||||
|
apply(pane = 'assets') {
|
||||||
|
const paneEl = document.querySelector(`.tab-pane[data-pane="${pane}"]`);
|
||||||
|
if (paneEl) applyFilters(paneEl);
|
||||||
|
},
|
||||||
|
applyAssets() {
|
||||||
|
this.apply('assets');
|
||||||
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// 编辑商品信息 · 在卡片内 inline 切换 view ↔ edit
|
// 编辑商品信息 · 在卡片内 inline 切换 view ↔ edit
|
||||||
@ -1899,6 +1915,10 @@
|
|||||||
return (document.getElementById('pd-name')?.textContent || '商品').trim();
|
return (document.getElementById('pd-name')?.textContent || '商品').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshAssetFilters() {
|
||||||
|
window.ProductDetailFilters?.applyAssets?.();
|
||||||
|
}
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
pop.classList.add('show');
|
pop.classList.add('show');
|
||||||
btn.classList.add('is-open');
|
btn.classList.add('is-open');
|
||||||
@ -1983,6 +2003,7 @@
|
|||||||
pill.setAttribute('data-status', isAdopted ? 'pass' : 'fail');
|
pill.setAttribute('data-status', isAdopted ? 'pass' : 'fail');
|
||||||
pill.setAttribute('title', isAdopted ? '当前采用版本' : '未被采用');
|
pill.setAttribute('title', isAdopted ? '当前采用版本' : '未被采用');
|
||||||
});
|
});
|
||||||
|
refreshAssetFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendLibraryCard(ver) {
|
function appendLibraryCard(ver) {
|
||||||
@ -2007,6 +2028,7 @@
|
|||||||
const n = m ? Number(m[1]) + 1 : 1;
|
const n = m ? Number(m[1]) + 1 : 1;
|
||||||
ct.textContent = `(${n})`;
|
ct.textContent = `(${n})`;
|
||||||
}
|
}
|
||||||
|
refreshAssetFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换主图预览(不动采用状态、不动素材库)
|
// 切换主图预览(不动采用状态、不动素材库)
|
||||||
|
|||||||