Explorar o código

完成广告组详情的定向、否定版块

guojing_wu hai 1 ano
pai
achega
01423356c4
Modificáronse 20 ficheiros con 871 adicións e 6 borrados
  1. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/api.ts
  2. 95 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/crud.tsx
  3. 67 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/index.vue
  4. 12 6
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/index.vue
  5. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/api.ts
  6. 95 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/crud.tsx
  7. 67 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/index.vue
  8. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/api.ts
  9. 88 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/crud.tsx
  10. 74 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/index.vue
  11. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/api.ts
  12. 63 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/crud.tsx
  13. 35 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/index.vue
  14. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/api.ts
  15. 66 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/crud.tsx
  16. 35 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/index.vue
  17. 11 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/api.ts
  18. 66 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/crud.tsx
  19. 26 0
      src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/index.vue
  20. 16 0
      src/views/adManage/utils/enum.ts

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/targets/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 95 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/crud.tsx

@@ -0,0 +1,95 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: true,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: true,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        keyword: {
+          title: '目标群体',
+          column: {
+            width: '200px',
+            fixed: 'left',
+            align: 'center',
+          },
+        },
+        state: {
+          title: '状态',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        suggestedBid: {
+          title: '建议竞价',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        bid: {
+          title: '出价',
+          column: {
+            width: '60px',
+            align: 'center',
+          },
+        },
+        // currentBid: {
+        //   title: '当前竞价',
+        //   column: {
+        //     width: '100px',
+        //     align: 'center',
+        //   },
+        // },
+        ...BaseColumn,
+      },
+    },
+  }
+}

+ 67 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/autoTarget/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+      <template #search-left>
+        <DateRangePicker v-model="dateRange"></DateRangePicker>
+      </template>
+
+      <template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <DataCompare
+          :field="field"
+          :value="scope.row[field]"
+          :prev-val="scope.row[`prev${field}`]"
+          :gap-val="scope.row[`gap${field}`]"
+          :date-range="dateRange"
+          :show-compare="showCompare"
+        />
+      </template>
+      <template #toolbar-left>
+        <div class="campare-switch">
+          <span>数据对比 </span>
+          <el-switch v-model="showCompare" size="small" />
+        </div>
+      </template>
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+// import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
+import { LocationQueryValue } from 'vue-router'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import DataCompare from '/@/components/dataCompare/index.vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+// const shopInfo = useShopInfo()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+// const { profile } = storeToRefs(shopInfo)
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+  dateRange,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+const showCompare = ref(false)
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 12 - 6
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/index.vue

@@ -17,26 +17,26 @@
       </el-tab-pane>
       <template v-if="route.query.targetingType === 'automatic'">
         <el-tab-pane label="定向" name="tab2">
-          <div v-if="tabActiveName === 'tab2'">定向</div>
+          <AutoTarget v-if="tabActiveName === 'tab2'" :adGroupId="route.query.adGroupId">定向</AutoTarget>
         </el-tab-pane>
         <el-tab-pane label="否定投放" name="tab3">
-          <div v-if="tabActiveName === 'tab3'">否定商品</div>
+          <NegTarget v-if="tabActiveName === 'tab3'" :adGroupId="route.query.adGroupId">否定投放</NegTarget>
         </el-tab-pane>
       </template>
       <template v-else-if="route.query.targetingType ==='product'">
         <el-tab-pane label="商品投放" name="tab2">
-          <div v-if="tabActiveName === 'tab2'">商品投放</div>
+          <ManualTarget v-if="tabActiveName === 'tab2'" :adGroupId="route.query.adGroupId">商品投放</ManualTarget>
         </el-tab-pane>
         <el-tab-pane label="否定商品" name="tab3">
-          <div v-if="tabActiveName === 'tab3'">否定商品</div>
+          <NegProduct v-if="tabActiveName === 'tab3'" :adGroupId="route.query.adGroupId">否定商品</NegProduct>
         </el-tab-pane>
       </template>
       <template v-else>
         <el-tab-pane label="关键词" name="tab2">
-          <div v-if="tabActiveName === 'tab2'">关键词</div>
+          <Keyword v-if="tabActiveName === 'tab2'" :ad-group-id="route.query.adGroupId">关键词</Keyword>
         </el-tab-pane>
         <el-tab-pane label="否定词" name="tab3">
-          <div v-if="tabActiveName === 'tab3'">否定词</div>
+          <NegKeyword v-if="tabActiveName === 'tab3'" :ad-group-id="route.query.adGroupId">否定词</NegKeyword>
         </el-tab-pane>
       </template>
       <el-tab-pane label="搜索关键词" name="searchTerm">
@@ -55,6 +55,12 @@ import { useShopInfo } from '/@/stores/shopInfo'
 import { storeToRefs } from 'pinia'
 import Ads from './ads/index.vue'
 import SearchTerm from './searchTerm/index.vue'
+import Keyword from './keyword/index.vue'
+import AutoTarget from './autoTarget/index.vue'
+import ManualTarget from './manualTarget/index.vue'
+import NegProduct from './negProduct/index.vue'
+import NegKeyword from './negKeyword/index.vue'
+import NegTarget from './negTarget/index.vue'
 
 const tabActiveName = ref('adProducts')
 const shopInfo = useShopInfo()

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/targets/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 95 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/crud.tsx

@@ -0,0 +1,95 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: true,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        keywordText: {
+          title: '关键词',
+          column: {
+            width: '200px',
+            fixed: 'left',
+            align: 'center',
+          },
+        },
+        state: {
+          title: '状态',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        suggestedBid: {
+          title: '建议竞价',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        bid: {
+          title: '出价',
+          column: {
+            width: '60px',
+            align: 'center',
+          },
+        },
+        // currentBid: {
+        //   title: '当前竞价',
+        //   column: {
+        //     width: '100px',
+        //     align: 'center',
+        //   },
+        // },
+        ...BaseColumn,
+      },
+    },
+  }
+}

+ 67 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/keyword/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+      <template #search-left>
+        <DateRangePicker v-model="dateRange"></DateRangePicker>
+      </template>
+
+      <template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <DataCompare
+          :field="field"
+          :value="scope.row[field]"
+          :prev-val="scope.row[`prev${field}`]"
+          :gap-val="scope.row[`gap${field}`]"
+          :date-range="dateRange"
+          :show-compare="showCompare"
+        />
+      </template>
+      <template #toolbar-left>
+        <div class="campare-switch">
+          <span>数据对比 </span>
+          <el-switch v-model="showCompare" size="small" />
+        </div>
+      </template>
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+// import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
+import { LocationQueryValue } from 'vue-router'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import DataCompare from '/@/components/dataCompare/index.vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+// const shopInfo = useShopInfo()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+// const { profile } = storeToRefs(shopInfo)
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+  dateRange,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+const showCompare = ref(false)
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/targets/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 88 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/crud.tsx

@@ -0,0 +1,88 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: true,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        resolvedExpression: {
+          title: '商品',
+          column: {
+            width: '300px',
+            fixed: 'left',
+            align: 'center',
+          },
+        },
+        state: {
+          title: '状态',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        suggestedBid: {
+          title: '建议竞价',
+          column: {
+            width: '100px',
+            align: 'center',
+          },
+        },
+        bid: {
+          title: '出价',
+          column: {
+            width: '60px',
+            align: 'center',
+          },
+        },
+        ...BaseColumn,
+      },
+    },
+  }
+}

+ 74 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/manualTarget/index.vue

@@ -0,0 +1,74 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+      <template #search-left>
+        <DateRangePicker v-model="dateRange"></DateRangePicker>
+      </template>
+
+      <template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <DataCompare
+          :field="field"
+          :value="scope.row[field]"
+          :prev-val="scope.row[`prev${field}`]"
+          :gap-val="scope.row[`gap${field}`]"
+          :date-range="dateRange"
+          :show-compare="showCompare"
+        />
+      </template>
+      <template #toolbar-left>
+        <div class="campare-switch">
+          <span>数据对比 </span>
+          <el-switch v-model="showCompare" size="small" />
+        </div>
+      </template>
+
+      <template #cell_resolvedExpression="scope">
+        <p v-for="[key, val] of Object.entries(scope.row.resolvedExpression)">
+          {{ TargetExpressionEnum.find((item) => item.value === key)?.label }}:{{ val }}
+        </p>
+      </template>
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+// import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
+import { LocationQueryValue } from 'vue-router'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import DataCompare from '/@/components/dataCompare/index.vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+import { TargetExpressionEnum } from '/@/views/adManage/utils/enum'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+// const shopInfo = useShopInfo()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+// const { profile } = storeToRefs(shopInfo)
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+  dateRange,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+const showCompare = ref(false)
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/negativekeyword/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 63 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/crud.tsx

@@ -0,0 +1,63 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: false,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: true,
+      },
+      columns: {
+        keywordText: {
+          title: '否定词',
+        },
+        matchType: {
+          title: '匹配类型',
+        },
+      },
+    },
+  }
+}

+ 35 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negKeyword/index.vue

@@ -0,0 +1,35 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+import { LocationQueryValue } from 'vue-router'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/negativetarget/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 66 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/crud.tsx

@@ -0,0 +1,66 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: false,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        resolvedExpression_type: {
+          title: '否定类型',
+        },
+        product: {
+          title: '商品和品牌',
+        },
+        resolvedExpression_value: {
+          title: 'SKU/ASIN',
+        },
+      },
+    },
+  }
+}

+ 35 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negProduct/index.vue

@@ -0,0 +1,35 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+import { LocationQueryValue } from 'vue-router'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/negativetarget/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 66 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/crud.tsx

@@ -0,0 +1,66 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: false,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        resolvedExpression_type: {
+          title: '否定类型',
+        },
+        product: {
+          title: '商品和品牌',
+        },
+        resolvedExpression_value: {
+          title: 'SKU/ASIN',
+        },
+      },
+    },
+  }
+}

+ 26 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/negTarget/index.vue

@@ -0,0 +1,26 @@
+<template>
+  <el-tabs v-model="tabActiveName">
+    <el-tab-pane label="否定商品" name="negProduct">
+      <NegProduct v-if="tabActiveName === 'negProduct'" :ad-group-id="props.adGroupId"/>
+    </el-tab-pane>
+    <el-tab-pane label="否定词" name="negKeyword">
+      <NegKeyword v-if="tabActiveName === 'negKeyword'" :ad-group-id="props.adGroupId"/>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { LocationQueryValue } from 'vue-router'
+import NegProduct from '../negProduct/index.vue'
+import NegKeyword from '../negKeyword/index.vue'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+const tabActiveName = ref('negProduct')
+
+</script>
+
+<style scoped></style>

+ 16 - 0
src/views/adManage/utils/enum.ts

@@ -97,3 +97,19 @@ export const barOptionsMap = {
     'TopOfSearchImpressionShare': '搜索结果顶部展示份额'
 }
 
+export const TargetExpressionEnum = [
+  { label: '类目', value: 'ASIN_CATEGORY_SAME_AS' },
+  { label: '品牌', value: 'ASIN_BRAND_SAME_AS' },
+  { label: '价格小于', value: 'ASIN_PRICE_LESS_THAN' },
+  { label: '价格区间', value: 'ASIN_PRICE_BETWEEN' },
+  { label: '价格大于', value: 'ASIN_PRICE_GREATER_THAN' },
+  { label: '评分小于', value: 'ASIN_REVIEW_RATING_LESS_THAN' },
+  { label: '评分区间', value: 'ASIN_REVIEW_RATING_BETWEEN' },
+  { label: '评分大于', value: 'ASIN_REVIEW_RATING_GREATER_THAN' },
+  { label: 'ASIN', value: 'ASIN_SAME_AS' },
+  // { label: '', value: 'ASIN_IS_PRIME_SHIPPING_ELIGIBLE' },
+  // { label: '', value: 'ASIN_AGE_RANGE_SAME_AS' },
+  // { label: '', value: 'ASIN_GENRE_SAME_AS' },
+  // { label: '', value: 'ASIN_EXPANDED_FROM' },
+  { label: '其它', value: 'OTHER' },
+]