Jelajahi Sumber

Merge branch 'test'

xinyan 8 bulan lalu
induk
melakukan
860d6d2c47

+ 2 - 1
src/views/efTools/automation/api.ts

@@ -69,7 +69,7 @@ export function updateAdCampaign(body) {
   });
 }
 
-// 获取定向规则列表
+// 定向规则弹窗:获取定向规则列表
 export function getTargetingRuleList(query) {
   return request({
     url: '/api/ad_manage/template/target_select',
@@ -78,6 +78,7 @@ export function getTargetingRuleList(query) {
   });
 }
 
+// 定向规则弹窗:获取商品列表
 export function getProductsList(query) {
   return request({
     url: '/api/ad_manage/template/target_select/products/',

+ 123 - 25
src/views/efTools/automation/components/adActivityDialog.vue

@@ -5,7 +5,7 @@
  * @Author: xinyan
  */
 import { computed, onMounted, reactive, ref, toRefs, watch } from 'vue';
-import { getAdGroupList, getRelationCampaign } from '/@/views/efTools/automation/api';
+import { getAdGroupList, getRelationCampaign, updateAdCampaign } from '/@/views/efTools/automation/api';
 import { storeToRefs } from 'pinia';
 import { useShopInfo } from '/@/stores/shopInfo';
 import { ElMessage } from 'element-plus';
@@ -26,14 +26,13 @@ const props = defineProps({
     required: true,
   },
 });
-const emits = defineEmits(['update:modelValue']);
+const emits = defineEmits(['update:modelValue', 'confirmSuccess']);
 const shopInfo = useShopInfo();
 const { profile } = storeToRefs(shopInfo);
 const { templateId } = toRefs(props);
-// const { activeModel } = toRefs(props);
-const activeModel = ref('specified');
+const { activeModel } = toRefs(props);
 
-const dialogVisible = ref(true);
+const dialogVisible = ref(false);
 const targetRuleDialogVisible = ref(false);
 
 // 定向规则
@@ -106,6 +105,13 @@ const gridOptions = reactive({
     labelField: 'name',
     reserve: true,
     checkStrictly: false,
+    checkMethod: ({ row }) => {
+      if (activeModel.value === 'specified') {
+        // 如果是 specified 模式,只有已选择的行可以被取消选中
+        return row.isSelected;
+      }
+      return true; // 其他模式下都可以选中
+    }
   },
   columns: computed(() => [
     {
@@ -114,7 +120,12 @@ const gridOptions = reactive({
       slots: { default: 'campaignName_default' },
       treeNode: activeModel.value == 'specified' || activeModel.value == 'adGroup'
     },
-    { type: 'checkbox', width: 55, fixed: 'right' },
+    {
+      type: 'checkbox',
+      width: 55,
+      fixed: 'right',
+      slots: activeModel.value == 'specified' ? { header: 'checkbox_header', checkbox: 'checkbox_cell' } : ''
+    },
   ]),
   data: []
 });
@@ -166,13 +177,49 @@ async function fetchAdCampaign() {
 }
 
 // 处理表格复选框选择变化(是否为树形结构)
-const handleGridChange = (event) => {
-  if (activeModel.value == 'specified' || activeModel.value == 'adGroup') {
-    handleSelectionChange(event);
+// const handleGridChange = (event) => {
+//   if (activeModel.value == 'specified' || activeModel.value == 'adGroup') {
+//     handleSelectionChange(event);
+//   } else {
+//     handelSelect(event);
+//   }
+// };
+
+function handleGridChange({ records, row, checked }) {
+  if (activeModel.value === 'specified') {
+    if (row) {
+      if (!checked) {
+        row.isSelected = false;
+        updateSelectedAds();
+      }
+    } else {
+      // 全选/取消全选
+      records.forEach(record => {
+        if (record.isSelected) {
+          record.isSelected = checked;
+        }
+      });
+      updateSelectedAds();
+    }
+  } else if (activeModel.value === 'adGroup') {
+    handleSelectionChange({ records });
   } else {
-    handelSelect(event);
+    handelSelect({ records });
   }
-};
+}
+
+function toggleCheckboxEvent(row) {
+  if (activeModel.value === 'specified') {
+    if (row.isSelected) {
+      // 只有已选择的行可以被取消选中
+      xGridOne.value.setCheckboxRow(row, false);
+      handleGridChange({ records: [row], row, checked: false });
+    }
+  } else {
+    // 其他模式下正常切换复选框状态
+    xGridOne.value.toggleCheckboxRow(row);
+  }
+}
 
 // 非树形结构的表格选择变化
 function handelSelect({ records }) {
@@ -190,6 +237,7 @@ function updateSelectedAds() {
         campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.isSelected),
         page: currentPage.value
       }));
+  console.log('selectedAds.value', selectedAds.value);
 }
 
 // 树形结构的表格选择变化
@@ -242,7 +290,9 @@ function handleSelectTarget(row) {
     campaignType: parent.campaignType,
     campaignId: parent.campaignId,
     adGroupId: row.adGroupId,
-    isSelected: row.isSelected || false, // 同步 isSelected 状态
+    isSelected: row.isSelected || false,
+    keywordInfo: row.keywordInfo || [],
+    campaignTargetInfo: row.campaignTargetInfo || [],
   };
   targetRuleDialogVisible.value = true;
 }
@@ -320,32 +370,59 @@ function cancel() {
   dialogVisible.value = false;
 }
 
-//TODO: 确认按钮-商品
+//TODO: 确认按钮-adGroupInfo
 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 campaignInfo = selectedTargetedRow.value.keywordInfo.map(keyword => ({
-    keywordId: keyword.keywordId,
-    adGroup_id: keyword.adGroup_id,
-    bid: keyword.bid
-  }));
+  let campaignKeywordInfo = [];
+  let campaignTargetInfo = [];
+
+  selectedAds.value.forEach(campaign => {
+    campaign.campaignGroupInfo.forEach(group => {
+      if (group.keywordInfo && group.keywordInfo.length > 0) {
+        campaignKeywordInfo = campaignKeywordInfo.concat(
+            group.keywordInfo.map(keyword => ({
+              keywordId: keyword.keywordId,
+              adGroup_id: group.adGroupId,
+              bid: keyword.bid
+            }))
+        );
+      }
+      if (group.campaignTargetInfo && group.campaignTargetInfo.length > 0) {
+        campaignTargetInfo = campaignTargetInfo.concat(
+            group.campaignTargetInfo.map(target => ({
+              targetId: target.targetId,
+              adGroup_id: group.adGroupId,
+              bid: target.bid
+            }))
+        );
+      }
+    });
+  });
 
   const requestData = {
     profileId: profile.value.profile_id,
     templateId: templateId.value,
-    campaignItems,
-    adGroupInfo,
+    campaignItems: campaignItems,
+    adGroupInfo: [],
     campaignTargetInfo,
-    campaignInfo
+    campaignKeywordInfo
   };
   console.log('requestData', requestData);
+  try {
+    const response = await updateAdCampaign(requestData);
+    if (response.code === 2000) {
+      dialogVisible.value = false;
+      emits('confirmSuccess');
+      ElMessage({ message: '创建成功', type: 'success', });
+    }
+  } catch (error) {
+    console.error('API error:', error);
+    ElMessage({ message: '创建失败', type: 'error', });
+  }
 }
 
 // 获取广告组下拉框
@@ -479,6 +556,27 @@ onMounted(() => {
             <span v-else-if="row.adGroupName">已选择定向</span>
             </span>
           </template>
+          <template #checkbox_header="{ checked, indeterminate }">
+        <span class="custom-checkbox" @click.stop="toggleAllCheckboxEvent">
+          <i v-if="indeterminate" class="vxe-icon-square-minus-fill" style="color: #0d84ff"></i>
+          <i v-else-if="checked" class="vxe-icon-square-checked-fill" style="color: #0d84ff"></i>
+          <i v-else class="vxe-icon-checkbox-unchecked" style="color: #0d84ff"></i>
+        </span>
+          </template>
+          <template #checkbox_cell="{ row, checked, indeterminate }">
+            <span class="custom-checkbox" @click.stop="toggleCheckboxEvent(row)">
+              <i v-if="indeterminate" class="vxe-icon-square-minus-fill" style="color: #0d84ff"></i>
+              <i v-else-if="checked" class="vxe-icon-square-checked-fill" style="color: #0d84ff"></i>
+              <el-tooltip v-else
+                          class="box-item"
+                          content="请选择定向"
+                          effect="dark"
+                          placement="top"
+              >
+                <i class="vxe-icon-checkbox-unchecked" style="color: #0d84ff"></i>
+              </el-tooltip>
+            </span>
+          </template>
         </vxe-grid>
         <div class="pagination-container mt-4">
           <el-pagination

+ 57 - 16
src/views/efTools/automation/components/targetRuleDialog.vue

@@ -55,6 +55,9 @@ const gridOptions = reactive({
   height: 550,
   showOverflow: true,
   loading: false,
+  checkboxConfig:{
+    checkField: 'isSelected'
+  },
   rowConfig: {
     isHover: true,
     height: 85,
@@ -84,32 +87,61 @@ async function fetchTargetRuleList() {
 
     targetType.value = resp.data.targetType;
 
-    // 动态设置表格列
-    if (resp.data.targetType === 'keyword') {
+    if (targetType.value === 'keyword') {
       gridOptions.columns = keyWordColumn;
       gridOptions.rowConfig.height = 34;
-      gridOptions.data = resp.data.targetData;
-
-    } else if (resp.data.targetType === 'target') {
+      gridOptions.data = resp.data.targetData.map(item => ({
+        ...item,
+        isSelected: selectedTargetedRow.value.keywordInfo.some(keyword => keyword.keywordId === item.keywordId)
+      }));
+    } else if (targetType.value === 'target') {
       gridOptions.rowConfig.height = 85;
       gridOptions.columns = targetColumn;
-      gridOptions.data = resp.data.targetData;
+      gridOptions.data = resp.data.targetData.map(item => ({
+        ...item,
+        isSelected: selectedTargetedRow.value.campaignTargetInfo.some(target => target.targetId === item.targetId)
+      }));
 
-      // 检查是否有 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
+      // 处理 ASIN_SAME_AS 字段
+      const asinSameAsList = gridOptions.data
+          .filter(item => item.expressionDesc && item.expressionDesc.ASIN_SAME_AS)
+          .map(item => item.expressionDesc.ASIN_SAME_AS);
 
       if (asinSameAsList.length > 0) {
         gridOptions.columns = productColumn;
         gridOptions.rowConfig.height = 120;
-        gridOptions.data = await fetchProductsList(asinSameAsList); // 传递 ASIN 列表
+        const products = await fetchProductsList(asinSameAsList);
+
+        // 创建一个映射,用于快速查找原始定向规则数据
+        const targetMap = new Map(
+            gridOptions.data
+                .filter(item => item.expressionDesc && item.expressionDesc.ASIN_SAME_AS)
+                .map(item => [item.expressionDesc.ASIN_SAME_AS, item])
+        );
+
+        gridOptions.data = products.map(product => {
+          const originalTarget = targetMap.get(product.asin);
+          return {
+            ...product,
+            targetId: originalTarget?.targetId,
+            adGroup_id: originalTarget?.adGroup_id,
+            bid: originalTarget?.bid,
+            isSelected: selectedTargetedRow.value.campaignTargetInfo.some(
+                target => target.targetId === originalTarget?.targetId
+            )
+          };
+        });
       }
     }
+
+    // 预选已选择的定向规则
+    campaignInfo.value = gridOptions.data.filter(item => item.isSelected);
+
     gridOptions.loading = false;
   } catch (error) {
+    console.error('Error fetching target rule list:', error);
     ElMessage.error('请求定向数据失败');
-    gridOptions.loading = false; // 确保在错误情况下也关闭 loading 状态
+    gridOptions.loading = false;
   }
 }
 
@@ -126,8 +158,15 @@ async function fetchProductsList(asinList) {
   }
 }
 
-function handleCheckChange({ records }) {
-  campaignInfo.value = records;
+function handleCheckChange({ records, row, checked }) {
+  if (row) {
+    // 单个选择/取消选择
+    row.isSelected = checked;
+  } else {
+    // 全选/取消全选
+    records.forEach(record => record.isSelected = checked);
+  }
+  campaignInfo.value = gridOptions.data.filter(item => item.isSelected);
 }
 
 function getMatchTypeLabel(type: string) {
@@ -142,11 +181,13 @@ function cancel() {
   targetRuleDialogVisible.value = false;
 }
 
-async function confirm() {
+function confirm() {
   targetRuleDialogVisible.value = false;
   emits('confirm:targetRule', {
     campaignInfo: campaignInfo.value,
-    targetType: targetType.value, // 将 targetType 作为参数传递给父组件
+    targetType: targetType.value,
+    adGroupId: selectedTargetedRow.value.adGroupId,  // 添加这行
+    campaignId: selectedTargetedRow.value.campaignId  // 添加这行
   });
 }
 

+ 1 - 1
src/views/efTools/automation/index.vue

@@ -290,7 +290,7 @@ onMounted(() => {
           @refresh="refreshTable"></component>
     </div>
   </el-drawer>
-  <AdActivityDialog v-model="isDialogVisible" :activeModel="activeModel" :templateId="templateId" @confirm="handleConfirm"/>
+  <AdActivityDialog v-model="isDialogVisible" :activeModel="activeModel" :templateId="templateId" @confirmSuccess="getList"/>
   <AutomatedRuleTips v-model="autoInfo"></AutomatedRuleTips>
 </template>
 

+ 19 - 5
src/views/reportManage/TaskManage/index.vue

@@ -196,11 +196,12 @@ const handleEditActived = ({ row, column }) => {
 };
 
 const handleEditClosed = ({ row, column }) => {
+  // const $grid = xGrid.value;
   if (column.property === 'user_name') {
     // 将 user 转换为 user_name 并更新 row
     row.user_name = userToUserName(row.user);
     // 强制刷新视图
-    $grid.refreshRow(row);
+    // $grid.refreshRow(row);
   }
 };
 
@@ -899,9 +900,22 @@ onMounted(() => {
           />
         </template>
         <template #operation_edit="{ row }">
-          <vxe-select v-model="row.user" multiple>
-            <vxe-option v-for="item in operationList" :key="item.value" :label="item.label"
-                        :value="item.value"></vxe-option>
+          <!--<vxe-select v-model="row.user" multiple>-->
+          <!--  <vxe-option v-for="item in operationList" :key="item.value" :label="item.label"-->
+          <!--              :value="item.value"></vxe-option>-->
+          <!--</vxe-select>-->
+          <vxe-select
+              v-model="row.user"
+              multiple
+              filterable
+              clearable
+          >
+            <vxe-option
+                v-for="item in operationList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            ></vxe-option>
           </vxe-select>
         </template>
         <template #operater_name_edit="{ row }">
@@ -1039,7 +1053,7 @@ onMounted(() => {
       </el-radio-group>
     </div>
     <el-form-item align-center label="录入人员:" prop="operation" width="500">
-      <el-select v-model="taskRuleForm.operation" collapse-tags collapse-tags-tooltip multiple
+      <el-select v-model="taskRuleForm.operation" collapse-tags collapse-tags-tooltip multiple filterable clearable
                  placeholder="请选择录入人员">
         <el-option v-for="item in operationList" :key="item.value" :label="item.label"
                    :value="item.value"></el-option>

+ 1 - 1
src/views/reportManage/TaskManage/utils/columns.ts

@@ -43,7 +43,7 @@ export const taskColumns = ref([
       //default: 'operation_default'
     },
     align: 'center',
-    minWidth: 104
+    minWidth: 120
   },
   {
     field: 'operater',