generateImagesPlan.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import canvasTemplate from './canvas.json'
  2. import goodsTemplate from './goods.json'
  3. import goodsTemplate1 from './goods1.json'
  4. /**
  5. * 规范化商品数据:拍平为一个货号数组
  6. * 每个元素结构:
  7. * {
  8. * sku: 'A596351',
  9. * color: '黑色',
  10. * pics: { '俯视': '...', '侧视': '...' },
  11. * design: '设计理念文案',
  12. * styleKey: 'AC5120913',
  13. * styleNo: 'E305-01003',
  14. * raw: 原始对象
  15. * }
  16. */
  17. export function normalizeGoods(goodsList = []) {
  18. const skus = []
  19. goodsList.forEach(group => {
  20. if (!group) return
  21. Object.entries(group).forEach(([styleKey, entry]) => {
  22. if (!entry || !Array.isArray(entry['货号资料'])) return
  23. const styleNo = entry['款号'] || styleKey
  24. entry['货号资料'].forEach(sku => {
  25. const design = sku['设计理念'] || entry['设计理念'] || ''
  26. skus.push({
  27. sku: sku['货号'],
  28. color: sku['颜色'],
  29. pics: sku['pics'] || {},
  30. design,
  31. styleKey,
  32. styleNo,
  33. // raw 中同时带上整条 entry(可能包含 卖点 / 使用场景 等其他字段)
  34. raw: {
  35. ...entry,
  36. ...sku,
  37. },
  38. })
  39. })
  40. })
  41. })
  42. return skus
  43. }
  44. /**
  45. * 根据画布配置和商品数据,计算每个画布需要生成多少张图片、每张图片使用哪些货号。
  46. *
  47. * 这里只做「任务规划」,不真正去用 fabric 生成图片,方便在任意地方静默调用。
  48. *
  49. * @param {Array} canvasList - 画布配置数组(同 marketingEdit 保存出去的结构)
  50. * @param {Array} goodsList - 商品数据数组,例如 [goods, goods1]
  51. * @returns {Array} renderPlans
  52. *
  53. * renderPlans 结构示例:
  54. * [
  55. * {
  56. * canvasIndex: 2,
  57. * multi_goods_mode: 'multiple',
  58. * max_goods_count: 2,
  59. * perCanvasSlots: 2, // 此画布里,单张图最多可展示的货号数(same-angle 图层个数)
  60. * images: [
  61. * { imageIndex: 0, skuIndexes: [0,1] },
  62. * { imageIndex: 1, skuIndexes: [2,null] } // null 表示该位置需要隐藏图层
  63. * ]
  64. * },
  65. * ...
  66. * ]
  67. */
  68. export function buildRenderPlans(canvasList = canvasTemplate, goodsList = [goodsTemplate, goodsTemplate1]) {
  69. const skus = normalizeGoods(goodsList)
  70. const totalSkuCount = skus.length
  71. if (!totalSkuCount) return []
  72. const plans = []
  73. canvasList.forEach((canvasItem, cIdx) => {
  74. if (!canvasItem || !canvasItem.canvas_json) return
  75. const mode = canvasItem.multi_goods_mode || '' // '' | 'single' | 'multiple'
  76. const maxGoods = Number(canvasItem.max_goods_count) || null
  77. // 解析 canvas_json,统计「货号相关图层」数量,用于 multiple 模式下计算每张图可放几个货号
  78. let perCanvasSlots = 1
  79. try {
  80. const json = typeof canvasItem.canvas_json === 'string'
  81. ? JSON.parse(canvasItem.canvas_json)
  82. : canvasItem.canvas_json
  83. const objs = (json && Array.isArray(json.objects)) ? json.objects : []
  84. const imgPlaceholders = objs.filter(o => o && o['data-type'] === 'img')
  85. if (mode === 'multiple') {
  86. perCanvasSlots = Math.max(imgPlaceholders.length, 1)
  87. } else {
  88. perCanvasSlots = 1
  89. }
  90. } catch (e) {
  91. perCanvasSlots = 1
  92. }
  93. const images = []
  94. if (!mode) {
  95. /**
  96. * 默认(单货号):
  97. * - 无论有多少货号,只生成 1 张图片
  98. * - 画布内所有动态文字 / 图片都使用「第一个货号」的数据
  99. */
  100. images.push({
  101. imageIndex: 0,
  102. skuIndexes: [0], // 始终只用第一个货号
  103. })
  104. } else if (mode === 'single') {
  105. /**
  106. * 一个货号多角度:
  107. * - 一张图只展示一个货号
  108. * - 生成张数 = min(总货号数, 最多货号数量)
  109. * 例:总 3 个货号,最多货号数为 2 -> 生成 2 张图,分别用前两个货号
  110. */
  111. const limit = maxGoods
  112. ? Math.min(totalSkuCount, maxGoods)
  113. : totalSkuCount
  114. for (let i = 0; i < limit; i++) {
  115. images.push({
  116. imageIndex: i,
  117. skuIndexes: [i], // 每张图只使用一个货号
  118. })
  119. }
  120. } else if (mode === 'multiple') {
  121. /**
  122. * 多个货号同角度:
  123. * - 一张图里可以有 perCanvasSlots 个货号位(根据画布中 img 占位层数量推算)
  124. * - 需要的张数 = ceil(总货号数 / 每张可展示数)
  125. * - 最终张数 = min(需要的张数, 最多货号数量(如果有的话))
  126. * 例:画布中有 2 个货号位,商品有 3 个货号,最多货号数=4
  127. * -> 需要张数 ceil(3/2)=2,受限于 4 -> 实际生成 2 张:
  128. * 第 1 张:[sku0, sku1],第 2 张:[sku2, null]
  129. */
  130. let maxImagesByGoods = Math.ceil(totalSkuCount / perCanvasSlots)
  131. if (maxGoods) {
  132. maxImagesByGoods = Math.min(maxImagesByGoods, maxGoods)
  133. }
  134. for (let i = 0; i < maxImagesByGoods; i++) {
  135. const skuIndexes = []
  136. for (let s = 0; s < perCanvasSlots; s++) {
  137. const skuIdx = i * perCanvasSlots + s
  138. skuIndexes.push(skuIdx < totalSkuCount ? skuIdx : null)
  139. }
  140. images.push({
  141. imageIndex: i,
  142. skuIndexes,
  143. })
  144. }
  145. }
  146. if (!images.length) return
  147. plans.push({
  148. canvasIndex: cIdx,
  149. multi_goods_mode: mode,
  150. max_goods_count: maxGoods,
  151. perCanvasSlots,
  152. images,
  153. })
  154. })
  155. return plans
  156. }