Forráskód Böngészése

✨ feat: 新增SD新建广告活动--创意图片裁切上传

WanGxC 1 éve
szülő
commit
101c360932

+ 150 - 163
src/views/adManage/sd/campaigns/CreateCampaigns/component/Creativity.vue

@@ -7,19 +7,22 @@
       </div>
       <div class="main-container" v-loading="createLoading">
         <div class="left-container">
-          <p class="left-part-title">请选择您想要自定义商品广告的方式</p>
-          <el-scrollbar height="655px">
+          <div style="display: flex; justify-content: space-between;">
+            <p class="left-part-title">请选择您想要自定义商品广告的方式</p>
+          </div>
+          
+          <el-scrollbar height="755px">
             <div class="demo-collapse">
               <el-collapse v-model="activeNames" style="border: none">
-                <el-collapse-item title="徽标" name="logo" v-loading="pictureLoading">
+                <el-collapse-item title="徽标" name="logo" v-loading="logoLoading">
                   <div style="display: flex; margin-bottom: 5px">
                     <p style="font-weight: 700; color: #1d2129; margin-right: 8px">品牌标识</p>
                     <el-switch v-model="isOpenLogo" size="small" />
                   </div>
                   <el-upload
-                    v-model:file-list="pictureFileList"
-                    :on-change="changePicture"
-                    :on-remove="handleRemovePicture"
+                    v-model:file-list="logoFileList"
+                    :on-change="changeLogo"
+                    :on-remove="removeLogo"
                     action="#"
                     accept=".png, .jpg"
                     :limit="1"
@@ -30,10 +33,10 @@
                       <div>
                         <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
                         <span class="el-upload-list__item-actions">
-                          <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
+                          <span class="el-upload-list__item-preview" @click="logoPreview(file)">
                             <el-icon><zoom-in /></el-icon>
                           </span>
-                          <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemovePicture(file)">
+                          <span v-if="!disabled" class="el-upload-list__item-delete" @click="removeLogo()">
                             <el-icon><Delete /></el-icon>
                           </span>
                         </span>
@@ -45,7 +48,7 @@
                           <span style="line-height: 17px; font-weight: 600; color: #1e2128">徽标要求</span>
                           <!-- <el-button type="primary" :icon="Picture" @click="openDialog">从素材库中选择</el-button> -->
                         </div>
-                        <div class="introduce-item">1、图片大小: 600x100 像素</div>
+                        <div class="introduce-item">1、图片大小: 至少600x100 像素</div>
                         <div class="introduce-item">2、文件大小: 小于1MB</div>
                         <div style="display: flex; justify-content: space-between">
                           <span class="introduce-item">3、文件格式: PNG 或 JPG</span>
@@ -65,9 +68,9 @@
                   </el-upload>
                   <!-- 预览弹窗 -->
                   <el-dialog v-model="dialogVisible">
-                    <img w-full :src="dialogImageUrl" alt="Preview Image" />
+                    <img w-full :src="dialogLogoUrl" alt="Preview Image" />
                   </el-dialog>
-                  <CropperImg />
+                  <CropperLogo />
                 </el-collapse-item>
                 <el-collapse-item title="标题" name="title">
                   <div style="display: flex">
@@ -85,56 +88,59 @@
                     <el-icon size="14" style="margin-right: 1px"><Link /></el-icon>
                   </div>
                 </el-collapse-item>
-                <el-collapse-item title="图片" name="image">
-                  <div style="display: flex; margin-bottom: 5px">
-                    <p style="font-weight: 700; color: #1d2129; margin-right: 8px">添加图片</p>
-                    <el-switch v-model="isOpenPicture" size="small" />
-                  </div>
-                  <el-upload
-                    v-model:file-list="pictureFileList2"
-                    :on-change="changePicture2"
-                    v-loading="pictureLoading2"
-                    :on-remove="handleRemovePicture2"
-                    action="#"
-                    accept=".png, .jpeg, .gif"
-                    :limit="1"
-                    list-type="picture-card"
-                    :auto-upload="false">
-                    <el-icon><Plus /></el-icon>
-                    <template #file="{ file }">
-                      <div>
-                        <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
-                        <span class="el-upload-list__item-actions">
-                          <span class="el-upload-list__item-preview" @click="handlePicturePreview(file)">
-                            <el-icon><zoom-in /></el-icon>
+                <el-collapse-item title="图片" name="image" v-loading="imgLoading">
+                  <div>
+                    <div style="display: flex; margin-bottom: 5px">
+                      <p style="font-weight: 700; color: #1d2129; margin-right: 8px">添加图片</p>
+                      <el-switch v-model="isOpenPicture" size="small" />
+                    </div>
+                    <el-upload
+                      v-model:file-list="imgFileList"
+                      :on-change="changeImg"
+                      :on-remove="removeImg"
+                      action="#"
+                      accept=".png, .jpeg, .gif"
+                      :limit="1"
+                      list-type="picture-card"
+                      :auto-upload="false"
+                      style="margin-bottom: 8px;">
+                      <el-icon><Plus /></el-icon>
+                      <template #file="{ file }">
+                        <div>
+                          <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
+                          <span class="el-upload-list__item-actions">
+                            <span class="el-upload-list__item-preview" @click="imgPreview(file)">
+                              <el-icon><zoom-in /></el-icon>
+                            </span>
+                            <span v-if="!disabled" class="el-upload-list__item-delete" @click="removeImg()">
+                              <el-icon><Delete /></el-icon>
+                            </span>
                           </span>
-                          <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemovePicture2()">
-                            <el-icon><Delete /></el-icon>
-                          </span>
-                        </span>
-                      </div>
-                    </template>
-                    <template #tip>
-                      <div style="margin-top: 10px">
-                        <div style="display: flex; align-items: center; justify-content: space-between">
-                          <span style="line-height: 17px; font-weight: 600; color: #1e2128">徽标要求</span>
                         </div>
-                        <div class="introduce-item">1、图片大小: 1200x628 像素</div>
-                        <div class="introduce-item">2、文件大小: 小于5MB</div>
-                        <div class="introduce-item">3、文件格式: PNG, JPEG 或GIF</div>
-                        <div class="introduce-item">4、没有在图片上添加文字, 图形或徽标</div>
-                      </div>
-                    </template>
-                  </el-upload>
-                  <el-dialog v-model="dialogVisible2">
-                    <img w-full :src="dialogImageUrl2" alt="Preview Image" />
-                  </el-dialog>
+                      </template>
+                      <template #tip>
+                        <div style="margin-top: 10px">
+                          <div style="display: flex; align-items: center; justify-content: space-between">
+                            <span style="line-height: 17px; font-weight: 600; color: #1e2128">徽标要求</span>
+                          </div>
+                          <div class="introduce-item">1、图片大小: 至少1200x628 像素</div>
+                          <div class="introduce-item">2、文件大小: 小于5MB</div>
+                          <div class="introduce-item">3、文件格式: PNG, JPEG 或GIF</div>
+                          <div class="introduce-item">4、没有在图片上添加文字, 图形或徽标</div>
+                        </div>
+                      </template>
+                    </el-upload>
+                    <el-dialog v-model="imgDialogVisible">
+                      <img w-full :src="dialogImgUrl" alt="Preview Image" />
+                    </el-dialog>
+                    <CropperImg />
+                  </div>
                   <!-- 628x628图片上传 -->
                   <el-divider border-style="dashed" />
                   <div style="display: flex; margin-bottom: 5px">
                     <p style="font-weight: 700; color: #1d2129; margin-right: 8px">628x628</p>
                   </div>
-                  
+                  <CropperImg628 />
                 </el-collapse-item>
               </el-collapse>
             </div>
@@ -143,17 +149,18 @@
         <div class="right-container">
           <div class="preview-title-line">
             <p class="right-part-title">广告预览</p>
-            <el-select></el-select>
-            <el-button style="margin-left: 10px" @click="createCreativity">保存</el-button>
+            <el-select v-model="previewSelect"></el-select>
           </div>
+          <el-button style="position: absolute; bottom: 5px; right: 50%;" type="primary" :icon="Check" circle @click="createCreativity" />
         </div>
       </div>
+      
     </el-card>
   </div>
 </template>
 
 <script setup lang="ts">
-import { Delete, Plus, ZoomIn } from '@element-plus/icons-vue'
+import { Delete, Plus, ZoomIn, Check } from '@element-plus/icons-vue'
 import type { UploadFile } from 'element-plus'
 import { ElMessage } from 'element-plus'
 import { storeToRefs } from 'pinia'
@@ -161,7 +168,9 @@ import { Ref, inject, onMounted, onBeforeUnmount, ref } from 'vue'
 import { checkAsset, postCreative, uploadFile } from '../api/index'
 import { useShopInfo } from '/@/stores/shopInfo'
 import emitter from '/@/utils/emitter'
+import CropperLogo from '../component/CropperLogo.vue'
 import CropperImg from '../component/CropperImg.vue'
+import CropperImg628 from './CropperImg-628.vue'
 
 
 const respAdGroupId = inject<Ref>('respAdGroupId')
@@ -172,14 +181,14 @@ const { profile } = storeToRefs(shopInfo)
 const activeNames = ref([''])
 
 // logo相关
-const pictureFileList = ref([])
-const pictureLoading = ref(false)
+const logoFileList = ref([])
+const logoLoading = ref(false)
 const disabled = ref(false)
 const dialogVisible = ref(false)
-const dialogImageUrl = ref('')
+const dialogLogoUrl = ref('')
 const centerDialogVisible = ref(false)
 const logoImg = ref('')
-let imgDimension = {
+let logoDimension = {
   left: 0,
   top: 0,
   width: 0,
@@ -187,14 +196,14 @@ let imgDimension = {
 }
 
 emitter.on('send-logo-leftTop', (value: any) => {
-  imgDimension.left = value.left
-  imgDimension.top = value.top
-  imgDimension.width = value.width
-  imgDimension.height = value.height
+  logoDimension.left = value.left
+  logoDimension.top = value.top
+  logoDimension.width = value.width
+  logoDimension.height = value.height
 })
 
-function changePicture(file: UploadFile) {
-  emitter.emit('img-src', file.url)
+function changeLogo(file: UploadFile) {
+  emitter.emit('logo-src', file.url)
   logoImg.value = file.url
   const reader = new FileReader()
   reader.readAsDataURL(file.raw)
@@ -202,23 +211,23 @@ function changePicture(file: UploadFile) {
     const img = new Image()
     img.src = imgInfo.target.result
     img.onload = () => {
-      emitter.emit('img-data', img)
+      emitter.emit('logo-data', img)
       if (img.width >= 600 && img.height >= 100) {
-        // handleUpload(file)
+        handleUpload(file)
       } else {
-        pictureFileList.value = []
+        logoFileList.value = []
         ElMessage.error('不符合尺寸要求')
       }
     }
   }
 }
 
-function handleRemovePicture(file: UploadFile) {
-  pictureFileList.value = []
+function removeLogo() {
+  logoFileList.value = []
 }
 
-function handlePictureCardPreview(file: UploadFile) {
-  dialogImageUrl.value = file.url!
+function logoPreview(file: UploadFile) {
+  dialogLogoUrl.value = file.url!
   dialogVisible.value = true
 }
 
@@ -235,7 +244,7 @@ async function handleUpload(file: UploadFile) {
   formData.append('brandEntityId', brandEntityId.value)
   formData.append('assetType', 'IMAGE')
   formData.append('assetSubTypeList', JSON.stringify(['LOGO']))
-  pictureLoading.value = true
+  logoLoading.value = true
   try {
     const response = await uploadFile(formData)
     brandLogoAssetVersion = response.data.third.versionId
@@ -249,13 +258,13 @@ async function handleUpload(file: UploadFile) {
     if (resp.data.checkresult == 'success') {
       ElMessage({ message: '上传成功', type: 'success' })
     } else {
-      pictureFileList.value = []
+      logoFileList.value = []
       ElMessage.error('上传失败')
     }
   } catch (error) {
     console.error('上传失败:', error)
   } finally {
-    pictureLoading.value = false
+    logoLoading.value = false
   }
 }
 
@@ -269,38 +278,66 @@ const titleInp = ref('')
 const isOpenTitle = ref(false)
 
 // 图片相关
-const pictureFileList2 = ref([])
-const pictureLoading2 = ref(false)
+const imgFileList = ref([])
+const imgLoading = ref(false)
 const isOpenPicture = ref(false)
-const dialogVisible2 = ref(false)
-const dialogImageUrl2 = ref('')
+const imgDialogVisible = ref(false)
+const dialogImgUrl = ref('')
 let pictureAssetVersion2 = ''
 let pictureAssetID2 = ''
+let imgDimension = {
+  left: 0,
+  top: 0,
+  width: 0,
+  height: 0,
+}
+let imgDimension628 = {
+  left: 0,
+  top: 0,
+  width: 0,
+  height: 0,
+}
+
+emitter.on('send-img-leftTop', (value: any) => {  // 获取图片的坐标和尺寸
+  imgDimension.left = value.left
+  imgDimension.top = value.top
+  imgDimension.width = value.width
+  imgDimension.height = value.height
+})
+
+emitter.on('send-img628-leftTop', (value: any) => {
+  imgDimension628.left = value.left
+  imgDimension628.top = value.top
+  imgDimension628.width = value.width
+  imgDimension628.height = value.height
+})
 
-function changePicture2(file: UploadFile) {
+function changeImg(file: UploadFile) {
+  emitter.emit('img-src', file.url)
   const reader = new FileReader()
   reader.readAsDataURL(file.raw)
   reader.onload = (imgInfo: any) => {
     const img = new Image()
     img.src = imgInfo.target.result
     img.onload = () => {
-      if (img.width == 1200 && img.height == 628) {
+      emitter.emit('img-data', img)
+      if (img.width >= 1200 && img.height >= 628) {
         handleUploadPicture2(file)
       } else {
-        pictureFileList2.value = []
+        imgFileList.value = []
         ElMessage.error('不符合尺寸要求')
       }
     }
   }
 }
 
-function handleRemovePicture2() {
-  pictureFileList2.value = []
+function removeImg() {
+  imgFileList.value = []
 }
 
-function handlePicturePreview(file: UploadFile) {
-  dialogImageUrl2.value = file.url!
-  dialogVisible2.value = true
+function imgPreview(file: UploadFile) {
+  dialogImgUrl.value = file.url!
+  imgDialogVisible.value = true
 }
 
 async function handleUploadPicture2(file: UploadFile) {
@@ -310,7 +347,7 @@ async function handleUploadPicture2(file: UploadFile) {
   formData.append('brandEntityId', brandEntityId.value)
   formData.append('assetType', 'IMAGE')
   formData.append('assetSubTypeList', JSON.stringify(['LIFESTYLE_IMAGE', 'OTHER_IMAGE']))
-  pictureLoading2.value = true
+  imgLoading.value = true
   try {
     const response = await uploadFile(formData)
     pictureAssetVersion2 = response.data.third.versionId
@@ -330,71 +367,7 @@ async function handleUploadPicture2(file: UploadFile) {
   } catch (error) {
     console.error('上传失败:', error)
   } finally {
-    pictureLoading2.value = false
-  }
-}
-
-const pictureFileList3 = ref([])
-const pictureLoading3 = ref()
-const dialogVisible3 = ref(false)
-const dialogImageUrl3 = ref('')
-let pictureAssetVersion3 = ''
-let pictureAssetID3 = ''
-
-function changePicture3(file: UploadFile) {
-  const reader = new FileReader()
-  reader.readAsDataURL(file.raw)
-  reader.onload = (imgInfo: any) => {
-    const img = new Image()
-    img.src = imgInfo.target.result
-    img.onload = () => {
-      // console.log(img.width, img.height)
-      if (img.width == 628 && img.height == 628) {
-        handleUploadPicture3(file)
-      } else {
-        pictureFileList3.value = []
-        ElMessage.error('不符合尺寸要求')
-      }
-    }
-  }
-}
-
-function handleRemovePicture3() {
-  pictureFileList3.value = []
-}
-
-function handlePicturePreview3(file: UploadFile) {
-  dialogImageUrl3.value = file.url!
-  dialogVisible3.value = true
-}
-
-async function handleUploadPicture3(file: UploadFile) {
-  const formData = new FormData()
-  formData.append('file', file.raw)
-  formData.append('profile_id', profile.value.profile_id)
-  formData.append('brandEntityId', brandEntityId.value)
-  formData.append('assetType', 'IMAGE')
-  formData.append('assetSubTypeList', JSON.stringify(['LOGO']))
-  pictureLoading3.value = true
-  try {
-    const response = await uploadFile(formData)
-    pictureAssetVersion3 = response.data.third.versionId
-    const fileName = response.data.file_name
-    const obj = {
-      profile_id: profile.value.profile_id,
-      file_name: fileName,
-    }
-    const resp = await checkAsset(obj)
-    pictureAssetID3 = resp.data.assetId
-    if (resp.data.checkresult == 'success') {
-      ElMessage({ message: '上传成功', type: 'success' })
-    } else {
-      ElMessage.error('上传失败')
-    }
-  } catch (error) {
-    console.error('上传失败:', error)
-  } finally {
-    pictureLoading3.value = false
+    imgLoading.value = false
   }
 }
 
@@ -412,12 +385,11 @@ async function createCreativity() {
     headline: titleInp.value,
     brandLogo_assetId: brandLogoAssetID,
     brandLogo_assetVersion: brandLogoAssetVersion,
-    brandLogo_croppingCoordinates: imgDimension,
-    // contentType: '',
-    rectCustomImage_assetId: pictureAssetID2,
-    rectCustomImage_assetVersion: pictureAssetVersion2,
-    squareCustomImage_assetId: pictureAssetID3,
-    squareCustomImage_assetVersion: pictureAssetVersion3,
+    brandLogo_croppingCoordinates: logoDimension,
+    custom_assetId: pictureAssetID2,
+    custom_assetVersion: pictureAssetVersion2,
+    rectCustomImage_croppingCoordinates: imgDimension,
+    squareCustomImage_croppingCoordinates: imgDimension628,
   }
   try {
     const response = await postCreative(obj)
@@ -433,13 +405,15 @@ async function createCreativity() {
   }
 }
 
+const previewSelect = ref('')
+
 onMounted(() => {
   emitter.on('send-brandEntityId', (value: any) => {
     brandEntityId.value = value.brandEntityId[0].brandEntityId
   })
 })
 
-onBeforeUnmount(()=>{
+onBeforeUnmount(() => {
   emitter.all.clear()
 })
 </script>
@@ -463,7 +437,7 @@ onBeforeUnmount(()=>{
 .main-container {
   display: flex;
   border: 1px solid #e5e7eb;
-  height: 700px;
+  height: 800px;
   margin-bottom: 20px;
   border-radius: 6px;
 }
@@ -483,6 +457,12 @@ onBeforeUnmount(()=>{
   display: flex;
   align-items: center;
 }
+.right-container {
+  position: relative;
+  width: 50%;
+  padding: 10px;
+  border-right: 1px solid #e5e7eb;
+}
 .right-part-title {
   color: #4e5969;
   padding-bottom: 4px;
@@ -496,4 +476,11 @@ onBeforeUnmount(()=>{
   font-size: 12px;
   color: #88909b;
 }
+::v-deep(.el-collapse > .el-collapse-item:last-child .el-collapse-item__header) { /* 移除collapse底部的线 */
+  border-bottom: none;
+}
+::v-deep(.el-collapse > .el-collapse-item:last-child .el-collapse-item__wrap) {
+  border-bottom: none;
+}
+
 </style>

+ 102 - 0
src/views/adManage/sd/campaigns/CreateCampaigns/component/CropperImg-628.vue

@@ -0,0 +1,102 @@
+<template>
+  <div ref="outContianer" style="width: 100%">
+    <div :style="{ width: containerDimensions.width + 'px', height: containerDimensions.height + 'px' }">
+      <vueCropper
+        ref="cropper"
+        :img="option.img"
+        :canScale="option.canScale"
+        :full="option.full"
+        :outputSize="option.size"
+        :centerBox="option.centerBox"
+        :autoCrop="option.autoCrop"
+        :autoCropWidth="option.autoCropWidth"
+        :autoCropHeight="option.autoCropHeight"
+        :fixedBox="option.fixedBox"
+        :canMove="option.canMove"
+        :mode="option.mode"
+        :info="option.info"
+        @cropMoving="debouncedCropMoving"></vueCropper>
+    </div>
+  </div>
+  <div style="padding-top: 8px">
+    <el-text class="mx-1">预览:</el-text>
+    <img :src="previewImg" style="width: 30%;" />
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeUnmount } from 'vue'
+import 'vue-cropper/dist/index.css'
+import { VueCropper } from 'vue-cropper'
+import emitter from '/@/utils/emitter'
+import _ from 'lodash'
+
+const cropper = ref()
+const containerDimensions = ref({ width: 100, height: 100 })
+
+const option = ref({
+  img: '',
+  size: 1,
+  info: false,
+  autoCrop: true,
+  autoCropWidth: 600,
+  autoCropHeight: 100,
+  canScale: false,
+  full: false,
+  fixedBox: true,
+  centerBox: true,
+  canMove: false,
+  enlarge: 1,
+  mode: 'contain',
+  original: false,
+  outputType: 'png',
+})
+
+// 根据图片大小缩放设置容器大小以及裁剪框大小
+const outContianer = ref()
+let scale
+
+emitter.on('img-src', (value) => {
+  option.value.img = value
+})
+
+emitter.on('img-data', (value) => {
+  option.value.autoCropWidth = 628
+  option.value.autoCropHeight = 628
+
+  scale = Math.min(outContianer.value.offsetWidth / value.width, 1) // 缩放图片宽度到最大不超过容器宽度
+
+  // 计算缩放后的图片尺寸
+  const scaledWidth = value.width * scale
+  const scaledHeight = value.height * scale
+
+  // 更新容器尺寸
+  containerDimensions.value.width = scaledWidth
+  containerDimensions.value.height = scaledHeight
+
+  // 更新裁剪框尺寸
+  option.value.autoCropWidth = option.value.autoCropWidth * scale
+  option.value.autoCropHeight = option.value.autoCropHeight * scale
+})
+
+// 移动裁剪框后获取坐标 以及 渲染裁剪框图片
+const previewImg = ref('')
+
+function cropMoving(data) {
+  const left = parseInt((data.axis.x1 / scale).toFixed(0)) // 结果保留整数
+  const top = parseInt((data.axis.y1 / scale).toFixed(0))
+
+  emitter.emit('send-img628-leftTop', { left: left, top: top, width: 628, height: 628 })
+  
+  cropper.value.getCropData((data) => {
+    // 获取base64数据图片
+    previewImg.value = data
+  })
+}
+
+const debouncedCropMoving = _.debounce(cropMoving, 100)
+
+onBeforeUnmount(() => {
+  emitter.all.clear()
+})
+</script>

+ 8 - 13
src/views/adManage/sd/campaigns/CreateCampaigns/component/CropperImg.vue

@@ -18,9 +18,9 @@
         @cropMoving="debouncedCropMoving"></vueCropper>
     </div>
   </div>
-  <div style="padding-top: 8px;">
+  <div style="padding-top: 8px">
     <el-text class="mx-1">预览:</el-text>
-    <img :src="previewImg" />
+    <img :src="previewImg" style="width: 30%;" />
   </div>
 </template>
 
@@ -54,21 +54,17 @@ const option = ref({
 
 // 根据图片大小缩放设置容器大小以及裁剪框大小
 const outContianer = ref()
-let scale = 1
+let scale
 
 emitter.on('img-src', (value) => {
   option.value.img = value
 })
 
 emitter.on('img-data', (value) => {
-  option.value.autoCropWidth = 600
-  option.value.autoCropHeight = 100
+  option.value.autoCropWidth = 1200
+  option.value.autoCropHeight = 628
 
-  if (!(value.width == 600 && value.height == 100)) {
-    scale = Math.min(outContianer.value.offsetWidth / value.width, 1) // 缩放图片宽度到最大不超过容器宽度
-  } else {
-    scale = 1
-  }
+  scale = Math.min(outContianer.value.offsetWidth / value.width, 1) // 缩放图片宽度到最大不超过容器宽度
 
   // 计算缩放后的图片尺寸
   const scaledWidth = value.width * scale
@@ -87,16 +83,15 @@ emitter.on('img-data', (value) => {
 const previewImg = ref('')
 
 function cropMoving(data) {
-  const left = parseInt((data.axis.x1 / scale).toFixed(0))  // 结果保留整数
+  const left = parseInt((data.axis.x1 / scale).toFixed(0)) // 结果保留整数
   const top = parseInt((data.axis.y1 / scale).toFixed(0))
 
-  emitter.emit('send-logo-leftTop', { left: left, top: top, width: 600, height: 100})
+  emitter.emit('send-img-leftTop', { left: left, top: top, width: 1200, height: 628 })
 
   cropper.value.getCropData((data) => {
     // 获取base64数据图片
     previewImg.value = data
   })
-  
 }
 
 const debouncedCropMoving = _.debounce(cropMoving, 100)

+ 107 - 0
src/views/adManage/sd/campaigns/CreateCampaigns/component/CropperLogo.vue

@@ -0,0 +1,107 @@
+<template>
+  <div ref="outContianer" style="width: 100%">
+    <div :style="{ width: containerDimensions.width + 'px', height: containerDimensions.height + 'px' }">
+      <vueCropper
+        ref="cropper"
+        :img="option.img"
+        :canScale="option.canScale"
+        :full="option.full"
+        :outputSize="option.size"
+        :centerBox="option.centerBox"
+        :autoCrop="option.autoCrop"
+        :autoCropWidth="option.autoCropWidth"
+        :autoCropHeight="option.autoCropHeight"
+        :fixedBox="option.fixedBox"
+        :canMove="option.canMove"
+        :mode="option.mode"
+        :info="option.info"
+        @cropMoving="debouncedCropMoving"></vueCropper>
+    </div>
+  </div>
+  <div style="padding-top: 8px;">
+    <el-text class="mx-1">预览:</el-text>
+    <img :src="previewImg" style="width: 40%;" />
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeUnmount } from 'vue'
+import 'vue-cropper/dist/index.css'
+import { VueCropper } from 'vue-cropper'
+import emitter from '/@/utils/emitter'
+import _ from 'lodash'
+
+const cropper = ref()
+const containerDimensions = ref({ width: 100, height: 100 })
+
+const option = ref({
+  img: '',
+  size: 1,
+  info: false,
+  autoCrop: true,
+  autoCropWidth: 600,
+  autoCropHeight: 100,
+  canScale: false,
+  full: false,
+  fixedBox: true,
+  centerBox: true,
+  canMove: false,
+  enlarge: 1,
+  mode: 'contain',
+  original: false,
+  outputType: 'png',
+})
+
+// 根据图片大小缩放设置容器大小以及裁剪框大小
+const outContianer = ref()
+let scale = 1
+
+emitter.on('logo-src', (value) => {
+  option.value.img = value
+})
+
+emitter.on('logo-data', (value) => {
+  option.value.autoCropWidth = 600
+  option.value.autoCropHeight = 100
+
+  if (!(value.width == 600 && value.height == 100)) {
+    scale = Math.min(outContianer.value.offsetWidth / value.width, 1) // 缩放图片宽度到最大不超过容器宽度
+  } else {
+    scale = 1
+  }
+
+  // 计算缩放后的图片尺寸
+  const scaledWidth = value.width * scale
+  const scaledHeight = value.height * scale
+
+  // 更新容器尺寸
+  containerDimensions.value.width = scaledWidth
+  containerDimensions.value.height = scaledHeight
+
+  // 更新裁剪框尺寸
+  option.value.autoCropWidth = option.value.autoCropWidth * scale
+  option.value.autoCropHeight = option.value.autoCropHeight * scale
+})
+
+// 移动裁剪框后获取坐标 以及 渲染裁剪框图片
+const previewImg = ref('')
+
+function cropMoving(data) {
+  const left = parseInt((data.axis.x1 / scale).toFixed(0))  // 结果保留整数
+  const top = parseInt((data.axis.y1 / scale).toFixed(0))
+
+  emitter.emit('send-logo-leftTop', { left: left, top: top, width: 600, height: 100})
+
+  cropper.value.getCropData((data) => {
+    // 获取base64数据图片
+    previewImg.value = data
+  })
+  
+}
+
+const debouncedCropMoving = _.debounce(cropMoving, 100)
+
+onBeforeUnmount(() => {
+  emitter.all.clear()
+})
+</script>