浏览代码

Merge branch 'xinyan' into test

# Conflicts:
#	src/views/adManage/sp/campaigns/campaignDetail/index.vue
xinyan 7 月之前
父节点
当前提交
de435a9498
共有 25 个文件被更改,包括 1078 次插入555 次删除
  1. 1 1
      src/components/TimerBidTable/index.vue
  2. 4 2
      src/components/conditionBuilder/condition-group2.vue
  3. 1 1
      src/components/input-float/index.vue
  4. 3 1
      src/theme/app.scss
  5. 74 63
      src/views/adManage/sb/campaigns/campaignDetail/automation/index.vue
  6. 49 32
      src/views/adManage/sb/campaigns/campaignDetail/index.vue
  7. 160 0
      src/views/adManage/sd/campaigns/campaignDetail/automation/index.vue
  8. 53 34
      src/views/adManage/sd/campaigns/campaignDetail/index.vue
  9. 3 3
      src/views/adManage/sp/campaigns/campaignDetail/automation/index.vue
  10. 34 34
      src/views/adManage/sp/campaigns/campaignDetail/index.vue
  11. 251 236
      src/views/adManage/utils/enum.ts
  12. 15 0
      src/views/adManage/utils/tools.ts
  13. 2 2
      src/views/components/auto/auto-campaigns/switch-campaign.vue
  14. 3 5
      src/views/components/auto/auto-campaigns/target-rule.vue
  15. 33 33
      src/views/components/auto/auto-campaigns/view-target-rules.vue
  16. 1 1
      src/views/components/auto/auto-templates/timer-bid.vue
  17. 46 11
      src/views/components/auto/search-term-bid.vue
  18. 169 59
      src/views/components/auto/target-rule-setting.vue
  19. 10 0
      src/views/efTools/automation/api.ts
  20. 161 33
      src/views/efTools/automation/components/adActivityDialog.vue
  21. 1 1
      src/views/efTools/automation/components/targetRuleDialog.vue
  22. 1 0
      src/views/reportManage/TaskManage/index.vue
  23. 2 2
      src/views/reportManage/TaskManage/utils/columns.ts
  24. 0 1
      src/views/reportManage/dataCenter/normalDisplay/components/DateTendency/index.vue
  25. 1 0
      src/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue

+ 1 - 1
src/components/TimerBidTable/index.vue

@@ -223,7 +223,7 @@ watch(
         for (let i = 0; i < 7; i++) {
           const tmp = [];
           for (let j = 0; j < 24; j++) {
-            tmp.push(0);
+            tmp.push(1);
           }
           props.data.push(tmp);
         }

+ 4 - 2
src/components/conditionBuilder/condition-group2.vue

@@ -19,6 +19,7 @@
           :prop="getFormProp(conditionInfo, index)"
           :rules="{ validator: checkRight, trigger: getFormTrigger(conditionInfo) }">
           <template #label>
+            <span class="required-star" v-if="index === 0" style="color:#f56c6c; margin-right: 4px;">*</span>
             <span v-if="index === 0">条件:</span>
             <span class="and-span" v-else>&</span>
           </template>
@@ -81,14 +82,15 @@
         添加条件
       </el-button>
     </div>
-    <el-button type="danger" @click="deleteConditionGroup" :icon="Delete" v-show="showDelGroupBtn">删除组</el-button>
+    <el-button type="danger" @click="deleteConditionGroup" :icon="RemoveFilled" link
+               size="large" v-show="showDelGroupBtn"></el-button>
   </div>
   <el-divider border-style="dashed" class="condition-group-divider"> 或 </el-divider>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, computed, watch } from 'vue'
-import { Delete, Plus } from '@element-plus/icons-vue'
+import { Delete, Plus, RemoveFilled } from '@element-plus/icons-vue';
 import InputFloat from '/@/components/input-float/index.vue'
 import TagsInput from '/@/components/tags-input/index.vue'
 import RangeFloat from '/@/components/range-float/index.vue'

+ 1 - 1
src/components/input-float/index.vue

@@ -14,7 +14,7 @@ const props = defineProps({
   },
   precision: {
     type: Number,
-    default: 2,
+    // default: 2,
   },
   prefix: {
     type: String,

+ 3 - 1
src/theme/app.scss

@@ -432,10 +432,12 @@ body,
   flex-direction: row;
   gap: 30px;
   .label {
-    color: #999;
+    color:#999;
+    font-size: 13px;
   }
   .value {
     color: #333;
+    font-size: 13px;
   }
 }
 

+ 74 - 63
src/views/adManage/sb/campaigns/campaignDetail/automation/index.vue

@@ -17,34 +17,14 @@ const props = defineProps<Props>();
 const campaignType = 'sb';
 const activeTab = ref(1);
 const RuleStatusButton = ref({
-  '1': '-',
-  '2': '-',
-  '3': '-',
-  '4': '-',
-  '5': '-',
-  '6': '-',
-});
-// const height = ref(800)
-// const campaignRuleStatus = ref({
-//   campaignId: props.campaignId,
-//   campaignType: campaignType,
-//   profileId: props.profileId,
-//   ruleType: activeTab.value,
-//   templateName: '',
-//   useTmpl: false,
-//   template: null,
-//   rule: {
-//     type: activeTab.value,
-//     campaignType: campaignType,
-//     campaignAd: [],
-//     action: {},
-//     activeModel: '',
-//     setTime: '',
-//     weekdays: [],
-//     conditions: [],
-//   },
-//   RuleStatusButton: RuleStatusButton,
-// })
+  '1': 0,
+  '2': 0,
+  '3': 0,
+  '4': 0,
+  '5': 0,
+  '6': 0,
+})
+
 const baseData = ref({
   campaignId: props.campaignId,
   campaignType: campaignType,
@@ -52,22 +32,33 @@ const baseData = ref({
   ruleType: activeTab,
 });
 
-onMounted(async () => {
-  await initData();
-});
-
-async function initData() {
-  const resp = await api.getCampaignRuleInfo(baseData.value);
-  const info = resp.data[0];
-  if (info) {
-    for (const i of ['1', '2', '3', '4', '5', '6']) {
-      RuleStatusButton.value[i] = info.RuleStatusButton[i];
+async function changeStatus(newStatus: number, tabIndex: string) {
+  const body = {
+    ...baseData.value,
+    ruleType: Number(tabIndex),
+    status: newStatus
+  };
+  try {
+    const response = await postCampaignStatus(body);
+    if (response.code === 2000) {
+      ElMessage.success('更新状态成功');
+      // const resp = await getCampaignRuleInfo(baseData.value);
+      // RuleStatusButton.value = resp.data[0].RuleStatusButton;
+    } else if (response.code == 5000) {
+      ElMessage.warning(`${ response.data.description }`);
+    } else {
+      ElMessage.error('状态更新失败');
     }
+  } catch (error) {
+    console.log('error:', error);
   }
 }
-
-async function changeTab() {
-  // await initData()
+function updateRuleStatusButton(newValue) {
+  if (newValue !== undefined) {
+    RuleStatusButton.value = { ...RuleStatusButton.value, ...newValue }
+  } else {
+    console.error('接收到的 newValue 是 undefined')
+  }
 }
 
 async function refresh(tab: number) {
@@ -76,64 +67,84 @@ async function refresh(tab: number) {
     activeTab.value = tab;
   });
 }
+
+onMounted(async () => {
+  // await initData();
+});
 </script>
 
 <template>
-  <el-tabs tab-position="left" v-model="activeTab" @tab-change="changeTab">
-    <el-tab-pane label="分时调价" :name="1">
+  <el-tabs v-model="activeTab" tab-position="left" @tab-change="changeTab">
+    <el-tab-pane :name="1" label="分时调价">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['1']" :disabled="RuleStatusButton['1'] === '-'"></el-switch>
+          <el-switch v-model="RuleStatusButton['1']" :active-value="1" :disabled="RuleStatusButton['1'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '1')" @click.stop="">
+          </el-switch>
           <span>分时调价</span>
         </div>
       </template>
-      <TimerBid :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(1)" v-if="activeTab === 1" />
+      <TimerBid v-if="activeTab === 1" :RuleStatusButton="RuleStatusButton" :data="baseData" @refresh="refresh(1)"  @updateRuleStatusButton="updateRuleStatusButton"/>
     </el-tab-pane>
-    <el-tab-pane label="分时预算" :name="2">
+    <el-tab-pane :name="2" label="分时预算">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['2']" :disabled="RuleStatusButton['2'] === '-'"></el-switch>
+          <el-switch v-model="RuleStatusButton['2']" :active-value="1" :disabled="RuleStatusButton['2'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '2')" @click.stop="">
+          </el-switch>
           <span>分时预算</span>
         </div>
       </template>
-      <TimerBudget :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(2)" v-if="activeTab === 2" />
+      <TimerBudget v-if="activeTab === 2" :RuleStatusButton="RuleStatusButton" :data="baseData" @refresh="refresh(2)" />
     </el-tab-pane>
-    <el-tab-pane label="广告活动" :name="3">
+    <el-tab-pane :name="3" label="开启/暂停广告活动">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['3']" :disabled="RuleStatusButton['3'] === '-'"></el-switch>
-          <span>广告活动</span>
+          <el-switch v-model="RuleStatusButton['3']" :active-value="1" :disabled="RuleStatusButton['3'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '3')" @click.stop="">
+          </el-switch>
+          <span>开启/暂停广告活动</span>
         </div>
       </template>
-      <SwitchCampaign :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(3)" v-if="activeTab === 3" />
+      <SwitchCampaign v-if="activeTab === 3" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                      @refresh="refresh(3)" />
     </el-tab-pane>
-    <el-tab-pane label="定向规则" :name="4">
+    <el-tab-pane :name="4" label="定向规则">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['4']" :disabled="RuleStatusButton['4'] === '-'"></el-switch>
+          <el-switch v-model="RuleStatusButton['4']" :active-value="1" :disabled="RuleStatusButton['4'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '4')" @click.stop="">
+          </el-switch>
           <span>定向规则</span>
         </div>
       </template>
-      <TargetRule :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(4)" v-if="activeTab === 4"></TargetRule>
+      <TargetRule v-if="activeTab === 4" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                  @refresh="refresh(4)"></TargetRule>
     </el-tab-pane>
-    <el-tab-pane label="添加搜索词" :name="5">
+    <el-tab-pane :name="5" label="添加搜索词">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['5']" :disabled="RuleStatusButton['5'] === '-'"></el-switch>
+          <el-switch v-model="RuleStatusButton['5']" :active-value="1" :disabled="RuleStatusButton['5'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '5')" @click.stop="">
+          </el-switch>
           <span>添加搜索词</span>
         </div>
       </template>
-      <SearchTermRule :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(5)" v-if="activeTab === 5">
+      <SearchTermRule v-if="activeTab === 5" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                      @refresh="refresh(5)">
       </SearchTermRule>
     </el-tab-pane>
-    <el-tab-pane label="添加否定词" :name="6">
+    <el-tab-pane :name="6" label="添加否定词">
       <template #label>
         <div class="tab-label">
-          <el-switch size="small" v-model="RuleStatusButton['6']" :disabled="RuleStatusButton['6'] === '-'"></el-switch>
+          <el-switch v-model="RuleStatusButton['6']" :active-value="1" :disabled="RuleStatusButton['6'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '6')" @click.stop="">
+          </el-switch>
           <span>添加否定词</span>
         </div>
       </template>
-      <NegKeywordRule :data="baseData" :RuleStatusButton="RuleStatusButton" @refresh="refresh(6)" v-if="activeTab === 6">
+      <NegKeywordRule v-if="activeTab === 6" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                      @refresh="refresh(6)">
       </NegKeywordRule>
     </el-tab-pane>
   </el-tabs>
@@ -144,6 +155,6 @@ async function refresh(tab: number) {
   display: flex;
   justify-content: space-between;
   align-items: center;
-  width: 120px;
+  width: 155px;
 }
-</style>
+</style>

+ 49 - 32
src/views/adManage/sb/campaigns/campaignDetail/index.vue

@@ -5,61 +5,77 @@
         <span> {{ campaignInfo.campaignName }}</span>
       </span>
       <div class="asj-detail-info">
-        <span style="color: rgb(177, 177, 177)">状态: </span>
-        <span>
-          <el-button class="no-hover-effect" type="success" round plain size="small" style="margin-top: -3px">{{ dynStatusEnum[campaignInfo.state] }}</el-button>
-        </span><!--({{ campaignInfo.servingStatus }})-->
-        <span class="head-span">预算: </span> <span>{{ profile.currency_symbol + campaignInfo.budget }} | {{ campaignInfo.budgetType }}</span>
+        <div>
+          <span class="label">状态: </span>
+          <el-tag :type="getTagType(campaignInfo.state)">
+            {{ dynStatusEnum[campaignInfo.state] }}
+          </el-tag>
+        </div>
+        <div>
+          <span class="label">预算: </span>
+          <span class="value">{{ profile.currency_symbol + campaignInfo.budget }} | {{ dynStatusEnum[campaignInfo.budgetType] }}</span>
+        </div>
         <!-- <span>投放类型:{{ campaignInfo.targetingType }}</span> -->
-        <span class="head-span">投放日期: </span> <span>{{ campaignInfo.startDate }} ~ {{ campaignInfo.endDate ?? '无结束日期' }}</span>
-        <span class="head-span">广告组合: </span> <span>{{ campaignInfo?.portfolioName }}</span>
-        <span class="head-span">竞价: </span> <span>{{ getEnumLabel(dynBidOptimizationEnum, campaignInfo.bidOptimization) }}</span>
+        <div>
+          <span class="label">投放日期: </span>
+          <span class="value">{{ campaignInfo.startDate }} ~ {{ campaignInfo.endDate ?? '无结束日期' }}</span>
+        </div>
+        <div>
+          <span class="label">广告组合: </span>
+          <span class="value">{{ campaignInfo?.portfolioName }}</span>
+        </div>
+        <div>
+          <span class="label">竞价: </span>
+          <span class="value">{{ getEnumLabel(dynBidOptimizationEnum, campaignInfo.bidOptimization) }}</span>
+        </div>
       </div>
     </div>
-    <el-tabs type="border-card" class="asj-detail-tabs" v-model="tabActiveName">
+    <el-tabs v-model="tabActiveName" class="asj-detail-tabs" type="border-card">
       <el-tab-pane label="广告组" name="adGroup">
-        <AdGroups :campaignId="route.query.campaignId" v-if="tabActiveName==='adGroup'"></AdGroups>
+        <AdGroups v-if="tabActiveName==='adGroup'" :campaignId="route.query.campaignId"></AdGroups>
       </el-tab-pane>
       <el-tab-pane label="预算" name="budget">
-        <Budget :campaignId="route.query.campaignId" v-if="tabActiveName==='budget'"></Budget>
+        <Budget v-if="tabActiveName==='budget'" :campaignId="route.query.campaignId"></Budget>
       </el-tab-pane>
       <el-tab-pane label="自动化" name="automation">
-        <Automation :campaignId="campaignId" :profileId="profile.profile_id" v-if="tabActiveName==='automation'"></Automation>
+        <Automation v-if="tabActiveName==='automation'" :campaignId="campaignId"
+                    :profileId="profile.profile_id"></Automation>
       </el-tab-pane>
       <el-tab-pane label="广告位" name="placement">
-        <Placement :campaignId="route.query.campaignId" v-if="tabActiveName==='placement'"/>
+        <Placement v-if="tabActiveName==='placement'" :campaignId="route.query.campaignId" />
       </el-tab-pane>
     </el-tabs>
   </div>
 </template>
 
 <script lang="ts" setup>
-import {onMounted, ref, Ref} from 'vue'
-import {useRoute} from 'vue-router'
-import AdGroups from './adGroups/index.vue'
-import Placement from './placement/index.vue'
-import Budget from './budget/index.vue'
-import {getEnumLabel} from '/@/views/adManage/utils/tools.js'
-import {dynBidOptimizationEnum, dynStatusEnum} from '/@/views/adManage/utils/enum.js'
-import {useShopInfo} from '/@/stores/shopInfo'
+import { onMounted, ref, Ref } from 'vue';
+import { useRoute } from 'vue-router';
+import AdGroups from './adGroups/index.vue';
+import Placement from './placement/index.vue';
+import Budget from './budget/index.vue';
+import { getEnumLabel, getTagType } from '/@/views/adManage/utils/tools.js';
+import { dynBidOptimizationEnum, dynStatusColorEnum, dynStatusEnum } from '/@/views/adManage/utils/enum.js';
+import { useShopInfo } from '/@/stores/shopInfo';
 // import { usePublicData } from '/@/stores/publicData'
-import {storeToRefs} from 'pinia'
+import { storeToRefs } from 'pinia';
 
-import {GetObj} from './api'
+import { GetObj } from './api';
 import Automation from '/@/views/adManage/sb/campaigns/campaignDetail/automation/index.vue';
 
-const shopInfo = useShopInfo()
-const { profile } = storeToRefs(shopInfo)
-const route = useRoute()
-const campaignInfo: Ref<SpCampaign> = ref({})
-const tabActiveName = ref('adGroup')
 
-const campaignId = ref(route.query.campaignId)
+const shopInfo = useShopInfo();
+const { profile } = storeToRefs(shopInfo);
+const route = useRoute();
+const campaignInfo: Ref<SpCampaign> = ref({});
+const tabActiveName = ref('adGroup');
+
+const campaignId = ref(route.query.campaignId);
 
 onMounted(async () => {
-  const resp = await GetObj(route.query.campaignId)
-  campaignInfo.value = resp.data
-})
+  const resp = await GetObj(route.query.campaignId);
+  campaignInfo.value = resp.data;
+});
 
 </script>
 
@@ -69,6 +85,7 @@ onMounted(async () => {
   color: rgb(177, 177, 177);
   margin-left: 40px;
 }
+
 :deep(.el-tabs--border-card) {
   border: none;
 }

+ 160 - 0
src/views/adManage/sd/campaigns/campaignDetail/automation/index.vue

@@ -0,0 +1,160 @@
+<script lang="ts" setup>
+import { nextTick, onMounted, ref } from 'vue';
+import TimerBid from '/@/views/components/auto/auto-campaigns/timer-bid.vue';
+import TimerBudget from '/@/views/components/auto/auto-campaigns/timer-budget.vue';
+import SwitchCampaign from '/@/views/components/auto/auto-campaigns/switch-campaign.vue';
+import TargetRule from '/@/views/components/auto/auto-campaigns/target-rule.vue';
+import SearchTermRule from '/@/views/components/auto/auto-campaigns/search-term.vue';
+import NegKeywordRule from '/@/views/components/auto/auto-campaigns/neg-keyword.vue';
+// import * as api from './api';
+
+interface Props {
+  campaignId: string;
+  profileId: string;
+}
+
+const props = defineProps<Props>();
+const campaignType = 'sd';
+const activeTab = ref(1);
+const RuleStatusButton = ref({
+  '1': 0,
+  '2': 0,
+  '3': 0,
+  '4': 0,
+  '5': 0,
+  '6': 0,
+})
+
+const baseData = ref({
+  campaignId: props.campaignId,
+  campaignType: campaignType,
+  profileId: props.profileId,
+  ruleType: activeTab,
+});
+
+async function changeStatus(newStatus: number, tabIndex: string) {
+  const body = {
+    ...baseData.value,
+    ruleType: Number(tabIndex),
+    status: newStatus
+  };
+  try {
+    const response = await postCampaignStatus(body);
+    if (response.code === 2000) {
+      ElMessage.success('更新状态成功');
+      // const resp = await getCampaignRuleInfo(baseData.value);
+      // RuleStatusButton.value = resp.data[0].RuleStatusButton;
+    } else if (response.code == 5000) {
+      ElMessage.warning(`${ response.data.description }`);
+    } else {
+      ElMessage.error('状态更新失败');
+    }
+  } catch (error) {
+    console.log('error:', error);
+  }
+}
+function updateRuleStatusButton(newValue) {
+  if (newValue !== undefined) {
+    RuleStatusButton.value = { ...RuleStatusButton.value, ...newValue }
+  } else {
+    console.error('接收到的 newValue 是 undefined')
+  }
+}
+
+async function refresh(tab: number) {
+  activeTab.value = 0;
+  nextTick(() => {
+    activeTab.value = tab;
+  });
+}
+
+onMounted(async () => {
+  // await initData();
+});
+</script>
+
+<template>
+  <el-tabs v-model="activeTab" tab-position="left" @tab-change="changeTab">
+    <el-tab-pane :name="1" label="分时调价">
+      <template #label>
+        <div class="tab-label">
+          <el-switch v-model="RuleStatusButton['1']" :active-value="1" :disabled="RuleStatusButton['1'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '1')" @click.stop="">
+          </el-switch>
+          <span>分时调价</span>
+        </div>
+      </template>
+      <TimerBid v-if="activeTab === 1" :RuleStatusButton="RuleStatusButton" :data="baseData" @refresh="refresh(1)"  @updateRuleStatusButton="updateRuleStatusButton"/>
+    </el-tab-pane>
+    <el-tab-pane :name="2" label="分时预算" v-if="false">
+    <!--  <template #label>-->
+    <!--    <div class="tab-label">-->
+    <!--      <el-switch v-model="RuleStatusButton['2']" :active-value="1" :disabled="RuleStatusButton['2'] === '-'"-->
+    <!--                 :inactive-value="0" size="small" @change="(val) => changeStatus(val, '2')" @click.stop="">-->
+    <!--      </el-switch>-->
+    <!--      <span>分时预算</span>-->
+    <!--    </div>-->
+    <!--  </template>-->
+    <!--  <TimerBudget v-if="activeTab === 2" :RuleStatusButton="RuleStatusButton" :data="baseData" @refresh="refresh(2)" />-->
+    </el-tab-pane>
+    <el-tab-pane :name="3" label="开启/暂停广告活动">
+      <template #label>
+        <div class="tab-label">
+          <el-switch v-model="RuleStatusButton['3']" :active-value="1" :disabled="RuleStatusButton['3'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '3')" @click.stop="">
+          </el-switch>
+          <span>开启/暂停广告活动</span>
+        </div>
+      </template>
+      <SwitchCampaign v-if="activeTab === 3" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                      @refresh="refresh(3)" />
+    </el-tab-pane>
+    <el-tab-pane :name="4" label="定向规则">
+      <template #label>
+        <div class="tab-label">
+          <el-switch v-model="RuleStatusButton['4']" :active-value="1" :disabled="RuleStatusButton['4'] === '-'"
+                     :inactive-value="0" size="small" @change="(val) => changeStatus(val, '4')" @click.stop="">
+          </el-switch>
+          <span>定向规则</span>
+        </div>
+      </template>
+      <TargetRule v-if="activeTab === 4" :RuleStatusButton="RuleStatusButton" :data="baseData"
+                  @refresh="refresh(4)"></TargetRule>
+    </el-tab-pane>
+    <el-tab-pane :name="5" label="添加搜索词" v-if="false">
+      <!--<template #label>-->
+      <!--  <div class="tab-label">-->
+      <!--    <el-switch v-model="RuleStatusButton['5']" :active-value="1" :disabled="RuleStatusButton['5'] === '-'"-->
+      <!--               :inactive-value="0" size="small" @change="(val) => changeStatus(val, '5')" @click.stop="">-->
+      <!--    </el-switch>-->
+      <!--    <span>添加搜索词</span>-->
+      <!--  </div>-->
+      <!--</template>-->
+      <!--<SearchTermRule v-if="activeTab === 5" :RuleStatusButton="RuleStatusButton" :data="baseData"-->
+      <!--                @refresh="refresh(5)">-->
+      <!--</SearchTermRule>-->
+    </el-tab-pane>
+    <el-tab-pane :name="6" label="添加否定词" v-if="false">
+      <!--<template #label>-->
+      <!--  <div class="tab-label">-->
+      <!--    <el-switch v-model="RuleStatusButton['6']" :active-value="1" :disabled="RuleStatusButton['6'] === '-'"-->
+      <!--               :inactive-value="0" size="small" @change="(val) => changeStatus(val, '6')" @click.stop="">-->
+      <!--    </el-switch>-->
+      <!--    <span>添加否定词</span>-->
+      <!--  </div>-->
+      <!--</template>-->
+      <!--<NegKeywordRule v-if="activeTab === 6" :RuleStatusButton="RuleStatusButton" :data="baseData"-->
+      <!--                @refresh="refresh(6)">-->
+      <!--</NegKeywordRule>-->
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<style lang="scss" scoped>
+.tab-label {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 155px;
+}
+</style>

+ 53 - 34
src/views/adManage/sd/campaigns/campaignDetail/index.vue

@@ -5,29 +5,44 @@
         <span> {{ campaignInfo.campaignName }}</span>
       </span>
       <div class="asj-detail-info">
-        <span style="color: rgb(177, 177, 177)">状态: </span>
-        <span v-if="campaignInfo.state==='enabled'">
-          <el-button class="no-hover-effect" type="success" round plain size="small" style="margin-top: -3px">{{( dynStatusEnum[campaignInfo.state] )}}</el-button>
-        </span><!--({{ campaignInfo.servingStatus }})-->
-        <span v-else>
-          <el-button class="no-hover-effect-ban" type="danger" round plain size="small" style="margin-top: -3px">{{( dynStatusEnum[campaignInfo.state] )}}</el-button>
-        </span>
-        <span class="head-span">预算: </span> <span>{{ profile.currency_symbol + campaignInfo.budget }} | {{ dynStatusEnum[campaignInfo.budgetType] }}</span>
-        <span class="head-span">投放类型:</span> <span>{{ dynStatusEnum[campaignInfo.tactic] }}</span>
-        <span class="head-span">投放日期: </span> <span>{{ campaignInfo.startDate }} ~ {{ campaignInfo.endDate ?? '无结束日期' }}</span>
-        <span class="head-span">广告组合: </span> <span>{{ campaignInfo?.portfolio_name ? campaignInfo.portfolio_name : '--'}}</span>
-        <span class="head-span">成本类型: </span> <span>{{ campaignInfo.costType }}</span>
+        <div>
+          <span class="label">状态: </span>
+          <el-tag :type="getTagType(campaignInfo.state)">
+            {{ dynStatusEnum[campaignInfo.state] }}
+          </el-tag>
+        </div>
+        <div>
+          <span class="label">预算: </span>
+          <span class="value"> {{ profile.currency_symbol + campaignInfo.budget }} | {{dynStatusEnum[campaignInfo.budgetType] }}</span>
+        </div>
+        <div>
+          <span class="label">投放类型:</span>
+          <span class="value">{{ targetTypeEnum[campaignInfo.tactic] }}</span>
+        </div>
+        <div>
+          <span class="label">投放日期: </span>
+          <span class="value">{{ campaignInfo.startDate }} ~ {{ campaignInfo.endDate ?? '无结束日期' }}</span>
+        </div>
+        <div>
+          <span class="label">广告组合: </span>
+          <span class="value">{{ campaignInfo?.portfolio_name ? campaignInfo.portfolio_name : '--' }}</span>
+        </div>
+        <div>
+          <span class="label">成本类型: </span>
+          <span class="value">{{ campaignInfo.costType }}</span>
+        </div>
       </div>
     </div>
-    <el-tabs type="border-card" class="asj-detail-tabs" v-model="tabActiveName">
+    <el-tabs v-model="tabActiveName" class="asj-detail-tabs" type="border-card">
       <el-tab-pane label="广告组" name="adGroup">
-        <AdGroups :campaignId="route.query.campaignId" v-if="tabActiveName==='adGroup'"></AdGroups>
+        <AdGroups v-if="tabActiveName==='adGroup'" :campaignId="route.query.campaignId"></AdGroups>
       </el-tab-pane>
       <!-- <el-tab-pane label="预算" name="budget">
         <Budget :campaignId="route.query.campaignId" v-if="tabActiveName==='budget'"></Budget>
       </el-tab-pane> -->
       <el-tab-pane label="自动化" name="automation">
-        自动化
+        <Automation v-if="tabActiveName==='automation'" :campaignId="campaignId"
+                    :profileId="profile.profile_id"></Automation>
       </el-tab-pane>
       <!-- <el-tab-pane label="广告位" name="placement">
         <Placement :campaignId="route.query.campaignId" v-if="tabActiveName==='placement'"/>
@@ -37,29 +52,30 @@
 </template>
 
 <script lang="ts" setup>
-import {onMounted, ref, Ref} from 'vue'
-import {useRoute} from 'vue-router'
-import AdGroups from './adGroups/index.vue'
-import Placement from './placement/index.vue'
-import Budget from './budget/index.vue'
-import {getEnumLabel} from '/@/views/adManage/utils/tools.js'
-import {dynBidOptimizationEnum, dynStatusEnum} from '/@/views/adManage/utils/enum.js'
-import {useShopInfo} from '/@/stores/shopInfo'
+import { onMounted, ref, Ref } from 'vue';
+import { useRoute } from 'vue-router';
+import AdGroups from './adGroups/index.vue';
+import { getTagType } from '/@/views/adManage/utils/tools.js';
+import { dynStatusEnum, targetTypeEnum } from '/@/views/adManage/utils/enum.js';
+import { useShopInfo } from '/@/stores/shopInfo';
 // import { usePublicData } from '/@/stores/publicData'
-import {storeToRefs} from 'pinia'
+import { storeToRefs } from 'pinia';
 
-import {GetObj} from './api'
-const x = 't20'
-const shopInfo = useShopInfo()
-const { profile } = storeToRefs(shopInfo)
-const route = useRoute()
-const campaignInfo: Ref<SpCampaign> = ref({})
-const tabActiveName = ref('adGroup')
+import { GetObj } from './api';
+import Automation from '/@/views/adManage/sd/campaigns/campaignDetail/automation/index.vue';
+
+
+const x = 't20';
+const shopInfo = useShopInfo();
+const { profile } = storeToRefs(shopInfo);
+const route = useRoute();
+const campaignInfo: Ref<SpCampaign> = ref({});
+const tabActiveName = ref('adGroup');
 
 onMounted(async () => {
-  const resp = await GetObj(route.query.campaignId)
-  campaignInfo.value = resp.data[0]
-})
+  const resp = await GetObj(route.query.campaignId);
+  campaignInfo.value = resp.data[0];
+});
 
 </script>
 
@@ -69,12 +85,15 @@ onMounted(async () => {
   color: rgb(177, 177, 177);
   margin-left: 40px;
 }
+
 :deep(.el-tabs--border-card) {
   border: none;
 }
+
 ::v-deep(.el-table .el-table__header-wrapper .cell) {
   border-right: 1px solid rgb(218, 221, 223);
 }
+
 ::v-deep(.el-table__header-wrapper .el-table__header tr th) {
   border-right: none;
 }

+ 3 - 3
src/views/adManage/sp/campaigns/campaignDetail/automation/index.vue

@@ -20,6 +20,7 @@ import { userFormData } from '/@/views/components/auto/auto-campaigns/common';
 interface Props {
   campaignId: string;
   profileId: string;
+  targetingType: string;
 }
 
 const props = defineProps<Props>();
@@ -53,7 +54,6 @@ async function changeStatus(newStatus: number, tabIndex: string) {
       ElMessage.success('更新状态成功');
       // const resp = await getCampaignRuleInfo(baseData.value);
       // RuleStatusButton.value = resp.data[0].RuleStatusButton;
-      console.log("=>(index.vue:57) RuleStatusButton.value", RuleStatusButton.value);
     } else if (response.code == 5000) {
       ElMessage.warning(`${ response.data.description }`);
     } else {
@@ -119,7 +119,7 @@ onMounted(async () => {
       <SwitchCampaign v-if="activeTab === 3" :RuleStatusButton="RuleStatusButton" :data="baseData"
                       @refresh="refresh(3)" />
     </el-tab-pane>
-    <el-tab-pane :name="4" label="定向规则">
+    <el-tab-pane :name="4" label="定向规则" v-if="targetingType !== 'AUTO'">
       <template #label>
         <div class="tab-label">
           <el-switch v-model="RuleStatusButton['4']" :active-value="1" :disabled="RuleStatusButton['4'] === '-'"
@@ -131,7 +131,7 @@ onMounted(async () => {
       <TargetRule v-if="activeTab === 4" :RuleStatusButton="RuleStatusButton" :data="baseData"
                   @refresh="refresh(4)"></TargetRule>
     </el-tab-pane>
-    <el-tab-pane :name="5" label="添加搜索词">
+    <el-tab-pane :name="5" label="添加搜索词" v-if="targetingType !== 'AUTO'">
       <template #label>
         <div class="tab-label">
           <el-switch v-model="RuleStatusButton['5']" :active-value="1" :disabled="RuleStatusButton['5'] === '-'"

+ 34 - 34
src/views/adManage/sp/campaigns/campaignDetail/index.vue

@@ -1,31 +1,30 @@
 <script lang="ts" setup>
-import { onMounted, ref, Ref } from 'vue';
-import { useRoute } from 'vue-router';
-import AdGroups from './adGroups/index.vue';
-import Placement from './placement/index.vue';
-import Budget from './budget/index.vue';
-import Automation from './automation/index.vue';
-import { getEnumLabel } from '/@/views/adManage/utils/tools.js';
-import { dynBidStrategyEnum } from '/@/views/adManage/utils/enum.js';
-import { useShopInfo } from '/@/stores/shopInfo';
+import {onMounted, ref, Ref} from 'vue'
+import {useRoute} from 'vue-router'
+import AdGroups from './adGroups/index.vue'
+import Placement from './placement/index.vue'
+import Budget from './budget/index.vue'
+import Automation from './automation/index.vue'
+import {getEnumLabel} from '/@/views/adManage/utils/tools.js'
+import {dynBidStrategyEnum} from '/@/views/adManage/utils/enum.js'
+import {useShopInfo} from '/@/stores/shopInfo'
 // import { usePublicData } from '/@/stores/publicData'
-import { storeToRefs } from 'pinia';
+import {storeToRefs} from 'pinia'
 
-import { GetObj } from './api';
+import {GetObj} from './api'
 
+const shopInfo = useShopInfo()
+const { profile } = storeToRefs(shopInfo)
+const route = useRoute()
+const campaignInfo: Ref<SpCampaign> = ref({})
+const tabActiveName = ref('adGroup')
 
-const shopInfo = useShopInfo();
-const { profile } = storeToRefs(shopInfo);
-const route = useRoute();
-const campaignInfo: Ref<SpCampaign> = ref({});
-const tabActiveName = ref('adGroup');
-
-const campaignId = ref(route.query.campaignId);
+const campaignId = ref(route.query.campaignId)
 
 onMounted(async () => {
-  const resp = await GetObj(route.query.campaignId);
-  campaignInfo.value = resp.data;
-});
+  const resp = await GetObj(route.query.campaignId)
+  campaignInfo.value = resp.data
+})
 
 
 </script>
@@ -38,16 +37,18 @@ onMounted(async () => {
       </span>
       <div class="asj-detail-info">
         <div>
-          <span class="label">状态:</span>
-          <span class="value">{{ campaignInfo.state }} ({{ campaignInfo.servingStatus }})</span>
+          <span class="label">状态: </span>
+          <el-tag :type="getTagType(campaignInfo.state)">
+            {{ dynStatusEnum[campaignInfo.state] }}
+          </el-tag>
         </div>
         <div>
           <span class="label">预算:</span>
-          <span class="value">{{ profile.currency_symbol + campaignInfo.budget }} | {{ campaignInfo.budgetType }}</span>
+          <span class="value">{{ profile.currency_symbol + campaignInfo.budget }} | {{ dynStatusEnum[campaignInfo.budgetType] }}</span>
         </div>
         <div>
           <span class="label">投放类型:</span>
-          <span class="value">{{ campaignInfo.targetingType }}</span>
+          <span class="value">{{ spTargetTypeEnum[campaignInfo.targetingType] }}</span>
         </div>
         <div>
           <span class="label">投放日期:</span>
@@ -63,23 +64,22 @@ onMounted(async () => {
         </div>
       </div>
     </div>
-    <el-tabs v-model="tabActiveName" class="asj-detail-tabs" type="border-card">
+    <el-tabs type="border-card" class="asj-detail-tabs" v-model="tabActiveName">
       <el-tab-pane label="广告组" name="adGroup">
-        <AdGroups v-if="tabActiveName==='adGroup'" :campaignId="campaignId"></AdGroups>
+        <AdGroups :campaignId="campaignId" v-if="tabActiveName==='adGroup'"></AdGroups>
       </el-tab-pane>
       <el-tab-pane label="预算" name="budget">
-        <Budget v-if="tabActiveName==='budget'" :campaignId="campaignId"></Budget>
+        <Budget :campaignId="campaignId" v-if="tabActiveName==='budget'"></Budget>
       </el-tab-pane>
       <el-tab-pane label="自动化" name="automation">
-        <Automation v-if="tabActiveName==='automation'" :campaignId="campaignId"
-                    :profileId="profile.profile_id"></Automation>
+        <Automation :campaignId="campaignId" :profileId="profile.profile_id" :targetingType="campaignInfo.targetingType" v-if="tabActiveName==='automation'"></Automation>
       </el-tab-pane>
       <el-tab-pane label="广告位" name="placement">
-        <Placement v-if="tabActiveName==='placement'" :campaignId="campaignId" />
+        <Placement :campaignId="campaignId" v-if="tabActiveName==='placement'"/>
+      </el-tab-pane>
+      <el-tab-pane label="否定投放" name="negative">
+        否定投放
       </el-tab-pane>
-      <!--<el-tab-pane label="否定投放" name="negative">-->
-      <!--  否定投放-->
-      <!--</el-tab-pane>-->
       <!-- <el-tab-pane label="操作日志" :lazy="true"></el-tab-pane> -->
     </el-tabs>
   </div>

+ 251 - 236
src/views/adManage/utils/enum.ts

@@ -1,251 +1,267 @@
 export const spCampaignMetricsEnum = [
-  {label: '曝光量', value: 'Impression'},
-  {label: '点击量', value: 'Click'},
-  {label: '花费', value: 'Spend'},
-  {label: '订单量', value: 'TotalPurchases'},
-  {label: '销售额', value: 'TotalSales'},
-  {label: '销量', value: 'TotalUnitOrdered'},
-  {label: '点击率', value: 'CTR'},
-  {label: '点击成本', value: 'CPC'},
-  {label: '转化率', value: 'PurchasesRate'},
-  {label: 'ACOS', value: 'ACOS'},
-  {label: 'ROAS', value: 'ROAS'},
-  {label: '订单成本', value: 'CPA'},
-  {label: '推广商品销售额', value: 'TotalSalesSameSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '推广商品订单量', value: 'TotalPurchasesSameSKU'},
-  {label: '其它商品订单量', value: 'TotalPurchasesOtherSKU'},
-  {label: '推广商品销量', value: 'TotalUnitOrderedSameSKU'},
-  {label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU'},
-]
+  { label: '曝光量', value: 'Impression' },
+  { label: '点击量', value: 'Click' },
+  { label: '花费', value: 'Spend' },
+  { label: '订单量', value: 'TotalPurchases' },
+  { label: '销售额', value: 'TotalSales' },
+  { label: '销量', value: 'TotalUnitOrdered' },
+  { label: '点击率', value: 'CTR' },
+  { label: '点击成本', value: 'CPC' },
+  { label: '转化率', value: 'PurchasesRate' },
+  { label: 'ACOS', value: 'ACOS' },
+  { label: 'ROAS', value: 'ROAS' },
+  { label: '订单成本', value: 'CPA' },
+  { label: '推广商品销售额', value: 'TotalSalesSameSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '推广商品订单量', value: 'TotalPurchasesSameSKU' },
+  { label: '其它商品订单量', value: 'TotalPurchasesOtherSKU' },
+  { label: '推广商品销量', value: 'TotalUnitOrderedSameSKU' },
+  { label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU' },
+];
 
 export const sbCampaignMetricsEnum = [
-  {label: '曝光量', value: 'Impression'},
-  {label: '点击量', value: 'Click'},
-  {label: '花费', value: 'Spend'},
-  {label: '订单量', value: 'TotalPurchases'},
-  {label: '销售额', value: 'TotalSales'},
-  {label: '销量', value: 'TotalUnitOrdered'},
-  {label: '点击率', value: 'CTR'},
-  {label: '点击成本', value: 'CPC'},
-  {label: '转化率', value: 'PurchasesRate'},
-  {label: 'ACOS', value: 'ACOS'},
-  {label: 'ROAS', value: 'ROAS'},
-  {label: '订单成本', value: 'CPA'},
-  {label: '推广商品销售额', value: 'TotalSalesSameSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '推广商品订单量', value: 'TotalPurchasesSameSKU'},
-  {label: '其它商品订单量', value: 'TotalPurchasesOtherSKU'},
-  {label: '推广商品销量', value: 'TotalUnitOrderedSameSKU'},
-  {label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU'},
-  {label: '详情页浏览次数', value: 'DPV'},
-  {label: '详情页浏览率', value: 'DPVR'},
-  {label: '新客订单数', value: 'NTBOrder'},
-  {label: '新客订单占比', value: 'NTBOrderRate'},
-  {label: '新客销售额', value: 'NTBSales'},
-  {label: '新客销售占比', value: 'NTBSalesRate'},
-  {label: '新客销量', value: 'NTBUnitOrdered'},
-  {label: '新客销量占比', value: 'NTBUnitOrderedRate'},
-  {label: '浏览点击率', value: 'VCTR'},
-  {label: '可见曝光率', value: 'VIR'},
-  {label: '浏览率', value: 'VTR'},
-  {label: '播放5秒或完成数占比', value: 'Video5SecondViewRate'},
-  {label: '播放5秒或完成数', value: 'Video5SecondViews'},
-  {label: '视频播放25%', value: 'VideoFirstQuartileViews'},
-  {label: '视频播放50%', value: 'VideoMidpointViews'},
-  {label: '视频播放75%', value: 'VideoThirdQuartileViews'},
-  {label: '视频播放100%', value: 'VideoCompleteViews'},
-  {label: '视频取消静音数', value: 'VideoUnmutes'},
-  {label: '可见曝光量', value: 'ViewableImpression'},
-]
-export const sbBarOptions1= [
-  {label: '曝光量', value: 'Impression'},
-  {label: '点击量', value: 'Click'},
-  {label: '花费', value: 'Spend'},
-  {label: '订单量', value: 'TotalPurchases'},
-  {label: '销售额', value: 'TotalSales'},
-  {label: '销量', value: 'TotalUnitOrdered'},
-  {label: '点击率', value: 'CTR'},
-  {label: '点击成本', value: 'CPC'},
-  {label: '转化率', value: 'PurchasesRate'},
-  {label: 'ACOS', value: 'ACOS'},
-  {label: 'ROAS', value: 'ROAS'},
-  {label: '订单成本', value: 'CPA'},
-  {label: '推广商品销售额', value: 'TotalSalesSameSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '推广商品订单量', value: 'TotalPurchasesSameSKU'},
-  {label: '其它商品订单量', value: 'TotalPurchasesOtherSKU'},
-  {label: '推广商品销量', value: 'TotalUnitOrderedSameSKU'},
-  {label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU'},
-  {label: '详情页浏览次数', value: 'DPV'},
-  {label: '详情页浏览率', value: 'DPVR'},
-  {label: '新客订单数', value: 'NTBOrder'},
-  {label: '新客订单占比', value: 'NTBOrderRate'},
-  {label: '新客销售额', value: 'NTBSales'},
-  {label: '新客销售占比', value: 'NTBSalesRate'},
-  {label: '新客销量', value: 'NTBUnitOrdered'},
-  {label: '新客销量占比', value: 'NTBUnitOrderedRate'},
-  {label: '浏览点击率', value: 'VCTR'},
-  {label: '可见曝光率', value: 'VIR'},
-  {label: '浏览率', value: 'VTR'},
-  {label: '播放5秒或完成数占比', value: 'Video5SecondViewRate'},
-  {label: '播放5秒或完成数', value: 'Video5SecondViews'},
-  {label: '视频播放25%', value: 'VideoFirstQuartileViews'},
-  {label: '视频播放50%', value: 'VideoMidpointViews'},
-  {label: '视频播放75%', value: 'VideoThirdQuartileViews'},
-  {label: '视频播放100%', value: 'VideoCompleteViews'},
-  {label: '视频取消静音数', value: 'VideoUnmutes'},
-  {label: '可见曝光量', value: 'ViewableImpression'},
-]
-export const sbBarOptions2= [
-  {label: '曝光量', value: 'Impression'},
-  {label: '点击量', value: 'Click'},
-  {label: '花费', value: 'Spend'},
-  {label: '订单量', value: 'TotalPurchases'},
-  {label: '销售额', value: 'TotalSales'},
-  {label: '销量', value: 'TotalUnitOrdered'},
-  {label: '点击率', value: 'CTR'},
-  {label: '点击成本', value: 'CPC'},
-  {label: '转化率', value: 'PurchasesRate'},
-  {label: 'ACOS', value: 'ACOS'},
-  {label: 'ROAS', value: 'ROAS'},
-  {label: '订单成本', value: 'CPA'},
-  {label: '推广商品销售额', value: 'TotalSalesSameSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '推广商品订单量', value: 'TotalPurchasesSameSKU'},
-  {label: '其它商品订单量', value: 'TotalPurchasesOtherSKU'},
-  {label: '推广商品销量', value: 'TotalUnitOrderedSameSKU'},
-  {label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU'},
-  {label: '详情页浏览次数', value: 'DPV'},
-  {label: '详情页浏览率', value: 'DPVR'},
-  {label: '新客订单数', value: 'NTBOrder'},
-  {label: '新客订单占比', value: 'NTBOrderRate'},
-  {label: '新客销售额', value: 'NTBSales'},
-  {label: '新客销售占比', value: 'NTBSalesRate'},
-  {label: '新客销量', value: 'NTBUnitOrdered'},
-  {label: '新客销量占比', value: 'NTBUnitOrderedRate'},
-  {label: '浏览点击率', value: 'VCTR'},
-  {label: '可见曝光率', value: 'VIR'},
-  {label: '浏览率', value: 'VTR'},
-  {label: '播放5秒或完成数占比', value: 'Video5SecondViewRate'},
-  {label: '播放5秒或完成数', value: 'Video5SecondViews'},
-  {label: '视频播放25%', value: 'VideoFirstQuartileViews'},
-  {label: '视频播放50%', value: 'VideoMidpointViews'},
-  {label: '视频播放75%', value: 'VideoThirdQuartileViews'},
-  {label: '视频播放100%', value: 'VideoCompleteViews'},
-  {label: '视频取消静音数', value: 'VideoUnmutes'},
-  {label: '可见曝光量', value: 'ViewableImpression'},
-]
+  { label: '曝光量', value: 'Impression' },
+  { label: '点击量', value: 'Click' },
+  { label: '花费', value: 'Spend' },
+  { label: '订单量', value: 'TotalPurchases' },
+  { label: '销售额', value: 'TotalSales' },
+  { label: '销量', value: 'TotalUnitOrdered' },
+  { label: '点击率', value: 'CTR' },
+  { label: '点击成本', value: 'CPC' },
+  { label: '转化率', value: 'PurchasesRate' },
+  { label: 'ACOS', value: 'ACOS' },
+  { label: 'ROAS', value: 'ROAS' },
+  { label: '订单成本', value: 'CPA' },
+  { label: '推广商品销售额', value: 'TotalSalesSameSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '推广商品订单量', value: 'TotalPurchasesSameSKU' },
+  { label: '其它商品订单量', value: 'TotalPurchasesOtherSKU' },
+  { label: '推广商品销量', value: 'TotalUnitOrderedSameSKU' },
+  { label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU' },
+  { label: '详情页浏览次数', value: 'DPV' },
+  { label: '详情页浏览率', value: 'DPVR' },
+  { label: '新客订单数', value: 'NTBOrder' },
+  { label: '新客订单占比', value: 'NTBOrderRate' },
+  { label: '新客销售额', value: 'NTBSales' },
+  { label: '新客销售占比', value: 'NTBSalesRate' },
+  { label: '新客销量', value: 'NTBUnitOrdered' },
+  { label: '新客销量占比', value: 'NTBUnitOrderedRate' },
+  { label: '浏览点击率', value: 'VCTR' },
+  { label: '可见曝光率', value: 'VIR' },
+  { label: '浏览率', value: 'VTR' },
+  { label: '播放5秒或完成数占比', value: 'Video5SecondViewRate' },
+  { label: '播放5秒或完成数', value: 'Video5SecondViews' },
+  { label: '视频播放25%', value: 'VideoFirstQuartileViews' },
+  { label: '视频播放50%', value: 'VideoMidpointViews' },
+  { label: '视频播放75%', value: 'VideoThirdQuartileViews' },
+  { label: '视频播放100%', value: 'VideoCompleteViews' },
+  { label: '视频取消静音数', value: 'VideoUnmutes' },
+  { label: '可见曝光量', value: 'ViewableImpression' },
+];
+export const sbBarOptions1 = [
+  { label: '曝光量', value: 'Impression' },
+  { label: '点击量', value: 'Click' },
+  { label: '花费', value: 'Spend' },
+  { label: '订单量', value: 'TotalPurchases' },
+  { label: '销售额', value: 'TotalSales' },
+  { label: '销量', value: 'TotalUnitOrdered' },
+  { label: '点击率', value: 'CTR' },
+  { label: '点击成本', value: 'CPC' },
+  { label: '转化率', value: 'PurchasesRate' },
+  { label: 'ACOS', value: 'ACOS' },
+  { label: 'ROAS', value: 'ROAS' },
+  { label: '订单成本', value: 'CPA' },
+  { label: '推广商品销售额', value: 'TotalSalesSameSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '推广商品订单量', value: 'TotalPurchasesSameSKU' },
+  { label: '其它商品订单量', value: 'TotalPurchasesOtherSKU' },
+  { label: '推广商品销量', value: 'TotalUnitOrderedSameSKU' },
+  { label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU' },
+  { label: '详情页浏览次数', value: 'DPV' },
+  { label: '详情页浏览率', value: 'DPVR' },
+  { label: '新客订单数', value: 'NTBOrder' },
+  { label: '新客订单占比', value: 'NTBOrderRate' },
+  { label: '新客销售额', value: 'NTBSales' },
+  { label: '新客销售占比', value: 'NTBSalesRate' },
+  { label: '新客销量', value: 'NTBUnitOrdered' },
+  { label: '新客销量占比', value: 'NTBUnitOrderedRate' },
+  { label: '浏览点击率', value: 'VCTR' },
+  { label: '可见曝光率', value: 'VIR' },
+  { label: '浏览率', value: 'VTR' },
+  { label: '播放5秒或完成数占比', value: 'Video5SecondViewRate' },
+  { label: '播放5秒或完成数', value: 'Video5SecondViews' },
+  { label: '视频播放25%', value: 'VideoFirstQuartileViews' },
+  { label: '视频播放50%', value: 'VideoMidpointViews' },
+  { label: '视频播放75%', value: 'VideoThirdQuartileViews' },
+  { label: '视频播放100%', value: 'VideoCompleteViews' },
+  { label: '视频取消静音数', value: 'VideoUnmutes' },
+  { label: '可见曝光量', value: 'ViewableImpression' },
+];
+export const sbBarOptions2 = [
+  { label: '曝光量', value: 'Impression' },
+  { label: '点击量', value: 'Click' },
+  { label: '花费', value: 'Spend' },
+  { label: '订单量', value: 'TotalPurchases' },
+  { label: '销售额', value: 'TotalSales' },
+  { label: '销量', value: 'TotalUnitOrdered' },
+  { label: '点击率', value: 'CTR' },
+  { label: '点击成本', value: 'CPC' },
+  { label: '转化率', value: 'PurchasesRate' },
+  { label: 'ACOS', value: 'ACOS' },
+  { label: 'ROAS', value: 'ROAS' },
+  { label: '订单成本', value: 'CPA' },
+  { label: '推广商品销售额', value: 'TotalSalesSameSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '推广商品订单量', value: 'TotalPurchasesSameSKU' },
+  { label: '其它商品订单量', value: 'TotalPurchasesOtherSKU' },
+  { label: '推广商品销量', value: 'TotalUnitOrderedSameSKU' },
+  { label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU' },
+  { label: '详情页浏览次数', value: 'DPV' },
+  { label: '详情页浏览率', value: 'DPVR' },
+  { label: '新客订单数', value: 'NTBOrder' },
+  { label: '新客订单占比', value: 'NTBOrderRate' },
+  { label: '新客销售额', value: 'NTBSales' },
+  { label: '新客销售占比', value: 'NTBSalesRate' },
+  { label: '新客销量', value: 'NTBUnitOrdered' },
+  { label: '新客销量占比', value: 'NTBUnitOrderedRate' },
+  { label: '浏览点击率', value: 'VCTR' },
+  { label: '可见曝光率', value: 'VIR' },
+  { label: '浏览率', value: 'VTR' },
+  { label: '播放5秒或完成数占比', value: 'Video5SecondViewRate' },
+  { label: '播放5秒或完成数', value: 'Video5SecondViews' },
+  { label: '视频播放25%', value: 'VideoFirstQuartileViews' },
+  { label: '视频播放50%', value: 'VideoMidpointViews' },
+  { label: '视频播放75%', value: 'VideoThirdQuartileViews' },
+  { label: '视频播放100%', value: 'VideoCompleteViews' },
+  { label: '视频取消静音数', value: 'VideoUnmutes' },
+  { label: '可见曝光量', value: 'ViewableImpression' },
+];
 
 export const sdCampaignMetricsEnum = [
-  {label: '花费', value: 'Spend'},
-  {label: '销售额', value: 'TotalSales'},
-  {label: '销售额(vCPM)', value: 'TotalUnitOrderedVCPM'},
-  {label: '销售额(Click)', value: 'TotalSalesClick'},
-  {label: '订单数', value: 'TotalPurchases'},
-  {label: '订单数(vCPM)', value: 'TotalPurchasesVCPM'},
-  {label: '订单数(Click)', value: 'TotalPurchasesClick'},
-  {label: 'ACOS', value: 'ACOS'},
-  {label: 'ROAS', value: 'ROAS'},
-  {label: '点击成本', value: 'CPC'},
-  {label: '千次可见曝光成本', value: 'VCPM' },
-  {label: '转化率', value: 'PurchasesRate'},
-  {label: '订单成本', value: 'CPA'},
-  {label: '销量', value: 'TotalUnitOrdered'},
-  {label: '销量(vCPM)', value: 'TotalUnitOrdered'},
-  {label: '销量(Click)', value: 'TotalPurchasesClick'},
-  {label: '曝光量', value: 'Impression'},
-  {label: '点击量', value: 'Click'},
-  {label: '点击率', value: 'CTR'},
-  {label: '可见曝光量', value: 'ViewableImpression'},
-  {label: '可见曝光率', value: 'VIR'},
-  {label: '详情页浏览次数', value: 'DPV'},
-  {label: '详情页浏览率', value: 'DPVR'},
-  {label: '商品详情页浏览次数(vCPM)', value: 'DPVVCPM'},
-  {label: '商品详情页浏览次数(Click)', value: 'DPVClick'},
+  { label: '花费', value: 'Spend' },
+  { label: '销售额', value: 'TotalSales' },
+  { label: '销售额(vCPM)', value: 'TotalUnitOrderedVCPM' },
+  { label: '销售额(Click)', value: 'TotalSalesClick' },
+  { label: '订单数', value: 'TotalPurchases' },
+  { label: '订单数(vCPM)', value: 'TotalPurchasesVCPM' },
+  { label: '订单数(Click)', value: 'TotalPurchasesClick' },
+  { label: 'ACOS', value: 'ACOS' },
+  { label: 'ROAS', value: 'ROAS' },
+  { label: '点击成本', value: 'CPC' },
+  { label: '千次可见曝光成本', value: 'VCPM' },
+  { label: '转化率', value: 'PurchasesRate' },
+  { label: '订单成本', value: 'CPA' },
+  { label: '销量', value: 'TotalUnitOrdered' },
+  { label: '销量(vCPM)', value: 'TotalUnitOrdered' },
+  { label: '销量(Click)', value: 'TotalPurchasesClick' },
+  { label: '曝光量', value: 'Impression' },
+  { label: '点击量', value: 'Click' },
+  { label: '点击率', value: 'CTR' },
+  { label: '可见曝光量', value: 'ViewableImpression' },
+  { label: '可见曝光率', value: 'VIR' },
+  { label: '详情页浏览次数', value: 'DPV' },
+  { label: '详情页浏览率', value: 'DPVR' },
+  { label: '商品详情页浏览次数(vCPM)', value: 'DPVVCPM' },
+  { label: '商品详情页浏览次数(Click)', value: 'DPVClick' },
   // {label: '浏览订单', value: 'DPVR'},
-  {label: '推广商品销售额', value: 'TotalSalesSameSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '推广商品订单数', value: 'TotalPurchasesSameSKU'},
-  {label: '其它商品订单数', value: 'TotalPurchasesOtherSKU'},
-  {label: '新客订单数', value: 'NTBOrder'},
-  {label: '新客订单占比', value: 'NTBOrderRate'},
-  {label: '新客销售额', value: 'NTBSales'},
-  {label: '新客销售占比', value: 'NTBSalesRate'},
-  {label: '新客销量', value: 'NTBUnitOrdered'},
-  {label: '新客销量占比', value: 'NTBUnitOrderedRate'},
-  {label: '视频播放25%', value: 'VideoFirstQuartileViews'},
-  {label: '视频播放50%', value: 'VideoMidpointViews'},
-  {label: '视频播放75%', value: 'VideoThirdQuartileViews'},
-  {label: '视频播放100%', value: 'VideoCompleteViews'},
-  {label: '视频取消静音数', value: 'VideoUnmutes'},
-  {label: '浏览率(VTR)', value: 'VTR'},
-  {label: '浏览点击率(vCTR)', value: 'VCTR'},
-]
+  { label: '推广商品销售额', value: 'TotalSalesSameSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '推广商品订单数', value: 'TotalPurchasesSameSKU' },
+  { label: '其它商品订单数', value: 'TotalPurchasesOtherSKU' },
+  { label: '新客订单数', value: 'NTBOrder' },
+  { label: '新客订单占比', value: 'NTBOrderRate' },
+  { label: '新客销售额', value: 'NTBSales' },
+  { label: '新客销售占比', value: 'NTBSalesRate' },
+  { label: '新客销量', value: 'NTBUnitOrdered' },
+  { label: '新客销量占比', value: 'NTBUnitOrderedRate' },
+  { label: '视频播放25%', value: 'VideoFirstQuartileViews' },
+  { label: '视频播放50%', value: 'VideoMidpointViews' },
+  { label: '视频播放75%', value: 'VideoThirdQuartileViews' },
+  { label: '视频播放100%', value: 'VideoCompleteViews' },
+  { label: '视频取消静音数', value: 'VideoUnmutes' },
+  { label: '浏览率(VTR)', value: 'VTR' },
+  { label: '浏览点击率(vCTR)', value: 'VCTR' },
+];
 
 export const spCampaignPuchasedOtherProductsMetricsEnum = [
-  {label: '其它商品订单数', value: 'TotalPurchasesOtherSKU'},
-  {label: '其它商品销售额', value: 'TotalSalesOtherSKU'},
-  {label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU'},
-  {label: '其它商品订单数占比', value: 'PurchasesOtherSKURate'},
-  {label: '其它商品销售额占比', value: 'SalesOtherSKURate'},
-  {label: '其它商品销量占比', value: 'UnitOrderedOtherSKURate'},
-]
+  { label: '其它商品订单数', value: 'TotalPurchasesOtherSKU' },
+  { label: '其它商品销售额', value: 'TotalSalesOtherSKU' },
+  { label: '其它商品销量', value: 'TotalUnitOrderedOtherSKU' },
+  { label: '其它商品订单数占比', value: 'PurchasesOtherSKURate' },
+  { label: '其它商品销售额占比', value: 'SalesOtherSKURate' },
+  { label: '其它商品销量占比', value: 'UnitOrderedOtherSKURate' },
+];
 
 export const dynBidStrategyEnum = [
-  {label: '动态竞价-仅降低', value: 'LEGACY_FOR_SALES', color: ''},
-  {label: '动态竞价-提高和降低', value: 'AUTO_FOR_SALES', color: 'success'},
-  {label: '固定竞价', value: 'MANUAL', color: 'warning'}
-]
+  { label: '动态竞价-仅降低', value: 'LEGACY_FOR_SALES', color: '' },
+  { label: '动态竞价-提高和降低', value: 'AUTO_FOR_SALES', color: 'success' },
+  { label: '固定竞价', value: 'MANUAL', color: 'warning' }
+];
 
 export const dynBidOptimizationEnum = [
-  {label: '自动竞价', value: 1, color: 'success'},
-  {label: '自定义竞价', value: 0, color: 'warning'}
-]
+  { label: '自动竞价', value: 1, color: 'success' },
+  { label: '自定义竞价', value: 0, color: 'warning' }
+];
+
+export const dynStatusEnum: any = {
+  'ENABLED': '投放中',
+  'DISABLED': '禁用',
+  'PAUSED': '已暂停',
+  'paused': '已暂停',
+  'enabled': '已开启',
+  'daily': '每日',
+  'DAILY': '每日',
+  'T00020': '内容相关投放',
+  'T00030': '受众',
+};
+
+export const spTargetTypeEnum: any = {
+  'AUTO': '自动',
+  'MANUAL': '手动',
+};
+
+export const dynStatusColorEnum = {
+  'ENABLED': 'success',
+  'DISABLED': 'danger',
+  'PAUSED': 'warning',
+  'paused': 'warning',
+  'enabled': 'success',
+};
 
-export const dynStatusEnum:any = {
-    'ENABLED' : '投放中',
-    'DISABLED' : '禁用',
-    'paused': '已暂停',
-    'enabled': '已开启',
-    'daily': '每日',
-    'T00020': '内容相关投放',
-    'T00030': '受众'
-  }
 export const targetTypeEnum = {
   'PRODUCT_COLLECTION': '商品集',
   'VIDEO': '视频',
   'BRAND_VIDEO': '品牌视频',
   'STORE_SPOTLIGHT': '店铺关注(STORE_SPOTLIGHT)'
-}
+};
 
 export const spCampaignPlacementEnum = [
-  {label: '搜索结果顶部', value: 'top'},
-  {label: '商品页面', value: 'product_page'},
-  {label: '搜索结果的其余位置', value: 'rest_of_search'},
-]
+  { label: '搜索结果顶部', value: 'top' },
+  { label: '商品页面', value: 'product_page' },
+  { label: '搜索结果的其余位置', value: 'rest_of_search' },
+];
 
 export const sbCampaignPlacementEnum = [
-  {label: '详细信息首页', value: 'Detail Page on-Amazon'},
-  {label: '其他页面', value: 'Other on-Amazon'},
-  {label: '顶部搜索', value: 'Top of Search on-Amazon'},
-  {label: '首页', value: 'Homepage on-Amazon'},
-]
+  { label: '详细信息首页', value: 'Detail Page on-Amazon' },
+  { label: '其他页面', value: 'Other on-Amazon' },
+  { label: '顶部搜索', value: 'Top of Search on-Amazon' },
+  { label: '首页', value: 'Homepage on-Amazon' },
+];
 
 export const spCampaignStateEnum = [
-  {label: '投放中', value: 'ENABLE'},
-  {label: '已暂停', value: 'PAUSED'},
-  {label: '已归档', value: 'ARCHIVED'},
+  { label: '投放中', value: 'ENABLE' },
+  { label: '已暂停', value: 'PAUSED' },
+  { label: '已归档', value: 'ARCHIVED' },
   // { label: '', value: 'ENABLING' },
   // { label: '', value: 'USER_DELETED' },
-  {label: '其它', value: 'OTHER'},
-]
+  { label: '其它', value: 'OTHER' },
+];
 
 export const spCampaignServingStatusEnum = [
-  {label: '投放中', value: 'CAMPAIGN_STATUS_ENABLED'},
-  {label: '已暂停', value: 'CAMPAIGN_PAUSED'},
-  {label: '已归档', value: 'CAMPAIGN_ARCHIVED'},
-  {label: '超出预算', value: 'CAMPAIGN_OUT_OF_BUDGET'},
+  { label: '投放中', value: 'CAMPAIGN_STATUS_ENABLED' },
+  { label: '已暂停', value: 'CAMPAIGN_PAUSED' },
+  { label: '已归档', value: 'CAMPAIGN_ARCHIVED' },
+  { label: '超出预算', value: 'CAMPAIGN_OUT_OF_BUDGET' },
   // { label: '', value: '' },
   // { label: '', value: '' },
   // { label: '', value: '' },
@@ -256,7 +272,7 @@ export const spCampaignServingStatusEnum = [
   // { label: '', value: '' },
   // { label: '', value: '' },
   // { label: '', value: '' }
-]
+];
 
 export const metricMap = {
   'Spend': '花费',
@@ -265,15 +281,15 @@ export const metricMap = {
   'TotalUnitOrdered': '销量',
   'Impression': '曝光量',
   'Click': '点击量',
-}
+};
 
-export const sdTypeMap: {[key: string]: string} = {
+export const sdTypeMap: { [key: string]: string } = {
   'views': '浏览再营销',
   'purchases': '购买再营销',
   'audience': '亚马逊受众'
-}
+};
 
-export const sdtargetMap: {[key: string]: string} = {
+export const sdtargetMap: { [key: string]: string } = {
   'asinBrandSameAs': '品牌',
   'asinSameAs': '商品',
   'asinPriceGreaterThan': '商品价格>',
@@ -285,7 +301,7 @@ export const sdtargetMap: {[key: string]: string} = {
   'asinIsPrimeShippingEligible': 'asinIsPrimeShippingEligible',
   'asinReviewRatingBetween': '评分 ',
   'lookback': '回溯期'
-}
+};
 export const targetEnum = [
   { label: '类目', value: 'ASIN_CATEGORY_SAME_AS' },
   { label: '品牌', value: 'ASIN_BRAND_SAME_AS' },
@@ -301,7 +317,7 @@ export const targetEnum = [
   // { label: '', value: 'ASIN_GENRE_SAME_AS' },
   // { label: '', value: 'ASIN_EXPANDED_FROM' },
   { label: '其它', value: 'OTHER' },
-]
+];
 
 export const sdBarOptionsMap = {
   'Spend': '花费',
@@ -347,7 +363,7 @@ export const sdBarOptionsMap = {
   'VideoUnmutes': '视频取消静音数',
   'VTR': '浏览率(VTR)',
   'VCTR': '浏览点击率(vCTR)'
-}
+};
 export const sdBarOptions1 = [
   { value: 'Spend', label: '花费' },
   { value: 'TotalSales', label: '销售额' },
@@ -391,7 +407,7 @@ export const sdBarOptions1 = [
   { value: 'VideoUnmutes', label: '视频取消静音数' },
   { value: 'VTR', label: '浏览率(VTR)' },
   { value: 'VCTR', label: '浏览点击率(vCTR)' }
-]
+];
 export const sdBarOptions2 = [
   { value: 'Spend', label: '花费' },
   { value: 'TotalSales', label: '销售额' },
@@ -435,8 +451,7 @@ export const sdBarOptions2 = [
   { value: 'VideoUnmutes', label: '视频取消静音数' },
   { value: 'VTR', label: '浏览率(VTR)' },
   { value: 'VCTR', label: '浏览点击率(vCTR)' }
-]
-
+];
 
 export const TargetExpressionEnum = [
   { label: '类目', value: 'ASIN_CATEGORY_SAME_AS' },
@@ -453,7 +468,7 @@ export const TargetExpressionEnum = [
   // { label: '', value: 'ASIN_GENRE_SAME_AS' },
   // { label: '', value: 'ASIN_EXPANDED_FROM' },
   { label: '其它', value: 'OTHER' },
-]
+];
 
 export const barOptionsMap = {
   'ACOS': 'ACOS',
@@ -475,7 +490,7 @@ export const barOptionsMap = {
   'TotalUnitOrderedSameSKU': '推广商品销量',
   'TotalUnitOrderedOtherSKU': '其他商品销量',
   'TopOfSearchImpressionShare': '搜索结果顶部展示份额'
-}
+};
 export const barOptions1 = [
   {
     value: 'ACOS',
@@ -554,7 +569,7 @@ export const barOptions1 = [
     value: 'TopOfSearchImpressionShare',
     label: '搜索结果顶部展示份额'
   },
-]
+];
 export const barOptions2 = [
   {
     value: 'ACOS',
@@ -633,7 +648,7 @@ export const barOptions2 = [
     value: 'TopOfSearchImpressionShare',
     label: '搜索结果顶部展示份额'
   },
-]
+];
 export const pieOptions = [
   {
     value: 'Spend',
@@ -659,4 +674,4 @@ export const pieOptions = [
     value: 'Click',
     label: '点击量',
   },
-]
+];

+ 15 - 0
src/views/adManage/utils/tools.ts

@@ -129,3 +129,18 @@ export function parseQueryParams(body: any) {
 export function getEnumLabel(enumObj: { label: string; value: string }[], value: any) {
   return enumObj.find((item) => item.value === value)?.label
 }
+
+export function getTagType(state){
+    switch (state) {
+      case 'ENABLED':
+      case 'enabled':
+        return 'success';  // 投放中或已开启
+      case 'PAUSED':
+      case 'paused':
+        return 'warning';  // 已暂停
+      case 'DISABLED':
+        return 'danger';   // 禁用
+      default:
+        return '';  // 其他状态
+    }
+}

+ 2 - 2
src/views/components/auto/auto-campaigns/switch-campaign.vue

@@ -93,8 +93,8 @@ async function handleSubmit() {
 
     if (formData.value.rule.action.state.length === 0) return;
 
-    const selectedTime = new Date(formData.value.rule.action.setTime);
-    if (selectedTime <= new Date()) {
+    const selectedTime = new Date(formData.value.rule.action.setTime).setSeconds(0, 0);;
+    if (selectedTime < new Date().setSeconds(0, 0)) {
       ElMessage.error('保存失败,请重新选择时间');
       return;
     }

+ 3 - 5
src/views/components/auto/auto-campaigns/target-rule.vue

@@ -64,12 +64,10 @@ async function handleSubmit() {
     <el-card class="mt-3">
       <el-form ref="formRef" :model="formData" class="custom-card" label-position="top">
         <el-form-item>
-          <!--<template #label>
-            <span class="custom-title-icon"></span>
-            <span class="asj-h3">生效对象</span>
-          </template>-->
+          <!-- 频率设置 -->
           <FreqSetting :disabled="formData.useTmpl" :rule="formData.rule" />
           <el-divider />
+          <!-- 生效对象 -->
           <TargetSelect
               :campaign-id="data.campaignId"
               :data="formData.rule"
@@ -78,7 +76,7 @@ async function handleSubmit() {
         </el-form-item>
       </el-form>
     </el-card>
-
+    <!--规则设置-->
     <el-card class="mt-3">
       <TargetRuleSetting ref="ruleSettingRef" :disabled="formData.useTmpl" :rule="formData.rule" />
     </el-card>

+ 33 - 33
src/views/components/auto/auto-campaigns/view-target-rules.vue

@@ -123,39 +123,40 @@ async function fetchAdCampaign() {
       campaignId: props.checkTarget.campaignId
     });
 
-    // 计算并设置 targetLength
-    resp.data.adGroupInfo = resp.data.adGroupInfo.map(group => ({
-      ...group,
-      targetLength: (group.selectTargetId?.length || 0) + (group.keywordInfo?.length || 0) + (group.campaignTargetInfo?.length || 0)
-    }));
-
-    gridOptions.data = [resp.data];
-
-    // 默认勾选有 selectTargetId 的数据
-    const adGroupsToCheck = resp.data.adGroupInfo.filter(group =>
-        group.selectTargetId && group.selectTargetId.length > 0
-    );
-
-    // 更新选中的广告
-    selectedAds.value = [{
-      campaignId: resp.data.campaignId,
-      campaignType: resp.data.campaignType,
-      campaignName: resp.data.campaignName,
-      adGroupInfo: adGroupsToCheck
-    }];
-    console.log('selectedAds', selectedAds.value);
-
-    // 在 nextTick 中设置选中状态,确保表格已经渲染
-    nextTick(() => {
-      if (xGridOne.value) {
-        adGroupsToCheck.forEach(group => {
-          group.isSelected = true;
-          xGridOne.value.setCheckboxRow(group, true);
-        });
-      }
-    });
+    if (resp.code === 2000 && resp.data.length > 0){
+      // 计算并设置 targetLength
+      resp.data.adGroupInfo = resp.data.adGroupInfo.map(group => ({
+        ...group,
+        targetLength: (group.selectTargetId?.length || 0) + (group.keywordInfo?.length || 0) + (group.campaignTargetInfo?.length || 0)
+      }));
+
+      gridOptions.data = resp.data;
+
+      // 默认勾选有 selectTargetId 的数据
+      const adGroupsToCheck = resp.data.adGroupInfo.filter(group =>
+          group.selectTargetId && group.selectTargetId.length > 0
+      );
+
+      // 更新选中的广告
+      selectedAds.value = [{
+        campaignId: resp.data.campaignId,
+        campaignType: resp.data.campaignType,
+        campaignName: resp.data.campaignName,
+        adGroupInfo: adGroupsToCheck
+      }];
+
+      // 在 nextTick 中设置选中状态,确保表格已经渲染
+      nextTick(() => {
+        if (xGridOne.value) {
+          adGroupsToCheck.forEach(group => {
+            group.isSelected = true;
+            xGridOne.value.setCheckboxRow(group, true);
+          });
+        }
+      });
+    }
   } catch (error) {
-    ElMessage.error('请求广告活动数据失败');
+    console.log("=>(view-target-rules.vue:158) error", error);
   } finally {
     // gridOptions.loading = false;
   }
@@ -221,7 +222,6 @@ function handleConfirm({ campaignInfo, targetType }) {
 
 // 删除选中的广告
 const removeSelectedAd = async (row) => {
-  console.log('=>(view-target-rules.vue:224) row', row);
   const $grid = xGridTwo.value;
   if ($grid) {
     if (row.adGroupId) {

+ 1 - 1
src/views/components/auto/auto-templates/timer-bid.vue

@@ -76,7 +76,7 @@ if (props.mode === 'add') {
   for (let i = 0; i < 7; i++) {
     const tmp = [];
     for (let j = 0; j < 24; j++) {
-      tmp.push(0);
+      tmp.push(1);
     }
     formData.value.rule.conditions.push(tmp);
   }

+ 46 - 11
src/views/components/auto/search-term-bid.vue

@@ -9,11 +9,14 @@ import { computed } from 'vue';
 import STBidInput from './st-bid-input.vue';
 import XEUtils from 'xe-utils';
 
+
 interface Props {
   action: RuleSearchTermAction;
 }
 
 const props = defineProps<Props>();
+const keywordBidding = ref(false);
+const productBidding = ref(false);
 const MatchTypeMap = {
   EXACT: '精准匹配',
   BROAD: '广泛匹配',
@@ -21,9 +24,36 @@ const MatchTypeMap = {
 };
 if (!props.action.keywords) {
   props.action.keywords = [
-    { bidType: '1', type: 'increase', numType: '1', defaultBidding: '1.00', min: '', max: '', matchType: 'EXACT', use: false },
-    { bidType: '1', type: 'increase', numType: '1', defaultBidding: '1.00', min: '', max: '', matchType: 'BROAD', use: false },
-    { bidType: '1', type: 'increase', numType: '1', defaultBidding: '1.00', min: '', max: '', matchType: 'PHRASE', use: false },
+    {
+      bidType: '1',
+      type: 'increase',
+      numType: '1',
+      defaultBidding: '1.00',
+      min: '',
+      max: '',
+      matchType: 'EXACT',
+      use: false
+    },
+    {
+      bidType: '1',
+      type: 'increase',
+      numType: '1',
+      defaultBidding: '1.00',
+      min: '',
+      max: '',
+      matchType: 'BROAD',
+      use: false
+    },
+    {
+      bidType: '1',
+      type: 'increase',
+      numType: '1',
+      defaultBidding: '1.00',
+      min: '',
+      max: '',
+      matchType: 'PHRASE',
+      use: false
+    },
   ];
 }
 if (!props.action.target) {
@@ -54,17 +84,22 @@ defineExpose({ valid });
       <span class="custom-title-icon"></span>
       设置竞价
     </div>
-    <div>关键词竞价:</div>
-    <div v-for="(info, index) in action.keywords" class="bid">
+    <div>
+      <el-checkbox v-model="keywordBidding" label="关键词竞价" size="large"></el-checkbox>
+    </div>
+
+    <div v-for="(info, index) in action.keywords" v-if="keywordBidding" class="bid">
       <el-checkbox v-model="info.use" :label="info.matchType">{{ MatchTypeMap[info.matchType] }}</el-checkbox>
-      <STBidInput :bid-data="info" :index="index" v-show="info.use"></STBidInput>
+      <STBidInput v-show="info.use" :bid-data="info" :index="index"></STBidInput>
+    </div>
+    <div>
+      <el-checkbox v-model="productBidding" label="商品定向竞价" size="large"></el-checkbox>
     </div>
-    <div>商品定向竞价:</div>
-    <div class="bid target">
-      <el-checkbox label="target" v-model="action.target.use">竞价</el-checkbox>
-      <STBidInput :bid-data="action.target" v-show="action.target.use"></STBidInput>
+    <div v-if="productBidding" class="bid target">
+      <el-checkbox v-model="action.target.use" label="target">竞价</el-checkbox>
+      <STBidInput v-show="action.target.use" :bid-data="action.target"></STBidInput>
     </div>
-    <p v-show="!valid" style="font-size: small; color: #f56c6c">请至少设置一种竞价方式!</p>
+    <p v-show="!valid" style="font-size: small; color: #f56c6c">请至少选择一种竞价方式!</p>
   </div>
 </template>
 

+ 169 - 59
src/views/components/auto/target-rule-setting.vue

@@ -5,14 +5,15 @@
  * @Author: Cheney
  */
 
-import { ref, computed, onBeforeUnmount } from 'vue';
-import { Plus } from '@element-plus/icons-vue';
+import { computed, ref } from 'vue';
+import { Bottom, InfoFilled, Plus, Top } from '@element-plus/icons-vue';
 import conditionBuilder from '/@/components/conditionBuilder/index.vue';
 import InputFloat from '/@/components/input-float/index.vue';
 import { useSymbolOptions } from '/@/components/conditionBuilder/utils';
 import { ActionList, RuleNameMap } from './enum';
 import XEUtils from 'xe-utils';
 
+
 interface Props {
   rule: AutoRule;
   disabled?: boolean;
@@ -44,12 +45,16 @@ const candidateFields = [
   { label: '关键词名称', value: 'keywords', type: 'array' },
 ];
 
+const isVisible = ref(true);
+const setting = ref('edit');
+
 const { getSymbolOptions } = useSymbolOptions(candidateFields);
 const maxIsDisabled = ref([]);
 const minIsDisabled = ref([]);
 const ruleFormRef = ref();
 const condiBuilderRef = ref();
-const actionModel = ref('bid-ratio');
+const actionModel = ref([]);
+let ruleInfo = {};
 
 const maxRuleNumber = computed(() => {
   const ret = {
@@ -64,10 +69,10 @@ const maxRuleNumber = computed(() => {
   return ret;
 });
 
-onMounted(() => {
-  maxIsDisabled.value.push(!props.rule.action.max);
-  minIsDisabled.value.push(!props.rule.action.min);
-})
+
+watch(() => props.rule, (newRule) => {
+  ruleInfo = newRule.conditions[0].action;
+});
 
 function checkFloat(rule: any, value: any, callback: any) {
   if (XEUtils.toNumber(value) <= 0) {
@@ -115,15 +120,19 @@ function addRule(actionType: string, index: number = -1) {
   };
 
   if (index === -1) {
+    // 第一条规则
     props.rule.conditions.push(rule);
-    if (actionType === 'increase'){
-      maxIsDisabled.value.push(false);
-    }else if (actionType === 'decrease'){
-      minIsDisabled.value.push(false);
+    if (actionType === 'increase') {
+      actionModel.value.push('bid-ratio');
+      maxIsDisabled.value.push(ruleInfo.max !== '' && ruleInfo.max !== undefined);
+    } else if (actionType === 'decrease') {
+      actionModel.value.push('bid-ratio');
+      minIsDisabled.value.push(ruleInfo.min);
     }
   } else {
     props.rule.conditions.splice(index + 1, 0, rule);
     maxIsDisabled.value.splice(index + 1, 0, false); // 添加一个初始值
+    actionModel.value.splice(index + 1, 0, 'bid-ratio'); // 添加一个初始值
     minIsDisabled.value.splice(index + 1, 0, false); // 添加一个初始值
   }
 }
@@ -131,20 +140,21 @@ function addRule(actionType: string, index: number = -1) {
 if (props.rule.conditions.length === 0) {
   props.rule.campaignType = 'sp';
   props.rule.activeModel = 'campaign';
-  // addRule('increase', -1);
+  console.log('actionModel.value',actionModel.value);
+  addRule('increase', -1);
 }
 
-function delRule(index: number) {
+function delRule(actionType: string, index: number) {
   props.rule.conditions.splice(index, 1);
-  if (actionType === 'increase'){
+  if (actionType === 'increase') {
     maxIsDisabled.value.splice(index, 1);
-  }else if (actionType === 'decrease'){
+  } else if (actionType === 'decrease') {
     minIsDisabled.value.splice(index, 1);
   }
 }
 
-function changeAction(action: any) {
-  const [a, b] = actionModel.value.split('-');
+function changeAction(actionModel: any, action: any) {
+  const [a, b] = actionModel.split('-');
   action.baseType = a;
   action.numType = b;
   action.set = '';
@@ -174,25 +184,60 @@ async function validateRuleForm() {
 }
 
 async function validateForm() {
+  // 当没有勾选最大值、最小值时,清空action的max和min值
+  props.rule.conditions.forEach((condition, index) => {
+    if (!maxIsDisabled.value[index]) {
+      condition.action.max = '';
+    }
+    if (!minIsDisabled.value[index]) {
+      condition.action.min = '';
+    }
+  });
   const valid1 = await validateRuleForm();
   const valid2 = await validateConditionsForm();
   return valid1 && valid2;
 }
 
-function onMaxCheckboxChange(index: number) {
-  if (!maxIsDisabled.value[index]) {
-    // 如果复选框被取消勾选,禁用输入框并清空对应的值
-    props.rule.conditions[index].action.max = '';
+function handleClose() {
+  isVisible.value = false;
+}
+
+function moveUp(index) {
+  if (index > 0) {
+    const temp = props.rule.conditions[index];
+    props.rule.conditions[index] = props.rule.conditions[index - 1];
+    props.rule.conditions[index - 1] = temp;
+    // 更新ordering
+    updateOrdering();
   }
 }
 
-function onMinCheckboxChange(index: number) {
-  if (!minIsDisabled.value[index]) {
-    // 如果复选框被取消勾选,禁用输入框并清空对应的值
-    props.rule.conditions[index].action.min = '';
+function moveDown(index) {
+  if (index < props.rule.conditions.length - 1) {
+    const temp = props.rule.conditions[index];
+    props.rule.conditions[index] = props.rule.conditions[index + 1];
+    props.rule.conditions[index + 1] = temp;
+    // 更新ordering
+    updateOrdering();
   }
 }
 
+function updateOrdering() {
+  props.rule.conditions.forEach((condition, index) => {
+    condition.ordering = index + 1; // 重新计算ordering,从1开始
+  });
+}
+
+function getActionModelLabel(value) {
+  const option = ActionList.find(option => option.value === value);
+  return option ? option.label : '';
+}
+
+// 获取对应 set 值的后缀
+function getSetSuffix(numType) {
+  return numType === 'num' ? '$' : numType === 'ratio' ? '%' : '';
+}
+
 defineExpose({ validateForm });
 </script>
 
@@ -202,22 +247,42 @@ defineExpose({ validateForm });
       <span class="custom-title-icon"></span>
       规则设置
     </div>
-    <div class="rule-setting-container">
-      <div v-for="(info, index) in rule.conditions" class="rule-setting-item" :key="info.actionType + info.ordering">
+    <!--提示信息-->
+    <div>
+      <el-tag v-if="isVisible" class="custom-tag" closable color="#e7edf4" @close="handleClose">
+        <template #default>
+          <div class="tag-content">
+            <el-icon style="color: #0b52a7">
+              <InfoFilled />
+            </el-icon>
+            可设置多个规则,如果一个对象命中多个规则,系统只是执行优先级最高的规则
+          </div>
+        </template>
+      </el-tag>
+    </div>
+    <div class="mt-5">
+      <el-radio-group v-model="setting" size="small">
+        <el-radio-button label="编辑" value="edit" />
+        <el-radio-button label="排序" value="order" />
+      </el-radio-group>
+    </div>
+    <div v-if="setting === 'edit'" class="rule-setting-container">
+      <div v-for="(info, index) in rule.conditions" :key="info.actionType + info.ordering" class="rule-setting-item">
         <div class="rule-setting-title">
           <div class="asj-h3">{{ RuleNameMap[info.actionType] }} - 规则{{ info.ordering }}</div>
           <div class="rule-setting-opration">
-            <el-tooltip content="添加规则" placement="top" effect="dark" v-if="!disabled">
-              <el-icon @click="addRule(info.actionType, index)" :disabled="disabled">
+            <el-tooltip v-if="!disabled" content="添加规则" effect="dark" placement="top">
+              <el-icon :disabled="disabled" @click="addRule(info.actionType, index)">
                 <Plus />
               </el-icon>
             </el-tooltip>
-            <el-tooltip content="删除规则" placement="top" effect="dark" v-if="rule.conditions.length !== 1 && !disabled">
-              <el-icon @click="delRule(index)">
+            <el-tooltip v-if="rule.conditions.length !== 1 && !disabled" content="删除规则" effect="dark"
+                        placement="top">
+              <el-icon @click="delRule(info.actionType,index)">
                 <DeleteFilled />
               </el-icon>
             </el-tooltip>
-            <el-tooltip content="收起/展开规则" placement="top" effect="dark">
+            <el-tooltip content="收起/展开规则" effect="dark" placement="top">
               <el-icon @click="info.show = !info.show">
                 <ArrowDown />
               </el-icon>
@@ -225,62 +290,92 @@ defineExpose({ validateForm });
           </div>
         </div>
         <div v-show="info.show">
-          <el-form :inline="true" :model="info.action" ref="ruleFormRef" :disabled="disabled">
+          <el-form ref="ruleFormRef" :disabled="disabled" :inline="true" :model="info.action">
             <!-- 竞价规则下面的下拉框和两个输入框 -->
             <div v-if="info.actionType === 'increase' || info.actionType === 'decrease'">
               <el-form-item>
-                <el-select v-model="actionModel" @change="changeAction(info.action)" style="width: 250px">
+                <el-select v-model="actionModel[index]" style="width: 250px"
+                           @change="changeAction(actionModel[index], info.action)">
                   <el-option
-                    v-for="item in ActionList"
-                    :key="item.value"
-                    :value="item.value"
-                    :label="item.label.replace('_', info.actionType === 'increase' ? '升高' : '降低')">
+                      v-for="item in ActionList"
+                      :key="item.value"
+                      :label="item.label.replace('_', info.actionType === 'increase' ? '升高' : '降低')"
+                      :value="item.value">
                   </el-option>
                 </el-select>
               </el-form-item>
-              <el-form-item prop="set" :rules="{ validator: checkFloat }">
+              <el-form-item :rules="{ validator: checkFloat }" prop="set">
                 <InputFloat
-                  v-model="info.action.set"
-                  :prefix="info.action.numType === 'num' ? '$' : ''"
-                  :suffix="info.action.numType === 'ratio' ? '%' : ''"
-                  style="width: 120px">
+                    v-model="info.action.set"
+                    :prefix="info.action.numType === 'num' ? '$' : ''"
+                    :suffix="info.action.numType === 'ratio' ? '%' : ''"
+                    style="width: 120px">
                 </InputFloat>
               </el-form-item>
-              <el-form-item label="最大值" v-if="info.actionType === 'increase'" prop="max">
+              <el-form-item v-if="info.actionType === 'increase'" label="最大值" prop="max">
                 <template v-slot:label>
-                  <el-checkbox v-model="maxIsDisabled[index]" @change="onMaxCheckboxChange(index)">最大值</el-checkbox>
+                  <el-checkbox v-model="maxIsDisabled[index]">最大值</el-checkbox>
                 </template>
-                <InputFloat :disabled="!maxIsDisabled[index]" v-model="info.action.max" style="width: 120px" prefix="$"></InputFloat>
+                <InputFloat v-model="info.action.max" :disabled="!maxIsDisabled[index]" prefix="$"
+                            style="width: 120px"></InputFloat>
               </el-form-item>
-              <el-form-item label="最小值" v-else prop="min" >
+              <el-form-item v-else label="最小值" prop="min">
                 <template v-slot:label>
-                  <el-checkbox v-model="minIsDisabled[index]" @change="onMinCheckboxChange(index)">最小值</el-checkbox>
+                  <el-checkbox v-model="minIsDisabled[index]">最小值</el-checkbox>
                 </template>
-                <InputFloat :disabled="!minIsDisabled[index]" v-model="info.action.min" style="width: 120px" prefix="$"></InputFloat>
+                <InputFloat v-model="info.action.min" :disabled="!minIsDisabled[index]" prefix="$"
+                            style="width: 120px"></InputFloat>
               </el-form-item>
             </div>
             <el-form-item v-if="info.actionType === 'set'" prop="set">
-              <InputFloat v-model="info.action.set" style="width: 120px" prefix="$"></InputFloat>
+              <InputFloat v-model="info.action.set" prefix="$" style="width: 120px"></InputFloat>
             </el-form-item>
           </el-form>
           <conditionBuilder
-            :data="info.conditions"
-            :candidate-fields="candidateFields"
-            ref="condiBuilderRef"
-            :disabled="disabled" />
+              ref="condiBuilderRef"
+              :candidate-fields="candidateFields"
+              :data="info.conditions"
+              :disabled="disabled" />
         </div>
       </div>
-      <el-popover placement="bottom-start" trigger="hover" :disabled="disabled">
+      <el-popover :disabled="disabled" placement="bottom-start" trigger="hover">
         <template #reference>
-          <el-button :icon="Plus" type="warning" :disabled="disabled">添加规则</el-button>
+          <el-button :disabled="disabled" :icon="Plus" type="warning">添加规则</el-button>
         </template>
         <div class="popver-content">
-          <span class="popver-content-item" v-for="[name, title] of Object.entries(RuleNameMap)" @click="addRule(name)">{{
-            title
-          }}</span>
+          <span v-for="[name, title] of Object.entries(RuleNameMap)" class="popver-content-item" @click="addRule(name)">{{
+              title
+            }}</span>
         </div>
       </el-popover>
     </div>
+    <div v-if="setting === 'order'" class="mt-2">
+      <div v-for="(info, index) in rule.conditions" :key="info.actionType + info.ordering">
+        <el-row>
+          <el-col :span="20">
+            <el-card body-style="padding: 12px 10px;" class="rule-setting-item" draggable="true" shadow="hover">
+              {{ RuleNameMap[info.actionType] }} - 规则{{ info.ordering }}
+              <span v-if="info.actionType === 'increase' || info.actionType === 'decrease'">
+                 {{
+                  getActionModelLabel(actionModel[index]).replace('_', info.actionType === 'increase' ? '升高' : '降低')
+                }}
+                {{ info.action.set }}
+              <span v-if="info.action.set && info.action.numType">
+                {{ getSetSuffix(info.action.numType) }}
+              </span>
+              </span>
+            </el-card>
+          </el-col>
+          <el-col :span="4">
+            <div class="mt-3 pl-2">
+              <el-button :disabled="index === 0" :icon="Top" link type="primary" @click="moveUp(index)"></el-button>
+              <el-button :disabled="index === rule.conditions.length - 1" :icon="Bottom" link type="primary"
+                         @click="moveDown(index)"></el-button>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -336,4 +431,19 @@ defineExpose({ validateForm });
   position: absolute;
   transform: translateY(25%);
 }
+
+.tag-content {
+  display: block;
+  max-width: 100%;
+}
+
+.rule-setting-item {
+  width: 100%;
+  margin: 5px 0;
+  padding: 0;
+  border-radius: 0 !important;
+  font-size: 13px;
+  font-weight: 500;
+}
+
 </style>

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

@@ -69,6 +69,15 @@ export function updateAdCampaign(body) {
   });
 }
 
+// 广告关联活动
+export function getSelectedAdCampaign(query) {
+  return request({
+    url: '/api/ad_manage/template/selected-campaigns/',
+    method: 'GET',
+    params: query,
+  });
+}
+
 // 定向规则弹窗:获取定向规则列表
 export function getTargetingRuleList(query) {
   return request({
@@ -89,3 +98,4 @@ export function getProductsList(query) {
 
 
 
+

+ 161 - 33
src/views/efTools/automation/components/adActivityDialog.vue

@@ -5,19 +5,25 @@
  * @Author: xinyan
  */
 import { computed, onMounted, reactive, ref, toRefs, watch } from 'vue';
-import { getAdGroupList, getRelationCampaign, updateAdCampaign } from '/@/views/efTools/automation/api';
+import {
+  getAdGroupList,
+  getRelationCampaign,
+  getSelectedAdCampaign,
+  updateAdCampaign
+} 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';
 import { DocumentAdd } from '@element-plus/icons-vue';
-import { allCampaignTypes ,campaignStatus} from '/@/views/efTools/utils/enum';
+import { allCampaignTypes, campaignStatus } from '/@/views/efTools/utils/enum';
+
 
 const shopInfo = useShopInfo();
 const { profile } = storeToRefs(shopInfo);
 
 const props = defineProps({
-  rowData:{
+  rowData: {
     type: Object,
     required: true,
   },
@@ -26,23 +32,22 @@ const props = defineProps({
     required: true,
   },
 });
-const emits = defineEmits([ 'confirmSuccess']);
+const emits = defineEmits(['confirmSuccess']);
 const { rowData } = toRefs(props);
 const templateId = ref(rowData.value.id);
 const activeModel = ref(rowData.value.rule.activeModel);
 const campaignType = ref(rowData.value.rule.campaignType);
 const dialogVisible = defineModel({ default: false });
 
-
 // 定向规则
+const selected = ref([]); //存储后端中已存在的广告组
+
 const targetRuleDialogVisible = ref(false);
 const selectedTargetedRow = ref(null);
-const targetLength = ref(0);
 let selectedGroups = [];
 
 // 筛选条件
 const searchAdCampaign = ref('');
-// const selectedCampaignType = ref('');
 const selectedCampaignType = ref(campaignType.value || ''); // 默认选中当前的 campaignType
 const selectedAdGroup = ref('');
 const selectedStatus = ref('');
@@ -149,27 +154,27 @@ function handleAdCampaignChange() {
 }
 
 async function fetchAdCampaign() {
-  const savedAdCampaign = localStorage.getItem('searchAdCampaign');
-  if (savedAdCampaign) {
-    searchAdCampaign.value = JSON.parse(savedAdCampaign);
-  }
+  // const savedAdCampaign = localStorage.getItem('searchAdCampaign');
+  // if (savedAdCampaign) {
+  //   searchAdCampaign.value = JSON.parse(savedAdCampaign);
+  // }
   try {
     loading.value = true;
     // const cachedSelectedAds = [...selectedAds.value];
-    if (profile.value.profile_id && templateId.value){
-    const resp = await getRelationCampaign({
-      profileId: profile.value.profile_id,
-      templateId: templateId.value,
-      campaignName: searchAdCampaign.value,
-      portfolioId: selectedAdGroup.value,
-      campaignStatus: selectedStatus.value,
-      campaignType: selectedCampaignType.value,
-      page: currentPage.value,
-      limit: pageSize.value,
-    });
-    gridOptions.data = resp.data;
-    total.value = resp.total;
-    currentPage.value = resp.page;
+    if (profile.value.profile_id && templateId.value) {
+      const resp = await getRelationCampaign({
+        profileId: profile.value.profile_id,
+        templateId: templateId.value,
+        campaignName: searchAdCampaign.value,
+        portfolioId: selectedAdGroup.value,
+        campaignStatus: selectedStatus.value,
+        campaignType: selectedCampaignType.value,
+        page: currentPage.value,
+        limit: pageSize.value,
+      });
+      gridOptions.data = resp.data;
+      total.value = resp.total;
+      currentPage.value = resp.page;
     }
   } catch (error) {
     ElMessage.error('请求广告活动数据失败');
@@ -192,6 +197,16 @@ function handleGridChange({ records, row, checked }) {
     if (row) {
       if (!checked) {
         row.isSelected = false;
+        if (row.campaignGroupInfo) {
+          // 如果是父节点,清空所有子节点的 keywordInfo 和 campaignTargetInfo
+          row.campaignGroupInfo.forEach(group => {
+            group.keywordInfo = []; // 清空每个子节点的关键词信息
+            group.campaignTargetInfo = []; // 清空每个子节点的定向目标信息
+          });
+        } else if (row.keywordInfo || row.campaignTargetInfo) {
+          row.keywordInfo = []; // 清空关键词信息
+          row.campaignTargetInfo = []; // 清空定向目标信息
+        }
         updateSelectedAds();
       }
     } else {
@@ -213,7 +228,6 @@ function handleGridChange({ records, row, checked }) {
 function toggleCheckboxEvent(row) {
   if (activeModel.value === 'specified') {
     if (row.isSelected) {
-      // 只有已选择的行可以被取消选中
       xGridOne.value.setCheckboxRow(row, false);
       handleGridChange({ records: [row], row, checked: false });
     }
@@ -231,14 +245,21 @@ function handelSelect({ records }) {
   ];
 }
 
+// 处理一开始selectedAds.value为空时能正常
 function updateSelectedAds() {
-  selectedAds.value = gridOptions.data
+  // 获取当前选中广告组
+  const filteredAds = 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
       }));
+  // 合并新选中的广告组与现有的 selectedAds
+  selectedAds.value = [
+    ...selected.value, // 保留已存在的选中广告
+    ...filteredAds
+  ];
 }
 
 // 树形结构的表格选择变化
@@ -277,16 +298,23 @@ function handleSelectionChange({ records }) {
 
   // 更新选中的广告
   selectedAds.value = [
+    ...selectedAds.value.filter(ad => ad.page !== currentPage.value),
     ...updatedRecords.map(ad => ({ ...ad, page: currentPage.value })),
   ];
 }
 
 // 选择定向按钮
 function handleSelectTarget(row) {
-  // 获取父节点数据
-  const parent = gridOptions.data.find(campaign =>
-      campaign.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
-  );
+  let parent = null;
+  if (row.keywordInfo && row.keywordInfo.length > 0 || row.campaignTargetInfo && row.campaignTargetInfo.length > 0) {
+    parent = selectedAds.value.find(campaign =>
+        campaign.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
+    );
+  } else {
+    parent = gridOptions.data.find(campaign =>
+        campaign.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
+    );
+  }
   selectedTargetedRow.value = {
     campaignType: parent.campaignType,
     campaignId: parent.campaignId,
@@ -333,6 +361,71 @@ function handleConfirm({ campaignInfo, targetType }) {
 
 // 删除选中的广告
 const removeSelectedAd = async (row) => {
+  if (activeModel.value === 'specified'){
+    removeSpecificAd(row)
+  }else {
+    await removedAd(row)
+  }
+};
+
+async function removeSpecificAd(row) {
+  const $grid = xGridTwo.value;
+  if ($grid) {
+    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);
+      // 更新 selected.value
+      selected.value = selected.value.map(ad => {
+        // 仅在父节点存在时进行过滤
+        if (ad.campaignGroupInfo) {
+          // 过滤掉与 row.adGroupId 匹配的子节点
+          const filteredGroupInfo = ad.campaignGroupInfo.filter(group => group.adGroupId !== row.adGroupId);
+          return {
+            ...ad,
+            campaignGroupInfo: filteredGroupInfo // 更新子节点
+          };
+        }
+        return ad; // 返回未修改的广告
+      }).filter(ad => ad.campaignGroupInfo.length > 0); // 删除无子节点的父节点
+
+    } else {
+      // 删除父节点(广告活动)
+      selectedAds.value = selectedAds.value.filter(ad => ad.campaignId !== row.campaignId);
+      console.log("=>(adActivityDialog.vue:394) selectedAds.value", selectedAds.value);
+      // 更新 selected.value
+      selected.value = selected.value.filter(ad => ad.campaignId !== row.campaignId);
+    }
+
+    await $grid.remove(row);
+  }
+
+  if (xGridOne.value) {
+    // 始终取消选中当前行
+    await xGridOne.value.setCheckboxRow(row, false);
+    handleGridChange({ records: [row], row, checked: false });
+
+    if (!row.isSelected) {
+      // 如果当前行不是选中的状态,查找并取消选中对应的子节点
+      const parentRow = xGridOne.value.data.find(ad => ad.campaignId === row.campaignId);
+      if (parentRow && parentRow.campaignGroupInfo) {
+        parentRow.campaignGroupInfo.forEach(group => {
+          // 将子节点的 isSelected 设置为 false
+          group.isSelected = false;
+        });
+      }
+    }
+  }
+}
+
+const removedAd = async (row) => {
   const $grid = xGridTwo.value;
   if ($grid) {
     if (row.adGroupId) {
@@ -356,14 +449,29 @@ const removeSelectedAd = async (row) => {
 
   if (xGridOne.value) {
     await xGridOne.value.toggleCheckboxRow(row);
+    row.isSelected = false;
   }
 };
 
 function removeAllSelectedAds() {
+  // 清空 selectedAds 中的所有值
   selectedAds.value = [];
+
   const $grid = xGridOne.value;
   if ($grid) {
+    // 清除表格中所有行的勾选状态
     $grid.clearCheckboxRow();
+
+    // 循环遍历 gridOptions.data,设置所有子节点的 isSelected 为 false 并清空相关信息
+    gridOptions.data.forEach(ad => {
+      if (ad.campaignGroupInfo) {
+        ad.campaignGroupInfo.forEach(group => {
+          group.isSelected = false; // 设置子节点的 isSelected 为 false
+          group.keywordInfo = []; // 清空每个子节点的关键词信息
+          group.campaignTargetInfo = []; // 清空每个子节点的定向目标信息
+        });
+      }
+    });
   }
 }
 
@@ -371,7 +479,6 @@ function cancel() {
   dialogVisible.value = false;
 }
 
-//TODO: 确认按钮-adGroupInfo
 async function confirm() {
   const campaignItems = selectedAds.value.map(ad => ({
     campaignId: ad.campaignId,
@@ -402,7 +509,7 @@ async function confirm() {
             }))
         );
       }
-      if(activeModel.value === 'adGroup'){
+      if (activeModel.value === 'adGroup') {
         if (!adGroupInfo.includes(group.adGroupId)) {
           adGroupInfo.push(group.adGroupId); // 直接推送 adGroupId
         }
@@ -431,6 +538,26 @@ async function confirm() {
   }
 }
 
+// 获取已添加定向的广告
+async function getSelectedAds() {
+  const resp = await getSelectedAdCampaign({
+    templateId: templateId.value,
+  });
+  selected.value = resp.data;
+  // 处理已选择的广告
+  if (activeModel.value === 'specified') {
+    selected.value.forEach(ad => {
+      if (ad.campaignGroupInfo && ad.campaignGroupInfo.length > 0) {
+        ad.campaignGroupInfo.forEach(group => {
+          group.isSelected = true;
+          group.targetLength = group.keywordInfo?.length + (group.campaignTargetInfo?.length || 0);
+        });
+      }
+    });
+  }
+  selectedAds.value.push(...selected.value);
+}
+
 // 获取广告组下拉框
 async function fetchAdGroupList() {
   try {
@@ -496,6 +623,7 @@ watch(selectedStatus, () => {
 onMounted(() => {
   fetchAdGroupList();
   fetchAdCampaign();
+  getSelectedAds();
 });
 
 </script>

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

@@ -95,8 +95,8 @@ async function fetchTargetRuleList() {
         isSelected: selectedTargetedRow.value.keywordInfo.some(keyword => keyword.keywordId === item.keywordId)
       }));
     } else if (targetType.value === 'target') {
-      gridOptions.rowConfig.height = 85;
       gridOptions.columns = targetColumn;
+      gridOptions.rowConfig.height = 85;
       gridOptions.data = resp.data.targetData.map(item => ({
         ...item,
         isSelected: selectedTargetedRow.value.campaignTargetInfo.some(target => target.targetId === item.targetId)

+ 1 - 0
src/views/reportManage/TaskManage/index.vue

@@ -147,6 +147,7 @@ const gridOptions = reactive<VxeGridProps<RowVO>>({
     trigger: 'click',
     mode: 'row',
     showStatus: true,
+    showIcon:false,
     //autoClear: false,
   },
   checkboxConfig: {

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

@@ -2,7 +2,7 @@ import { ref } from 'vue';
 
 // 任务管理表格列
 export const taskColumns = ref([
-  { type: 'checkbox', width: 50 ,fixed: 'left'},
+  { type: 'checkbox', width: 50 ,fixed: 'left',align: 'center' },
   {
     field: 'platformNumber',
     title: '平台编号',
@@ -18,7 +18,7 @@ export const taskColumns = ref([
     editRender: { autofocus: '.vxe-input--inner' },
     slots: { edit: 'name_edit' },
     align: 'center',
-    minWidth: 98, isEditing: false
+    minWidth: 95, isEditing: false
   },
   {
     field: 'country',

+ 0 - 1
src/views/reportManage/dataCenter/normalDisplay/components/DateTendency/index.vue

@@ -351,7 +351,6 @@ function changeMetric() {
   chartObj.setOption(option, true); // 使用 true 作为第二个参数,强制完全刷新
 
   // 获取所有 y 轴的刻度标签
-  console.log('123', statDim.value);
   if (statDim.value != 'week') {
     const salesLabels = ['销售额', '上年销售额', '上月销售额'];
     let commonMin = Infinity;

+ 1 - 0
src/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue

@@ -100,6 +100,7 @@ async function fetchFilteredData() {
   }
 
   filteredData.value = filters;
+  console.log("=>(index.vue:103) filters", filters);
   try {
     const response = await getTasksId(filters);
     updateData.value = response.data.map(item => item.id).join(',');