|
|
@@ -38,7 +38,7 @@ import headerBar from "@/components/header-bar/index.vue";
|
|
|
import marketingEdit from '@/views/components/marketingEdit/index.vue'
|
|
|
import useClientStore from '@/stores/modules/client'
|
|
|
|
|
|
-import { saveCustomerTemplate } from '@/apis/other'
|
|
|
+import { saveCustomerTemplate, uploadBaseImg } from '@/apis/other'
|
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
@@ -154,9 +154,62 @@ const doSave = async (payload: any) => {
|
|
|
// 新增时必填模板名称
|
|
|
requestData.template_name = templateName.value?.trim() || ''
|
|
|
}
|
|
|
- // 封面图:取第一张画布的 preview
|
|
|
- if (payload && payload.length > 0 && payload[0].preview) {
|
|
|
- requestData.template_cover_image = payload[0].preview
|
|
|
+ // 生成并上传模板封面:把每个画布的 preview(若有)合并为一张长图并上传,取回 URL
|
|
|
+ async function composeAndUpload(previewList: string[]) {
|
|
|
+ try {
|
|
|
+ const infos = await Promise.all(previewList.map(src => new Promise((res) => {
|
|
|
+ if (!src) return res(null)
|
|
|
+ const img = new Image()
|
|
|
+ img.crossOrigin = 'anonymous'
|
|
|
+ img.onload = () => res({ img, w: img.width, h: img.height })
|
|
|
+ img.onerror = () => res(null)
|
|
|
+ img.src = src
|
|
|
+ })))
|
|
|
+ const valid = infos.filter(Boolean)
|
|
|
+ if (!valid.length) return null
|
|
|
+ const maxW = Math.max(...valid.map(i => i.w))
|
|
|
+ const totalH = valid.reduce((s, i) => s + i.h, 0)
|
|
|
+ const canvas = document.createElement('canvas')
|
|
|
+ canvas.width = maxW
|
|
|
+ canvas.height = totalH
|
|
|
+ const ctx = canvas.getContext('2d')
|
|
|
+ if (!ctx) return null
|
|
|
+ ctx.fillStyle = '#fff'
|
|
|
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
+ let top = 0
|
|
|
+ for (const v of valid) {
|
|
|
+ const left = Math.round((maxW - v.w) / 2)
|
|
|
+ ctx.drawImage(v.img, left, top, v.w, v.h)
|
|
|
+ top += v.h
|
|
|
+ }
|
|
|
+ const dataUrl = canvas.toDataURL('image/jpeg', 0.9)
|
|
|
+ const res = await uploadBaseImg({ image: dataUrl })
|
|
|
+ return res?.data?.url || res?.url || null
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('composeAndUpload error', e)
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // collect previews from payload, use placeholders for model/scene canvases
|
|
|
+ const MODEL_PLACEHOLDER = 'https://ossimg.valimart.net/uploads/vali_ai/20251230/176707412156399.png'
|
|
|
+ const SCENE_PLACEHOLDER = 'https://ossimg.valimart.net/uploads/vali_ai/20251230/176707413531824.png'
|
|
|
+ const previews = (payload || []).map((p: any) => {
|
|
|
+ if (!p) return null
|
|
|
+ if (p.canvas_type === 'model') return MODEL_PLACEHOLDER
|
|
|
+ if (p.canvas_type === 'scene') return SCENE_PLACEHOLDER
|
|
|
+ return p.preview || null
|
|
|
+ }).filter(Boolean)
|
|
|
+ if (previews.length) {
|
|
|
+ const uploadedUrl = await composeAndUpload(previews)
|
|
|
+ if (uploadedUrl) requestData.template_preview_image = uploadedUrl
|
|
|
+ // also set cover to first preview if not set
|
|
|
+ if (!requestData.template_cover_image && previews[0]) requestData.template_cover_image = previews[0]
|
|
|
+ } else {
|
|
|
+ // fallback: if no previews, keep existing logic of using first canvas preview if available
|
|
|
+ if (payload && payload.length > 0 && payload[0].preview) {
|
|
|
+ requestData.template_cover_image = payload[0].preview
|
|
|
+ }
|
|
|
}
|
|
|
requestData.customer_template_images = goodsImages.value
|
|
|
requestData.template_image_order = templateImageOrder.value || undefined
|