Просмотр исходного кода

✨ feat: sb新建广告活动基本完成

WanGxC 1 год назад
Родитель
Сommit
d2d32edce1

+ 70 - 22
package-lock.json

@@ -2275,26 +2275,26 @@
 			}
 		},
 		"@floating-ui/core": {
-			"version": "1.5.0",
-			"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.5.0.tgz",
-			"integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==",
+			"version": "1.6.0",
+			"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.0.tgz",
+			"integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
 			"requires": {
-				"@floating-ui/utils": "^0.1.3"
+				"@floating-ui/utils": "^0.2.1"
 			}
 		},
 		"@floating-ui/dom": {
-			"version": "1.5.3",
-			"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.5.3.tgz",
-			"integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==",
+			"version": "1.6.1",
+			"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.1.tgz",
+			"integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==",
 			"requires": {
-				"@floating-ui/core": "^1.4.2",
-				"@floating-ui/utils": "^0.1.3"
+				"@floating-ui/core": "^1.6.0",
+				"@floating-ui/utils": "^0.2.1"
 			}
 		},
 		"@floating-ui/utils": {
-			"version": "0.1.6",
-			"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz",
-			"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
+			"version": "0.2.1",
+			"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.1.tgz",
+			"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
 		},
 		"@httptoolkit/websocket-stream": {
 			"version": "6.0.1",
@@ -2574,6 +2574,7 @@
 				"string-width-cjs": "npm:string-width@^4.2.0",
 				"strip-ansi": "^7.0.1",
 				"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+				"wrap-ansi": "^8.1.0",
 				"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
 			},
 			"dependencies": {
@@ -3279,14 +3280,14 @@
 			"dev": true
 		},
 		"@types/lodash": {
-			"version": "4.14.199",
-			"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.199.tgz",
-			"integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg=="
+			"version": "4.14.202",
+			"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
+			"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
 		},
 		"@types/lodash-es": {
-			"version": "4.17.9",
-			"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.9.tgz",
-			"integrity": "sha512-ZTcmhiI3NNU7dEvWLZJkzG6ao49zOIjEgIE0RgV7wbPxU0f2xT3VSAHw2gmst8swH6V0YkLRGp4qPlX/6I90MQ==",
+			"version": "4.17.12",
+			"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+			"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
 			"requires": {
 				"@types/lodash": "*"
 			}
@@ -4556,12 +4557,12 @@
 			"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
 		},
 		"element-plus": {
-			"version": "2.3.14",
-			"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.14.tgz",
-			"integrity": "sha512-9yvxUaU4jXf2ZNPdmIxoj/f8BG8CDcGM6oHa9JIqxLjQlfY4bpzR1E5CjNimnOX3rxO93w1TQ0jTVt0RSxh9kA==",
+			"version": "2.4.4",
+			"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.4.4.tgz",
+			"integrity": "sha512-TlKubXJgxwhER0dw+8ULn9hr9kZjraV4R6Q/eidwWUwCKxwXYPBGmMKsZ/85tlxlhMYbcLZd/YZh6G3QkHX4fg==",
 			"requires": {
 				"@ctrl/tinycolor": "^3.4.1",
-				"@element-plus/icons-vue": "^2.0.6",
+				"@element-plus/icons-vue": "^2.3.1",
 				"@floating-ui/dom": "^1.0.1",
 				"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
 				"@types/lodash": "^4.14.182",
@@ -4575,6 +4576,13 @@
 				"lodash-unified": "^1.0.2",
 				"memoize-one": "^6.0.0",
 				"normalize-wheel-es": "^1.2.0"
+			},
+			"dependencies": {
+				"@element-plus/icons-vue": {
+					"version": "2.3.1",
+					"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
+					"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg=="
+				}
 			}
 		},
 		"element-resize-detector": {
@@ -7534,6 +7542,46 @@
 			"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
 			"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
 		},
+		"wrap-ansi": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+			"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+			"requires": {
+				"ansi-styles": "^6.1.0",
+				"string-width": "^5.0.1",
+				"strip-ansi": "^7.0.1"
+			},
+			"dependencies": {
+				"ansi-regex": {
+					"version": "6.0.1",
+					"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz",
+					"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="
+				},
+				"ansi-styles": {
+					"version": "6.2.1",
+					"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz",
+					"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+				},
+				"string-width": {
+					"version": "5.1.2",
+					"resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+					"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+					"requires": {
+						"eastasianwidth": "^0.2.0",
+						"emoji-regex": "^9.2.2",
+						"strip-ansi": "^7.0.1"
+					}
+				},
+				"strip-ansi": {
+					"version": "7.1.0",
+					"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz",
+					"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+					"requires": {
+						"ansi-regex": "^6.0.1"
+					}
+				}
+			}
+		},
 		"wrap-ansi-cjs": {
 			"version": "npm:wrap-ansi@7.0.0",
 			"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

+ 1 - 1
package.json

@@ -26,7 +26,7 @@
 		"echarts": "^5.4.1",
 		"echarts-gl": "^2.0.9",
 		"echarts-wordcloud": "^2.1.0",
-		"element-plus": "^2.3.9",
+		"element-plus": "^2.4.4",
 		"element-tree-line": "^0.2.1",
 		"font-awesome": "^4.7.0",
 		"js-cookie": "^3.0.1",

+ 7 - 1
src/views/adManage/sb/campaigns/CreateCampaigns/api/index.ts

@@ -136,7 +136,6 @@ export function postStoreSpotlight(obj) {
       data: obj
   })
 }
-
 export function postBrandVideo(obj) {
   return request({
       url: '/api/ad_manage/sbads/brandvideo/create/',
@@ -151,4 +150,11 @@ export function postVideo(obj) {
       data: obj
   })
 }
+export function postProductset(obj) {
+  return request({
+      url: '/api/ad_manage/sbads/productcollection/create/',
+      method: 'post',
+      data: obj
+  })
+}
 

+ 7 - 0
src/views/adManage/sb/campaigns/CreateCampaigns/component/AdCampaign.vue

@@ -284,6 +284,13 @@ watch([respCampaignId, respCampaignName], () => {
   }
 })
 
+watch(
+  () => campaignRuleForm.brand,
+  () => {
+    emitter.emit('brandEntityId', brandOptions.value)
+  }
+)
+
 onMounted(() => {
   buildAdMix()
   getBrandOption()

+ 9 - 0
src/views/adManage/sb/campaigns/CreateCampaigns/component/AdFormat.vue

@@ -307,6 +307,15 @@ function shopChanged() {
   }, 2000)
 }
 
+watch([adFormatRadio, arrivalsRadio], ()=>{
+  ruleForm.shop = ''
+  ruleForm.page = ''
+  
+  if (ruleFormRef.value) {
+    ruleFormRef.value.clearValidate(['shop', 'page']);
+  }
+})
+
 onMounted(() => {
   getShopOptions()
 })

+ 7 - 1
src/views/adManage/sb/campaigns/CreateCampaigns/component/FocusCreativity.vue

@@ -243,7 +243,7 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, inject, Ref, watch, computed, onMounted } from 'vue'
+import { reactive, ref, inject, Ref, watch, computed, onMounted, onUnmounted } from 'vue'
 import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Plus, Picture, Search, Delete, Download, ZoomIn } from '@element-plus/icons-vue'
@@ -661,6 +661,7 @@ async function handleUpload(file: UploadFile) {
   }
 }
 
+// TODO: url对应的是Home 暂时写死
 async function createStoreSpotlight() {
   createLoading.value = true
   try {
@@ -706,6 +707,11 @@ onMounted(() => {
   })
 })
 
+// 接收数据端在组件卸载时解绑事件
+onUnmounted(() => {
+  emitter.off('spotlight-shop')
+})
+
 const focusShop = inject<Ref>('focusShop')
 
 async function getCommodityCollapseData() {

+ 2 - 2
src/views/adManage/sb/campaigns/CreateCampaigns/component/KeywordTarget.vue

@@ -36,8 +36,8 @@
             </el-table>
           </el-tab-pane>
           <el-tab-pane label="输入" name="second">
-            <el-input v-model="keyWordsTextarea" :rows="10" type="textarea" placeholder="未完成" style="padding-left: 5px" />
-            <el-button type="primary" size="small" text bg @click="addKeyWords">添加</el-button>
+            <el-input v-model="keyWordsTextarea" :rows="10" type="textarea" disabled="true" style="padding-left: 5px" />
+            <!-- <el-button type="primary" size="small" text bg @click="addKeyWords">添加</el-button> -->
           </el-tab-pane>
         </el-tabs>
       </div>

+ 1 - 1
src/views/adManage/sb/campaigns/CreateCampaigns/component/NegativeGood.vue

@@ -49,7 +49,7 @@
                   v-model="negativeGoodsTextarea"
                   :rows="17"
                   type="textarea"
-                  placeholder="未完成"
+                  disabled="true"
                   maxlength="11000"
                   style="padding: 10px 10px" />
                 <div style="display: flex; flex-direction: row-reverse; margin-top: 10px">

+ 6 - 6
src/views/adManage/sb/campaigns/CreateCampaigns/component/ProductSetCommodity.vue

@@ -102,7 +102,7 @@
         <div style="padding: 0 10px 0 10px; margin-top: -12px">
           <el-table
             :data="addedTableData"
-            height="475"
+            height="510"
             style="width: 100%"
             :header-cell-style="headerCellStyle"
             @selection-change="handleAddedGoodsChange">
@@ -144,9 +144,9 @@
             </el-table-column>
           </el-table>
         </div>
-        <div style="display: flex; justify-content: space-around; padding-top: 5px">
+        <!-- <div style="display: flex; justify-content: space-around; padding-top: 5px">
           <el-button type="primary" plain :disabled="addedTableData.length < 3"  @click="submitProductForm">保存</el-button>
-        </div>
+        </div> -->
       </div>
     </div>
   </div>
@@ -210,7 +210,7 @@ function setTableData(asin = '', sku = '') {
 
 function addSingleGoods(scope) {
   // console.log('scope', scope.row)
-  const isAlreadyAdded = addedTableData.value.some((item) => item.sku === scope.row.sku)
+  const isAlreadyAdded = addedTableData.value.some((item) => item.asin === scope.row.asin)
   if (!isAlreadyAdded) {
     addedTableData.value.push(scope.row)
   } else {
@@ -236,7 +236,7 @@ function addGods() {
 }
 
 function delSingleGoods(scope) {
-  const index = addedTableData.value.findIndex((item) => item.sku === scope.row.sku)
+  const index = addedTableData.value.findIndex((item) => item.asin === scope.row.asin)
   if (index !== -1) {
     addedTableData.value.splice(index, 1)
     console.log('Item removed successfully.')
@@ -290,7 +290,7 @@ function handleAddedGoodsChange(selection) {
 function handleGoodsAdd() {
   // 过滤掉已经存在于addedData.value中的项
   const newSelections = selections.filter(
-    (sel) => !addedTableData.value.some((added) => added.sku === sel.sku) // 使用sku作为唯一标识
+    (sel) => !addedTableData.value.some((added) => added.asin === sel.asin) // 使用asin作为唯一标识
   )
   // 如果有新的不重复项,加入到addedData.value中
   if (newSelections.length > 0) {

+ 136 - 24
src/views/adManage/sb/campaigns/CreateCampaigns/component/ProductSetCreativity1.vue

@@ -17,7 +17,7 @@
         <el-form-item label="广告名称" prop="name">
           <el-input v-model="ruleForm.name" style="width: 50%" />
         </el-form-item>
-        <div style="display: flex; border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px">
+        <div style="display: flex; border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px" v-loading="createLoading">
           <div style="width: 50%; padding-left: 5px; border-right: 1px solid #dddfe6">
             <el-scrollbar height="700px">
               <el-collapse v-model="activeNames" @change="handleChange" style="border-top: none; border-bottom: none">
@@ -31,6 +31,7 @@
                     v-model:file-list="fileList"
                     :on-change="changeFile"
                     v-loading="upLoading"
+                    :on-remove="handleRemove"
                     action="#"
                     accept=".png, .jpg"
                     :limit="1"
@@ -82,7 +83,16 @@
                 </el-collapse-item>
                 <el-collapse-item name="2" style="padding-right: 10px">
                   <template #title>自定义图片(可选)</template>
-                  <el-upload v-model:file-list="fileList" action="#" accept=".png, .jpg" :limit="1" list-type="picture-card" :auto-upload="false">
+                  <el-upload
+                    v-model:file-list="customFileList"
+                    :on-change="changeCustomFile"
+                    :on-remove="handleCustomRemove"
+                    v-loading="customUpLoading"
+                    action="#"
+                    accept=".png, .jpg"
+                    :limit="1"
+                    list-type="picture-card"
+                    :auto-upload="false">
                     <el-icon><Plus /></el-icon>
                     <template #file="{ file }">
                       <div>
@@ -153,8 +163,13 @@
             </el-scrollbar>
           </div>
           <div style="width: 50%; padding: 0 10px; position: relative">
-            <el-button type="primary" plain @click="clickSave" :disabled="fileList.length == 0" style="position: absolute; top: 92%; left: 46%"
-              >保存</el-button
+            <el-button
+              type="primary"
+              plain
+              @click="submitForm(ruleFormRef)"
+              :disabled="!fileList.length"
+              style="position: absolute; top: 92%; left: 46%">
+              保存</el-button
             >
           </div>
         </div>
@@ -267,16 +282,13 @@ import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'eleme
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Plus, Picture, Search, Delete, Download, ZoomIn } from '@element-plus/icons-vue'
 import type { UploadFile } from 'element-plus'
-import { getAssets, getLifeStyleAssets, getPageAsins, getCommodityCard, uploadFile, checkAsset } from '../api/index'
+import { getAssets, getLifeStyleAssets, getPageAsins, getCommodityCard, uploadFile, checkAsset, postProductset } from '../api/index'
 import emitter from '/@/utils/emitter'
 import { storeToRefs } from 'pinia'
 import { useShopInfo } from '/@/stores/shopInfo'
 import axios from 'axios'
 
-// async function uploadFile(formData) {
-//   return axios.post('http://192.168.1.225/api/ad_manage/assets/upload/', formData)
-// }
-
+const respAdGroupId = inject<Ref>('respAdGroupId')
 const shopInfo = useShopInfo()
 const { profile } = storeToRefs(shopInfo)
 
@@ -304,6 +316,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
   await formEl.validate((valid, fields) => {
     if (valid) {
       console.log('submit!')
+      createCreativity()
     } else {
       console.log('error submit!', fields)
     }
@@ -355,12 +368,69 @@ function handleRemove(file: UploadFile) {
   fileList.value = []
 }
 
+function handleCustomRemove(file: UploadFile) {
+  customFileList.value = []
+}
+
 function handlePictureCardPreview(file: UploadFile) {
   handleUpload(file)
   dialogImageUrl.value = file.url!
   dialogVisible.value = true
 }
 
+// 自定义文件上传
+const customFileList = ref([])
+const customUpLoading = ref(false)
+let customImageAssetId = ''
+
+function changeCustomFile(file: UploadFile) {
+  handleCustomUpload(file)
+}
+
+async function handleCustomUpload(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']))
+  upLoading.value = true
+  try {
+    const response = await uploadFile(formData)
+    const fileName = response.data.file_name
+    const obj = {
+      profile_id: profile.value.profile_id,
+      file_name: fileName,
+    }
+    const resp = await checkAsset(obj)
+    customImageAssetId = resp.data.assetId
+    const { width, height } = resp.data.fileMetadata
+    customImageCrop = {
+      width,
+      height,
+      top: 0,
+      left: 0,
+    }
+    if (resp.data.checkresult == 'success') {
+      ElMessage({ message: '上传成功', type: 'success' })
+    } else {
+      ElMessage.error('上传失败')
+    }
+  } catch (error) {
+    console.error('上传失败:', error)
+  } finally {
+    upLoading.value = false
+  }
+}
+
+const createLoading = ref(false)
+const casins = ref('')
+let brandLogoCrop = {}
+let customImageCrop = {}
+let url = ''
+let brandName = ''
+let brandLogoAssetID = ''
+
 function changeFile(file: UploadFile) {
   handleUpload(file)
 }
@@ -381,6 +451,14 @@ async function handleUpload(file: UploadFile) {
       file_name: fileName,
     }
     const resp = await checkAsset(obj)
+    brandLogoAssetID = resp.data.assetId
+    const { width, height } = resp.data.fileMetadata
+    brandLogoCrop = {
+      width,
+      height,
+      top: 0,
+      left: 0,
+    }
     if (resp.data.checkresult == 'success') {
       ElMessage({ message: '上传成功', type: 'success' })
     } else {
@@ -393,6 +471,37 @@ async function handleUpload(file: UploadFile) {
   }
 }
 
+async function createCreativity() {
+  createLoading.value = true
+  try {
+    const obj = {
+      profile_id: profile.value.profile_id,
+      casins: casins.value,
+      lasins: [],
+      url: url,
+      name: ruleForm.name,
+      state: 'PAUSED',
+      adGroupId: respAdGroupId.value,
+      brandName: brandName,
+      brandLogoCrop: brandLogoCrop,
+      brandLogoAssetID: brandLogoAssetID,
+      customImageCrop: customImageCrop,
+      customImageAssetId: customImageAssetId,
+      headline: ruleForm.title,
+    }
+    const response = await postProductset(obj)
+    if (response.data.creative_state == 'success') {
+      ElMessage({ message: '创建成功', type: 'success' })
+    } else {
+      ElMessage.error('上传失败')
+    }
+  } catch (error) {
+    console.error('error:', error)
+  } finally {
+    createLoading.value = false
+  }
+}
+
 // 选择卡片功能
 function selectCard(item) {
   if (isSelected(item.id)) {
@@ -417,14 +526,10 @@ function handleConfirmSelection() {
       url: selectedImageUrl.value,
       // 根据需要添加更多属性
     }
-
-    // 将新的文件对象添加到 fileList 中
-    fileList.value.push(newFile)
+    fileList.value.push(newFile) // 将新的文件对象添加到 fileList 中
   }
 
-  // 清空选中卡片
-  selectedCards.value = []
-  // 关闭对话框
+  selectedCards.value = [] // 清空选中卡片
   centerDialogVisible.value = false
 }
 
@@ -439,8 +544,6 @@ async function getAssetsData() {
     assetSubType: 'LOGO',
   }
   const response = await getAssets(query)
-  console.log('🚀 ~ getAssetsData ~ response-->>', response)
-
   cards.splice(0, cards.length)
 
   response.data.forEach((asset) => {
@@ -511,7 +614,6 @@ async function getCommodityCollapseData() {
     }
     const response = await getPageAsins(query)
     asinList.value = response.data.asinList
-    console.log('asinList', asinList.value)
   } catch (error) {
     console.log('error:', error)
   } finally {
@@ -519,10 +621,6 @@ async function getCommodityCollapseData() {
   }
 }
 
-function clickSave() {
-  console.log(123, fileList.value)
-}
-
 let lastQueriedAsins = []
 const commodityCard = ref([])
 async function getCommodityCardData() {
@@ -637,18 +735,32 @@ const flattenedReplaceableCommodity = computed(() => {
   return replaceableCommodity.value.flat()
 })
 
+watch(
+  flattenedCommodityCard,
+  (newValue: any) => {
+    casins.value = newValue.map((item) => item.asin)
+  },
+  { deep: true }
+)
+
 const brandEntityId = ref('')
+
 onMounted(() => {
   emitter.on('send-brandEntityId', (value: any) => {
-    // console.log('接收到', value.brandEntityId[0].brandEntityId)
     brandEntityId.value = value.brandEntityId[0].brandEntityId
   })
+  emitter.on('page', (value: any) => {
+    url = value
+  })
+  emitter.on('video-shop', (value: any) => {
+    brandName = value.brandRegistryName
+  })
 })
 
 // 接收数据端在组建卸载时解绑事件
 onUnmounted(() => {
   emitter.off('send-brandEntityId')
-  emitter.off('test')
+  emitter.off('page')
 })
 </script>
 

+ 87 - 26
src/views/adManage/sb/campaigns/CreateCampaigns/component/ProductSetCreativity2.vue

@@ -17,10 +17,10 @@
         <el-form-item label="广告名称" prop="name">
           <el-input v-model="ruleForm.name" style="width: 50%" />
         </el-form-item>
-        <div style="border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px">
+        <div style="display: flex; border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px" v-loading="createLoading">
           <div style="width: 50%; padding-left: 5px; border-right: 1px solid #dddfe6">
             <el-scrollbar height="700px">
-              <el-collapse v-model="activeNames" @change="handleChange">
+              <el-collapse v-model="activeNames" @change="handleChange" style="border-top: none; border-bottom: none; padding-right: 10px;">
                 <el-collapse-item name="1">
                   <template #title> <span style="color: #e47470; margin-right: 4px">*</span>品牌名称和徽标</template>
                   <el-form-item prop="brandName">
@@ -68,7 +68,7 @@
                   <template #title>自定义图片(可选)</template>
                   <el-upload
                     class="avatar-uploader"
-                    v-model:file-list="fileList"
+                    v-model:file-list="customFileList"
                     :on-change="changeCustomFile"
                     v-loading="upCustomLoading"
                     action="#"
@@ -92,10 +92,9 @@
                     </template>
                   </el-upload>
                 </el-collapse-item>
-
-                <el-collapse-item name="3" style="padding: 0 10px 0 5px">
+                <el-collapse-item name="3">
                   <template #title> <span style="color: #e47470; margin-right: 4px">*</span>商品</template>
-                  <div v-for="item in commodityCard" :key="item.asin">
+                  <div v-for="item in commodityCard" :key="item.asin" style="padding-bottom: 5px;">
                     <el-card shadow="hover" body-style="padding: 10px">
                       <div style="padding: 10px; display: flex; align-items: center">
                         <div style="margin-right: 8px; line-height: normal">
@@ -123,6 +122,16 @@
               </el-collapse>
             </el-scrollbar>
           </div>
+          <div style="width: 50%; padding: 0 10px; position: relative">
+            <el-button
+              type="primary"
+              plain
+              @click="submitForm(ruleFormRef)"
+              :disabled="!fileList.length"
+              style="position: absolute; top: 92%; left: 46%">
+              保存
+            </el-button>
+          </div>
         </div>
       </el-form>
     </el-card>
@@ -130,9 +139,9 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, inject, Ref, watch, onMounted } from 'vue'
+import { reactive, ref, inject, Ref, watch, onMounted, onUnmounted } from 'vue'
 import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
-import { checkAsset, uploadFile } from '../api/index'
+import { checkAsset, uploadFile, postProductset } from '../api/index'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Plus, Picture } from '@element-plus/icons-vue'
 import emitter from '/@/utils/emitter'
@@ -141,9 +150,10 @@ import { useShopInfo } from '/@/stores/shopInfo'
 
 const shopInfo = useShopInfo()
 const { profile } = storeToRefs(shopInfo)
+const respAdGroupId = inject<Ref>('respAdGroupId')
+const setBrandName = inject<Ref>('setBrandName')
 const addedTableDataForVc2 = inject<Ref>('addedTableDataForVc2')
 
-
 interface RuleForm {
   name: string
   brandName: string
@@ -168,6 +178,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
   await formEl.validate((valid, fields) => {
     if (valid) {
       console.log('submit!')
+      createCreativity()
     } else {
       console.log('error submit!', fields)
     }
@@ -181,10 +192,16 @@ const handleChange = (val: string[]) => {
 
 const fileList = ref<UploadUserFile[]>([])
 
+const customFileList = ref<UploadUserFile[]>([])
+
 const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
   fileList.value = []
 }
 
+const handleCustomRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
+  fileList.value = []
+}
+
 const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
   console.log(uploadFile)
 }
@@ -200,20 +217,21 @@ const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
   )
 }
 
+const upLoading = ref(false)
+let brandLogoAssetID = ''
+let brandLogoCrop = {}
+let createLoading = ref(false)
+const brandEntityId = inject<Ref>('brandEntityId')
+
 function changeFile(file) {
   handleUpload(file)
 }
 
-const upLoading = ref(false)
-let respAssetId = ''
-let brandEntityId = ''
-let brandLogoCrop = {}
-
 async function handleUpload(file) {
   const formData = new FormData()
   formData.append('file', file.raw)
   formData.append('profile_id', profile.value.profile_id)
-  // formData.append('brandEntityId', brandEntityId)
+  formData.append('brandEntityId', brandEntityId.value)
   formData.append('assetType', 'IMAGE')
   formData.append('assetSubTypeList', JSON.stringify(['LOGO']))
   upLoading.value = true
@@ -225,7 +243,7 @@ async function handleUpload(file) {
       file_name: fileName,
     }
     const resp = await checkAsset(obj)
-    respAssetId = resp.data.assetId
+    brandLogoAssetID = resp.data.assetId
     const { width, height } = resp.data.fileMetadata
     brandLogoCrop = {
       width,
@@ -233,7 +251,6 @@ async function handleUpload(file) {
       top: 0,
       left: 0,
     }
-
     if (resp.data.checkresult == 'success') {
       ElMessage({ message: '上传成功', type: 'success' })
     } else {
@@ -246,8 +263,12 @@ async function handleUpload(file) {
   }
 }
 
-// 定义图片上传
+// 定义图片上传
 const upCustomLoading = ref(false)
+const lasins = ref('')
+let customImageAssetId = ''
+let customImageCrop = {}
+
 
 function changeCustomFile(file) {
   handleCustomUpload(file)
@@ -257,7 +278,7 @@ async function handleCustomUpload(file) {
   const formData = new FormData()
   formData.append('file', file.raw)
   formData.append('profile_id', profile.value.profile_id)
-  // formData.append('brandEntityId', brandEntityId)
+  formData.append('brandEntityId', brandEntityId.value)
   formData.append('assetType', 'IMAGE')
   formData.append('assetSubTypeList', JSON.stringify(['LOGO']))
   upCustomLoading.value = true
@@ -269,7 +290,7 @@ async function handleCustomUpload(file) {
       file_name: fileName,
     }
     const resp = await checkAsset(obj)
-    respAssetId = resp.data.assetId
+    customImageAssetId = resp.data.assetId
     const { width, height } = resp.data.fileMetadata
     brandLogoCrop = {
       width,
@@ -290,6 +311,38 @@ async function handleCustomUpload(file) {
   }
 }
 
+// TODO: url对应的是Home 暂时写死
+async function createCreativity() {
+  createLoading.value = true
+  try {
+    const obj = {
+      profile_id: profile.value.profile_id,
+      casins: lasins.value,
+      lasins: lasins.value,
+      url: 'https://www.amazon.com/stores/page/1D1DD2FD-CF54-4FE5-B1A0-9E01F12F8144',
+      name: ruleForm.name,
+      state: 'PAUSED',
+      adGroupId: respAdGroupId.value,
+      brandName: setBrandName.value,
+      brandLogoCrop: brandLogoCrop,
+      brandLogoAssetID: brandLogoAssetID,
+      customImageCrop: customImageCrop,
+      customImageAssetId: customImageAssetId,
+      headline: ruleForm.title,
+    }
+    const response = await postProductset(obj)
+    if (response.data.creative_state == 'success') {
+      ElMessage({ message: '创建成功', type: 'success' })
+    } else {
+      ElMessage.error('上传失败')
+    }
+  } catch (error) {
+    console.error('error:', error)
+  } finally {
+    createLoading.value = false
+  }
+}
+
 let asins = ref([])
 
 watch(
@@ -309,17 +362,25 @@ watch(
 )
 
 const commodityCard = ref([])
-onMounted(()=> {
+
+watch(
+  commodityCard,
+  (newValue: any) => {
+    lasins.value = newValue.map(item => item.asin)
+  },
+  { deep: true }
+)
+
+onMounted(() => {
   emitter.on('addedTableData', (data: any) => {
-    console.log('data', data)
     commodityCard.value = data
-    console.log(commodityCard.value)
   })
 })
 
-function handleSelect() {
-  // console.log()
-}
+// 接收数据端在组件卸载时解绑事件
+onUnmounted(() => {
+  emitter.off('addedTableData')
+})
 
 const imageUrl = ref('')
 

+ 0 - 12
src/views/adManage/sb/campaigns/CreateCampaigns/component/VideoCommodity.vue

@@ -160,19 +160,7 @@ import { ElMessage } from 'element-plus'
 import { storeToRefs } from 'pinia'
 import { useShopInfo } from '/@/stores/shopInfo'
 import { request } from '/@/utils/service'
-import emitter from '/@/utils/emitter'
 
-// emitter.on('send-data', (newValue)=>{
-//   console.log(111, newValue)
-// })
-// // 接收数据端在组建卸载时解绑事件
-// onUnmounted(() => {
-//   emitter.off('send-data')
-// })
-
-// setTimeout(() => {
-//   emitter.emit('send-data', '123')
-// }, 2000);
 
 const shopInfo = useShopInfo()
 const { profile } = storeToRefs(shopInfo)

+ 13 - 3
src/views/adManage/sb/campaigns/CreateCampaigns/component/VideoCreativity1.vue

@@ -17,7 +17,7 @@
         <el-form-item label="广告名称" prop="name">
           <el-input v-model="ruleForm.name" style="width: 50%" />
         </el-form-item>
-        <div style="display: flex; border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px">
+        <div style="display: flex; border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px" v-loading="createLoading">
           <div style="width: 50%; padding-left: 5px; border-right: 1px solid #dddfe6">
             <el-scrollbar height="700px">
               <el-collapse v-model="activeNames" @change="handleChange" style="border-top: none; border-bottom: none">
@@ -284,7 +284,7 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, inject, Ref, watch, computed, onMounted } from 'vue'
+import { reactive, ref, inject, Ref, watch, computed, onMounted, onUnmounted } from 'vue'
 import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Plus, Picture, Search, Delete, Download, ZoomIn } from '@element-plus/icons-vue'
@@ -452,7 +452,7 @@ async function uploadVideo(file: UploadFile) {
   formData.append('profile_id', profile.value.profile_id)
   formData.append('brandEntityId', brandEntityId)
   formData.append('assetType', 'VIDEO')
-  formData.append('assetSubTypeList', JSON.stringify(['SPONSORED_BRANDS_VIDEO']))
+  formData.append('assetSubTypeList', JSON.stringify([]))
   videoLoading.value = true
   try {
     const response = await uploadFile(formData)
@@ -480,7 +480,9 @@ async function uploadVideo(file: UploadFile) {
 const respAdGroupId = inject<Ref>('respAdGroupId')
 let brandName = ''
 let selectedPage = ''
+const createLoading = ref(false)
 async function createBrandVideo() {
+  createLoading.value = true
   try {
     const obj = {
       profile_id: profile.value.profile_id,
@@ -500,6 +502,8 @@ async function createBrandVideo() {
     console.log('response', response)
   } catch (error) {
     console.log('error:', error)
+  } finally {
+    createLoading.value = false
   }
 }
 
@@ -513,6 +517,12 @@ onMounted(() => {
   })
 })
 
+// 接收数据端在组件卸载时解绑事件
+onUnmounted(() => {
+  emitter.off('video-shop')
+  emitter.off('page')
+})
+
 function selectCard(item) {
   if (isSelected(item.id)) {
     selectedCards.value = selectedCards.value.filter((card) => card.id !== item.id)

+ 0 - 2
src/views/adManage/sb/campaigns/CreateCampaigns/component/VideoCreativity2.vue

@@ -247,7 +247,6 @@ async function handleUpload(file) {
     }
     const resp = await checkAsset(obj)
     respAssetId = resp.data.assetId
-    console.log('🚀 ~ handleUpload ~ respAssetId-->>', respAssetId)
     if (resp.data.checkresult == 'success') {
       ElMessage({ message: '上传成功', type: 'success' })
     } else {
@@ -398,7 +397,6 @@ async function createCommodity() {
       videoAssetIds: selectedAssetId,
     }
     const response = await videoDetailCreate(obj)
-    console.log('🚀 ~ response-->>', response)
     if (response.data.creative_state == 'success') {
       ElMessage({
         message: '商品创意创建成功',

+ 11 - 1
src/views/adManage/sb/campaigns/CreateCampaigns/index.vue

@@ -18,7 +18,6 @@
     <VideoCreativity2 v-if="adFormatRadioValue === 'video' && arrivalsRadioValue === 'productDetailsPage'"></VideoCreativity2>
     <ProductSetCreativity2 v-if="adFormatRadioValue === 'productSet' && arrivalsRadioValue === 'newArrivals'"></ProductSetCreativity2>
     <DeliveryType></DeliveryType>
-    <p>12323{{ pageOptionsValue }}</p>
   </div>
 </template>
 
@@ -45,6 +44,8 @@ const pageOptionsValue = ref('')
 const addedTableDataForVc2 = ref('')
 const focusShop = ref('')
 const focusShopLabel = ref('')
+const brandEntityId = ref('')
+const setBrandName = ref('')
 
 provide('respCampaignId', respCampaignId)
 provide('respCampaignName', respCampaignName)
@@ -53,6 +54,8 @@ provide('pageOptionsValue', pageOptionsValue)
 provide('addedTableDataForVc2', addedTableDataForVc2)
 provide('focusShop', focusShop)
 provide('focusShopLabel', focusShopLabel)
+provide('brandEntityId', brandEntityId)
+provide('setBrandName', setBrandName)
 
 const handleCampaignUpdate = (data) => {
   respCampaignId.value = data.id
@@ -82,6 +85,13 @@ function handleFocusShopSelectChange(newValue) {
   focusShop.value = newValue
   focusShopLabel.value = 'ZOSI'
 }
+
+onMounted(()=>{
+  emitter.on('brandEntityId',(value: any)=>{
+    brandEntityId.value = value[0].brandEntityId
+    setBrandName.value = value[0].brandRegistryName
+  })
+})
 </script>
 
 <style scoped>

+ 6 - 1
src/views/adManage/sd/campaigns/CreateCampaigns/index.vue

@@ -7,7 +7,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref, computed } from 'vue'
+import { onMounted, ref, computed, onUnmounted } from 'vue'
 import AdCampaign from './component/AdCampaign.vue'
 import AdGroup from './component/AdGroup.vue'
 import CustomTarget from './component/CustomTarget.vue'
@@ -29,6 +29,11 @@ const currentComponent = computed(() => {
       return ContentTarget
   }
 })
+
+// 接收数据端在组件卸载时解绑事件
+onUnmounted(() => {
+  emitter.off('send-targetType')
+})
 </script>
 
 <style scoped>