浏览代码

feat(marketingEdit): 新增画布类型支持并优化编辑逻辑

- 新增画布类型选项:普通画布、模特图、场景图
- 模特图和场景图在编辑时自动跳转至普通画布
- 模特图和场景图不初始化 fabric 实例
- 调整画布列表展示样式,特殊画布类型居中显示
- 优化画布切换逻辑,避免非普通画布被选中编辑
- 更新模板加载逻辑以兼容新画布类型字段
- 添加画布类型变更处理函数,自动设置默认名称
panqiuyao 5 天之前
父节点
当前提交
38a887cec2

+ 59 - 11
frontend/src/views/components/marketingEdit/index.vue

@@ -72,6 +72,7 @@ export default {
         color:"#fff",
         bg_color:'#fff',
         visible:false,
+        canvas_type: 'normal', // normal:普通画布 model:模特图 scene:场景图
         multi_goods_mode: '', // 多货号模式:''(默认单货号), 'single'(一个货号多角度), 'multiple'(多个货号同角度)
         max_goods_count: null, // 多货号模式下,最多可追加多少个货号
       }
@@ -90,6 +91,25 @@ export default {
   watch: {
     index(newValue, oldValue) {
       if (this.isEmpty) return
+      const target = this.data[newValue]
+      // 模特图/场景图不进入编辑,自动跳到下一个普通画布
+      if(target && (target.canvas_type === 'model' || target.canvas_type === 'scene')){
+        let nextIdx = -1
+        for(let i = newValue + 1; i < this.data.length; i++){
+          if(this.data[i].canvas_type !== 'model' && this.data[i].canvas_type !== 'scene'){
+            nextIdx = i; break
+          }
+        }
+        if(nextIdx === -1){
+          for(let i = newValue - 1; i >= 0; i--){
+            if(this.data[i].canvas_type !== 'model' && this.data[i].canvas_type !== 'scene'){
+              nextIdx = i; break
+            }
+          }
+        }
+        this.$emit('update:index', nextIdx === -1 ? oldValue : nextIdx)
+        return
+      }
       this.saveCanvasSnapshot(oldValue)
       this.destroyCanvasInstance()
       this.$nextTick(() => {
@@ -111,7 +131,11 @@ export default {
   methods: {
     loadTemplate(){
       if(this.hasLoadedTpl) return
-      this.data.splice(0, this.data.length, ...JSON.parse(JSON.stringify(canvas)))
+      const tplData = JSON.parse(JSON.stringify(canvas)).map(item => ({
+        canvas_type: item.canvas_type || 'normal',
+        ...item,
+      }))
+      this.data.splice(0, this.data.length, ...tplData)
       this.$emit('update:index', 0)
       this.hasLoadedTpl = true
     },
@@ -169,17 +193,12 @@ export default {
         return;
       }
 
-/*
-      //画布下不存在模板OSS地址
-      if(!this.this_canvas){
-        this.$emit('update:index', 0)
-        return;
+      // 模特图/场景图不初始化 fabric,直接跳过
+      const canvasType = this.this_canvas?.canvas_type || 'normal'
+      if (canvasType === 'model' || canvasType === 'scene') {
+        return
       }
 
-
-      if(this.this_canvas.tpl_url){
-        return;
-      }*/
       this.$emit('canvasStyle:update',{
         width: this.this_canvas.width,
         height: this.this_canvas.height
@@ -232,6 +251,7 @@ export default {
       this.canvasForm.width = FIXED_CANVAS_WIDTH
       this.canvasForm.height = 1024
       this.canvasForm.bg_color = '#fff'
+      this.canvasForm.canvas_type = 'normal'
       this.canvasForm.multi_goods_mode = ''
       this.canvasForm.max_goods_count = null
       this.canvasForm.visible = true;
@@ -243,10 +263,21 @@ export default {
       this.canvasForm.width = FIXED_CANVAS_WIDTH
       this.canvasForm.height = this.this_canvas.height
       this.canvasForm.bg_color = this.this_canvas.bg_color
+      this.canvasForm.canvas_type = this.this_canvas.canvas_type || 'normal'
       this.canvasForm.multi_goods_mode = this.this_canvas.multi_goods_mode || ''
       this.canvasForm.max_goods_count = this.this_canvas.max_goods_count || null
       this.canvasForm.visible = true;
     },
+    handleCanvasTypeChange(type){
+      if(this.canvasForm.type !== 'add') return
+      if(type === 'model'){
+        this.canvasForm.name = '模特图'
+      }else if(type === 'scene'){
+        this.canvasForm.name = '场景图'
+      }else if(!this.canvasForm.name || this.canvasForm.name === '模特图' || this.canvasForm.name === '场景图'){
+        this.canvasForm.name = '画布_'+new Date().getTime().toString().substr(8)+Math.round(100)
+      }
+    },
     submitCanvasInfo() {
       // 假设 this.canvasForm 包含最新的 width, height 和 color
 
@@ -259,6 +290,7 @@ export default {
                width:FIXED_CANVAS_WIDTH,
                height:this.canvasForm.height,
                bg_color:this.canvasForm.bg_color,
+               canvas_type: this.canvasForm.canvas_type || 'normal',
                canvas_json:'',
                preview:'',
                multi_goods_mode: this.canvasForm.multi_goods_mode || '',
@@ -331,6 +363,7 @@ export default {
         this.data[this.index].width = FIXED_CANVAS_WIDTH
         this.data[this.index].height = newHeight
         this.data[this.index].bg_color = this.canvasForm.bg_color
+        this.data[this.index].canvas_type = this.canvasForm.canvas_type || 'normal'
         this.data[this.index].multi_goods_mode = this.canvasForm.multi_goods_mode || ''
         this.data[this.index].max_goods_count = this.canvasForm.multi_goods_mode ? (this.canvasForm.max_goods_count || null) : null
         this.canvasForm.visible = false;
@@ -410,7 +443,7 @@ export default {
               // 计算目标元素相对于滚动容器的位置
               const containerRect = scrollContainer.getBoundingClientRect()
               const targetRect = target.getBoundingClientRect()
-              
+
               // 计算需要滚动的距离(目标元素顶部 - 容器顶部 - 导航栏高度100px)
               const scrollTop = scrollContainer.scrollTop
               const targetPosition = scrollTop + (targetRect.top - containerRect.top) - 100
@@ -434,6 +467,17 @@ export default {
       })
     },
     canvasBodyStyle(item){
+      const canvasType = item?.canvas_type || 'normal'
+      // 模特图 / 场景图:保持方形显示
+      if(canvasType === 'model' || canvasType === 'scene'){
+        return {
+          width: `${FIXED_CANVAS_WIDTH}px`,
+          height: `${FIXED_CANVAS_WIDTH}px`,
+          lineHeight: `${FIXED_CANVAS_WIDTH}px`,
+          margin: '0 auto'
+        }
+      }
+      // 普通画布
       const height = Number(item?.height)
       const width = Number(item?.width) || FIXED_CANVAS_WIDTH
       const style = {
@@ -760,3 +804,7 @@ export default {
 
 
 </style>
+
+
+
+

+ 53 - 34
frontend/src/views/components/marketingEdit/tpl/header.js

@@ -55,46 +55,65 @@ export  default function tpl(){
             <!-- 新增:调整画布尺寸弹窗 -->
             <el-dialog :title="canvasForm.type === 'add' ? '新增画布' : '编辑画布'" v-model="canvasForm.visible" append-to-body width="700px">
               <el-form :model="canvasForm" label-width="100px">
+                <el-form-item label="画布类型">
+                  <el-radio-group
+                    v-model="canvasForm.canvas_type"
+                    :disabled="canvasForm.type === 'edit'"
+                    @change="handleCanvasTypeChange"
+                  >
+                    <el-radio label="normal">普通画布</el-radio>
+                    <el-radio label="model">模特图</el-radio>
+                    <el-radio label="scene">场景图</el-radio>
+                  </el-radio-group>
+                  <div style="font-size: 12px; color: #999; margin-top: 8px; line-height: 1.6;">
+                    <div v-if="canvasForm.canvas_type === 'normal'">普通画布,可编辑内容</div>
+                    <div v-else-if="canvasForm.canvas_type === 'model'">模特图占位,用于前台/后端生成时替换</div>
+                    <div v-else-if="canvasForm.canvas_type === 'scene'">场景图占位,用于前台/后端生成时替换</div>
+                  </div>
+                </el-form-item>
                 <el-form-item label="画布名称">
                   <el-input v-model="canvasForm.name" placeholder="请输入画布名称"></el-input>
                 </el-form-item>
           <!--      <el-form-item label="宽度">
                   <div class="fixed-width-tip">800(固定)</div>
                 </el-form-item>-->
-                <el-form-item label="高度">
-                  <el-input v-model.number="canvasForm.height" placeholder="请输入高度"></el-input>
-                </el-form-item>
-                <el-form-item label="背景颜色">
-                    <el-color-picker v-model="canvasForm.bg_color" />
-                </el-form-item>
-                <el-form-item label="多货号模式">
-                  <el-radio-group v-model="canvasForm.multi_goods_mode">
-                    <el-radio label="">默认(单货号)</el-radio>
-                    <el-radio label="single">一个货号多角度</el-radio>
-                    <el-radio label="multiple">多个货号同角度</el-radio>
-                  </el-radio-group>
-                  <div style="font-size: 12px; color: #999; margin-top: 8px; line-height: 1.6;">
-                    <div v-if="canvasForm.multi_goods_mode === ''">默认模式,画布对应单个货号</div>
-                    <div v-else-if="canvasForm.multi_goods_mode === 'single'">一个画布对应一个货号,画布中包含该货号不同角度的图片</div>
-                    <div v-else-if="canvasForm.multi_goods_mode === 'multiple'">画布中含有多个货号,画布中包含多个货号同一角度的图片</div>
-                  </div>
-                </el-form-item>
-                <el-form-item
-                  v-if="canvasForm.multi_goods_mode === 'single' || canvasForm.multi_goods_mode === 'multiple'"
-                  label="最多货号数量"
-                >
-                  <el-input-number
-                    v-model="canvasForm.max_goods_count"
-                    :min="1"
-                    :max="99"
-                    :step="1"
-                    controls-position="right"
-                    placeholder="请输入最多可追加的货号数量"
-                  />
-                  <div style="font-size: 12px; color: #999; margin-top: 4px;">
-                    用于限制此画布在生成详情图时,最多会为多少个货号生成内容;为空则不限制。
-                  </div>
-                </el-form-item>
+                <template v-if="canvasForm.canvas_type === 'normal'">
+                
+                  <el-form-item label="高度">
+                    <el-input v-model.number="canvasForm.height" placeholder="请输入高度"></el-input>
+                  </el-form-item>
+                  <el-form-item label="背景颜色">
+                      <el-color-picker v-model="canvasForm.bg_color" />
+                  </el-form-item>
+                  <el-form-item label="多货号模式">
+                    <el-radio-group v-model="canvasForm.multi_goods_mode">
+                      <el-radio label="">默认(单货号)</el-radio>
+                      <el-radio label="single">一个货号多角度</el-radio>
+                      <el-radio label="multiple">多个货号同角度</el-radio>
+                    </el-radio-group>
+                    <div style="font-size: 12px; color: #999; margin-top: 8px; line-height: 1.6;">
+                      <div v-if="canvasForm.multi_goods_mode === ''">默认模式,画布对应单个货号</div>
+                      <div v-else-if="canvasForm.multi_goods_mode === 'single'">一个画布对应一个货号,画布中包含该货号不同角度的图片</div>
+                      <div v-else-if="canvasForm.multi_goods_mode === 'multiple'">画布中含有多个货号,画布中包含多个货号同一角度的图片</div>
+                    </div>
+                  </el-form-item>
+                  <el-form-item
+                    v-if="canvasForm.multi_goods_mode === 'single' || canvasForm.multi_goods_mode === 'multiple'"
+                    label="最多货号数量"
+                  >
+                    <el-input-number
+                      v-model="canvasForm.max_goods_count"
+                      :min="1"
+                      :max="99"
+                      :step="1"
+                      controls-position="right"
+                      placeholder="请输入最多可追加的货号数量"
+                    />
+                    <div style="font-size: 12px; color: #999; margin-top: 4px;">
+                      用于限制此画布在生成详情图时,最多会为多少个货号生成内容;为空则不限制。
+                    </div>
+                  </el-form-item>
+                </template>
               </el-form>
               <div slot="footer" class="dialog-footer flex right">
                 <el-button @click="canvasForm.visible = false">取 消</el-button>

+ 24 - 12
frontend/src/views/components/marketingEdit/tpl/view.js

@@ -19,6 +19,7 @@ export default function() {
             :style="canvasBodyStyle(item)"
           >
             <el-button
+              v-if="item.canvas_type !== 'model' && item.canvas_type !== 'scene'"
               size="small"
               class="canvas-stack_switch"
               type="primary"
@@ -28,18 +29,29 @@ export default function() {
             >
               {{ idx === index ? '正在编辑' : '切换画布' }}
             </el-button>
-            <div v-if="idx !== index"  class="canvas-stack_placeholder">
-              <img v-if="item.preview" :src="item.preview" alt="">
-              <img v-else-if="item.image_path" :src="item.image_path" alt="">
-              <img v-else-if="item.tpl_url" :src="item.tpl_url" alt="">
-              <span v-else>点击“切换到此画布”开始编辑</span>
-            </div>
-            <template  v-else >
-              <canvas
-                :id="'marketing-canvas-' + idx"
-                :width="item.width"
-                :height="item.height"
-              />
+            <!-- 模特图/场景图占位 -->
+            <template v-if="item.canvas_type === 'model' || item.canvas_type === 'scene'">
+              <div class="canvas-special-placeholder" :class="item.canvas_type">
+                <div class="special-placeholder-content">
+                  {{ item.canvas_type === 'model' ? '模特图' : '场景图' }}
+                </div>
+              </div>
+            </template>
+            <!-- 普通画布 -->
+            <template v-else>
+              <div v-if="idx !== index"  class="canvas-stack_placeholder" @click="handleSelectCanvas(idx)" style="cursor: pointer;">
+                <img v-if="item.preview" :src="item.preview" alt="">
+                <img v-else-if="item.image_path" :src="item.image_path" alt="">
+                <img v-else-if="item.tpl_url" :src="item.tpl_url" alt="">
+                <span v-else>点击“切换到此画布”开始编辑</span>
+              </div>
+              <template  v-else >
+                <canvas
+                  :id="'marketing-canvas-' + idx"
+                  :width="item.width"
+                  :height="item.height"
+                />
+              </template>
             </template>
           </div>
         </div>