浏览代码

feat(marketingEdit): 新增生成图片功能并优化画布调整逻辑

- 在 header 组件中添加“生成图片”按钮
- 引入商品和画布模板数据文件
- 修改默认画布宽度为 395px
- 移除加载模板时的硬编码数据,改为从 canvas.json 加载
- 优化调整画布尺寸逻辑,支持从底部扩展/收缩画布
- 更新首页入口文案及跳转路径
- 编辑页引用本地图片资源用于展示
- 实现 createImg 方法以生成图片数据
panqiuyao 1 周之前
父节点
当前提交
4b80c49b05

+ 7 - 42
frontend/src/views/Home/index.vue

@@ -14,13 +14,13 @@
     <!-- 左侧图片区域 -->
     <div class="image-container left-image" @click="goCheck" v-log="{ describe: { action: '点击拍照检查入口' } }">
       <img src="@/assets/images/home/left.png" alt="拍摄产品并处理图像" class="zoom-on-hover" />
-      <div class="overlay-text">拍摄产品<br>并处理图像</div>
+      <div class="overlay-text">新加模板</div>
     </div>
 
     <!-- 右侧图片区域 -->
     <div class="image-container right-image"  @click="goShot" v-log="{ describe: { action: '点击仅处理图像入口' } }">
       <img src="@/assets/images/home/right.png" alt="仅处理图像" class="zoom-on-hover" />
-      <div class="overlay-text" style="line-height: 80px;">仅处理图像</div>
+      <div class="overlay-text" style="line-height: 80px;">编辑模板</div>
     </div>
   </div>
 </template>
@@ -74,53 +74,18 @@ function socketConnect(){
 }
 
 const goCheck = async () => {
-  // 检查登录状态
-  if (!tokenInfoStore.getToken) {
-    useUserInfoStore.updateLoginShow(true);
-    return;
-  }
-
-  // 如果正在同步,显示提示
-  if (syncLoading.value) {
-    console.log('正在同步配置,请稍候...');
-    return;
-  }
-
-  // 如果未同步完成,等待同步
-  if (!syncCompleted.value) {
-    ElMessage.error('等待配置同步完成');
-    return;
-  }
-
-  configInfoStore.updateAppModel(1);
   router.push({
-    name: 'PhotographyCheck'
+    name: 'addTpl'
   });
 };
 
 const goShot = async () => {
-  // 检查登录状态
-  if (!tokenInfoStore.getToken) {
-    useUserInfoStore.updateLoginShow(true);
-    return;
-  }
-
-  // 如果正在同步,显示提示
-  if (syncLoading.value) {
-    console.log('正在同步配置,请稍候...');
-    return;
-  }
 
-  // 如果未同步完成,等待同步
-  if (!syncCompleted.value) {
-    console.log('等待配置同步完成...');
-    return;
-  }
-
-  socketConnect();
-  configInfoStore.updateAppModel(2);
   router.push({
-    name: 'PhotographyProcessImage'
+    name: 'editTpl',
+    params: {
+      id: '1'
+    }
   });
 };
 

+ 9 - 5
frontend/src/views/Tpl/Edit/index.vue

@@ -20,28 +20,32 @@
           {
             key: '鞋底',
             value: '橡胶底'
+          },
+          {
+            key: '颜色',
+            value: '黑色'
           }
       ]"
       :goods_images="[
           {
             key: '俯视',
-            value: 'https://huilima.oss-cn-hangzhou.aliyuncs.com/frontend/demo/zhihuiyin/A596351/1.png'
+            value: 'C:\\Users\\Administrator\\Desktop\\img\\A596351\\1.png'
           },
           {
             key: '侧视',
-            value: 'https://huilima.oss-cn-hangzhou.aliyuncs.com/frontend/demo/zhihuiyin/A596351/2.png'
+            value: 'C:\\Users\\Administrator\\Desktop\\img\\A596351\\2.png'
           },
           {
             key: '后根',
-            value: 'https://huilima.oss-cn-hangzhou.aliyuncs.com/frontend/demo/zhihuiyin/A596351/3.png'
+            value: 'C:\\Users\\Administrator\\Desktop\\img\\A596351\\3.png'
           },
           {
             key: '鞋底',
-            value: 'https://huilima.oss-cn-hangzhou.aliyuncs.com/frontend/demo/zhihuiyin/A596351/4.png'
+            value: 'C:\\Users\\Administrator\\Desktop\\img\\A596351\\4.png'
           },
           {
             key: '内里',
-            value: 'https://huilima.oss-cn-hangzhou.aliyuncs.com/frontend/demo/zhihuiyin/A596351/5.png'
+            value: 'C:\\Users\\Administrator\\Desktop\\img\\A596351\\5.png'
           }
       ]"
       @save="save"

文件差异内容过多而无法显示
+ 4 - 0
frontend/src/views/components/marketingEdit/canvas.json


+ 30 - 0
frontend/src/views/components/marketingEdit/goods.json

@@ -0,0 +1,30 @@
+{
+  "AC5120913": {
+    "款号": "E305-01003",
+    "货号资料": [
+      {
+        "货号": "A596351",
+        "颜色": "黑色",
+        "pics": {
+          "俯视": "C:\\Users\\Administrator\\Desktop\\img\\A596351\\1.png",
+          "侧视": "C:\\Users\\Administrator\\Desktop\\img\\A596351\\2.png",
+          "后跟": "C:\\Users\\Administrator\\Desktop\\img\\A596351\\3.png",
+          "鞋底": "C:\\Users\\Administrator\\Desktop\\img\\A596351\\4.png",
+          "内里": "C:\\Users\\Administrator\\Desktop\\img\\A5963521\\5.png"
+        }
+      },
+      {
+        "货号": "A596352",
+        "颜色": "白色",
+        "pics": {
+          "俯视": "C:\\Users\\Administrator\\Desktop\\img\\A596352\\1.png",
+          "侧视": "C:\\Users\\Administrator\\Desktop\\img\\A596352\\2.png",
+          "后跟": "C:\\Users\\Administrator\\Desktop\\img\\A596352\\3.png",
+          "鞋底": "C:\\Users\\Administrator\\Desktop\\img\\A596352\\4.png",
+          "内里": "C:\\Users\\Administrator\\Desktop\\img\\A596352\\5.png"
+        }
+      }
+    ],
+    "设计理念": "平衡舒适性、美观性、功能性和工艺品质,以满足现代消费者在不同场景下的需求"
+  }
+}

+ 45 - 32
frontend/src/views/components/marketingEdit/index.vue

@@ -10,8 +10,10 @@ import colorMixins from '@/views/components/PictureEditor/mixin/color/index'
 import editMixins from '@/views/components/PictureEditor/mixin/edit/index'
 import { uploadBaseImg } from '@/apis/other'
 import {markRaw} from "vue";
+import goods from './goods.json'
+import canvas from './canvas.json'
 
-const FIXED_CANVAS_WIDTH = 800
+const FIXED_CANVAS_WIDTH = 395
 export default {
   name: 'marketingImageEditor',
   mixins: [ viewMixins,actionsMixins,layerMixins,colorMixins,editMixins],
@@ -88,7 +90,6 @@ export default {
     }
   },
   mounted() {
-    console.log(this.$route?.params?.id);
     if(this.$route?.name === 'editTpl'){
       this.loadTemplate()
     }
@@ -101,31 +102,7 @@ export default {
   methods: {
     loadTemplate(){
       if(this.hasLoadedTpl) return
-      const tpl  = [
-        {
-          index: 0,
-          preview: "https://ossimg.valimart.net/uploads/20251202/176466246730872.png",
-          canvas_json: "{\"version\":\"5.2.1\",\"objects\":[{\"type\":\"image\",\"version\":\"5.2.1\",\"originX\":\"left\",\"originY\":\"top\",\"left\":125.14,\"top\":128.86,\"width\":960,\"height\":1280,\"fill\":\"rgb(0,0,0)\",\"stroke\":null,\"strokeWidth\":0,\"strokeDashArray\":null,\"strokeLineCap\":\"butt\",\"strokeDashOffset\":0,\"strokeLineJoin\":\"miter\",\"strokeUniform\":false,\"strokeMiterLimit\":4,\"scaleX\":0.17,\"scaleY\":0.17,\"angle\":0,\"flipX\":false,\"flipY\":false,\"opacity\":1,\"shadow\":null,\"visible\":true,\"backgroundColor\":\"\",\"fillRule\":\"nonzero\",\"paintFirst\":\"fill\",\"globalCompositeOperation\":\"source-over\",\"skewX\":0,\"skewY\":0,\"erasable\":false,\"cropX\":0,\"cropY\":0,\"name\":\"image\",\"sort\":10,\"id\":\"1764662402644_1793_1\",\"selectable\":true,\"data-key\":\"俯视\",\"data-value\":\"https://ossimg.valimart.net/uploads/vali_ai/20250613/174979820315694.png\",\"src\":\"https://ossimg.valimart.net/uploads/vali_ai/20250613/174979820315694.png\",\"crossOrigin\":\"anonymous\",\"filters\":[]}],\"background\":\"#fff\"}",
-          width: 800,
-          height: 500,
-          bg_color: "#fff",
-          name: "画布_95086100",
-          tpl_url: "",
-          image_path: ""
-        },
-        {
-          index: 1,
-          preview: "https://ossimg.valimart.net/uploads/20251202/176466246756126.png",
-          canvas_json: "{\"version\":\"5.2.1\",\"objects\":[{\"type\":\"image\",\"version\":\"5.2.1\",\"originX\":\"left\",\"originY\":\"top\",\"left\":476,\"top\":16,\"width\":960,\"height\":1280,\"fill\":\"rgb(0,0,0)\",\"stroke\":null,\"strokeWidth\":0,\"strokeDashArray\":null,\"strokeLineCap\":\"butt\",\"strokeDashOffset\":0,\"strokeLineJoin\":\"miter\",\"strokeUniform\":false,\"strokeMiterLimit\":4,\"scaleX\":0.13,\"scaleY\":0.13,\"angle\":0,\"flipX\":false,\"flipY\":false,\"opacity\":1,\"shadow\":null,\"visible\":true,\"backgroundColor\":\"\",\"fillRule\":\"nonzero\",\"paintFirst\":\"fill\",\"globalCompositeOperation\":\"source-over\",\"skewX\":0,\"skewY\":0,\"erasable\":false,\"cropX\":0,\"cropY\":0,\"name\":\"image\",\"sort\":10,\"id\":\"1764662460778_4314_2\",\"selectable\":true,\"data-key\":\"侧视\",\"data-value\":\"https://ossimg.valimart.net/uploads/vali_ai/20250613/174979823853439.png\",\"src\":\"https://ossimg.valimart.net/uploads/vali_ai/20250613/174979823853439.png\",\"crossOrigin\":\"anonymous\",\"filters\":[]}],\"background\":\"#fff\"}",
-          width: 800,
-          height: 200,
-          bg_color: "#fff",
-          name: "画布_07492100",
-          tpl_url: "",
-          image_path: ""
-        }
-      ]
-      this.data.splice(0, this.data.length, ...tpl)
+      this.data.splice(0, this.data.length, ...JSON.parse(JSON.stringify(canvas)))
       this.$emit('update:index', 0)
       this.hasLoadedTpl = true
     },
@@ -293,21 +270,52 @@ export default {
 
 
         const newWidth = FIXED_CANVAS_WIDTH;
-        const newHeight = this.canvasForm.height;
+        const newHeight = Number(this.canvasForm.height) || this.this_canvas.height;
+        const oldHeight = Number(this.this_canvas.height) || 0;
         const newColor = this.canvasForm.bg_color;
 
         // 更新 fcanvas 的宽度和高度
         if(this.fcanvas){
-          if(newWidth !== this.this_canvas.width)this.fcanvas.setWidth(newWidth);
-          if(newHeight !== this.this_canvas.height)this.fcanvas.setHeight(newHeight);
+          // 更新画布尺寸
+          // 注意:Fabric.js 中对象的坐标是相对于画布左上角的,所以当画布高度变化时
+          // 对象的 top 值会自动保持相对于顶部的距离不变,实现从底部扩展/收缩的效果
+
+
+          if(newWidth !== this.this_canvas.width || newHeight !== oldHeight){
+            this.fcanvas.setDimensions({ width: newWidth, height: newHeight });
+          }
+         /* if(newHeight !== oldHeight){
+            this.fcanvas.setHeight(newHeight);
+            // 高度变小:检查是否有对象超出新高度
+            if(newHeight < oldHeight){
+              const objects = this.fcanvas.getObjects();
+              const outOfBounds = objects.filter(obj => {
+                const objBottom = obj.top + (obj.height * obj.scaleY || 0);
+                return objBottom > newHeight;
+              });
+              if(outOfBounds.length > 0){
+                this.$message.warning(`有 ${outOfBounds.length} 个对象超出新画布高度,请手动调整位置`);
+              }
+            }
+          }*/
           if(newColor !== this.this_canvas.bg_color)this.fcanvas.setBackgroundColor(newColor);
+          let objects = this.fcanvas.getObjects();
+
+          objects.forEach(function(object) {
+            // 调整对象的位置
+            object.left = object.left;
+            object.top = object.top ;
+            object.controls = fabric.Object.prototype.controls
+
+          });
+
           // 重新渲染以应用更改
-          this.fcanvas.renderAll();
+          this.fcanvas.requestRenderAll();
         }
 
         this.data[this.index].name = this.canvasForm.name
         this.data[this.index].width = FIXED_CANVAS_WIDTH
-        this.data[this.index].height = this.canvasForm.height
+        this.data[this.index].height = newHeight
         this.data[this.index].bg_color = this.canvasForm.bg_color
         this.data[this.index].multi_goods_mode = this.canvasForm.multi_goods_mode || ''
         this.canvasForm.visible = false;
@@ -404,6 +412,11 @@ export default {
         }
       }))
       this.$emit('save', payload)
+      return payload;
+    },
+    async createImg(){
+      let canvas_json = JSON.parse(JSON.stringify(canvas))
+      let goodsData = JSON.parse(JSON.stringify(goods))
     },
     saveCanvasSnapshot(targetIndex){
       const snapshotIndex = typeof targetIndex === 'number' ? targetIndex : this.index

+ 1 - 0
frontend/src/views/components/marketingEdit/tpl/header.js

@@ -48,6 +48,7 @@ export  default function tpl(){
             <el-button @click="addCanvas"  class="mar-left-10">新增画布</el-button>
             <el-button class="mar-left-10"  v-if="!isEmpty"  @click="handleAdjustCanvas">调整画布</el-button>
             <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button type="primary" @click="createImg">生成图片</el-button>
            </div>
            
             <!-- 新增:调整画布尺寸弹窗 -->

部分文件因为文件数量过多而无法显示