Sfoglia il codice sorgente

✨ feat: 新增添加商品,更换商品功能

WanGxC 1 anno fa
parent
commit
7bdda08b7a

+ 39 - 0
src/views/adManage/sb/campaigns/CreateCampaigns/api/index.ts

@@ -38,4 +38,43 @@ export function getAssets(query) {
       method: 'get',
       params: query
   })
+}
+
+export function getLifeStyleAssets(query) {
+  return request({
+      url: '/api/ad_manage/sb/assets/',
+      method: 'get',
+      params: query
+  })
+}
+
+export function getBrands(query) {
+  return request({
+    url: '/api/ad_manage/sb/getbrands/',
+      method: 'get',
+      params: query
+  })
+}
+
+export function getStoreurl(query) {
+  return request({
+    url: '/api/ad_manage/sb/storeurl/',
+      method: 'get',
+      params: query
+  })
+}
+
+export function getPageAsins(query) {
+  return request({
+    url: '/api/ad_manage/sb/getpageasins/',
+      method: 'get',
+      params: query
+  })
+}
+export function getCommodityCard(query) {
+  return request({
+    url: '/api/sellers/listings/all/',
+      method: 'get',
+      params: query
+  })
 }

+ 95 - 40
src/views/adManage/sb/campaigns/CreateCampaigns/component/AdFormat.vue

@@ -43,9 +43,9 @@
             <div>亚马逊品牌旗舰店(包括子页面)</div>
             <div>
               <el-form
-                ref="flagshipStoreRuleFormRef"
-                :model="flagshipStoreRuleForm"
-                :rules="flagshipStoreRules"
+                ref="ruleFormRef"
+                :model="ruleForm"
+                :rules="rules"
                 label-position="top"
                 label-width="120px"
                 class="demo-ruleForm"
@@ -53,13 +53,13 @@
                 status-icon>
                 <div style="display: flex; margin-top: 10px">
                   <el-form-item label="选择一个店铺" prop="shop" style="width: 48%; margin-right: 10px">
-                    <el-select v-model="flagshipStoreRuleForm.shop" clearable style="width: 100%">
+                    <el-select v-model="ruleForm.shop" clearable style="width: 100%" @blur="validateField('shop')">
                       <el-option v-for="item in shopOptions" :key="item.value" :label="item.label" :value="item.value" />
                     </el-select>
                   </el-form-item>
-                  <el-form-item label="选择一个页面" prop="page" clearable style="width: 48%">
-                    <el-select v-model="flagshipStoreRuleForm.page" style="width: 100%">
-                      <el-option v-for="item in pageOptions" :key="item.value" :label="item.label" :value="item.value" />
+                  <el-form-item label="选择一个页面" prop="page" style="width: 48%">
+                    <el-select v-model="ruleForm.page" clearable style="width: 100%" @blur="validateField('page')">
+                      <el-option v-for="item in pageOptions" :key="item.storePageId" :label="item.storePageName" :value="item.storePageUrl" />
                     </el-select>
                   </el-form-item>
                 </div>
@@ -113,26 +113,36 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, defineEmits, watch } from 'vue'
+import { ref, reactive, defineEmits, watch, onMounted } from 'vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import ProductSetCommodity from '../component/ProductSetCommodity.vue'
 import VideoCommodity from '../component/VideoCommodity.vue'
+import { getBrands, getStoreurl } from '../api/index'
+import { storeToRefs } from 'pinia'
+import { useShopInfo } from '/@/stores/shopInfo'
+
+const shopInfo = useShopInfo()
+const { profile } = storeToRefs(shopInfo)
 
 const adFormatRadio = ref('productSet')
 const arrivalsRadio = ref('flagshipStore')
-const flagshipStoreRuleFormRef = ref<FormInstance>()
-interface flagshipStoreRuleForm {
+const ruleFormRef = ref<FormInstance>()
+interface RuleForm {
   shop: string
   page: string
 }
-const flagshipStoreRuleForm = reactive<flagshipStoreRuleForm>({
+const ruleForm = reactive<RuleForm>({
   shop: '',
   page: '',
 })
-const flagshipStoreRules = reactive<FormRules<flagshipStoreRuleForm>>({
-  shop: { required: true, trigger: 'blur' },
-  page: { required: true, trigger: 'blur' },
+const rules = reactive<FormRules<RuleForm>>({
+  shop: [{ required: true, message:'请选择', trigger: 'change' }],
+  page: [{ required: true, message:'请选择', trigger: 'blur' }],
 })
+const validateField = (fieldName) => {
+  ruleFormRef.value.validateField(fieldName, () => {
+  })
+}
 
 const flagshipStoreRuleFormRef2 = ref<FormInstance>()
 interface flagshipStoreRuleForm2 {
@@ -145,22 +155,8 @@ const flagshipStoreRules2 = reactive<FormRules<flagshipStoreRuleForm2>>({
   focusShop: { required: true, message: 'xxx', trigger: 'blur' },
 })
 
-const shopOptions = [
-  {
-    value: 'zosi',
-    label: 'ZOSI',
-  },
-]
-const pageOptions = [
-  {
-    value: '1',
-    label: '1',
-  },
-  {
-    value: '2',
-    label: '2',
-  },
-]
+const shopOptions = ref([])
+const pageOptions = ref([])
 const focusShopOptions = [
   {
     value: '1',
@@ -172,21 +168,80 @@ const focusShopOptions = [
   },
 ]
 
-const emit = defineEmits(['update:adFormatRadio', 'update:arrivalsRadio', 'update:flagshipStoreShop'])
+async function getShopOptions() {
+  try {
+    const response = await getBrands({ profile_id: profile.value.profile_id })
+    const shopOption = response.data.map((item) => {
+      return {
+        value: item.brandRegistryName,
+        label: 'ZOSI',
+      }
+    })
+    shopOptions.value = shopOption
+  } catch (error) {
+    console.log('error:', error)
+  }
+}
 
-// 监听 adFormatRadio 的变化并触发事件
-watch(adFormatRadio, (newValue) => {
-  emit('update:adFormatRadio', newValue)
-},{ immediate: true })
+async function getPageOptions() {
+  try {
+    const response = await getStoreurl({ profile_id: profile.value.profile_id })
+    pageOptions.value = response.data.storePageInfo
+  } catch (error) {
+    console.log('error:', error)
+  }
+}
 
-watch(arrivalsRadio, (newValue) => {
-  emit('update:arrivalsRadio', newValue)
-},{ immediate: true })
+// watch(ruleForm.shop, async (newShopValue) => {
+//   if (newShopValue === 'ZOSI') {
+//     getPageOptions()
+//   }
+// })
 
-watch(() => flagshipStoreRuleForm.shop, (newValue) => {
-  emit('update:flagshipStoreShop', newValue)
+onMounted(() => {
+  getShopOptions()
+  // getPageOptions()
 })
 
+const emit = defineEmits(['update:adFormatRadio', 'update:arrivalsRadio', 'update:flagshipStoreShop', 'update:pageOptions'])
+
+// 监听 adFormatRadio 的变化并触发事件
+watch(
+  adFormatRadio,
+  (newValue) => {
+    emit('update:adFormatRadio', newValue)
+  },
+  { immediate: true }
+)
+
+watch(
+  arrivalsRadio,
+  (newValue) => {
+    emit('update:arrivalsRadio', newValue)
+  },
+  { immediate: true }
+)
+
+watch(
+  () => ruleForm.shop,
+  (newValue) => {
+    emit('update:flagshipStoreShop', newValue)
+    if (newValue === 'ZOSI') {
+      getPageOptions()
+    }
+    if (!ruleForm.shop) {
+      ruleForm.page = ''
+      pageOptions.value = []
+    }
+  }
+)
+
+watch(
+  () => ruleForm.page,
+  (newValue) => {
+    emit('update:pageOptions', newValue)
+  }
+)
 </script>
 
 <style scoped>

+ 410 - 72
src/views/adManage/sb/campaigns/CreateCampaigns/component/ProductSetCreativity1.vue

@@ -17,23 +17,31 @@
         <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">
           <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-item name="1">
+              <el-collapse v-model="activeNames" @change="handleChange" style="border-top: none; border-bottom: none">
+                <el-collapse-item name="1" style="padding-right: 10px">
                   <template #title> <span style="color: #e47470; margin-right: 4px">*</span>品牌名称和徽标</template>
                   <el-form-item prop="brandName">
-                    <el-input v-model="ruleForm.brandName" placeholder="请输入品牌名称" style="padding: 0 10px 5px 0"></el-input>
+                    <el-input v-model="ruleForm.brandName" placeholder="请输入品牌名称" style="padding: 0 0 5px 0"></el-input>
                   </el-form-item>
-                  <el-upload
-                    class="upload-demo"
-                    drag
-                    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
-                    multiple
-                    style="padding-right: 10px">
-                    <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-                    <div class="el-upload__text">Drop file here or <em>click to upload</em></div>
+
+                  <el-upload action="#" list-type="picture-card" :auto-upload="false" v-model:file-list="fileList" :limit="1">
+                    <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="handlePictureCardPreview(file)">
+                            <el-icon><zoom-in /></el-icon>
+                          </span>
+                          <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
+                            <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">
@@ -59,23 +67,39 @@
                       </div>
                     </template>
                   </el-upload>
+                  <!-- 预览弹窗 -->
+                  <el-dialog v-model="dialogVisible">
+                    <img w-full :src="dialogImageUrl" alt="Preview Image" />
+                  </el-dialog>
                 </el-collapse-item>
-                <el-collapse-item name="2">
+                <el-collapse-item name="2" style="padding-right: 10px">
                   <template #title>自定义图片(可选)</template>
                   <el-upload
-                    class="avatar-uploader"
-                    style="padding-right: 10px"
-                    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
-                    :show-file-list="false"
-                    :on-success="handleAvatarSuccess"
-                    :before-upload="beforeAvatarUpload">
-                    <img v-if="imageUrl" :src="imageUrl" class="avatar" />
-                    <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+                    action="#"
+                    list-type="picture-card"
+                    :auto-upload="false"
+                    v-model:file-list="fileList"
+                    :limit="1"
+                    style="padding-right: 10px">
+                    <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="handlePictureCardPreview(file)">
+                            <el-icon><zoom-in /></el-icon>
+                          </span>
+                          <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
+                            <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>
-                          <el-button type="primary" :icon="Picture">从素材库中选择</el-button>
+                          <el-button type="primary" :icon="Picture" @click="openLifeStyleDialog">从素材库中选择</el-button>
                         </div>
                         <div class="introduce-item">1、图片大小: 1200 x 628 像素或更大</div>
                         <div class="introduce-item">2、文件大小: 5MB 或更小</div>
@@ -84,13 +108,40 @@
                       </div>
                     </template>
                   </el-upload>
+                  <!-- 预览弹窗 -->
+                  <el-dialog v-model="dialogVisible">
+                    <img w-full :src="dialogImageUrl" alt="Preview Image" />
+                  </el-dialog>
                 </el-collapse-item>
 
-                <el-collapse-item name="3">
+                <el-collapse-item name="commodity" v-loading="commodityLoading" style="padding-right: 10px">
                   <template #title> <span style="color: #e47470; margin-right: 4px">*</span>商品</template>
-                  拖动带编号的选项卡,可以重新排列商品。商品可更换成你落地页上的任何商品。
+                  <div v-for="(item, index) in flattenedCommodityCard" :key="index" style="margin: 0 0 5px 0">
+                    <el-card shadow="hover" body-style="padding: 10px; display: flex;">
+                      <div style="margin-right: 8px; line-height: normal">
+                        <el-image class="img-box" :src="item.image_link" />
+                      </div>
+                      <div style="position: relative">
+                        <el-tooltip class="box-item" effect="dark" :content="item.title" placement="top">
+                          <div class="double-line">{{ item.title }}</div>
+                        </el-tooltip>
+                        <span>
+                          <span style="color: #6d7784">ASIN: </span>
+                          <span class="data-color" style="margin-right: 8px">{{ item.asin }}</span>
+                        </span>
+                        <el-button
+                          type="primary"
+                          size="small"
+                          link
+                          @click="() => openCommodityDialog(index)"
+                          style="position: absolute; bottom: 2px; right: 0">
+                          更换商品
+                        </el-button>
+                      </div>
+                    </el-card>
+                  </div>
                 </el-collapse-item>
-                <el-collapse-item name="4">
+                <el-collapse-item name="4" style="padding-right: 10px">
                   <template #title> <span style="color: #e47470; margin-right: 4px">*</span>标题</template>
                   <el-form-item prop="title">
                     <el-input v-model="ruleForm.title" maxlength="50" placeholder="请输入标题" show-word-limit style="padding: 0 10px 0 0"></el-input>
@@ -99,6 +150,9 @@
               </el-collapse>
             </el-scrollbar>
           </div>
+          <div style="width: 50%; padding: 0 10px; position: relative">
+            <el-button type="primary" plain @click="clickSave" style="position: absolute; top: 92%; left: 46%">保存</el-button>
+          </div>
         </div>
       </el-form>
     </el-card>
@@ -130,6 +184,41 @@
           </el-card>
         </div>
       </div>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="centerDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleConfirmSelection">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="lifeStyleDialog" title="从素材库中选择" width="65%">
+      <el-input :prefix-icon="Search"></el-input>
+      <div class="grid-container">
+        <div
+          class="grid-item"
+          v-for="item in lifeStyleCards"
+          :key="item.id"
+          @click="selectCard(item)"
+          :class="{ selected: isSelected(item.id), hover: hoverId === item.id }"
+          @mouseover="hoverId = item.id"
+          @mouseleave="hoverId = null">
+          <el-card :body-style="{ padding: '0px' }">
+            <el-image class="image" :src="item.imageUrl" fit="cover" />
+            <div style="padding: 10px">
+              <span>
+                <el-tooltip placement="top" :content="item.title">
+                  {{ item.title }}
+                </el-tooltip>
+              </span>
+              <div class="bottom">
+                <div class="bottom-item">{{ item.size }}KB</div>
+                <div class="bottom-item">{{ item.width }} * {{ item.height }}</div>
+                <div class="bottom-item">徽标</div>
+              </div>
+            </div>
+          </el-card>
+        </div>
+      </div>
       <template #footer>
         <span class="dialog-footer">
           <el-button @click="centerDialogVisible = false">取消</el-button>
@@ -137,28 +226,54 @@
         </span>
       </template>
     </el-dialog>
+    <el-dialog v-model="commodityDialog" title="更换商品" width="50%" v-loading="dialogLoading3">
+      <el-radio-group v-model="selectedCommodity" style="display: flex; flex-direction: column; align-content: flex-start; align-items: flex-start">
+        <div v-for="(item, index) in flattenedReplaceableCommodity" :key="index">
+          <el-radio :label="item.asin" style="height: 80px; border-bottom: 1px solid #ccc">
+            <div style="padding: 10px; display: flex">
+              <div style="margin-right: 8px; line-height: normal">
+                <el-image class="img-box" :src="item.image_link" />
+              </div>
+              <div style="position: relative">
+                <el-tooltip class="box-item" effect="dark" :content="item.title" placement="top">
+                  <div class="double-line">{{ item.title }}</div>
+                </el-tooltip>
+                <span>
+                  <span style="color: #6d7784">ASIN: </span>
+                  <span class="data-color" style="margin-right: 8px">{{ item.asin }}</span>
+                </span>
+              </div>
+            </div>
+          </el-radio>
+        </div>
+      </el-radio-group>
+      <div style="margin-top: 20px; display: flex; justify-content: center;">
+        <el-button type="primary" @click="handleSelectedCommodity">确定</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import { reactive, ref } from 'vue'
+import { reactive, ref, inject, Ref, watch, computed } from 'vue'
 import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus, Picture, Search } from '@element-plus/icons-vue'
-import { getAssets } from '../api/index'
+import { Plus, Picture, Search, Delete, Download, ZoomIn } from '@element-plus/icons-vue'
+import type { UploadFile } from 'element-plus'
+import { getAssets, getLifeStyleAssets, getPageAsins, getCommodityCard } from '../api/index'
 import { storeToRefs } from 'pinia'
 import { useShopInfo } from '/@/stores/shopInfo'
 
 const shopInfo = useShopInfo()
 const { profile } = storeToRefs(shopInfo)
 
+const ruleFormRef = ref<FormInstance>()
+
 interface RuleForm {
   name: string
   brandName: string
   title: string
 }
-
-const ruleFormRef = ref<FormInstance>()
 const ruleForm = reactive<RuleForm>({
   name: '视频 广告 - 1/15/2024 17:51:10.236',
   brandName: '',
@@ -185,46 +300,16 @@ const submitForm = async (formEl: FormInstance | undefined) => {
 const activeNames = ref(['1'])
 const handleChange = (val: string[]) => {
   console.log(val)
+  if (val.includes('commodity')) {
+    getCommodityCardData()
+  }
 }
 
-// const fileList = ref<UploadUserFile[]>([
-//   {
-//     name: 'element-plus-logo.svg',
-//     url: 'https://element-plus.org/images/element-plus-logo.svg',
-//   },
-//   {
-//     name: 'element-plus-logo2.svg',
-//     url: 'https://element-plus.org/images/element-plus-logo.svg',
-//   },
-// ])
-
-// const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
-//   console.log(file, uploadFiles)
-// }
-
-// const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
-//   console.log(uploadFile)
-// }
-
-// const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
-//   ElMessage.warning(`The limit is 3, you selected ${files.length} files this time, add up to ${files.length + uploadFiles.length} totally`)
-// }
-
-// const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
-//   return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then(
-//     () => true,
-//     () => false
-//   )
-// }
-
-// function handleSelect() {
-//   // console.log()
-// }
-
 const imageUrl = ref('')
 
 const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
   imageUrl.value = URL.createObjectURL(uploadFile.raw!)
+  console.log('success!')
 }
 
 const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
@@ -238,19 +323,65 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
   return true
 }
 
-
+// 图片上传相关
+const dialogImageUrl = ref('')
+const dialogVisible = ref(false)
+const disabled = ref(false)
+const fileList = ref([])
 const selectedId = ref(null)
 const hoverId = ref(null)
+const selectedCards = ref([])
+const selectedImageUrl = ref('')
+const centerDialogVisible = ref(false)
+const cards = reactive([])
+
+function handleRemove(file: UploadFile) {
+  fileList.value = []
+}
+
+function handlePictureCardPreview(file: UploadFile) {
+  dialogImageUrl.value = file.url!
+  dialogVisible.value = true
+}
 
-const selectCard = (item) => {
+function selectCard(item) {
+  if (isSelected(item.id)) {
+    selectedCards.value = selectedCards.value.filter((card) => card.id !== item.id)
+  } else {
+    selectedCards.value.push(item)
+  }
   selectedId.value = item.id
 }
 
-const isSelected = (id) => {
+function handleConfirmSelection() {
+  if (selectedCards.value.length > 0) {
+    // 清空 fileList
+    fileList.value.length = 0
+
+    // 假设每次只选择一个图片
+    selectedImageUrl.value = selectedCards.value[0].imageUrl
+
+    // 创建一个新的 UploadFile 对象
+    const newFile = {
+      name: selectedCards.value[0].title, // 或者任何你希望用作文件名的字符串
+      url: selectedImageUrl.value,
+      // 根据需要添加更多属性
+    }
+
+    // 将新的文件对象添加到 fileList 中
+    fileList.value.push(newFile)
+  }
+
+  // 清空选中卡片
+  selectedCards.value = []
+  // 关闭对话框
+  centerDialogVisible.value = false
+}
+
+function isSelected(id) {
   return selectedId.value === id
 }
 
-const cards = reactive([])
 async function getAssetsData() {
   const query = {
     profile_id: profile.value.profile_id,
@@ -260,10 +391,8 @@ async function getAssetsData() {
   const response = await getAssets(query)
   console.log('🚀 ~ getAssetsData ~ response-->>', response)
 
-  // 清空现有的 cards 数组
   cards.splice(0, cards.length)
 
-  // 添加新的数据到 cards 数组
   response.data.forEach((asset) => {
     cards.push({
       id: asset.assetId,
@@ -277,14 +406,186 @@ async function getAssetsData() {
 }
 
 function bytesToKB(bytes) {
-  return (bytes / 1024).toFixed(2); // 保留两位小数
+  return (bytes / 1024).toFixed(2) // 保留两位小数
 }
 
-const centerDialogVisible = ref(false)
 function openDialog() {
   centerDialogVisible.value = true
   getAssetsData()
 }
+
+const lifeStyleDialog = ref(false)
+const lifeStyleCards = reactive([])
+async function getLifeStyleAssetsData() {
+  try {
+    const query = {
+      profile_id: profile.value.profile_id,
+      assetType: 'IMAGE',
+      assetSubType: 'LIFESTYLE_IMAGE',
+    }
+    const response = await getLifeStyleAssets(query)
+    console.log('🚀 ~ getLifeStyleAssetsData ~ response-->>', response)
+
+    lifeStyleCards.splice(0, lifeStyleCards.length)
+
+    response.data.forEach((asset) => {
+      lifeStyleCards.push({
+        id: asset.assetId,
+        title: asset.name,
+        imageUrl: asset.storageLocationUrls.defaultUrl,
+        width: asset.fileMetadata.width,
+        height: asset.fileMetadata.height,
+        size: bytesToKB(asset.fileMetadata.sizeInBytes),
+      })
+    })
+  } catch (error) {
+    console.log('error:', error)
+  }
+}
+
+function openLifeStyleDialog() {
+  lifeStyleDialog.value = true
+  getLifeStyleAssetsData()
+}
+
+const pageOptionsValue = inject<Ref>('pageOptionsValue')
+const asinList = ref([])
+const commodityLoading = ref(false)
+
+async function getCommodityCollapseData() {
+  commodityLoading.value = true
+  try {
+    const query = {
+      profile_id: profile.value.profile_id,
+      pageurl: pageOptionsValue.value,
+    }
+    const response = await getPageAsins(query)
+    asinList.value = response.data.asinList
+    console.log('asinList', asinList.value)
+  } catch (error) {
+    console.log('error:', error)
+  } finally {
+    commodityLoading.value = false
+  }
+}
+
+function clickSave() {
+  console.log(123, fileList.value)
+}
+
+let lastQueriedAsins = []
+const commodityCard = ref([])
+async function getCommodityCardData() {
+  try {
+    commodityLoading.value = true
+    const topAsins = asinList.value.slice(0, 3)
+
+    const newAsins = topAsins.filter((asin) => !lastQueriedAsins.includes(asin))
+    if (newAsins.length === 0) {
+      commodityLoading.value = false
+      return // 如果没有新的 ASIN,直接返回
+    }
+
+    lastQueriedAsins = [...topAsins]
+
+    // 清空commodityCard,为新数据做准备
+    commodityCard.value = []
+
+    // 对每个新的 ASIN 发送请求
+    for (const asin of newAsins) {
+      const query = {
+        profile_id: profile.value.profile_id,
+        asin: asin,
+      }
+
+      try {
+        const response = await getCommodityCard(query)
+        commodityCard.value.push(response.data)
+        // console.log('Response for ASIN', asin, ':', response)
+      } catch (error) {
+        console.log('Error for ASIN', asin, ':', error)
+      }
+    }
+  } catch (error) {
+    console.log('Outer error:', error)
+  } finally {
+    commodityLoading.value = false
+  }
+}
+
+// 更改商品功能
+const commodityDialog = ref(false)
+const selectedCommodity = ref(3)
+const replaceableCommodity = ref([])
+const dialogLoading3 = ref(false)
+let currentEditingIndex = ref(null)
+
+function openCommodityDialog(index) {
+  currentEditingIndex.value = index
+  commodityDialog.value = true
+}
+
+async function getAdditionalCommodityData() {
+  dialogLoading3.value = true
+  try {
+    // 获取除前三个之外的所有 ASIN
+    const additionalAsins = asinList.value.slice(3)
+
+    // 清空 replaceableCommodity,为新数据做准备
+    replaceableCommodity.value = []
+
+    // 对每个额外的 ASIN 发送请求
+    for (const asin of additionalAsins) {
+      const query = {
+        profile_id: profile.value.profile_id,
+        asin: asin,
+      }
+
+      try {
+        const response = await getCommodityCard(query)
+        replaceableCommodity.value.push(response.data)
+        console.log('🚀 ~ getAdditionalCommodityData ~ replaceableCommodity-->>', replaceableCommodity.value)
+        // console.log('Response for additional ASIN', asin, ':', response)
+      } catch (error) {
+        console.log('Error for additional ASIN', asin, ':', error)
+      } finally {
+        dialogLoading3.value = false
+      }
+    }
+  } catch (error) {
+    console.log('Outer error:', error)
+  }
+}
+
+function handleSelectedCommodity() {
+  if (currentEditingIndex.value !== null && selectedCommodity.value) {
+    const selectedCommodityData = flattenedReplaceableCommodity.value.find((item) => item.asin === selectedCommodity.value)
+    if (selectedCommodityData) {
+      commodityCard.value[currentEditingIndex.value] = selectedCommodityData
+    }
+    commodityDialog.value = false
+    console.log('commodityCard', commodityCard.value)
+    currentEditingIndex.value = null
+    selectedCommodity.value = null
+  }
+}
+
+watch(
+  () => pageOptionsValue.value,
+  async () => {
+    await getCommodityCollapseData()
+    getCommodityCardData()
+    getAdditionalCommodityData()
+  }
+)
+
+const flattenedCommodityCard = computed(() => {
+  return commodityCard.value.flat()
+})
+
+const flattenedReplaceableCommodity = computed(() => {
+  return replaceableCommodity.value.flat()
+})
 </script>
 
 <style scoped>
@@ -376,4 +677,41 @@ function openDialog() {
   border-radius: 4px;
   padding: 0 3px;
 }
+.uploaded-image {
+  width: 100%; /* 或根据需要调整 */
+  height: auto; /* 保持图片的原始宽高比 */
+  display: block;
+  margin-bottom: 10px; /* 或根据需要调整 */
+}
+
+.upload-content {
+  text-align: center;
+  padding: 20px;
+}
+.el-carousel__item h3 {
+  color: #edf5fe;
+  opacity: 0.75;
+  line-height: 200px;
+  margin: 0;
+  text-align: center;
+}
+::v-deep(button.el-carousel__button) {
+  background-color: #3569d6;
+}
+.img-box {
+  width: 60px;
+  height: 60px;
+  border: 1px solid rgb(194, 199, 207);
+  border-radius: 4px;
+}
+.double-line {
+  color: #1e2128;
+  font-weight: 500;
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  white-space: pre-wrap;
+  word-break: break-word;
+}
 </style>

+ 16 - 8
src/views/adManage/sb/campaigns/CreateCampaigns/index.vue

@@ -2,11 +2,17 @@
   <div class="page-container">
     <AdCampaign @update-campaign="handleCampaignUpdate"></AdCampaign>
     <AdGroup @update-group-id="handleGroupIdUpdate"></AdGroup>
-    <AdFormat @update:adFormatRadio="handleAdFormatRadioChange"
+    <AdFormat
+      @update:adFormatRadio="handleAdFormatRadioChange"
       @update:arrivalsRadio="handleArrivalsRadioChange"
-      @update:flagshipStoreShop="handleFlagshipStoreShopChange"></AdFormat>
-      <VideoCreativity1 v-if="adFormatRadioValue === 'video' && arrivalsRadioValue === 'flagshipStore' && flagshipStoreShopValue === 'zosi' "></VideoCreativity1>
-      <ProductSetCreativity1 v-if="adFormatRadioValue === 'productSet' && arrivalsRadioValue === 'flagshipStore' && flagshipStoreShopValue === 'zosi' "></ProductSetCreativity1>
+      @update:flagshipStoreShop="handleFlagshipStoreShopChange"
+      @update:pageOptions="handlePageOptionsChange"></AdFormat>
+    <VideoCreativity1
+      v-if="adFormatRadioValue === 'video' && arrivalsRadioValue === 'flagshipStore' && flagshipStoreShopValue === 'ZOSI'">
+    </VideoCreativity1>
+    <ProductSetCreativity1
+      v-if="adFormatRadioValue === 'productSet' && arrivalsRadioValue === 'flagshipStore' && flagshipStoreShopValue === 'ZOSI'">
+    </ProductSetCreativity1>
     <VideoCreativity2 v-if="adFormatRadioValue === 'video' && arrivalsRadioValue === 'productDetailsPage'"></VideoCreativity2>
     <ProductSetCreativity2 v-if="adFormatRadioValue === 'productSet' && arrivalsRadioValue === 'newArrivals'"></ProductSetCreativity2>
     <DeliveryType></DeliveryType>
@@ -30,10 +36,12 @@ const respAdGroupId = ref('')
 const adFormatRadioValue = ref('')
 const arrivalsRadioValue = ref('')
 const flagshipStoreShopValue = ref('')
+const pageOptionsValue = ref('')
 
 provide('respCampaignId', respCampaignId)
 provide('respCampaignName', respCampaignName)
 provide('respAdGroupId', respAdGroupId)
+provide('pageOptionsValue', pageOptionsValue)
 
 const handleCampaignUpdate = (data) => {
   respCampaignId.value = data.id
@@ -44,15 +52,15 @@ const handleGroupIdUpdate = (data) => {
 }
 const handleAdFormatRadioChange = (newValue) => {
   adFormatRadioValue.value = newValue // 更新 adFormatRadioValue
-  console.log('adFormatRadio changed in child component:', newValue)
 }
 const handleArrivalsRadioChange = (newValue) => {
-arrivalsRadioValue.value = newValue
-console.log(newValue)
+  arrivalsRadioValue.value = newValue
 }
 const handleFlagshipStoreShopChange = (newValue) => {
   flagshipStoreShopValue.value = newValue
-console.log(111, newValue)
+}
+const handlePageOptionsChange = (newValue) => {
+  pageOptionsValue.value = newValue
 }
 </script>