import canvasTemplate from './canvas.json' import goodsTemplate from './goods.json' import goodsTemplate1 from './goods1.json' /** * 规范化商品数据:拍平为一个货号数组 * 每个元素结构: * { * sku: 'A596351', * color: '黑色', * pics: { '俯视': '...', '侧视': '...' }, * design: '设计理念文案', * styleKey: 'AC5120913', * styleNo: 'E305-01003', * raw: 原始对象 * } */ export function normalizeGoods(goodsList = []) { const skus = [] goodsList.forEach(group => { if (!group) return Object.entries(group).forEach(([styleKey, entry]) => { if (!entry || !Array.isArray(entry['货号资料'])) return const styleNo = entry['款号'] || styleKey entry['货号资料'].forEach(sku => { const design = sku['设计理念'] || entry['设计理念'] || '' skus.push({ sku: sku['货号'], color: sku['颜色'], pics: sku['pics'] || {}, design, styleKey, styleNo, // raw 中同时带上整条 entry(可能包含 卖点 / 使用场景 等其他字段) raw: { ...entry, ...sku, }, }) }) }) }) return skus } /** * 根据画布配置和商品数据,计算每个画布需要生成多少张图片、每张图片使用哪些货号。 * * 这里只做「任务规划」,不真正去用 fabric 生成图片,方便在任意地方静默调用。 * * @param {Array} canvasList - 画布配置数组(同 marketingEdit 保存出去的结构) * @param {Array} goodsList - 商品数据数组,例如 [goods, goods1] * @returns {Array} renderPlans * * renderPlans 结构示例: * [ * { * canvasIndex: 2, * multi_goods_mode: 'multiple', * max_goods_count: 2, * perCanvasSlots: 2, // 此画布里,单张图最多可展示的货号数(same-angle 图层个数) * images: [ * { imageIndex: 0, skuIndexes: [0,1] }, * { imageIndex: 1, skuIndexes: [2,null] } // null 表示该位置需要隐藏图层 * ] * }, * ... * ] */ export function buildRenderPlans(canvasList = canvasTemplate, goodsList = [goodsTemplate, goodsTemplate1]) { const skus = normalizeGoods(goodsList) const totalSkuCount = skus.length if (!totalSkuCount) return [] const plans = [] canvasList.forEach((canvasItem, cIdx) => { if (!canvasItem || !canvasItem.canvas_json) return const mode = canvasItem.multi_goods_mode || '' // '' | 'single' | 'multiple' const maxGoods = Number(canvasItem.max_goods_count) || null // 解析 canvas_json,统计「货号相关图层」数量,用于 multiple 模式下计算每张图可放几个货号 let perCanvasSlots = 1 try { const json = typeof canvasItem.canvas_json === 'string' ? JSON.parse(canvasItem.canvas_json) : canvasItem.canvas_json const objs = (json && Array.isArray(json.objects)) ? json.objects : [] const imgPlaceholders = objs.filter(o => o && o['data-type'] === 'img') if (mode === 'multiple') { perCanvasSlots = Math.max(imgPlaceholders.length, 1) } else { perCanvasSlots = 1 } } catch (e) { perCanvasSlots = 1 } const images = [] if (!mode) { /** * 默认(单货号): * - 无论有多少货号,只生成 1 张图片 * - 画布内所有动态文字 / 图片都使用「第一个货号」的数据 */ images.push({ imageIndex: 0, skuIndexes: [0], // 始终只用第一个货号 }) } else if (mode === 'single') { /** * 一个货号多角度: * - 一张图只展示一个货号 * - 生成张数 = min(总货号数, 最多货号数量) * 例:总 3 个货号,最多货号数为 2 -> 生成 2 张图,分别用前两个货号 */ const limit = maxGoods ? Math.min(totalSkuCount, maxGoods) : totalSkuCount for (let i = 0; i < limit; i++) { images.push({ imageIndex: i, skuIndexes: [i], // 每张图只使用一个货号 }) } } else if (mode === 'multiple') { /** * 多个货号同角度: * - 一张图里可以有 perCanvasSlots 个货号位(根据画布中 img 占位层数量推算) * - 需要的张数 = ceil(总货号数 / 每张可展示数) * - 最终张数 = min(需要的张数, 最多货号数量(如果有的话)) * 例:画布中有 2 个货号位,商品有 3 个货号,最多货号数=4 * -> 需要张数 ceil(3/2)=2,受限于 4 -> 实际生成 2 张: * 第 1 张:[sku0, sku1],第 2 张:[sku2, null] */ let maxImagesByGoods = Math.ceil(totalSkuCount / perCanvasSlots) if (maxGoods) { maxImagesByGoods = Math.min(maxImagesByGoods, maxGoods) } for (let i = 0; i < maxImagesByGoods; i++) { const skuIndexes = [] for (let s = 0; s < perCanvasSlots; s++) { const skuIdx = i * perCanvasSlots + s skuIndexes.push(skuIdx < totalSkuCount ? skuIdx : null) } images.push({ imageIndex: i, skuIndexes, }) } } if (!images.length) return plans.push({ canvasIndex: cIdx, multi_goods_mode: mode, max_goods_count: maxGoods, perCanvasSlots, images, }) }) return plans }