Ver Fonte

自动化规则

xinyan há 8 meses atrás
pai
commit
c6976cbf96

+ 10 - 0
src/views/efTools/automation/api.ts

@@ -78,3 +78,13 @@ export function getTargetingRuleList(query) {
   });
 }
 
+export function getProductsList(query) {
+  return request({
+    url: '/api/ad_manage/template/target_select/products/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+
+

+ 66 - 30
src/views/efTools/automation/components/adActivityDialog.vue

@@ -132,7 +132,16 @@ function handleSizeChange(newSize) {
   fetchAdCampaign();
 }
 
+function handleAdCampaignChange() {
+  localStorage.setItem('searchAdCampaign', JSON.stringify(searchAdCampaign.value));
+  fetchAdCampaign();
+}
+
 async function fetchAdCampaign() {
+  const savedAdCampaign = localStorage.getItem('searchAdCampaign');
+  if (savedAdCampaign) {
+    searchAdCampaign.value = JSON.parse(savedAdCampaign);
+  }
   try {
     loading.value = true;
     const cachedSelectedAds = [...selectedAds.value];
@@ -172,42 +181,49 @@ function handelSelect({ records }) {
 }
 
 function updateSelectedAds() {
-  selectedAds.value = gridOptions.data.filter(ad =>
-      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.isSelected)
-  ).map(ad => ({
-    ...ad,
-    campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.isSelected)
-  }));
+  selectedAds.value = gridOptions.data
+      .filter(ad => ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.isSelected))
+      .map(ad => ({
+        ...ad,
+        campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.isSelected),
+        page: currentPage.value
+      }));
 }
 
-function handleSelectionChange({ records }) {
-  records.forEach(record => {
-    if (record.adGroupId) {
-      // 这是一个广告组(子节点)
-      const parentCampaign = gridOptions.data.find(campaign =>
-          campaign.campaignGroupInfo.some(group => group.adGroupId === record.adGroupId)
-      );
-      if (parentCampaign) {
-        const group = parentCampaign.campaignGroupInfo.find(group => group.adGroupId === record.adGroupId);
-        if (group) {
-          group.isSelected = true;
+function handleSelectionChange({ records, checked, reserve }) {
+  console.log("=>(adActivityDialog.vue:193) records", records);
+
+  if (records.length === 0 && !reserve) {
+    // 当所有项目被取消选择时,清空 selectedAds
+    selectedAds.value = [];
+  } else {
+    records.forEach(record => {
+      if (record.adGroupId) {
+        // 这是一个广告组(子节点)
+        const parentCampaign = gridOptions.data.find(campaign =>
+            campaign.campaignGroupInfo.some(group => group.adGroupId === record.adGroupId)
+        );
+        if (parentCampaign) {
+          const group = parentCampaign.campaignGroupInfo.find(group => group.adGroupId === record.adGroupId);
+          if (group) {
+            group.isSelected = checked;
+          }
         }
+      } else {
+        // 这是一个广告活动(父节点)
+        record.campaignGroupInfo.forEach(group => {
+          group.isSelected = checked;
+        });
       }
-    } else {
-      // 这是一个广告活动(父节点)
-      record.campaignGroupInfo.forEach(group => {
-        group.isSelected = true;
-      });
-    }
-  });
+    });
 
-  updateSelectedAds();
+    updateSelectedAds();
+  }
+
+  console.log("=>(adActivityDialog.vue:224) selectedAds.value", selectedAds.value);
 }
 
 function handleSelectTarget(row) {
-  // if (row.isSelected) {
-  //   console.log('288:selectedAds.value',selectedAds.value);
-  // }
   // 获取父节点数据
   const parent = gridOptions.data.find(campaign =>
       campaign.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
@@ -254,11 +270,31 @@ function handleConfirm({ campaignInfo, targetType }) {
 }
 
 const removeSelectedAd = async (row) => {
+  console.log("=>(adActivityDialog.vue:274) row", row);
   const $grid = xGridTwo.value;
   if ($grid) {
+    console.log("=>(1) selectedAds.value", selectedAds.value);
+
+    if (row.adGroupId) {
+      // 删除子节点(广告组)
+      selectedAds.value = selectedAds.value.map(ad => {
+        if (ad.campaignGroupInfo) {
+          return {
+            ...ad,
+            campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.adGroupId !== row.adGroupId)
+          };
+        }
+        return ad;
+      }).filter(ad => ad.campaignGroupInfo && ad.campaignGroupInfo.length > 0);
+    } else {
+      // 删除父节点(广告活动)
+      selectedAds.value = selectedAds.value.filter(ad => ad.campaignId !== row.campaignId);
+    }
+
     await $grid.remove(row);
-    selectedAds.value = selectedAds.value.filter(ad => ad.campaignId !== row.campaignId);
+    console.log("=>(2) selectedAds.value", selectedAds.value);
   }
+
   if (xGridOne.value) {
     await xGridOne.value.toggleCheckboxRow(row);
   }
@@ -384,7 +420,7 @@ onMounted(() => {
     <div class="container">
       <div class="container-left">
         <el-input v-model="searchAdCampaign" clearable placeholder="请输入广告活动" style="width: 100%;"
-                  @change="fetchAdCampaign()"></el-input>
+                  @change="handleAdCampaignChange()"></el-input>
         <div class="custom-inline">
           <el-select v-model="selectedCampaignType" placeholder="选择广告类型">
             <el-option v-for="item in campaignType"

+ 94 - 22
src/views/efTools/automation/components/targetRuleDialog.vue

@@ -4,12 +4,17 @@
  * @Description: 关联广告活动-选择定向弹窗
  * @Author: xinyan
  */
-import { provide, reactive, ref, watch } from 'vue';
+import { reactive, ref, watch } from 'vue';
 import { MatchType } from '../../utils/enum';
-import { getTargetingRuleList } from '/@/views/efTools/automation/api';
+import { getProductsList, getTargetingRuleList } from '/@/views/efTools/automation/api';
 import { ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useShopInfo } from '/@/stores/shopInfo';
+import { Share } from '@element-plus/icons-vue';
 
 
+const shopInfo = useShopInfo();
+const { profile } = storeToRefs(shopInfo);
 const props = defineProps({
   modelValue: {
     type: Boolean,
@@ -41,9 +46,10 @@ const targetColumn = ref([
   { field: 'expressionDesc', title: '品牌', slots: { default: 'expressionDesc_default' } },
   { type: 'checkbox', align: 'right', width: 55 }
 ]);
-
-const keyWordData = [];
-const targetData = [];
+const productColumn = ref([
+  { field: 'itemName', title: '商品/分类', slots: { default: 'itemName_default' } },
+  { type: 'checkbox', align: 'right', width: 55 }
+]);
 
 const gridOptions = reactive({
   height: 550,
@@ -57,6 +63,14 @@ const gridOptions = reactive({
   data: []
 });
 
+const showProductSearch = computed(() => {
+  const columns = unref(gridOptions.columns);
+  return targetType.value === 'target' &&
+      Array.isArray(columns) &&
+      columns.length > 0 &&
+      columns[0].field === 'itemName';
+});
+
 async function fetchTargetRuleList() {
   try {
     gridOptions.loading = true;
@@ -67,19 +81,48 @@ async function fetchTargetRuleList() {
       matchType: matchType.value,
       search: keyWord.value,
     });
+
     targetType.value = resp.data.targetType;
+
     // 动态设置表格列
     if (resp.data.targetType === 'keyword') {
       gridOptions.columns = keyWordColumn;
       gridOptions.rowConfig.height = 34;
+      gridOptions.data = resp.data.targetData;
+
     } else if (resp.data.targetType === 'target') {
       gridOptions.rowConfig.height = 85;
       gridOptions.columns = targetColumn;
+      gridOptions.data = resp.data.targetData;
+
+      // 检查是否有 ASIN_SAME_AS 字段
+      const asinSameAsList = resp.data.targetData
+          .filter(item => item.expressionDesc && item.expressionDesc.ASIN_SAME_AS) // 过滤存在 ASIN_SAME_AS 的项
+          .map(item => item.expressionDesc.ASIN_SAME_AS); // 提取所有 ASIN_SAME_AS
+
+      if (asinSameAsList.length > 0) {
+        gridOptions.columns = productColumn;
+        gridOptions.rowConfig.height = 120;
+        gridOptions.data = await fetchProductsList(asinSameAsList); // 传递 ASIN 列表
+      }
     }
-    gridOptions.data = resp.data.targetData;
     gridOptions.loading = false;
   } catch (error) {
     ElMessage.error('请求定向数据失败');
+    gridOptions.loading = false; // 确保在错误情况下也关闭 loading 状态
+  }
+}
+
+// TODO: 待商品数据查询接口完成后,添加查询参数
+async function fetchProductsList(asinList) {
+  try {
+    const resp = await getProductsList({
+      asinList: asinList.join(','), // 将 ASIN 列表转换为逗号分隔的字符串
+      profileId: profile.value.profile_id,
+    });
+    return resp.data;
+  } catch (error) {
+    ElMessage.error('请求商品数据失败');
   }
 }
 
@@ -107,6 +150,15 @@ async function confirm() {
   });
 }
 
+// 打开指定 URL 的方法
+const openProductUrl = (url) => {
+  if (url) {
+    window.open(url, '_blank');  // 在新标签页中打开链接
+  } else {
+    console.warn('Product URL is not available');
+  }
+};
+
 const headerCellStyle = () => {
   return {
     fontSize: '13px',
@@ -144,22 +196,28 @@ watch(() => props.selectedTargetedRow, () => {
              style="border-radius: 10px;"
              title="关联广告活动 > 选择定向"
              width="1158px">
-    <div>
-      <el-select
-          v-model="matchType"
-          placeholder="全部匹配方式"
-          style="width: 128px; margin-bottom: 10px"
-          @change="fetchTargetRuleList"
-      >
-        <el-option
-            v-for="item in MatchType"
-            :key="item.value"
-            :label="item.label"
-            :value="item.value"
-        />
-      </el-select>
-    </div>
-    <el-input v-model="keyWord" class="mb-3" clearable placeholder="快速搜索关键词" @change="fetchTargetRuleList" />
+    <template v-if="targetType === 'keyword'">
+      <div>
+        <el-select
+            v-model="matchType"
+            placeholder="全部匹配方式"
+            style="width: 128px; margin-bottom: 10px"
+            @change="fetchTargetRuleList"
+        >
+          <el-option
+              v-for="item in MatchType"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+          />
+        </el-select>
+      </div>
+      <el-input v-model="keyWord" class="mb-3" clearable placeholder="快速搜索关键词" @change="fetchTargetRuleList" />
+    </template>
+    <template v-else-if="showProductSearch">
+      <el-input v-model="productName" class="mb-3" placeholder="快速搜索分类名称或商品名称" style="width: 517px"
+                @change="fetchProductsList"></el-input>
+    </template>
     <vxe-grid :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions"
               v-on="gridEvents" @checkbox-change="handleCheckChange">
       <template #expressionDesc_default="{ row }">
@@ -172,6 +230,20 @@ watch(() => props.selectedTargetedRow, () => {
         </div>
         <div v-if="row.expressionDesc.ASIN_SAME_AS">asin:{{ row.expressionDesc.ASIN_SAME_AS }}</div>
       </template>
+      <template #itemName_default="{ row }">
+        <div style="display: flex; align-items: flex-start;">
+          <!-- 左边图片部分 -->
+          <div style="flex-shrink: 0;">
+            <img :src="row.main_image" alt="Product Image" height="53" width="53" />
+          </div>
+          <!-- 右边文字部分 -->
+          <div style="margin-left: 10px;">
+            <div><strong>{{ row.itemName }}</strong></div>
+            <span>ASIN: {{ row.asin }}</span>
+            <el-button :icon="Share" link type="primary" @click="openProductUrl(row.productUrl)"></el-button>
+          </div>
+        </div>
+      </template>
     </vxe-grid>
     <template #footer>
       <div class="dialog-footer">