Kaynağa Gözat

mod:图片新增裁剪处理

panqiuyao 6 ay önce
ebeveyn
işleme
f8c0be145c

+ 2 - 2
frontend/src/views/Tpl/Edit/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <headerBar :title="title" />
+  <headerBar title="编辑模板" />
 
   <marketingEdit
       :canvasStyle="{
@@ -18,7 +18,7 @@ import { ref, reactive } from 'vue';
 import headerBar from "@/components/header-bar/index.vue";
 import marketingEdit from '@/views/components/marketingEdit'
 
-const title = ref('编辑-公用模板')
+
 
 const save = (data: any) => {
   console.log(data)

+ 1 - 1
frontend/src/views/components/PictureEditor/index.vue

@@ -40,7 +40,7 @@ export default {
       await this.viewInit()
       await  this.$nextTick()
       this.fcanvas = new fabric.Canvas('canvas', {
-        backgroundColor:"#ffffff",
+        backgroundColor:this.canvas.color,
         containerClass:"fcanvas",
         // 元素对象被选中时保持在当前z轴,不会跳到最顶层
         preserveObjectStacking:true,

+ 1 - 0
frontend/src/views/components/PictureEditor/mixin/actions/index.js

@@ -204,6 +204,7 @@ export default  {
           this.getLayers()
           if(selectable) this.select(img)
           this.fcanvas.renderAll();
+          console.log(img);
           resolve(img)
         }, {
           crossOrigin: 'anonymous',

+ 114 - 0
frontend/src/views/components/PictureEditor/mixin/edit/index.js

@@ -17,6 +17,19 @@ export default  {
         y:0,
         vague:0,
         color:'#000',
+      },
+
+      clipSettings: {
+        shape: 'rect',      // 形状类型
+        width: 100,         // 矩形宽度
+        height: 100,        // 矩形高度
+        radius: 50,         // 圆形半径
+        offsetX: 0,         // 水平偏移
+        offsetY: 0,          // 垂直偏移
+        showClipStroke: true,  // 是否显示描边
+        strokeColor: '#000000',    // 描边颜色
+        strokeWidth: 2,            // 描边宽度
+        rectRadius: 0              // 矩形圆角
       }
     }
   },
@@ -234,6 +247,107 @@ export default  {
         this.options = this.TextboxConfig.fontFamily
       }
     },
+    applyClipPath() {
+      if (!this.judge()) return;
+
+      const { shape, width, height, radius, offsetX, offsetY, rectRadius } = this.clipSettings;
+
+      try {
+        // 创建纯剪裁区域(不带描边)
+        let clipObj;
+        if (shape === 'rect') {
+          if (width <= 0 || height <= 0) throw new Error('尺寸必须大于0');
+
+          clipObj = new fabric.Rect({
+            width: width,
+            height: height,
+            left: offsetX,
+            top: offsetY,
+            rx: rectRadius,
+            ry: rectRadius,
+            absolutePositioned: true
+          });
+        } else {
+          if (radius <= 0) throw new Error('半径必须大于0');
+
+          clipObj = new fabric.Circle({
+            radius: radius,
+            left: offsetX,
+            top: offsetY,
+            absolutePositioned: true
+          });
+        }
+
+        // 应用剪裁区域
+        this.editLayer.set({ clipPath: clipObj });
+
+        // 更新画布
+        this.updateClipStroke()
+        this.updateCanvasState();
+        this.fcanvas.requestRenderAll();
+      } catch (error) {
+        console.error('剪裁区域创建失败:', error);
+        this.$message.error('剪裁设置错误: ' + error.message);
+      }
+    },
+    updateClipStroke() {
+      if (!this.judge() || !this.clipSettings.showClipStroke) return;
+
+      const { shape, width, height, radius, offsetX, offsetY,
+        strokeColor, strokeWidth, rectRadius } = this.clipSettings;
+
+      // 移除旧的描边图形
+      this.removeClipStroke();
+
+      // 创建新的描边图形
+      let strokeObj;
+      if (shape === 'rect') {
+        strokeObj = new fabric.Rect({
+          width: width,
+          height: height,
+          left: offsetX,
+          top: offsetY,
+          rx: rectRadius,
+          ry: rectRadius,
+          stroke: strokeColor,
+          strokeWidth: strokeWidth,
+          fill: null,
+          selectable: false,
+          evented: false
+        });
+      } else {
+        strokeObj = new fabric.Circle({
+          radius: radius,
+          left: offsetX,
+          top: offsetY,
+          stroke: strokeColor,
+          strokeWidth: strokeWidth,
+          fill: null,
+          selectable: false,
+          evented: false
+        });
+      }
+
+      // 添加到画布
+      this.fcanvas.add(strokeObj);
+      this.clipStrokeObject = strokeObj;
+      this.fcanvas.requestRenderAll();
+    },
+    removeClipStroke() {
+      if (this.clipStrokeObject) {
+        this.fcanvas.remove(this.clipStrokeObject);
+        this.clipStrokeObject = null;
+      }
+    },
+
+
+    clearClipPath() {
+      if (!this.judge()) return;
+
+      this.editLayer.set({ clipPath: null });
+      this.updateCanvasState();
+      this.fcanvas.requestRenderAll();
+    }
 
   }
 

+ 2 - 2
frontend/src/views/components/PictureEditor/mixin/edit/index.scss

@@ -1,8 +1,8 @@
 .picture-editor-wrap_edit {
   position: fixed;
-  width: 200px;
+  width: 300px;
   right: 0px;
-  top:30px;
+  top:80px;
   z-index: 10;
   bottom: 0px;
   background: #fff;

+ 1 - 0
frontend/src/views/components/PictureEditor/mixin/edit/module/image.js

@@ -9,6 +9,7 @@ let image = ()=>{
           ${tools.flip()}
           ${tools.straw()}
           ${tools.opacity()}
+          ${tools.cutout()}
       </div>
      `
 }

+ 42 - 1
frontend/src/views/components/PictureEditor/mixin/edit/module/tools.js

@@ -37,5 +37,46 @@ export default  {
 
           </div>
       `
-  }
+  },
+cutout(){
+  return `
+    <div class="title_two">剪裁</div>
+    <div class="flex left">
+      <el-select v-model="clipSettings.shape" @change="applyClipPath" size="small" style="width:100px">
+        <el-option label="矩形" value="rect"></el-option>
+        <el-option label="圆形" value="circle"></el-option>
+      </el-select>
+    </div>
+
+    <!-- 矩形参数 -->
+    <div v-if="clipSettings.shape === 'rect'" class="flex left" style="margin-left:10px">
+      <el-input-number v-model="clipSettings.width" @input="applyClipPath" :step="1" :min="1" size="small" style="width:80px"/>
+      <el-input-number v-model="clipSettings.height" @input="applyClipPath" :step="1" :min="1" size="small" style="width:80px"/>
+      <el-input-number v-model="clipSettings.rectRadius" @input="applyClipPath" :step="1" :min="0" size="small" style="width:60px" placeholder="圆角"/>
+    </div>
+
+    <!-- 圆形参数 -->
+    <div v-else class="flex left" style="margin-left:10px">
+      <el-input-number v-model="clipSettings.radius" @input="applyClipPath" :step="1" :min="1" size="small" style="width:80px"/>
+    </div>
+
+    <!-- 描边参数 -->
+    <div class="flex left" style="margin-top:8px">
+      <el-color-picker v-model="clipSettings.strokeColor" @change="applyClipPath" size="small"/>
+      <el-input-number v-model="clipSettings.strokeWidth" @input="applyClipPath" :step="1" :min="0" :max="10" size="small" style="width:60px" placeholder="描边"/>
+    </div>
+
+    <!-- 偏移参数 -->
+    <div class="flex left">
+      <el-input-number v-model="clipSettings.offsetX" @input="applyClipPath" :step="1" size="small" style="width:60px"/>
+      <el-input-number v-model="clipSettings.offsetY" @input="applyClipPath" :step="1" size="small" style="width:60px"/>
+    </div>
+
+    <div class="flex left">
+      <el-button @click="clearClipPath" size="small">清除</el-button>
+    </div>
+  `
+}
+
+
 }

+ 3 - 1
frontend/src/views/components/PictureEditor/mixin/view/index.js

@@ -10,7 +10,9 @@ const viewMixins = {
       },
       canvas: {
         width: 500,
-        height: 500
+        height: 500,
+        color:'#fff',
+
       },
       viewStatus: false,
       minimap: null,

+ 51 - 9
frontend/src/views/components/marketingEdit/index.vue

@@ -31,6 +31,15 @@ export default {
       fcanvas: {},
       scale: 1,
       sceneTplImg:"",//生成的时候记录下来,用户重做
+
+
+      canvasForm:{
+        width: '1024',
+        height: '1024',
+        color:"#fff",
+        visible:false,
+      }
+
     }
   },
   template: tpl(),
@@ -61,22 +70,55 @@ export default {
       })
       //保留最初的状态
       this.updateCanvasState()
-       this.minimapInit()
+      // this.minimapInit()
       this.actionInit()
       this.layerInit();
       await  this.$nextTick()
       this.$emit('init')
-      const img = await this.addMaps('https://ossimg.valimart.net/uploads/vali_ai/20250606/174920037639047.png',{
+      const img = await this.addMaps('https://ossimg.valimart.net/uploads/vali_ai/20250610/174954546275619.png',{
         name: 'subject',
         left: 0,
         top: 0,
         delable:false,
         erasable:true,
-        scaleX: this.scale,
-        scaleY: this.scale,
+        scaleX: 0.5,
+        scaleY: 0.5,
       })
 
     },
+    handleAdjustCanvas() {
+      console.log(this.canvas)
+      this.canvasForm.width = this.canvas.width
+      this.canvasForm.height = this.canvas.height
+      this.canvasForm.color = this.canvas.color
+      this.canvasForm.visible = true;
+    },
+    submitCanvasInfo() {
+      // 假设 this.canvasForm 包含最新的 width, height 和 color
+
+      this.canvas.width = this.canvasForm.width;
+      this.canvas.height = this.canvasForm.height;
+      this.canvas.color = this.canvasForm.color;
+      const newWidth = this.canvasForm.width;
+      const newHeight = this.canvasForm.height;
+      const newColor = this.canvasForm.color;
+
+      // 更新 fcanvas 的宽度和高度
+      this.fcanvas.setWidth(newWidth);
+      this.fcanvas.setHeight(newHeight);
+
+      // 更新 fcanvas 的背景颜色
+      this.fcanvas.setBackgroundColor(newColor);
+
+      // 重新渲染以应用更改
+      this.fcanvas.renderAll();
+      this.canvasForm.visible = false;
+
+    },
+    resizeCanvas(width, height) {
+      // TODO: 实现具体的画布调整逻辑
+      console.log('调整画布尺寸为:', width, 'x', height);
+    }
   }
 }
 </script>
@@ -89,14 +131,14 @@ export default {
   top:0;
   right: 0;
   bottom:0;
-  background: #efefef;
+  background: #e6e6e6;
   overflow: auto;
 
   .picture-editor-wrap_canvas {
     position: relative;
-    margin-top: 30px;
+    margin-top: 85px;
     .picture-editor-canvas {
-      height: calc(100vh - 40px);
+      height: calc(100vh - 85px);
       display: flex;
       align-items: center;
       justify-content: center;
@@ -108,6 +150,7 @@ export default {
   @import '../PictureEditor/mixin/layer/index.scss';
   @import '../PictureEditor/mixin/color/index.scss';
   @import '../PictureEditor/mixin/edit/index.scss';
+  @import './tpl/header.scss';
 
 
   .picture-editor-empty {
@@ -119,7 +162,7 @@ export default {
 
   .add-action-wrap {
     position: fixed;
-    top:30px;
+    top:80px;
     left: 0px;
     bottom: 0;
     width: 200px;
@@ -152,7 +195,6 @@ export default {
       font-size: 28px;
       color: #000;
       margin-right: 10px;
-      background: #E2E5FC;
     }
 
     .active {

+ 10 - 14
frontend/src/views/components/marketingEdit/tpl/add.js

@@ -2,7 +2,8 @@ import xz from '@/views/components/PictureEditor/mixin/actions/images/xuanze.png
 import tianjia from '@/views/components/PictureEditor/mixin/actions/images/tianjia.svg'
 import shanyibu from '@/views/components/PictureEditor/images/shanyibu.png'
 
-let svg = () => {
+
+let add = () => {
 
   return `
       <div class="add-action-wrap">
@@ -23,17 +24,9 @@ let svg = () => {
                       </UploadSlot>
                     </el-dropdown-item>
                     
-                    <el-dropdown-item>
-                    
-                        <UploadSlot background='none' ref="upload"
-                        :accept="['svg']"
-                        class="upload-wrap" @input="addSvg">
-                            <div>新增svg</div>
-                      </UploadSlot>
-                        
-                    </el-dropdown-item>
-                    
                     <el-dropdown-item  @click.native="addText"><div class="te-c">新增文字</div></el-dropdown-item>
+                    
+                    <el-dropdown-item  @click.native="addText"><div class="te-c">新增商品</div></el-dropdown-item>
                 
                     
                   </el-dropdown-menu>
@@ -44,17 +37,20 @@ let svg = () => {
                         <div class="icon flex"><img src="${xz}" class="icon"></div>
                         <div>选择</div>
               </el-button>
-              <el-button @click="toErase" :type=" action == 'erase' ? 'primary' : ''"  class="mar-left-10">
+        <!--      <el-button @click="toErase" :type=" action == 'erase' ? 'primary' : ''"  class="mar-left-10">
                   <svg-icon icon-class="xpc" class="icon"  />
                   <div>橡皮擦</div>
-              </el-button>
+              </el-button>-->
               <el-button @click="toReset">
                         <div class="icon flex"><img src="${shanyibu}" class="icon"></div>
                         <div>重置</div>
               </el-button>
+              
             </div>
           </div>
+        
+            
       </div>
      `
 }
-export default svg
+export default add

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

@@ -0,0 +1,39 @@
+
+
+export  default function tpl(){
+  return `
+      <div class="picture-editor-header flex between">
+           
+           <div class="flex mar-left-10"> 
+                <div class="title">编辑信息</div>
+           </div>
+           
+           <div class="button" style="margin-right: 10px;">
+            <el-button @click="handleAdjustCanvas">调整画布</el-button>
+            <el-button type="primary">保存</el-button>
+           </div>
+           
+            <!-- 新增:调整画布尺寸弹窗 -->
+            <el-dialog title="调整画布" v-model="canvasForm.visible" append-to-body width="30%">
+              <el-form :model="canvasForm" label-width="80px">
+                <el-form-item label="宽度">
+                  <el-input v-model.number="canvasForm.width" placeholder="请输入宽度"></el-input>
+                </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.color" />
+                </el-form-item>
+              </el-form>
+              <div slot="footer" class="dialog-footer flex right">
+                <el-button @click="canvasForm.visible = false">取 消</el-button>
+                <el-button type="primary" @click="submitCanvasInfo">确 定</el-button>
+              </div>
+            </el-dialog>
+            
+            
+
+      </div>
+  `
+}

+ 12 - 0
frontend/src/views/components/marketingEdit/tpl/header.scss

@@ -0,0 +1,12 @@
+.picture-editor-header {
+  width: 100%;
+  height: 50px;
+  position: fixed;
+  left: 0;
+  right: 0;
+  top:30px;
+  z-index: 99;
+  background: #f0f0f0;
+  line-height: 30px;
+}
+

+ 0 - 14
frontend/src/views/components/marketingEdit/tpl/image.js

@@ -1,14 +0,0 @@
-/*图片编辑*/
-import tools from "@/views/components/PictureEditor/mixin/edit/module/tools";
-
-let image = () => {
-  return `
-      <div class="edit-wrap">
-          <div class="title">编辑内容</div>
-          ${tools.flip()}
-          ${tools.straw()}
-          ${tools.opacity()}
-      </div>
-     `
-}
-export default image

+ 4 - 0
frontend/src/views/components/marketingEdit/tpl/index.js

@@ -2,6 +2,7 @@ import  viewTpl from  './view'
 import  colorTpl from  '@/views/components/PictureEditor/mixin//color/tpl'
 import  editTpl from  './edit'
 import add from "./add";
+import header from "./header";
 
 
 export  default function tpl(){
@@ -10,6 +11,9 @@ export  default function tpl(){
       <div ref="wrap" class="picture-editor-wrap flex">
       
           <!--新增-->
+          ${header()}
+          <!--新增-->
+          <!--新增-->
           ${add()}
           <!--新增-->
         <div

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

@@ -6,7 +6,7 @@ export default function() {
   return `
     <div class="picture-editor-canvas">
        <div  v-if="!isEmpty" style="width: calc(100% + 8px); height: 100%; overflow: auto">
-            <canvas id="marketing-canvas" width="500" height="500"/>
+            <canvas id="marketing-canvas" />
       </div>
     </div>
   `