Codex/ecommerce hot video responsive #7

Merged
stringadmin merged 4 commits from codex/ecommerce-hot-video-responsive into main 2026-06-11 16:06:38 +00:00
3 changed files with 471 additions and 243 deletions
+290 -203
View File
@@ -367,310 +367,310 @@ const platformSpecOptions: Array<{
}> = [
{
label: "淘宝/天猫",
ratios: ["淘宝主图 / SKU 鍥?800800px", "详情页宽 750px", "详情页宽 790px"],
defaultRatio: "淘宝主图 / SKU 鍥?800800px",
ratios: ["淘宝主图 / SKU 800×800px", "详情页宽 750px", "详情页宽 790px"],
defaultRatio: "淘宝主图 / SKU 800×800px",
ratioGroups: {
set: {
ratios: ["10001000px\u00a0\u00a0\u00a01锛?", "800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "10001000px\u00a0\u00a0\u00a01锛?",
ratios: ["1000×1000px\u00a0\u00a0\u00a01:1", "800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1000×1000px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: [
"7501000px\u00a0\u00a0\u00a03锛?",
"7901053px\u00a0\u00a0\u00a03锛?",
"7501125px\u00a0\u00a0\u00a02锛?",
"7901185px\u00a0\u00a0\u00a02锛?",
"750×1000px\u00a0\u00a0\u00a03:4",
"790×1053px\u00a0\u00a0\u00a03:4",
"750×1125px\u00a0\u00a0\u00a02:3",
"790×1185px\u00a0\u00a0\u00a02:3",
],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
model: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6", "10801440px\u00a0\u00a0\u00a03锛?", "10801080px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16", "1080×1440px\u00a0\u00a0\u00a03:4", "1080×1080px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["主图 / SKU 鍥?800800px锛屸墹3MB", "详情页宽 750px 或?790px锛屽崟寮犻珮鈮?546px"],
tip: "建议主图 200-400KB JPG锛岃秴杩?500KB 浼氬奖鍝嶅姞杞介€熷害銆?",
specs: ["主图 / SKU 800×800px,≤3MB", "详情页宽 750px 或 790px,单张高度≤1546px"],
tip: "建议主图 200-400KB JPG,超过 500KB 会影响加载速度。",
},
{
label: "京东",
ratios: ["京东主图 / SKU 鍥?800800px", "详情页宽 750px", "首图主体占比 鈮?0%"],
defaultRatio: "京东主图 / SKU 鍥?800800px",
ratios: ["京东主图 / SKU 800×800px", "详情页宽 750px", "首图主体占比 ≥80%"],
defaultRatio: "京东主图 / SKU 800×800px",
ratioGroups: {
set: {
ratios: ["10001000px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "10001000px\u00a0\u00a0\u00a01锛?",
ratios: ["1000×1000px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1000×1000px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: [
"7501000px\u00a0\u00a0\u00a03锛?",
"9901320px\u00a0\u00a0\u00a03锛?",
"7501125px\u00a0\u00a0\u00a02锛?",
"9901485px\u00a0\u00a0\u00a02锛?",
"750×1000px\u00a0\u00a0\u00a03:4",
"990×1320px\u00a0\u00a0\u00a03:4",
"750×1125px\u00a0\u00a0\u00a02:3",
"990×1485px\u00a0\u00a0\u00a02:3",
],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
model: {
ratios: ["7501125px\u00a0\u00a0\u00a02锛?", "9901485px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "7501125px\u00a0\u00a0\u00a02锛?",
ratios: ["750×1125px\u00a0\u00a0\u00a02:3", "990×1485px\u00a0\u00a0\u00a02:3"],
defaultRatio: "750×1125px\u00a0\u00a0\u00a02:3",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6", "19201080px\u00a0\u00a0\u00a016锛?"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16", "1920×1080px\u00a0\u00a0\u00a016:9"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["主图 / SKU 鍥?800800px锛岀櫧搴曪紝鈮?MB", "详情页宽 750px锛岄鍥句富浣撳崰姣?鈮?0%"],
specs: ["主图 / SKU 800×800px,白底,≤3MB", "详情页宽 750px,首图主体占比 ≥80%"],
},
{
label: "拼多多",
ratios: ["主图 750352px", "主图 800800px", "详情页宽 750px"],
defaultRatio: "主图 750352px",
ratios: ["主图 750×352px", "主图 800×800px", "详情页宽 750px"],
defaultRatio: "主图 750×352px",
ratioGroups: {
set: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?", "7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1", "750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?", "7501125px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4", "750×1125px\u00a0\u00a0\u00a02:3"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
model: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["主图 750352px 或?800800px锛屸墹1MB", "详情页宽 750px,要求纯白底、无水印、无拼接"],
specs: ["主图 750×352px 或 800×800px,≤1MB", "详情页宽 750px,要求纯白底、无水印、无拼接"],
},
{
label: "抖音电商",
ratios: ["鐭棰?10801920px"],
defaultRatio: "鐭棰?10801920px",
ratios: ["短视频1080×1920px"],
defaultRatio: "短视频1080×1920px",
ratioGroups: {
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["鐭棰?10801920px锛?:16", "30s 图呮渶浣?"],
specs: ["短视频 1080×1920px9:16", "30s 内最佳"],
},
{
label: "亚马逊 Amazon",
ratios: ["主图 鈮?6001600px", "建议 20002000px+", "鏈€灏?500500px"],
defaultRatio: "主图 鈮?6001600px",
ratios: ["主图 ≥1600×1600px", "建议 2000×2000px+", "最小 500×500px"],
defaultRatio: "主图 ≥1600×1600px",
ratioGroups: {
set: {
ratios: ["16001600px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "16001600px\u00a0\u00a0\u00a01锛?",
ratios: ["1600×1600px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1600×1600px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["16001600px\u00a0\u00a0\u00a01锛?", "12001800px\u00a0\u00a0\u00a02锛?", "12001600px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "12001800px\u00a0\u00a0\u00a02锛?",
ratios: ["1600×1600px\u00a0\u00a0\u00a01:1", "1200×1800px\u00a0\u00a0\u00a02:3", "1200×1600px\u00a0\u00a0\u00a03:4"],
defaultRatio: "1200×1800px\u00a0\u00a0\u00a02:3",
},
model: {
ratios: ["12001800px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "12001800px\u00a0\u00a0\u00a02锛?",
ratios: ["1200×1800px\u00a0\u00a0\u00a02:3"],
defaultRatio: "1200×1800px\u00a0\u00a0\u00a02:3",
},
video: {
ratios: ["19201080px\u00a0\u00a0\u00a016锛?"],
defaultRatio: "19201080px\u00a0\u00a0\u00a016锛?",
ratios: ["1920×1080px\u00a0\u00a0\u00a016:9"],
defaultRatio: "1920×1080px\u00a0\u00a0\u00a016:9",
},
hot: {
ratios: ["16001600px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "16001600px\u00a0\u00a0\u00a01锛?",
ratios: ["1600×1600px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1600×1600px\u00a0\u00a0\u00a01:1",
},
},
specs: ["主图 16001600px+,纯白底,≤10MB", "鏈€灏?500500px锛屽缓璁?2000px+ 浠ユ敮鎸佺缉鏀?"],
aliases: ["浜氶┈閫?"],
specs: ["主图 1600×1600px+,纯白底,≤10MB", "最小 500×500px,建议 2000px+ 以支持缩放"],
aliases: ["亚马逊"],
},
{
label: "Shopee",
ratios: ["商品主图 10241024px", "基础主图 800800px"],
defaultRatio: "商品主图 10241024px",
ratios: ["商品主图 1024×1024px", "基础主图 800×800px"],
defaultRatio: "商品主图 1024×1024px",
ratioGroups: {
set: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?", "7501125px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4", "750×1125px\u00a0\u00a0\u00a02:3"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
model: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["商品主图推荐 10241024px,基础 800800px", "鈮?MB锛岀櫧搴曟垨娴呰壊搴?"],
specs: ["商品主图推荐 1024×1024px,基础 800×800px", "≤2MB,白底或浅色底"],
aliases: ["虾皮 Shopee/Lazada", "虾皮"],
},
{
label: "Lazada",
ratios: ["商品主图 800800px"],
defaultRatio: "商品主图 800800px",
ratios: ["商品主图 800×800px"],
defaultRatio: "商品主图 800×800px",
ratioGroups: {
set: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?", "7501125px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4", "750×1125px\u00a0\u00a0\u00a02:3"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
model: {
ratios: ["7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "7501000px\u00a0\u00a0\u00a03锛?",
ratios: ["750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "750×1000px\u00a0\u00a0\u00a03:4",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["商品主图 800800px锛?:1"],
specs: ["商品主图 800×800px1:1"],
},
{
label: "Instagram",
ratios: ["帖子 10801350px", "帖子 10801080px", "Stories / Reels 10801920px", "头像 320320px"],
defaultRatio: "帖子 10801350px",
ratios: ["帖子 1080×1350px", "帖子 1080×1080px", "Stories / Reels 1080×1920px", "头像 320×320px"],
defaultRatio: "帖子 1080×1350px",
ratioGroups: {
set: {
ratios: ["10801080px\u00a0\u00a0\u00a01锛?", "10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801080px\u00a0\u00a0\u00a01锛?",
ratios: ["1080×1080px\u00a0\u00a0\u00a01:1", "1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1080px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801350px\u00a0\u00a0\u00a04锛?",
ratios: ["1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1350px\u00a0\u00a0\u00a04:5",
},
model: {
ratios: ["10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801350px\u00a0\u00a0\u00a04锛?",
ratios: ["1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1350px\u00a0\u00a0\u00a04:5",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6", "10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16", "1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
},
specs: ["帖子 10801350px 或?10801080px", "Stories / Reels 封面 10801920px锛屽ご鍍?320320px"],
tip: "建议 鈮?MB JPG銆?",
specs: ["帖子 1080×1350px 或 1080×1080px", "Stories / Reels 封面 1080×1920px,头像 320×320px"],
tip: "建议 ≤8MB JPG",
aliases: ["Instagram Reels"],
},
{
label: "速卖通",
ratios: ["主图 800800px", "主图 10001000px+"],
defaultRatio: "主图 800800px",
ratios: ["主图 800×800px", "主图 1000×1000px+"],
defaultRatio: "主图 800×800px",
ratioGroups: {
set: {
ratios: ["10001000px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "10001000px\u00a0\u00a0\u00a01锛?",
ratios: ["1000×1000px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1000×1000px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["7501125px\u00a0\u00a0\u00a02锛?", "7501000px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "7501125px\u00a0\u00a0\u00a02锛?",
ratios: ["750×1125px\u00a0\u00a0\u00a02:3", "750×1000px\u00a0\u00a0\u00a03:4"],
defaultRatio: "750×1125px\u00a0\u00a0\u00a02:3",
},
model: {
ratios: ["7501125px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "7501125px\u00a0\u00a0\u00a02锛?",
ratios: ["750×1125px\u00a0\u00a0\u00a02:3"],
defaultRatio: "750×1125px\u00a0\u00a0\u00a02:3",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6", "19201080px\u00a0\u00a0\u00a016锛?"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16", "1920×1080px\u00a0\u00a0\u00a016:9"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["主图建议 800800px 或更高,1:1", "适合跨境电商主图、SKU 鍥惧拰鍦烘櫙鍥?"],
specs: ["主图建议 800×800px 或更高,1:1", "适合跨境电商主图、SKU 图和场景图"],
},
{
label: "eBay",
ratios: ["鍟嗗搧鍥?1:1", "白底多角度展示图 1:1"],
defaultRatio: "鍟嗗搧鍥?1:1",
ratios: ["商品图1:1", "白底多角度展示图 1:1"],
defaultRatio: "商品图1:1",
ratioGroups: {
set: {
ratios: ["16001600px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "16001600px\u00a0\u00a0\u00a01锛?",
ratios: ["1600×1600px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1600×1600px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["10001500px\u00a0\u00a0\u00a02锛?", "12001600px\u00a0\u00a0\u00a03锛?"],
defaultRatio: "10001500px\u00a0\u00a0\u00a02锛?",
ratios: ["1000×1500px\u00a0\u00a0\u00a02:3", "1200×1600px\u00a0\u00a0\u00a03:4"],
defaultRatio: "1000×1500px\u00a0\u00a0\u00a02:3",
},
model: {
ratios: ["10001500px\u00a0\u00a0\u00a02锛?"],
defaultRatio: "10001500px\u00a0\u00a0\u00a02锛?",
ratios: ["1000×1500px\u00a0\u00a0\u00a02:3"],
defaultRatio: "1000×1500px\u00a0\u00a0\u00a02:3",
},
video: {
ratios: ["19201080px\u00a0\u00a0\u00a016锛?", "10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "19201080px\u00a0\u00a0\u00a016锛?",
ratios: ["1920×1080px\u00a0\u00a0\u00a016:9", "1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1920×1080px\u00a0\u00a0\u00a016:9",
},
hot: {
ratios: ["16001600px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "16001600px\u00a0\u00a0\u00a01锛?",
ratios: ["1600×1600px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1600×1600px\u00a0\u00a0\u00a01:1",
},
},
specs: ["鍟嗗搧鍥惧缓璁?1:1锛屼富浣撴竻鏅板眳涓?", "閫傚悎白底主图鍜屽瑙掑害灞曠ず鍥?"],
specs: ["商品图建议 1:1,主体清晰居中", "适合白底主图和多角度展示图"],
},
{
label: "TikTok Shop",
ratios: ["商品主图 1:1", "鐭棰?/ 竖版封面 9:16"],
ratios: ["商品主图 1:1", "短视频/ 竖版封面 9:16"],
defaultRatio: "商品主图 1:1",
ratioGroups: {
set: {
ratios: ["12801280px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "12801280px\u00a0\u00a0\u00a01锛?",
ratios: ["1280×1280px\u00a0\u00a0\u00a01:1"],
defaultRatio: "1280×1280px\u00a0\u00a0\u00a01:1",
},
detail: {
ratios: ["10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801350px\u00a0\u00a0\u00a04锛?",
ratios: ["1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1350px\u00a0\u00a0\u00a04:5",
},
model: {
ratios: ["10801350px\u00a0\u00a0\u00a04锛?"],
defaultRatio: "10801350px\u00a0\u00a0\u00a04锛?",
ratios: ["1080×1350px\u00a0\u00a0\u00a04:5"],
defaultRatio: "1080×1350px\u00a0\u00a0\u00a04:5",
},
video: {
ratios: ["10801920px\u00a0\u00a0\u00a09锛?6"],
defaultRatio: "10801920px\u00a0\u00a0\u00a09锛?6",
ratios: ["1080×1920px\u00a0\u00a0\u00a09:16"],
defaultRatio: "1080×1920px\u00a0\u00a0\u00a09:16",
},
hot: {
ratios: ["800800px\u00a0\u00a0\u00a01锛?"],
defaultRatio: "800800px\u00a0\u00a0\u00a01锛?",
ratios: ["800×800px\u00a0\u00a0\u00a01:1"],
defaultRatio: "800×800px\u00a0\u00a0\u00a01:1",
},
},
specs: ["商品主图建议 1:1", "鐭棰?竖版封面建议 9:16"],
specs: ["商品主图建议 1:1", "短视频竖版封面建议 9:16"],
},
];
const platformOptions = platformSpecOptions.map((option) => option.label);
@@ -684,7 +684,7 @@ const getPlatformLogoSources = (value: string) => {
if (normalized.includes("shopee")) return [shopeeLogo];
if (normalized.includes("lazada")) return [lazadaLogo];
if (normalized.includes("instagram")) return [instagramLogo];
if (value.includes("速卖通") || value.includes("閫熷崠閫")) return [aliexpressLogo];
if (value.includes("速卖通")) return [aliexpressLogo];
if (normalized.includes("ebay")) return [ebayLogo];
if (normalized.includes("tiktok")) return [tiktokShopLogo];
return [];
@@ -744,7 +744,7 @@ const languageAliases: Record<string, string> = {
"西班牙语": "西班牙语",
"葡文": "葡萄牙语",
"葡萄牙语": "葡萄牙语",
"鍗板凹璇?": "印度尼西亚语",
"印尼语": "印度尼西亚语",
"印度尼西亚语": "印度尼西亚语",
"菲律宾语": "菲律宾语(他加禄语)",
"菲律宾语(他加禄语)": "菲律宾语(他加禄语)",
@@ -755,10 +755,10 @@ const getPlatformSpec = (value: string) =>
const legacyPlatformAliases: Record<string, string> = {
"淘宝/天猫": "淘宝/天猫",
"京东": "京东",
"鎷煎澶?": "拼多多",
"拼多多": "拼多多",
"抖音电商": "抖音电商",
"浜氶┈閫?Amazon": "亚马逊 Amazon",
"閫熷崠閫?": "速卖通",
"亚马逊Amazon": "亚马逊 Amazon",
"速卖通": "速卖通",
};
const normalizePlatform = (value: string) => getPlatformSpec(legacyPlatformAliases[value] ?? value).label;
const domesticPlatformLabels = new Set(["淘宝/天猫", "京东", "拼多多", "抖音电商"]);
@@ -811,13 +811,13 @@ const formatRatioDisplayValue = (value: string) => {
return `${width}×${height}px\u00a0\u00a0\u00a0${formatAspectRatio(width, height)}`;
}
return normalizedValue
.replace("淘宝主图 / SKU 鍥?", "淘宝主图 / SKU 图 ")
.replace("京东主图 / SKU 鍥?", "京东主图 / SKU 图 ")
.replace("淘宝主图 / SKU ", "淘宝主图 / SKU 图 ")
.replace("京东主图 / SKU ", "京东主图 / SKU 图 ")
.replace("详情页宽", "详情页宽")
.replace("鐭棰?", "短视频")
.replace("短视频", "短视频")
.replace("主图", "主图")
.replace("商品主图", "商品主图")
.replace("鍟嗗搧鍥?", "商品图");
.replace("商品图", "商品图");
};
/** Extract CSS aspect-ratio from a ratio string like "1000x1000px 1:1" -> "1 / 1" */
const parseRatioToAspectCss = (ratioStr: string): string => {
@@ -825,7 +825,7 @@ const parseRatioToAspectCss = (ratioStr: string): string => {
if (!match) return "1 / 1";
return `${match[1]} / ${match[2]}`;
};
/** Normalize ratio display string ("10001000px 1锛?") to API format ("1:1") */
/** Normalize ratio display string ("1000×1000px 1:1") to API format ("1:1") */
const normalizeRatioForApi = (ratioStr: string): string => {
const match = ratioStr.match(/(\d+)\D+(\d+)/u);
if (!match) return "1:1";
@@ -938,7 +938,7 @@ const productSetAssets = ossAssets.ecommerce.productSet;
const productSetPreviewCards = [
{ id: "main", label: "01 主图 (白底/合规)", src: productSetAssets.main },
{ id: "scene", label: "02 场景展示", src: productSetAssets.scene },
{ id: "model", label: "03 妯$壒鍦烘櫙鍥?", src: productSetAssets.model },
{ id: "model", label: "03 模特场景图", src: productSetAssets.model },
{ id: "detail", label: "04 细节说明", src: productSetAssets.detail },
{ id: "selling", label: "05 卖点详解", src: productSetAssets.selling },
];
@@ -998,6 +998,22 @@ function getImageFileFormat(file: File) {
return file.name.split(".").pop()?.toUpperCase() ?? "";
}
function getRemoteImageFormat(mimeType: string, imageUrl: string) {
const mimeFormat = mimeType.split("/")[1]?.replace("jpeg", "jpg").toUpperCase();
if (mimeFormat) return mimeFormat;
return imageUrl.split("?")[0].split(".").pop()?.toUpperCase() ?? "IMAGE";
}
function getRemoteImageName(imageUrl: string, fallback: string) {
try {
const parsed = new URL(imageUrl);
const filename = decodeURIComponent(parsed.pathname.split("/").filter(Boolean).pop() || "");
return filename || fallback;
} catch {
return fallback;
}
}
function readImageDimensions(src: string): Promise<{ width: number; height: number }> {
return new Promise((resolve, reject) => {
const image = new Image();
@@ -1219,7 +1235,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const cloneReferenceInputRef = useRef<HTMLInputElement>(null);
const smartCutoutInputRef = useRef<HTMLInputElement>(null);
const imageWorkbenchInputRef = useRef<HTMLInputElement>(null);
const imageWorkbenchUrlInputRef = useRef<HTMLInputElement>(null);
const watermarkInputRef = useRef<HTMLInputElement>(null);
const watermarkUrlInputRef = useRef<HTMLInputElement>(null);
const watermarkProcessTimeoutRef = useRef<number | null>(null);
const smartCutoutTransitionTimeoutRef = useRef<number | null>(null);
const smartCutoutPendingUrlsRef = useRef<string[]>([]);
@@ -1698,7 +1716,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
});
setProductSetStatus("ready");
} catch (err) {
toast.error(err instanceof Error ? err.message : "鍟嗗搧鍥句笂浼犲け璐?");
toast.error(err instanceof Error ? err.message : "商品图上传失败");
}
};
@@ -1793,6 +1811,49 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
setIsCloneSettingsCollapsed(false);
};
const loadRemoteImageFromInput = async (input: HTMLInputElement | null, fallbackName: string) => {
const rawValue = input?.value.trim() ?? "";
if (!rawValue) {
toast.info("请先粘贴图片 URL");
return null;
}
let imageUrl: string;
try {
const parsed = new URL(rawValue, window.location.href);
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new Error("仅支持 http 或 https 图片链接");
}
imageUrl = parsed.toString();
} catch (error) {
toast.error(error instanceof Error ? error.message : "图片 URL 不正确");
return null;
}
try {
const response = await fetch(imageUrl);
if (!response.ok) throw new Error(`图片读取失败(${response.status}`);
const blob = await response.blob();
if (!blob.type.startsWith("image/")) throw new Error("链接内容不是图片");
const src = URL.createObjectURL(blob);
try {
await readImageDimensions(src);
} catch {
URL.revokeObjectURL(src);
throw new Error("图片无法预览,请换一个链接");
}
if (input) input.value = "";
return {
src,
name: getRemoteImageName(imageUrl, fallbackName),
format: getRemoteImageFormat(blob.type, imageUrl),
};
} catch (error) {
toast.error(error instanceof Error ? error.message : "图片导入失败");
return null;
}
};
const closeWatermarkRemovalPage = () => {
if (watermarkProcessTimeoutRef.current !== null) {
window.clearTimeout(watermarkProcessTimeoutRef.current);
@@ -1846,6 +1907,18 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
if (file) addWatermarkImage(file);
};
const handleWatermarkUrlImport = async () => {
const nextImage = await loadRemoteImageFromInput(watermarkUrlInputRef.current, "watermark-source");
if (!nextImage) return;
setWatermarkImage((current) => {
if (current?.src) URL.revokeObjectURL(current.src);
return nextImage;
});
setWatermarkStatus("idle");
setActiveQuickTool("watermark");
toast.success("图片已导入");
};
const handleWatermarkGenerate = () => {
if (!watermarkImage || watermarkStatus === "processing") return;
if (watermarkProcessTimeoutRef.current !== null) window.clearTimeout(watermarkProcessTimeoutRef.current);
@@ -1943,6 +2016,22 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
if (file) addImageWorkbenchImage(file);
};
const handleImageWorkbenchUrlImport = async () => {
const nextImage = await loadRemoteImageFromInput(imageWorkbenchUrlInputRef.current, "image-workbench-source");
if (!nextImage) return;
setImageWorkbenchImage((current) => {
if (current?.src) URL.revokeObjectURL(current.src);
return nextImage;
});
setImageWorkbenchStatus("idle");
setImageWorkbenchMaskStrokes([]);
setImageWorkbenchBrushCursor(null);
clearImageWorkbenchMaskCanvas();
imageWorkbenchActiveStrokeIdRef.current = null;
setActiveQuickTool("image-edit");
toast.success("图片已导入");
};
const handleImageWorkbenchGenerate = () => {
if (!imageWorkbenchImage) {
toast.info("请先上传图片");
@@ -2444,7 +2533,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
setStatus("ready");
setResults([]);
} catch (err) {
toast.error(err instanceof Error ? err.message : "鍟嗗搧鍥句笂浼犲け璐?");
toast.error(err instanceof Error ? err.message : "商品图上传失败");
}
};
@@ -2961,7 +3050,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
setGarmentImages((current) => [...current, ...nextImages].slice(0, 5));
setTryOnStatus("ready");
} catch (err) {
toast.error(err instanceof Error ? err.message : "鏈嶉グ鍥句笂浼犲け璐?");
toast.error(err instanceof Error ? err.message : "服饰图上传失败");
}
})();
event.target.value = "";
@@ -3011,7 +3100,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
setDetailStatus("ready");
setDetailResultUrl(null);
} catch (err) {
toast.error(err instanceof Error ? err.message : "璇︽儏鍥句笂浼犲け璐?");
toast.error(err instanceof Error ? err.message : "详情图上传失败");
}
};
@@ -3040,9 +3129,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const IMAGE_MODEL = "gpt-image-2";
const setCountLabels: Record<CloneSetCountKey, { label: string; promptDesc: string }> = {
selling: { label: "鍗栫偣鍥?", promptDesc: "selling-point infographic image highlighting core product advantages and detail close-ups" },
white: { label: "白底鍥?", promptDesc: "clean white-background product photo showing the item from its best angle, studio lighting, no props" },
scene: { label: "鍦烘櫙鍥?", promptDesc: "lifestyle scene image showing the product in a realistic usage environment with natural surroundings" },
selling: { label: "卖点图", promptDesc: "selling-point infographic image highlighting core product advantages and detail close-ups" },
white: { label: "白底", promptDesc: "clean white-background product photo showing the item from its best angle, studio lighting, no props" },
scene: { label: "场景图", promptDesc: "lifestyle scene image showing the product in a realistic usage environment with natural surroundings" },
};
const buildDetailModulePrompt = (moduleIds: string[]): string => {
@@ -3182,7 +3271,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
imageGen.updateTask(storeId, { status: "completed", progress: 100, resultUrl: persistedUrl });
} else {
generatedUrls.push("");
imageGen.updateTask(storeId, { status: "failed", error: "生成鏈繑鍥炵粨鏋?" });
imageGen.updateTask(storeId, { status: "failed", error: "生成未返回结果" });
}
}
}
@@ -3270,7 +3359,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
imageGen.updateTask(storeId, { status: "completed", progress: 100, resultUrl: persistedUrl });
} else {
statusFn?.("idle");
imageGen.updateTask(storeId, { status: "failed", error: "生成鏈繑鍥炵粨鏋?" });
imageGen.updateTask(storeId, { status: "failed", error: "生成未返回结果" });
}
} catch (err) {
if (imageAbortRef.current.current) {
@@ -3467,7 +3556,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const handleDetailAiWrite = () => {
setDetailRequirement(
"1.产品名称:无线降噪蓝牙耳机\n2.鏍稿績鍗栫偣锛氫富鍔ㄩ檷鍣€?4H续航、低延迟连接、舒适佩戴\n3.适用人群:通勤、办公、运动和旅行用户\n4.期望场景:地铁通勤、居家办公、户外运动\n5.鍏蜂綋参数锛氳摑鐗?.3銆両PX4闃叉按銆佸揩鍏?0分钟使用2小时",
"1.产品名称:无线降噪蓝牙耳机\n2.核心卖点:主动降噪、24H续航、低延迟连接、舒适佩戴\n3.适用人群:通勤、办公、运动和旅行用户\n4.期望场景:地铁通勤、居家办公、户外运动\n5.具体参数:蓝牙5.3、IPX4防水、快充10分钟使用2小时",
);
};
@@ -3547,19 +3636,19 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const isWatermarkTool = isCloneTool && activeQuickTool === "watermark";
const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
const isHotVideoTool = isCloneTool && activeQuickTool === "hot-video";
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/璇︽儏椤?" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
const setPrimaryLabel =
setImages.length === 0
? "请先上传商品原图"
: productSetStatus === "generating"
? "生成涓?.."
? "生成中..."
: "生成" + selectedProductSetOutput.label;
const tryOnPrimaryLabel =
garmentImages.length === 0 ? "请先上传服装图片" : tryOnStatus === "generating" ? "生成涓?.." : "生成服饰穿戴鍥?";
garmentImages.length === 0 ? "请先上传服装图片" : tryOnStatus === "generating" ? "生成中..." : "生成服饰穿戴";
const detailPrimaryLabel =
detailProductImages.length === 0 ? "请上传产品图" : detailStatus === "generating" ? "生成涓?.." : "生成A+璇︽儏椤?";
detailProductImages.length === 0 ? "请上传产品图" : detailStatus === "generating" ? "生成中..." : "生成A+详情页";
const clonePrimaryLabel =
productImages.length === 0 ? "请先上传商品原图" : status === "generating" ? "生成涓?.." : "生成" + selectedCloneOutput.label;
productImages.length === 0 ? "请先上传商品原图" : status === "generating" ? "生成中..." : "生成" + selectedCloneOutput.label;
const setPreviewCards: CloneResult[] = [];
let setIndex = 0;
for (const countKey of cloneSetCountKeys) {
@@ -3693,7 +3782,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
if (historyRefreshLockRef.current) return;
historyRefreshLockRef.current = true;
setIsHistoryRefreshing(true);
setHistoryRefreshMessage("鍒锋柊涓?..");
setHistoryRefreshMessage("刷新中...");
setHistoryRefreshStamp(Date.now());
window.setTimeout(() => {
@@ -4023,7 +4112,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
value={productSetRequirement}
onChange={(event) => setProductSetRequirement(event.target.value)}
maxLength={500}
placeholder="建议鍖呭惈浠ヤ笅淇℃伅锛氫骇鍝佸悕绉般€佹牳蹇冨崠鐐广€佹湡鏈涘満鏅€佸叿浣撳弬鏁?"
placeholder="建议包含以下信息:产品名称、核心卖点、期望场景、具体参数"
/>
<button type="button" className="product-set-floating-submit" disabled={!canGenerateSet} onClick={handleSetGenerate}>
{productSetStatus === "generating" ? <LoadingOutlined /> : null}
@@ -4290,7 +4379,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
<span className="clone-ai-flow-node__label"></span>
</div>
{/* Connector —鍒嗘敮杩炴帴绾?*/}
{/* Connector - branch lines */}
<div className="clone-ai-flow-connector" aria-hidden="true">
<div className="clone-ai-flow-connector__trunk" />
<div className="clone-ai-flow-connector__branches">
@@ -4503,7 +4592,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
<strong></strong>
</button>
{productImages.length || videoOutfitVideoFile ? (
<div className="ecom-command-asset-popover" aria-label="宸蹭笂浼犵礌鏉?">
<div className="ecom-command-asset-popover" aria-label="已上传素材">
{productImages.map((image) => (
<figure key={image.id} className="ecom-command-asset-thumb">
<img src={image.src} alt={image.name || "上传图片"} />
@@ -4675,7 +4764,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
);
const smartCutoutPreview = (
<main className={`ecom-smart-cutout-page${smartCutoutImage ? " is-editor" : " is-upload"}${isSmartCutoutTransitioning ? " is-transitioning" : ""}`} aria-label="智能抠图">
<main key={`smart-cutout-${smartCutoutImage ? "editor" : "upload"}`} className={`ecom-smart-cutout-page ecom-tool-page-enter${smartCutoutImage ? " is-editor" : " is-upload"}${isSmartCutoutTransitioning ? " is-transitioning" : ""}`} aria-label="智能抠图">
<input
ref={smartCutoutInputRef}
type="file"
@@ -4950,7 +5039,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
);
const imageWorkbenchPreview = (
<main className="ecom-image-workbench-page" aria-label="图片修改局部重绘">
<main key="image-workbench" className="ecom-image-workbench-page ecom-tool-page-enter" aria-label="图片修改局部重绘">
<input
ref={imageWorkbenchInputRef}
type="file"
@@ -5021,8 +5110,15 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
)}
</div>
<div className="ecom-image-workbench-url-row">
<input placeholder="粘贴图片 URL" aria-label="粘贴图片 URL" />
<button type="button" onClick={() => toast.info("请先使用本地上传")}></button>
<input
ref={imageWorkbenchUrlInputRef}
placeholder="粘贴图片 URL"
aria-label="粘贴图片 URL"
onKeyDown={(event) => {
if (event.key === "Enter") void handleImageWorkbenchUrlImport();
}}
/>
<button type="button" onClick={() => void handleImageWorkbenchUrlImport()}></button>
</div>
</section>
@@ -5155,7 +5251,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
);
const watermarkPreview = (
<main className="ecom-watermark-page" aria-label="去水印">
<main key="watermark" className="ecom-watermark-page ecom-tool-page-enter" aria-label="去水印">
<input
ref={watermarkInputRef}
type="file"
@@ -5165,7 +5261,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
/>
<aside className="ecom-watermark-side">
<header className="ecom-quick-set-panel-head ecom-watermark-panel-head">
<strong className="ecom-quick-set-page-title">/</strong>
<strong className="ecom-quick-set-page-title"></strong>
<button type="button" className="ecom-quick-set-back" onClick={closeWatermarkRemovalPage}></button>
<button type="button" className="ecom-quick-set-back" onClick={closeWatermarkRemovalPage}></button>
</header>
@@ -5225,8 +5321,15 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
)}
</div>
<div className="ecom-watermark-url-row">
<input placeholder="粘贴图片 URL" aria-label="粘贴图片 URL" />
<button type="button" onClick={() => toast.info("请先使用本地上传")}></button>
<input
ref={watermarkUrlInputRef}
placeholder="粘贴图片 URL"
aria-label="粘贴图片 URL"
onKeyDown={(event) => {
if (event.key === "Enter") void handleWatermarkUrlImport();
}}
/>
<button type="button" onClick={() => void handleWatermarkUrlImport()}></button>
</div>
</section>
@@ -5350,7 +5453,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
);
const hotVideoPreview = (
<main className="ecom-quick-set-page ecom-hot-video-page" aria-label="广告视频">
<main key="hot-video" className="ecom-quick-set-page ecom-hot-video-page ecom-tool-page-enter" aria-label="广告视频">
<div className="ecom-quick-set-body">
<aside className="ecom-quick-set-panel" aria-label="广告视频设置">
<header className="ecom-quick-set-panel-head">
@@ -5475,7 +5578,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
);
const quickProductSetPreview = (
<main className={`ecom-quick-set-page${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="AI商品套图">
<main key="quick-set" className={`ecom-quick-set-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="AI商品套图">
<div className="ecom-quick-set-body">
<aside className="ecom-quick-set-panel" aria-label="商品套图设置" onWheel={handleQuickPanelWheel}>
<header className="ecom-quick-set-panel-head">
@@ -5621,14 +5724,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
</section>
</section>
</div>
<button
type="button"
className="ecom-quick-set-collapse"
aria-label={isQuickPanelCollapsed ? "展开设置" : "收起设置"}
onClick={() => setIsQuickPanelCollapsed((value) => !value)}
>
{isQuickPanelCollapsed ? "" : ""}
</button>
<button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传商品原图后,可选择平台、语言、比例并开始生成。")}>?</button>
</main>
);
@@ -5636,7 +5731,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
const quickDetailPreview = (
<main className={`ecom-quick-set-page ecom-quick-detail-page${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
<main key="quick-detail" className={`ecom-quick-set-page ecom-quick-detail-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
<div className="ecom-quick-set-body">
<aside className="ecom-quick-set-panel" aria-label="A+详情页设置" onWheel={handleQuickPanelWheel}>
<header className="ecom-quick-set-panel-head">
@@ -5794,14 +5889,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
</section>
</section>
</div>
<button
type="button"
className="ecom-quick-set-collapse"
aria-label={isQuickPanelCollapsed ? "展开设置" : "收起设置"}
onClick={() => setIsQuickPanelCollapsed((value) => !value)}
>
{isQuickPanelCollapsed ? "" : ""}
</button>
<button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传商品图后,选择平台和详情模块即可生成 A+ 详情页。")}>?</button>
</main>
);
@@ -5930,7 +6017,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
? smartCutoutPreview
: isQuickSetTool || isQuickDetailTool || isHotVideoTool
? (
<div className="ecom-quick-page-wrap">
<div key={`quick-${activeQuickTool}`} className="ecom-quick-page-wrap ecom-tool-page-enter">
{quickPageSidebar}
{isQuickSetTool ? quickProductSetPreview : isQuickDetailTool ? quickDetailPreview : hotVideoPreview}
</div>
@@ -5948,7 +6035,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
return (
<section
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickSetTool || isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotVideoTool ? " is-hot-video-page" : ""}`}
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickSetTool || isQuickDetailTool || isHotVideoTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotVideoTool ? " is-hot-video-page" : ""}`}
data-tool={activeTool}
aria-label={pageLabel}
>
@@ -5994,7 +6081,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
productImageFiles={ecommerceVideoImageFiles}
requirement={requirement}
platform={platform}
aspectRatio={ratio.includes("9锛?6") || ratio.includes("9:16") ? "9:16" : ratio.includes("16锛?") || ratio.includes("16:9") ? "16:9" : ratio.includes("3锛?") || ratio.includes("3:4") ? "3:4" : "9:16"}
aspectRatio={ratio.includes("9:16") || ratio.includes("9:16") ? "9:16" : ratio.includes("16:9") || ratio.includes("16:9") ? "16:9" : ratio.includes("3:4") || ratio.includes("3:4") ? "3:4" : "9:16"}
durationSeconds={cloneVideoDuration}
resolution={cloneVideoQuality === "standard" ? "720P" : "1080P"}
onRequestLogin={() => (isAuthenticated ? undefined : requestLogin())}
@@ -622,7 +622,6 @@ export default function EcommerceVideoWorkspace({
const imagedScenes = scenes.filter((s) => s.imageUrl);
const primaryVideo = completedScenes[0]?.resultUrl;
const sourceImage = sourceImageUrls[0] || planResult?.imageUrls[0] || productImageDataUrls[0] || "";
const flowHasStarted = stage !== "idle" || completedSteps.length > 0;
const flowMeta = `${platform} / ${aspectRatio} / ${durationSeconds}s / ${resolution}`;
const hasImaging = stage === "imaging" || stage === "imaged" || stage === "rendering" || stage === "completed" || stage === "partial_failed";
const hasRendering = stage === "rendering" || stage === "completed" || stage === "partial_failed";
@@ -630,11 +629,16 @@ export default function EcommerceVideoWorkspace({
return (
<div className="ecom-video-workspace" data-stage={stage}>
{/* ── Flow bar ──────────────────────────────────── */}
<header className="ecom-video-flowbar">
<div className="ecom-video-flowbar__title" aria-label={`短视频分镜流,${flowMeta}`} title={flowMeta}>
<span className={`ecom-video-flowbar__pulse${flowHasStarted ? " is-active" : ""}`} aria-hidden="true" />
<span className="ecom-video-flowbar__wave" aria-hidden="true"><i /><i /><i /></span>
{/* ── Preview header ─────────────────────────────── */}
<header className="ecom-video-flowbar ecom-video-preview-head" title={flowMeta}>
<div className="ecom-video-preview-copy">
<h1></h1>
<p>AI <span></span> </p>
<div className="ecom-video-flowbar__zoom">
<button type="button" onClick={() => setFlowZoom((z) => Math.max(0.25, z - 0.1))} disabled={flowZoom <= 0.25} aria-label="缩小"></button>
<span>{Math.round(flowZoom * 100)}%</span>
<button type="button" onClick={() => setFlowZoom((z) => Math.min(2, z + 0.1))} disabled={flowZoom >= 2} aria-label="放大">+</button>
</div>
</div>
<div className="ecom-video-step-dots" aria-label="策划进度">
@@ -645,12 +649,6 @@ export default function EcommerceVideoWorkspace({
})}
</div>
<div className="ecom-video-flowbar__zoom">
<button type="button" onClick={() => setFlowZoom((z) => Math.max(0.25, z - 0.1))} disabled={flowZoom <= 0.25} aria-label="缩小"></button>
<span>{Math.round(flowZoom * 100)}%</span>
<button type="button" onClick={() => setFlowZoom((z) => Math.min(2, z + 0.1))} disabled={flowZoom >= 2} aria-label="放大">+</button>
</div>
<div className="ecom-video-flowbar__actions">
{onOpenHistory ? (
<button type="button" className="ecom-video-flow-action ecom-video-flow-action--ghost" onClick={onOpenHistory} title="生成记录">
+171 -28
View File
@@ -2394,7 +2394,21 @@
.ecommerce-standalone .ecommerce-auth-modal__scrim,
.ecommerce-standalone .ecommerce-profile-popover__backdrop,
.ecommerce-standalone .product-set-preview-backdrop,
.ecommerce-standalone .ecom-command-asset-popover {
.ecommerce-standalone .ecom-command-asset-popover,
.ecommerce-standalone .ecom-tool-page-enter,
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-page-sidebar,
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-side,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-side,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-cutout-nav,
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-stage,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-workspace,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-stage,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-cutout-upload,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-editor,
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel > section,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-panel,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-panel {
animation: none !important;
}
@@ -4028,6 +4042,10 @@
/* Smart cutout quick tool: standalone upload and editor flow. */
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-smart-cutout-page {
display: block !important;
width: 100% !important;
min-width: 100% !important;
height: 100% !important;
min-height: calc(100vh - 58px) !important;
overflow: hidden !important;
background: #feffff !important;
}
@@ -4035,7 +4053,9 @@
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-smart-cutout-page .product-clone-shell {
display: block !important;
width: 100% !important;
min-width: 100% !important;
height: 100% !important;
min-height: calc(100vh - 58px) !important;
padding: 0 !important;
}
@@ -4797,6 +4817,63 @@
font-weight: 750 !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-head {
display: grid !important;
grid-template-columns: minmax(0, 1fr) auto !important;
align-items: start !important;
gap: 16px !important;
min-height: 0 !important;
padding: 0 0 18px !important;
border: 0 !important;
border-bottom: 1px solid rgba(16, 115, 204, 0.08) !important;
background: transparent !important;
box-shadow: none !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-copy {
display: grid !important;
gap: 8px !important;
justify-items: start !important;
min-width: 0 !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-copy h1 {
margin: 0 !important;
color: #172636 !important;
font-size: 21px !important;
font-weight: 950 !important;
line-height: 1.25 !important;
letter-spacing: 0 !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-copy p {
margin: 0 !important;
color: #657686 !important;
font-size: 13px !important;
font-weight: 750 !important;
line-height: 1.55 !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-copy p span {
color: #1073cc !important;
font-weight: 950 !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-head .ecom-video-flowbar__zoom {
display: inline-flex !important;
align-items: center !important;
gap: 8px !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-head .ecom-video-step-dots {
display: none !important;
}
.ecommerce-standalone .ecom-hot-video-page .ecom-video-preview-head .ecom-video-flowbar__actions {
align-self: start !important;
min-height: 24px !important;
}
/* ── Step dots — light theme ──────────────────────── */
.ecommerce-standalone .ecom-hot-video-page .ecom-video-step-dot {
background: #cfd8dc !important;
@@ -7649,33 +7726,7 @@
}
.ecommerce-standalone .ecom-quick-set-collapse {
position: absolute !important;
left: 428px !important;
top: 50% !important;
z-index: 30 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 42px !important;
height: 42px !important;
min-height: 42px !important;
border: 1px solid rgba(30, 189, 219, 0.22) !important;
border-radius: 15px !important;
color: #1073cc !important;
background: #feffff !important;
box-shadow: 0 16px 34px rgba(16, 115, 204, 0.14) !important;
font-size: 25px !important;
font-weight: 800 !important;
line-height: 1 !important;
cursor: pointer !important;
transform: translateY(-50%) !important;
transition:
left 680ms cubic-bezier(0.22, 1, 0.36, 1),
transform 220ms ease,
border-color 260ms ease,
background 260ms ease,
color 260ms ease,
box-shadow 460ms ease !important;
display: none !important;
}
.ecommerce-standalone .ecom-quick-set-collapse:hover {
@@ -7692,7 +7743,9 @@
.ecommerce-standalone .ecom-smart-cutout-page {
position: relative !important;
width: 100% !important;
min-width: 100% !important;
height: 100% !important;
min-height: calc(100vh - 58px) !important;
overflow: auto !important;
color: #111820 !important;
background: #feffff !important;
@@ -8837,6 +8890,96 @@
}
}
@keyframes ecom-tool-page-enter {
from {
opacity: 0;
transform: translateY(14px) scale(0.992);
filter: saturate(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
filter: saturate(1);
}
}
@keyframes ecom-tool-panel-enter {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes ecom-tool-side-enter {
from {
opacity: 0;
transform: translateX(-14px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes ecom-tool-stage-enter {
from {
opacity: 0;
transform: translateX(16px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.ecommerce-standalone .ecom-tool-page-enter {
animation: ecom-tool-page-enter 460ms cubic-bezier(0.16, 1, 0.3, 1) both !important;
will-change: opacity, transform;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-page-sidebar,
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-side,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-side,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-cutout-nav {
animation: ecom-tool-side-enter 430ms cubic-bezier(0.16, 1, 0.3, 1) 40ms both !important;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-stage,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-workspace,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-stage,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-cutout-upload,
.ecommerce-standalone .ecom-tool-page-enter .ecom-smart-editor {
animation: ecom-tool-stage-enter 500ms cubic-bezier(0.16, 1, 0.3, 1) 90ms both !important;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel > section,
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-panel,
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-panel {
animation: ecom-tool-panel-enter 420ms cubic-bezier(0.16, 1, 0.3, 1) both !important;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel > section:nth-of-type(1),
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-panel:nth-of-type(1),
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-panel:nth-of-type(1) {
animation-delay: 80ms !important;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel > section:nth-of-type(2),
.ecommerce-standalone .ecom-tool-page-enter .ecom-watermark-panel:nth-of-type(2),
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-panel:nth-of-type(2) {
animation-delay: 130ms !important;
}
.ecommerce-standalone .ecom-tool-page-enter .ecom-quick-set-panel > section:nth-of-type(3),
.ecommerce-standalone .ecom-tool-page-enter .ecom-image-workbench-panel:nth-of-type(3) {
animation-delay: 180ms !important;
}
@keyframes ecom-smart-upload-enter {
from {
opacity: 0;