Jelajahi Sumber

自动化规则

xinyan 9 bulan lalu
induk
melakukan
863c195ab2

+ 590 - 3
src/views/demo/index.vue

@@ -1,11 +1,598 @@
-<template>
+<script lang="ts" setup>
+/**
+ * @Name: adActivityDialog.vue
+ * @Description:广告关联活动弹窗
+ * @Author: xinyan
+ */
+import { computed, onMounted, ref, watch } from 'vue';
+import { getAdGroupList, getRelationCampaign } from '/@/views/efTools/automation/api';
+import { storeToRefs } from 'pinia';
+import { useShopInfo } from '/@/stores/shopInfo';
+import { ElMessage } from 'element-plus';
+import TargetRuleDialog from '/@/views/efTools/automation/components/targetRuleDialog.vue';
 
-</template>
+const props = defineProps({
+  templateId: {
+    type: [String, Number],
+    required: true,
+  },
+  activeModel: {
+    type: String,
+  },
+  modelValue: {
+    type: Boolean,
+    required: true,
+  },
+});
+const emits = defineEmits(['update:modelValue']);
+const shopInfo = useShopInfo();
+const { profile } = storeToRefs(shopInfo);
+const { templateId } = toRefs(props);
+// const { activeModel } = toRefs(props);
+const activeModel = ref('specified');
+
+const dialogVisible = ref(true);
+const targetRuleDialogVisible = ref(false);
+
+// 定向规则
+//const adGroupId = ref('')
+//const campaignId = ref('')
+const selectedTargetedRow = ref(null);
+
+// 筛选条件
+const searchAdCampaign = ref('');
+const selectedCampaignType = ref('sp');
+const selectedAdGroup = ref('');
+const selectedStatus = ref('');
+
+const adCampaignName = ref([]);
+const campaignType = [
+  { value: 'sb', label: 'SB' },
+  { value: 'sp', label: 'SP' },
+  { value: 'sd', label: 'SD' },
+];
+const adGroups = ref([]);
+const campaignStatus = [
+  { value: 0, label: '未存档' },
+  { value: 'ENABLED', label: '已启用' },
+  { value: 'PAUSED', label: '已暂停' },
+];
+
+// 表格
+const currentPage = ref(1);
+const pageSize = ref(25);
+const total = ref(0);
+
+const loading = ref(false);
+const refTable = ref(null);
+let selectedAds = ref([]);
+let selections = []; // 添加选中的项
+
+function handleCurrentChange(newPage) {
+  currentPage.value = newPage;
+  loading.value = true;
+  fetchAdCampaign();
+}
+
+// 处理分页器每页显示条目数变化
+function handleSizeChange(newSize) {
+  pageSize.value = newSize;
+  currentPage.value = 1;
+  fetchAdCampaign();
+}
+
+let isRestoringSelection = false;
+
+async function fetchAdCampaign() {
+  try {
+    loading.value = true;
+    const cachedSelectedAds = [...selectedAds.value];
+    const resp = await getRelationCampaign({
+      profileId: profile.value.profile_id,
+      templateId: 51,
+      campaignName: searchAdCampaign.value,
+      portfolioId: selectedAdGroup.value,
+      campaignStatus: selectedStatus.value,
+      campaignType: selectedCampaignType.value,
+      page: currentPage.value,
+      limit: pageSize.value,
+    });
+    adCampaignName.value = resp.data;
+    total.value = resp.total;
+    currentPage.value = resp.page;
+    // 开始恢复勾选状态
+    isRestoringSelection = true;
+    nextTick(() => {
+      adCampaignName.value.forEach(item => {
+        if (cachedSelectedAds.some(ad => ad.campaignId === item.campaignId)) {
+          refTable.value.toggleRowSelection(item, true);
+        }
+      });
+      isRestoringSelection = false;
+    });
+  } catch (error) {
+    ElMessage.error('请求广告活动数据失败');
+  } finally {
+    loading.value = false;
+  }
+}
+
+function handleSelectionChange(selection) {
+  if (isRestoringSelection) return;
+
+  const newSelectedAds = [];
+
+  selection.forEach(item => {
+    if (item.campaignGroupInfo) {
+      // 这是父节点(广告活动)
+      newSelectedAds.push({
+        campaignId: item.campaignId,
+        campaignName: item.campaignName,
+        campaignType: item.campaignType,
+        isParent: true
+      });
+    } else {
+      // 这是子节点(广告组)
+      const parent = adCampaignName.value.find(ad =>
+          ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === item.adGroupId)
+      );
+      if (parent) {
+        // 添加父节点(如果还没有添加)
+        if (!newSelectedAds.some(ad => ad.campaignId === parent.campaignId)) {
+          newSelectedAds.push({
+            campaignId: parent.campaignId,
+            campaignName: parent.campaignName,
+            campaignType: parent.campaignType,
+            isParent: true
+          });
+        }
+        // 添加子节点(广告组)
+        newSelectedAds.push({
+          adGroupId: item.adGroupId,
+          adGroupName: item.adGroupName,
+          parentCampaignId: parent.campaignId,
+          parentCampaignName: parent.campaignName,
+          campaignType: parent.campaignType
+        });
+      }
+    }
+  });
+  selectedAds.value = newSelectedAds;
+}
+
+const groupedSelectedAds = computed(() => {
+  const groups = {};
+  selectedAds.value.forEach(ad => {
+    if (ad.isParent) {
+      if (!groups[ad.campaignId]) {
+        groups[ad.campaignId] = {
+          id: ad.campaignId,
+          name: ad.campaignName,
+          campaignType: ad.campaignType,
+          campaignGroupInfo: []
+        };
+      }
+    } else {
+      if (!groups[ad.parentCampaignId]) {
+        groups[ad.parentCampaignId] = {
+          id: ad.parentCampaignId,
+          name: ad.parentCampaignName,
+          campaignType: ad.campaignType,
+          campaignGroupInfo: []
+        };
+      }
+      groups[ad.parentCampaignId].campaignGroupInfo.push({
+        id: ad.adGroupId,
+        name: ad.adGroupName
+      });
+    }
+  });
+  return Object.values(groups);
+});
+
+function removeSelectedAd(index) {
+  const removedAd = selectedAds.value.splice(index, 1)[0];
+
+  if (removedAd.parentCampaignId) {
+    // 查找同一父节点下的其他子节点
+    const remainingChildren = selectedAds.value.filter(ad => ad.parentCampaignId === removedAd.parentCampaignId);
+
+    // 如果父节点下还有其他子节点,父节点不删除
+    if (remainingChildren.length > 0) {
+      // 取消表格1中该子节点的勾选
+      const targetParent = adCampaignName.value.find(ad => ad.campaignId === removedAd.parentCampaignId);
+      if (targetParent) {
+        const targetChildIndex = targetParent.campaignGroupInfo.findIndex(group => group.adGroupId === removedAd.adGroupId);
+        if (targetChildIndex !== -1) {
+          const adTable = refTable.value;
+          adTable.toggleRowSelection(targetParent.campaignGroupInfo[targetChildIndex], false);
+        }
+      }
+    } else {
+      // 如果没有其他子节点了,移除父节点
+      const parentIndex = selectedAds.value.findIndex(ad => ad.campaignId === removedAd.parentCampaignId);
+      if (parentIndex !== -1) {
+        selectedAds.value.splice(parentIndex, 1);
+      }
+
+      // 取消表格1中父节点的勾选
+      const parentIndexInTable = adCampaignName.value.findIndex(ad => ad.campaignId === removedAd.parentCampaignId);
+      if (parentIndexInTable !== -1) {
+        const adTable = refTable.value;
+        adTable.toggleRowSelection(adCampaignName.value[parentIndexInTable], false);
+      }
+    }
+  } else {
+    // 如果删除的项是父节点
+    const targetIndex = adCampaignName.value.findIndex(ad => ad.campaignId === removedAd.campaignId);
+    if (targetIndex !== -1) {
+      const adTable = refTable.value;
+      adTable.toggleRowSelection(adCampaignName.value[targetIndex], false);
+    }
+  }
+}
+
+function removeAllSelectedAds() {
+  selectedAds.value = [];
+  const adTable = refTable.value;
+  adTable.clearSelection();
+}
+
+function cancel() {
+  dialogVisible.value = false;
+}
+
+//TODO: 确认按钮
+async function confirm() {
+  const campaignItems = selectedAds.value.map(ad => ({
+    campaignId: ad.campaignId,
+    campaignType: ad.campaignType
+  }));
+  const adGroupInfo = [];
+  const campaignTargetInfo = [
+    { targetId: '492707808377423', adGroup_id: '448117369011017', bid: 0.45 },
+  ];
+  const campaignKeywordInfo = [
+    { keywordId: '416969576305724', adGroup_id: '393554556566271', bid: 0.04 },
+  ];
+
+  const requestData = {
+    profileId: profile.value.profile_id,
+    templateId: templateId.value,
+    campaignItems,
+    adGroupInfo,
+    campaignTargetInfo,
+    campaignKeywordInfo
+  };
+  //console.log('requestData', requestData);
+}
+
+// 获取广告组下拉框
+async function fetchAdGroupList() {
+  try {
+    const resp = await getAdGroupList({
+      profileId: profile.value.profile_id,
+    });
+    adGroups.value = resp.data.map((item: any) => {
+      return {
+        label: item.name,
+        value: item.portfolioId
+      };
+    });
+  } catch (error) {
+    ElMessage.error('请求失败');
+  }
+}
+
+function handleSelectTarget(row) {
+  console.log("=>(adActivityDialog.vue:287) row", row);
+  // 获取父节点数据
+  const parent = adCampaignName.value.find(ad =>
+      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
+  );
+
+  if (parent) {
+    // 构造包含父节点和子节点的数据对象
+    selectedTargetedRow.value = {
+      campaignId: parent.campaignId,
+      campaignName: parent.campaignName,
+      campaignType: parent.campaignType,
+      adGroupId: row.adGroupId,
+      adGroupName: row.adGroupName,
+      isSelected: row.isSelected || false, // 同步 isSelected 状态
+    };
+  }
 
-<script setup>
+  targetRuleDialogVisible.value = true;
+}
+
+function handleConfirm(campaignKeywordInfo) {
+  selectedTargetedRow.value.keywordInfo = campaignKeywordInfo;
+  selectedTargetedRow.value.isSelected = true;
+
+  // 更新子节点状态
+  const parent = adCampaignName.value.find(ad =>
+      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === selectedTargetedRow.value.adGroupId)
+  );
+  console.log("=>(adActivityDialog.vue:306) parent", parent);
+
+  if (parent) {
+    const group = parent.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
+    if (group) {
+      group.isSelected = true; // 更新子节点的 isSelected 状态
+    }
+  }
+
+  // 勾选子节点行
+  const table = refTable.value;
+  const targetRow = parent.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
+
+  if (table && targetRow) {
+    table.toggleRowSelection(targetRow, true); // 勾选目标子节点行
+  }
+}
+
+const headerCellStyle = (args) => {
+  if (args.rowIndex === 0) {
+    return {
+      backgroundColor: 'rgba(245, 245, 245, 0.9)',
+      fontWeight: '500',
+    };
+  }
+};
+
+const cellStyle = () => {
+  return {
+    fontSize: '13px',
+    //fontWeight: '500',
+  };
+};
+
+//监控筛选条件变化
+watch(selectedCampaignType, () => {
+  fetchAdCampaign();
+});
+
+watch(selectedAdGroup, (newVal) => {
+  if (newVal) {
+    selectedAdGroup.value = newVal;
+    fetchAdCampaign();
+  }
+});
+
+watch(selectedStatus, () => {
+  fetchAdCampaign();
+});
+
+watch(templateId, () => {
+  fetchAdCampaign();
+  fetchAdGroupList();
+});
+
+watch(() => props.modelValue, (newValue) => {
+  dialogVisible.value = newValue;
+});
+
+watch(dialogVisible, (newValue) => {
+  emits('update:modelValue', newValue);
+});
+
+const treeProps = computed(() => {
+  if (activeModel.value === 'adGroup' || activeModel.value === 'specified') {
+    return {
+      children: 'campaignGroupInfo',
+      checkStrictly: false
+    };
+  }
+  return {};
+});
+
+onMounted(() => {
+  fetchAdGroupList();
+  fetchAdCampaign()
+});
 
 </script>
 
+<template>
+  <el-dialog
+      v-model="dialogVisible"
+      style="border-radius: 10px;"
+      title="关联广告活动"
+      width="1158px"
+  >
+    <div class="container">
+      <div class="container-left">
+        <el-input v-model="searchAdCampaign" clearable placeholder="请输入广告活动" style="width: 100%;"
+                  @change="fetchAdCampaign()"></el-input>
+        <div class="custom-inline">
+          <el-select v-model="selectedCampaignType" placeholder="选择广告类型">
+            <el-option v-for="item in campaignType"
+                       :key="item.value"
+                       :label="item.label"
+                       :value="item.value"
+            ></el-option>
+          </el-select>
+
+          <el-select v-model="selectedAdGroup" clearable placeholder="广告组合" style="margin-bottom: 10px;">
+            <el-option
+                v-for="item in adGroups"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            ></el-option>
+          </el-select>
+
+          <el-select v-model="selectedStatus" placeholder="状态" style="margin-bottom: 10px;">
+            <el-option
+                v-for="item in campaignStatus"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            ></el-option>
+          </el-select>
+        </div>
+        <el-table
+            ref="refTable"
+            v-loading="loading"
+            :cell-style="cellStyle"
+            :data="adCampaignName"
+            :header-cell-style="headerCellStyle"
+            :row-key="'adGroupId'"
+            :tree-props="treeProps"
+            height="400"
+            style="width: 100%;"
+            @selection-change="handleSelectionChange"
+        >
+          <el-table-column label="广告活动名称">
+            <template #default="scope">
+              <el-tag
+                  v-if="scope.row.campaignType"
+                  :color="scope.row.campaignType === 'sb' ? '#0163d2' : (scope.row.campaignType === 'sp' ? '#ff7424' : '#365672')"
+                  class="campaign-type"
+                  disable-transitions
+                  round>
+                {{ scope.row.campaignType }}
+              </el-tag>
+              {{ scope.row.campaignName }}
+              {{ scope.row.adGroupName }}
+              <el-button
+                  v-if="scope.row.adGroupName && activeModel==='specified'&&!scope.row.isSelected"
+                  class="btn-link"
+                  link
+                  @click="handleSelectTarget(scope.row)">
+                选择定向
+              </el-button>
+              <span v-else-if="scope.row.adGroupName">已选择</span>
+            </template>
+          </el-table-column>
+          <el-table-column type="selection" width="55"></el-table-column>
+        </el-table>
+        <div class="pagination-container mt-4">
+          <el-pagination
+              v-model:current-page="currentPage"
+              :page-size="pageSize"
+              :page-sizes="[10, 25, 50,100,200]"
+              :total="total"
+              background
+              layout="total,sizes,prev, next, jumper"
+              small
+              @size-change="handleSizeChange"
+              @current-change="handleCurrentChange"
+          />
+        </div>
+      </div>
+      <div class="container-right">
+        <h3>已选择({{ selectedAds.length }})</h3>
+        <el-table
+            :cell-style="cellStyle"
+            :data="groupedSelectedAds"
+            :header-cell-style="headerCellStyle"
+            :row-key="'id'"
+            :tree-props="treeProps"
+            height="484"
+            style="width: 100%; margin-top: 20px;"
+            v-bind="activeModel === 'adGroup' || activeModel === 'specified' ? treeProps : {}"
+        >
+          <el-table-column label="广告活动" prop="name">
+            <template #default="scope">
+              <el-tag
+                  v-if="scope.row.campaignType"
+                  :color="scope.row.campaignType === 'sb' ? '#0163d2' : (scope.row.campaignType === 'sp' ? '#ff7424' : '#365672')"
+                  class="campaign-type"
+                  disable-transitions
+                  round>
+                {{ scope.row.campaignType }}
+              </el-tag>
+              {{ scope.row.campaignName }}
+              {{ scope.row.name }}
+            </template>
+          </el-table-column>
+          <el-table-column
+              align="center"
+              label="操作"
+              width="100"
+          >
+            <template #header>
+              <el-button link size="default" style="color: #2077d7" @click="removeAllSelectedAds">删除全部</el-button>
+            </template>
+            <template #default="scope">
+              <el-button type="text" @click="removeSelectedAd(scope.$index)">
+                <CircleClose style="width:16px;color:#4b5765" />
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="cancel">取消</el-button>
+        <el-button type="primary" @click="confirm">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+  <TargetRuleDialog v-if="activeModel === 'specified'"
+                    v-model="targetRuleDialogVisible"
+                    :selectedTargetedRow="selectedTargetedRow"
+                    @confirm:targetRule="handleConfirm"
+  ></TargetRuleDialog>
+</template>
+
 <style scoped>
+.pagination-container {
+  display: flex;
+  flex-direction: row-reverse;
+}
+
+.custom-inline {
+  display: flex;
+  justify-content: space-around;
+  margin: 12px 0;
+  gap: 4px;
+}
+
+.campaign-type {
+  width: 35px;
+  text-align: center;
+  height: 22px;
+  font-size: 13px;
+  font-weight: 400;
+  color: #fff;
+  border-color: #fff;
+  line-height: 22px;
+  border-radius: 12px;
+  margin-right: 4px;
+}
+
+.container {
+  width: 100%;
+  height: 100%;
+  border: 1px solid #d6dbe2;
+  border-radius: 4px;
+  display: flex;
+  overflow: hidden;
+  align-content: center;
+  flex-wrap: nowrap;
+  flex-direction: row;
+  /* padding: 10px; */
+}
+
+.container-left {
+  width: 50%;
+  border-right: 1px solid #d6dbe2;
+  padding: 15px
+}
+
+.container-right {
+  flex: 1;
+  padding: 15px
+}
+
+.btn-link {
+  font-size: 13px;
+  color: #0085ff;
+  /* ling-heigt: 23px; */
+}
 
 </style>

+ 241 - 258
src/views/efTools/automation/components/adActivityDialog.vue

@@ -10,6 +10,8 @@ import { storeToRefs } from 'pinia';
 import { useShopInfo } from '/@/stores/shopInfo';
 import { ElMessage } from 'element-plus';
 import TargetRuleDialog from '/@/views/efTools/automation/components/targetRuleDialog.vue';
+import { DocumentAdd } from '@element-plus/icons-vue';
+
 
 const props = defineProps({
   templateId: {
@@ -28,15 +30,17 @@ const emits = defineEmits(['update:modelValue']);
 const shopInfo = useShopInfo();
 const { profile } = storeToRefs(shopInfo);
 const { templateId } = toRefs(props);
-const { activeModel } = toRefs(props);
+// const { activeModel } = toRefs(props);
+const activeModel = ref('specified');
 
-const dialogVisible = ref(props.modelValue);
+const dialogVisible = ref(true);
 const targetRuleDialogVisible = ref(false);
 
 // 定向规则
 //const adGroupId = ref('')
 //const campaignId = ref('')
 const selectedTargetedRow = ref(null);
+const KeyWordLength = ref(0);
 
 // 筛选条件
 const searchAdCampaign = ref('');
@@ -56,16 +60,63 @@ const campaignStatus = [
   { value: 'ENABLED', label: '已启用' },
   { value: 'PAUSED', label: '已暂停' },
 ];
-
 // 表格
 const currentPage = ref(1);
 const pageSize = ref(25);
 const total = ref(0);
 
 const loading = ref(false);
-const refTable = ref(null);
+const xGridOne = ref(null);
+const xGridTwo = ref(null);
 let selectedAds = ref([]);
-let selections = []; // 添加选中的项
+
+const selectedColumns = computed(() => [
+  {
+    field: 'campaignName',
+    title: '广告活动',
+    slots: { default: 'campaignName_default' },
+    treeNode: activeModel.value == 'specified' || activeModel.value == 'adGroup'
+  },
+  { title: '操作', width: 100, align: 'center', slots: { header: 'header_operation', default: 'default_operation' } }
+]);
+
+const treeProps = computed(() => {
+  activeModel.value = 'specified';
+
+  if (activeModel.value === 'adGroup' || activeModel.value === 'specified') {
+    return {
+      rowField: 'campaignId',
+      childrenField: 'campaignGroupInfo',  // 子节点字段
+      expandAll: true,
+    };
+  }
+  return {};
+});
+
+const gridOptions = reactive({
+  border: 'inner',
+  height: 500,
+  rowConfig: {
+    isHover: true,
+    keyField: 'campaignId',
+  },
+  treeConfig: treeProps,
+  checkboxConfig: {
+    labelField: 'name',
+    reserve: true,
+    checkStrictly: false,
+  },
+  columns: computed(() => [
+    {
+      field: 'campaignName',
+      title: '广告活动',
+      slots: { default: 'campaignName_default' },
+      treeNode: activeModel.value == 'specified' || activeModel.value == 'adGroup'
+    },
+    { type: 'checkbox', width: 55, fixed: 'right' },
+  ]),
+  data: []
+});
 
 function handleCurrentChange(newPage) {
   currentPage.value = newPage;
@@ -80,15 +131,13 @@ function handleSizeChange(newSize) {
   fetchAdCampaign();
 }
 
-let isRestoringSelection = false;
-
 async function fetchAdCampaign() {
   try {
     loading.value = true;
     const cachedSelectedAds = [...selectedAds.value];
     const resp = await getRelationCampaign({
       profileId: profile.value.profile_id,
-      templateId: templateId.value,
+      templateId: 51,
       campaignName: searchAdCampaign.value,
       portfolioId: selectedAdGroup.value,
       campaignStatus: selectedStatus.value,
@@ -96,19 +145,18 @@ async function fetchAdCampaign() {
       page: currentPage.value,
       limit: pageSize.value,
     });
-    adCampaignName.value = resp.data;
+    gridOptions.data = resp.data;
+    console.log('=>(adActivityDialog.vue:131) gridOptions.data', gridOptions.data);
     total.value = resp.total;
     currentPage.value = resp.page;
-    // 开始恢复勾选状态
-    isRestoringSelection = true;
-    nextTick(() => {
-      adCampaignName.value.forEach(item => {
-        if (cachedSelectedAds.some(ad => ad.campaignId === item.campaignId)) {
-          refTable.value.toggleRowSelection(item, true);
-        }
-      });
-      isRestoringSelection = false;
-    });
+    // 恢复勾选状态
+    // nextTick(() => {
+    //   gridOptions.data.forEach(item => {
+    //     const isSelected = selectedAds.value.some(ad => ad.campaignId === item.campaignId);
+    //     // 如果在 selectedAds 中,则勾选
+    //     xGridOne.value.setCheckboxRow(item, isSelected);
+    //   });
+    // });
   } catch (error) {
     ElMessage.error('请求广告活动数据失败');
   } finally {
@@ -116,125 +164,120 @@ async function fetchAdCampaign() {
   }
 }
 
-function handleSelectionChange(selection) {
-  if (isRestoringSelection) return;
-
-  const newSelectedAds = [];
-
-  selection.forEach(item => {
-    if (item.campaignGroupInfo) {
-      // 这是父节点(广告活动)
-      newSelectedAds.push({
-        campaignId: item.campaignId,
-        campaignName: item.campaignName,
-        campaignType: item.campaignType,
-        isParent: true
-      });
-    } else {
-      // 这是子节点(广告组)
-      const parent = adCampaignName.value.find(ad =>
-          ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === item.adGroupId)
+const handleGridChange = (event) => {
+  if (activeModel.value == 'specified' || activeModel.value == 'adGroup') {
+    handleSelectionChange(event);
+  } else {
+    handelSelect(event);
+  }
+};
+
+function handelSelect({ records }) {
+  console.log('=>(adActivityDialog.vue:147) records', records);
+  selectedAds.value = [
+    ...selectedAds.value.filter(ad => ad.page !== currentPage.value),
+    ...records.map(ad => ({ ...ad, page: currentPage.value }))
+  ];
+  console.log('=>(adActivityDialog.vue:148) selectedAds.value', selectedAds.value);
+
+}
+
+function handleSelectionChange({ records }) {
+  let updatedRecords = [];
+  const parentCampaignMap = new Map();
+  records.isSelected = false;
+  records.forEach(record => {
+    if (record.adGroupId) {
+      // 这是一个广告组(子节点)
+      const parentCampaign = gridOptions.data.find(campaign =>
+          campaign.campaignGroupInfo.some(group => group.adGroupId === record.adGroupId)
       );
-      if (parent) {
-        // 添加父节点(如果还没有添加)
-        if (!newSelectedAds.some(ad => ad.campaignId === parent.campaignId)) {
-          newSelectedAds.push({
-            campaignId: parent.campaignId,
-            campaignName: parent.campaignName,
-            campaignType: parent.campaignType,
-            isParent: true
+      if (parentCampaign) {
+        // 如果父节点已经存在,则将子节点添加到父节点的 campaignGroupInfo 中
+        if (parentCampaignMap.has(parentCampaign.campaignId)) {
+          parentCampaignMap.get(parentCampaign.campaignId).campaignGroupInfo.push(record);
+        } else {
+          // 如果父节点不存在,则创建一个新的父节点对象
+          parentCampaignMap.set(parentCampaign.campaignId, {
+            campaignType: parentCampaign.campaignType,
+            campaignId: parentCampaign.campaignId,
+            campaignName: parentCampaign.campaignName,
+            campaignGroupInfo: [record]
           });
         }
-        // 添加子节点(广告组)
-        newSelectedAds.push({
-          adGroupId: item.adGroupId,
-          adGroupName: item.adGroupName,
-          parentCampaignId: parent.campaignId,
-          parentCampaignName: parent.campaignName,
-          campaignType: parent.campaignType
-        });
       }
     }
   });
-  selectedAds.value = newSelectedAds;
+
+  // 将所有父节点对象添加到 updatedRecords
+  updatedRecords = Array.from(parentCampaignMap.values());
+
+  // 更新选中的广告
+  selectedAds.value = [
+    ...updatedRecords.map(ad => ({ ...ad, page: currentPage.value }))
+  ];
+  console.log("=>(adActivityDialog.vue:217) selectedAds.value", selectedAds.value);
+
 }
 
-const groupedSelectedAds = computed(() => {
-  const groups = {};
-  selectedAds.value.forEach(ad => {
-    if (ad.isParent) {
-      if (!groups[ad.campaignId]) {
-        groups[ad.campaignId] = {
-          id: ad.campaignId,
-          name: ad.campaignName,
-          campaignType: ad.campaignType,
-          campaignGroupInfo: []
-        };
-      }
-    } else {
-      if (!groups[ad.parentCampaignId]) {
-        groups[ad.parentCampaignId] = {
-          id: ad.parentCampaignId,
-          name: ad.parentCampaignName,
-          campaignType: ad.campaignType,
-          campaignGroupInfo: []
-        };
-      }
-      groups[ad.parentCampaignId].campaignGroupInfo.push({
-        id: ad.adGroupId,
-        name: ad.adGroupName
-      });
-    }
-  });
-  return Object.values(groups);
-});
+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)
+  );
+  selectedTargetedRow.value = {
+    campaignType: parent.campaignType,
+    campaignId: parent.campaignId,
+    adGroupId: row.adGroupId,
+    isSelected: row.isSelected || false, // 同步 isSelected 状态
+  };
+  targetRuleDialogVisible.value = true;
+}
 
-function removeSelectedAd(index) {
-  const removedAd = selectedAds.value.splice(index, 1)[0];
-
-  if (removedAd.parentCampaignId) {
-    // 查找同一父节点下的其他子节点
-    const remainingChildren = selectedAds.value.filter(ad => ad.parentCampaignId === removedAd.parentCampaignId);
-
-    // 如果父节点下还有其他子节点,父节点不删除
-    if (remainingChildren.length > 0) {
-      // 取消表格1中该子节点的勾选
-      const targetParent = adCampaignName.value.find(ad => ad.campaignId === removedAd.parentCampaignId);
-      if (targetParent) {
-        const targetChildIndex = targetParent.campaignGroupInfo.findIndex(group => group.adGroupId === removedAd.adGroupId);
-        if (targetChildIndex !== -1) {
-          const adTable = refTable.value;
-          adTable.toggleRowSelection(targetParent.campaignGroupInfo[targetChildIndex], false);
-        }
-      }
-    } else {
-      // 如果没有其他子节点了,移除父节点
-      const parentIndex = selectedAds.value.findIndex(ad => ad.campaignId === removedAd.parentCampaignId);
-      if (parentIndex !== -1) {
-        selectedAds.value.splice(parentIndex, 1);
-      }
+let selectedGroups = [];
 
-      // 取消表格1中父节点的勾选
-      const parentIndexInTable = adCampaignName.value.findIndex(ad => ad.campaignId === removedAd.parentCampaignId);
-      if (parentIndexInTable !== -1) {
-        const adTable = refTable.value;
-        adTable.toggleRowSelection(adCampaignName.value[parentIndexInTable], false);
-      }
-    }
-  } else {
-    // 如果删除的项是父节点
-    const targetIndex = adCampaignName.value.findIndex(ad => ad.campaignId === removedAd.campaignId);
-    if (targetIndex !== -1) {
-      const adTable = refTable.value;
-      adTable.toggleRowSelection(adCampaignName.value[targetIndex], false);
+function handleConfirm(campaignKeywordInfo) {
+  selectedTargetedRow.value.keywordInfo = campaignKeywordInfo;
+  selectedTargetedRow.value.isSelected = true;
+  KeyWordLength.value = selectedTargetedRow.value.keywordInfo.length;
+  // 更新子节点状态
+  const parent = gridOptions.data.find(ad =>
+      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === selectedTargetedRow.value.adGroupId)
+  );
+
+  if (parent) {
+    const group = parent.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
+    if (group) {
+      group.isSelected = true; // 更新子节点的 isSelected 状态
+      xGridOne.value.toggleCheckboxRow(group);
+      selectedGroups.push(group);
+      console.log("=>(adActivityDialog.vue:322) selectedGroups", selectedGroups);
+      // 将 group 作为 records 数组传递
+      handleSelectionChange({ records: selectedGroups });
     }
   }
 }
 
+const removeSelectedAd = async (row) => {
+  const $grid = xGridTwo.value;
+  if ($grid) {
+    await $grid.remove(row);
+    selectedAds.value = selectedAds.value.filter(ad => ad.campaignId !== row.campaignId);
+  }
+  if (xGridOne.value) {
+    await xGridOne.value.toggleCheckboxRow(row);
+  }
+};
+
 function removeAllSelectedAds() {
   selectedAds.value = [];
-  const adTable = refTable.value;
-  adTable.clearSelection();
+  const $grid = xGridOne.value;
+  if ($grid) {
+    $grid.clearCheckboxRow();
+  }
 }
 
 function cancel() {
@@ -283,51 +326,6 @@ async function fetchAdGroupList() {
   }
 }
 
-function handleSelectTarget(row) {
-  console.log("=>(adActivityDialog.vue:287) row", row);
-  // 获取父节点数据
-  const parent = adCampaignName.value.find(ad =>
-      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
-  );
-  if (parent) {
-    // 构造包含父节点和子节点的数据对象
-    selectedTargetedRow.value = {
-      parentCampaignId: parent.campaignId,
-      parentCampaignName: parent.campaignName,
-      campaignType: parent.campaignType,
-      adGroupId: row.adGroupId,
-      adGroupName: row.adGroupName,
-      isSelected: row.isSelected || false, // 同步 isSelected 状态
-    };
-  }
-
-  targetRuleDialogVisible.value = true;
-}
-
-function handleConfirm(campaignKeywordInfo) {
-  selectedTargetedRow.value.keywordInfo = campaignKeywordInfo;
-  selectedTargetedRow.value.isSelected = true;
-
-  // 更新子节点状态
-  const parent = adCampaignName.value.find(ad =>
-      ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.adGroupId === selectedTargetedRow.value.adGroupId)
-  );
-  if (parent) {
-    const group = parent.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
-    if (group) {
-      group.isSelected = true; // 更新子节点的 isSelected 状态
-    }
-  }
-
-  // 勾选子节点行
-  const table = refTable.value;
-  const targetRow = parent.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
-
-  if (table && targetRow) {
-    table.toggleRowSelection(targetRow, true); // 勾选目标子节点行
-  }
-}
-
 const headerCellStyle = (args) => {
   if (args.rowIndex === 0) {
     return {
@@ -373,18 +371,9 @@ watch(dialogVisible, (newValue) => {
   emits('update:modelValue', newValue);
 });
 
-const treeProps = computed(() => {
-  if (activeModel.value === 'adGroup' || activeModel.value === 'specified') {
-    return {
-      children: 'campaignGroupInfo',
-      checkStrictly: false
-    };
-  }
-  return {};
-});
-
 onMounted(() => {
   fetchAdGroupList();
+  fetchAdCampaign();
 });
 
 </script>
@@ -427,42 +416,31 @@ onMounted(() => {
             ></el-option>
           </el-select>
         </div>
-        <el-table
-            ref="refTable"
-            v-loading="loading"
-            :cell-style="cellStyle"
-            :data="adCampaignName"
-            :header-cell-style="headerCellStyle"
-            :row-key="'adGroupId'"
-            :tree-props="treeProps"
-            height="400"
-            style="width: 100%;"
-            @selection-change="handleSelectionChange"
-        >
-          <el-table-column label="广告活动名称">
-            <template #default="scope">
-              <el-tag
-                  v-if="scope.row.campaignType"
-                  :color="scope.row.campaignType === 'sb' ? '#0163d2' : (scope.row.campaignType === 'sp' ? '#ff7424' : '#365672')"
-                  class="campaign-type"
-                  disable-transitions
-                  round>
-                {{ scope.row.campaignType }}
-              </el-tag>
-              {{ scope.row.campaignName }}
-              {{ scope.row.adGroupName }}
-              <el-button
-                  v-if="scope.row.adGroupName && activeModel==='specified'&&!scope.row.isSelected"
-                  class="btn-link"
-                  link
-                  @click="handleSelectTarget(scope.row)">
-                选择定向
-              </el-button>
-              <span v-else-if="scope.row.adGroupName">已选择</span>
-            </template>
-          </el-table-column>
-          <el-table-column type="selection" width="55"></el-table-column>
-        </el-table>
+        <vxe-grid ref="xGridOne" :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions"
+                  @checkbox-change="handleGridChange" @checkbox-all="handleGridChange">
+          <template #campaignName_default="{ row }">
+            <el-tag
+                v-if="row.campaignType"
+                :color="row.campaignType === 'sb' ? '#0163d2' : (row.campaignType === 'sp' ? '#ff7424' : '#365672')"
+                class="campaign-type"
+                disable-transitions
+                round>
+              {{ row.campaignType }}
+            </el-tag>
+            <span>  {{ row.campaignName }}</span>
+            <span class="flex-container">
+              {{ row.adGroupName }}
+            <el-button
+                v-if="row.adGroupName && activeModel==='specified'&&!row.isSelected"
+                class="btn-link"
+                link
+                @click="handleSelectTarget(row)">
+              选择定向
+            </el-button>
+            <span v-else-if="row.adGroupName">已选择定向</span>
+            </span>
+          </template>
+        </vxe-grid>
         <div class="pagination-container mt-4">
           <el-pagination
               v-model:current-page="currentPage"
@@ -479,45 +457,45 @@ onMounted(() => {
       </div>
       <div class="container-right">
         <h3>已选择({{ selectedAds.length }})</h3>
-        <el-table
-            :cell-style="cellStyle"
-            :data="groupedSelectedAds"
-            :header-cell-style="headerCellStyle"
-            :row-key="'id'"
-            :tree-props="treeProps"
-            height="484"
-            style="width: 100%; margin-top: 20px;"
-            v-bind="activeModel === 'adGroup' || activeModel === 'specified' ? treeProps : {}"
-        >
-          <el-table-column label="广告活动" prop="name">
-            <template #default="scope">
-              <el-tag
-                  v-if="scope.row.campaignType"
-                  :color="scope.row.campaignType === 'sb' ? '#0163d2' : (scope.row.campaignType === 'sp' ? '#ff7424' : '#365672')"
-                  class="campaign-type"
-                  disable-transitions
-                  round>
-                {{ scope.row.campaignType }}
-              </el-tag>
-              {{ scope.row.campaignName }}
-              {{ scope.row.name }}
-            </template>
-          </el-table-column>
-          <el-table-column
-              align="center"
-              label="操作"
-              width="100"
-          >
-            <template #header>
-              <el-button link size="default" style="color: #2077d7" @click="removeAllSelectedAds">删除全部</el-button>
-            </template>
-            <template #default="scope">
-              <el-button type="text" @click="removeSelectedAd(scope.$index)">
-                <CircleClose style="width:16px;color:#4b5765" />
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
+        <vxe-grid ref="xGridTwo"
+                  :cell-style="cellStyle"
+                  :columns="selectedColumns"
+                  :data="selectedAds"
+                  :header-cell-style="headerCellStyle"
+                  :tree-config="treeProps"
+                  border="inner"
+                  height="484">
+          <template #campaignName_default="{ row }">
+            <el-tag
+                v-if="row.campaignType"
+                :color="row.campaignType === 'sb' ? '#0163d2' : (row.campaignType === 'sp' ? '#ff7424' : '#365672')"
+                class="campaign-type"
+                disable-transitions
+                round>
+              {{ row.campaignType }}
+            </el-tag>
+            <span>  {{ row.campaignName }}</span>
+            <span class="flex-container">
+              {{ row.adGroupName }}
+              <el-button
+                  v-if="row.isSelected"
+                  :icon="DocumentAdd"
+                  class="btn-link"
+                  link
+                  @click="handleSelectTarget(row)">
+              共{{ KeyWordLength }}个定向规则
+            </el-button>
+            </span>
+          </template>
+          <template #header_operation>
+            <el-button link size="default" style="color: #2077d7" @click="removeAllSelectedAds">删除全部</el-button>
+          </template>
+          <template #default_operation="{row}">
+            <el-button type="text" @click="removeSelectedAd(row)">
+              <CircleClose style="width:16px;color:#4b5765" />
+            </el-button>
+          </template>
+        </vxe-grid>
       </div>
     </div>
     <template #footer>
@@ -548,14 +526,14 @@ onMounted(() => {
 }
 
 .campaign-type {
-  width: 35px;
-  text-align: center;
-  height: 22px;
-  font-size: 13px;
-  font-weight: 400;
+  /* width: 35px; */
+  /* text-align: center; */
+  /* height: 22px; */
+  /* font-size: 13px; */
+  /* font-weight: 400; */
   color: #fff;
   border-color: #fff;
-  line-height: 22px;
+  /* line-height: 21px; */
   border-radius: 12px;
   margin-right: 4px;
 }
@@ -590,4 +568,9 @@ onMounted(() => {
   /* ling-heigt: 23px; */
 }
 
+.flex-container {
+  display: flex;
+  justify-content: space-between;
+}
+
 </style>

+ 1 - 1
src/views/efTools/automation/components/targetRuleDialog.vue

@@ -54,7 +54,7 @@ async function fetchTargetRuleList() {
     gridOptions.loading = true;
     const resp = await getTargetingRuleList({
       campaignType: selectedTargetedRow.value.campaignType,
-      campaignId: selectedTargetedRow.value.parentCampaignId,
+      campaignId: selectedTargetedRow.value.campaignId,
       adGroupId: selectedTargetedRow.value.adGroupId,
       matchType: matchType.value,
       search: keyWord.value,