Explorar el Código

新增: 展示型推广SD-受众页面

WanGxC hace 1 año
padre
commit
1ea8f81418

+ 87 - 0
src/views/adManage/sd/audiences/api.ts

@@ -0,0 +1,87 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+import XEUtils from 'xe-utils';
+
+export const apiPrefix = '/api/ad_manage/sdtarget/report/';
+export function GetList(query: UserPageQuery) {
+    query["tactic"] = "T00030"
+    return request({
+        url: '/api/ad_manage/sdtarget/',
+        method: 'get',
+        params: query,
+    })
+}
+export function GetObj(id: any) {
+    return request({
+        url: apiPrefix + id + "/",
+        method: 'get',
+    });
+}
+
+export function AddObj(obj: AddReq) {
+    return request({
+        url: apiPrefix,
+        method: 'post',
+        data: obj,
+    });
+}
+
+export function UpdateObj(obj: EditReq) {
+    return request({
+        url: apiPrefix + obj.id + '/',
+        method: 'put',
+        data: obj,
+    });
+}
+
+export function DelObj(id: DelReq) {
+    return request({
+        url: apiPrefix + id + '/',
+        method: 'delete',
+        data: { id },
+    });
+}
+
+export function getCardData(query: UserPageQuery) {
+    query["tactic"] = "T00030"
+    return request({
+        url: apiPrefix + "amount",
+        method: 'GET',
+        params: query
+    })
+}
+
+export function getLineData(query: UserPageQuery) {
+    query["tactic"] = "T00030"
+    return request({
+        url: apiPrefix + "trend/daily",
+        method: 'GET',
+        params: query
+    })
+}
+
+export function getLineWeekData(query: UserPageQuery) {
+    query["tactic"] = "T00030"
+    return request({
+        url: apiPrefix + "trend/weekly",
+        method: 'GET',
+        params: query
+    })
+}
+
+export function getLineMonthData(query: UserPageQuery) {
+    query["tactic"] = "T00030"
+    return request({
+        url: apiPrefix + "trend/monthly",
+        method: 'GET',
+        params: query
+    })
+}
+
+export function getAdStructureData(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + "structure/",
+        method: 'GET',
+        params: query
+    })
+}

+ 236 - 0
src/views/adManage/sd/audiences/chartComponents/adStruct.vue

@@ -0,0 +1,236 @@
+<template>
+    <div v-loading="loading">
+        <el-row :gutter="5">
+            <el-col :span="24">
+                <div style="margin-left: 45%">
+                    <span style="background: #3a83f7; width: 18px; height: 10px; margin-top: 8px; display: inline-block; border-radius: 3px;"></span>
+                    <TextSelector v-model="barModelValue1" :options="computedBarOptions1" @change="changeBarOne" style="margin-top: 5px; margin-left: 8px;"/>
+                    <span style="background: #f19a37; width: 18px; height: 10px; margin-top: 8px; margin-left: 20px; display: inline-block; border-radius: 3px;"></span>
+                    <TextSelector v-model="barModelValue2" :options="computedBarOptions2" @change="changeBarTwo" style="margin-top: 5px; margin-left: 8px;"/>
+                </div>
+                <div ref="bar" style="height: 400px;"></div>
+            </el-col>
+        </el-row>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, inject, computed, watch } from "vue"
+import * as echarts from "echarts"
+import TextSelector from '/@/components/TextSelector/index.vue'
+import { getAdStructureData } from "/@/views/adManage/sp/targets/api"
+import { createDisabledOptions } from '../../../utils/dropdowndisable'
+import { barOptions1, barOptions2, barOptionsMap } from "/@/views/adManage/utils/enum"
+import { useShopInfo } from '/@/stores/shopInfo'
+
+
+const shopInfo = useShopInfo()
+let barChart = ref()
+const pie = ref()
+const bar = ref()
+const loading = ref(true)
+
+const dateRange = inject('dateRange')
+
+// 下拉框相关
+let barModelValue1 = ref(barOptions1[0].value)
+let barModelValue2 = ref(barOptions2[2].value)
+
+onMounted(async () => {
+    barChart = echarts.init(bar.value)
+
+    window.addEventListener('resize', resizeChart)  // 监听窗口大小变化,调整图表大小
+    setTimeout(() => {
+        resizeChart()
+    }, 0)
+
+    await initBarData()
+    initChart()
+})
+
+// 获取总数据
+let allData = null
+
+async function setAdStructureData() {
+    allData = await getAdStructureData({ startDate: dateRange.value[0], endDate: dateRange.value[1], profileId: shopInfo.profile.profile_id })
+    return allData.data
+}
+
+// 柱状图总数据
+let barData = null
+let responseData = null
+// 柱状图初始数据
+let ACOSList
+let SpendList
+let xAxisList
+let xAxisMapList
+
+async function initBarData() {
+    responseData = await setAdStructureData()
+    barData = responseData
+    // 柱状图初始化数据
+    ACOSList = barData.map(item => item.ACOS)
+    SpendList = barData.map(item => item.Spend)
+    // 将x轴映射为中文
+    xAxisList = barData.map(item => item.Classification)
+    const classificationMap = {
+        'BROAD': '关键词-广泛',
+        'category': '品类',
+        'EXACT': '关键词-精准',
+        'asin': '商品',
+        'PHRASE': '关键词-词组',
+        'close-match': '紧密匹配',
+        'loose-match': '广泛匹配',
+        'substitutes': '同类商品',
+        'complements': '关联商品'
+    }
+    xAxisMapList = xAxisList.map(item => classificationMap[item])
+    loading.value = false
+}
+
+// 重置图像
+let flag = ref()
+let option
+let option2
+
+// 点击下拉框后重新渲染柱状图
+function changeBarOne(newValue) {
+    barModelValue1.value = newValue
+    updateBarChart()
+}
+
+function changeBarTwo(newValue) {
+    barModelValue2.value = newValue
+    updateBarChart()
+}
+
+function updateBarChart() {
+    const barValues1 = barData.map(item => item[barModelValue1.value])
+    const barValues2 = barData.map(item => item[barModelValue2.value])
+
+    option.series[0].data = barValues1
+    option.series[1].data = barValues2
+    barChart.setOption(option)
+}
+
+// 监听时间变化重新渲染表格
+watch(dateRange, async () => {
+    if (dateRange.value) {
+        loading.value = true
+        const resp = await setAdStructureData()
+        updateBarChartData(resp)
+        loading.value = false
+    }
+})
+
+// 根据新数据和当前下拉框选择更新 柱状图数据
+function updateBarChartData(resp) {
+    const barValues1 = resp.map(item => item[barModelValue1.value])
+    const barValues2 = resp.map(item => item[barModelValue2.value])
+
+    option.series[0].data = barValues1
+    option.series[1].data = barValues2
+    barChart.setOption(option)
+}
+
+const computedBarOptions1 = computed(() => createDisabledOptions(barOptions1, barModelValue2.value, barModelValue1.value))
+const computedBarOptions2 = computed(() => createDisabledOptions(barOptions2, barModelValue1.value, barModelValue2.value))
+
+// 初始化图表
+function initChart() {
+    // 柱状图配置
+    option = {
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+                type: 'shadow',
+                label: {
+                    backgroundColor: '#6a7985'
+                }
+            }
+        },
+        // legend: {data: ['数据1', '数据2'],},
+        toolbox: {
+            feature: {
+                saveAsImage: { yAxisIndex: 'none' }
+            }
+        },
+        grid: { top: 50, right: 60, bottom: 50, left: 60 },
+        xAxis: [
+            {
+                type: 'category',
+                boundaryGap: true,
+                data: xAxisMapList,
+            },
+        ],
+        yAxis: [
+            {
+                type: 'value',
+                axisLine: {
+                    show: true,
+                    lineStyle: {
+                        color: '#3a83f7' // 第一个 Y 轴的颜色
+                    }
+                }
+            },
+            {
+                type: 'value',
+                splitLine: {
+                    show: false
+                },
+                axisLine: {
+                    show: true,
+                    lineStyle: {
+                        color: '#f19a37' // 第一个 Y 轴的颜色
+                    }
+                }
+            }
+        ],
+        series: [
+            {
+                name: barOptionsMap[barModelValue1.value],
+                type: 'bar',
+                barWidth: '3%',
+                data: ACOSList,
+                yAxisIndex: 0,
+                itemStyle: {
+                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                      { offset: 0, color: '#3a83f7' }, // 起始的鲜亮蓝色
+                      { offset: 0.5, color: '#5a9ef4' }, // 中间色,中度蓝色
+                      { offset: 1, color: '#8ab6f1' } // 结束的浅蓝色
+                    ]),
+                    // 柱状图圆角
+                    borderRadius: [6, 6, 6, 6],
+                },
+            },
+            {
+                name: barOptionsMap[barModelValue2.value],
+                type: 'bar',
+                barWidth: '3%',
+                data: SpendList,
+                yAxisIndex: 1,
+                itemStyle: {
+                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                      { offset: 0, color: '#f19a37' },
+                      { offset: 0.5, color: '#f7b96c' }, // 中间色,浅橙色
+                      { offset: 1, color: 'rgb(234, 207, 135)' } // 结束的浅黄色
+                    ]),
+                    // 柱状图圆角
+                    borderRadius: [6, 6, 6, 6],
+                },
+            },
+        ],
+    }
+    barChart.setOption(option)
+    resizeChart()
+}
+
+function resizeChart() {
+    barChart.resize()
+}
+defineExpose({ resizeChart })
+</script>
+
+<style scoped>
+
+</style>

+ 186 - 0
src/views/adManage/sd/audiences/crud.tsx

@@ -0,0 +1,186 @@
+import * as api from './api'
+import {AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery} from '@fast-crud/fast-crud'
+import {inject} from 'vue'
+import {SdBaseColumn} 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 editRequest = async ({form, row}: EditReq) => {
+    form.id = row.id
+    return await api.UpdateObj(form)
+  }
+  const delRequest = async ({row}: DelReq) => {
+    return await api.DelObj(row.id)
+  }
+  const addRequest = async ({form}: AddReq) => {
+    return await api.AddObj(form)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {
+        height: 800,
+        headerCellStyle: {
+          backgroundColor: '#f6f7fa', // 直接设置背景颜色
+          height: '20px',
+          borderRight: 'none',
+        },
+        cellStyle: {
+          border: 'none',
+          borderBottom: '0.5px solid #ddd',
+        },
+        showSummary: true,
+        stripe: false
+      },
+      container: {
+        fixedHeight: false
+      },
+      actionbar: {
+        show: false,
+        buttons: {
+          add: {
+            show: false
+          },
+        }
+      },
+      search: {
+        show: false
+      },
+      toolbar: {
+        buttons: {
+          search: {
+            show: true
+          },
+          compact: {
+            show: false
+          }
+        }
+      },
+      request: {
+        pageRequest,
+        addRequest,
+        editRequest,
+        delRequest,
+      },
+      rowHandle: {
+        fixed: 'right',
+        width: 100,
+        align: 'center',
+        buttons: {
+          view: {
+            show: false,
+          },
+          edit: {
+            iconRight: 'Edit',
+            type: 'text',
+            text: null
+            // show: hasPermissions('dictionary:Update'),
+          },
+          remove: {
+            show: false
+            // iconRight: 'Delete',
+            // type: 'text',
+            // text: null
+            // show: hasPermissions('dictionary:Delete'),
+          },
+        },
+      },
+      columns: {
+        id: {
+          title: 'ID',
+          column: {
+            show: false
+          },
+          form: {
+            show: false
+          }
+        },
+        expression:{
+          title: '定向投放',
+          column: {
+            fixed: 'left',
+            width: 300,
+            sortable: true
+          },
+        },
+        resolvedExpression: {
+          title: '类型',
+          column: {
+            align: 'center',
+            width: 130,
+            sortable: true
+          },
+        },
+        state: {
+          title: '状态',
+          column: {
+            width: '90px',
+            align: 'center',
+            sortable: true,
+          },
+          type: 'dict-select',
+          search: {
+            show: true
+          },
+          dict: dict({
+            data: [
+              {value: 'paused', label: '已暂停', color: 'warning'},
+              {value: 'enabled', label: '投放中', color: 'success'},
+            ]
+          })
+        },
+        campaignName: {
+          title: '广告活动名称',
+          column: {
+            width: 180,
+          }
+        },
+        adGroupName: {
+          title: '广告组名称',
+          column: {
+            width: 180,
+          }
+        },
+        suggestedBid: {
+          title: '建议竞价',
+          column: {
+            width: 130,
+            align: 'right'
+          }
+        },
+        bid: {
+          title: '出价',
+          column: {
+            width: 80,
+            align: 'center',
+            sortable: true,
+            formatter: (row) => {
+              return '$' + row.value
+            }
+          }
+        },
+        suggestedBid_lower: {
+          column: {
+            show: false,
+          }
+        },
+        suggestedBid_upper: {
+          column: {
+            show: false,
+          }
+        },
+
+        ...SdBaseColumn
+      }
+    }
+  }
+}

+ 238 - 0
src/views/adManage/sd/audiences/index.vue

@@ -0,0 +1,238 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+      <template #header-middle>
+        <el-tabs v-model="tabActiveName" class="chart-tabs" type="border-card" @tab-change="changeTab">
+          <el-tab-pane label="数据趋势" name="dataTendency">
+            <DataTendencyChart
+              :query="queryParams"
+              v-if="tabActiveName === 'dataTendency'"
+              :fetchCard="getCardData"
+              :fetch-line-month="getLineMonthData"
+              :fetch-line-week="getLineWeekData"
+              :fetchLine="getLineData"
+            >
+            </DataTendencyChart>
+          </el-tab-pane>
+          <el-tab-pane label="广告结构" name="adStruct">
+            <AdStructChart v-if="tabActiveName === 'adStruct'" />
+          </el-tab-pane>
+          <el-tab-pane label="散点视图" name="scatterView"></el-tab-pane>
+        </el-tabs>
+      </template>
+      <template #cell_percentTimeInBudget="scope">
+        <el-progress :percentage="scope.row.percentTimeInBudget > 0 ? scope.row.percentTimeInBudget * 100 : 0" />
+      </template>
+
+      <template #cell_expression="scope">
+        <div>
+          <!-- 单独渲染第一个元素 -->
+          <div
+            v-if="scope.row.resolvedExpression[0]?.value && scope.row.resolvedExpression[0].value.length > 0" style="font-weight: 500; color: #505968">
+            {{ scope.row.resolvedExpression[0].value[0].value ?? '' }}
+          </div>
+          <!-- 渲染其他所有元素 -->
+          <div v-if="scope.row.resolvedExpression[0]?.value && scope.row.resolvedExpression[0].value.length > 1">
+            <template v-for="(item, index) in scope.row.resolvedExpression[0].value" :key="index">
+              <span v-if="index > 0">
+                <span v-if="item.type &&
+                    (item.type === 'asinPriceGreaterThan' || 
+                    item.type === 'asinPriceLESSThan' || 
+                    item.type === 'asinReviewRatingGreaterThan' || 
+                    item.type === 'asinReviewRatingLessThan')">
+                  {{ sdtargetMap[item.type] }}
+                </span>
+                <span v-else>{{ item.type ? sdtargetMap[item.type] + ': ' : '' }}</span>
+                {{ item.value ?? '' }}
+                <span v-if="index < scope.row.resolvedExpression[0].value.length - 1">&nbsp;</span>
+              </span>
+            </template>
+          </div>
+        </div>
+      </template>
+      <template #cell_campaignName="scope">
+        <el-tooltip effect="dark" :content="scope.row.campaignName" placement="top">
+          <el-link type="primary" :underline="false" @click="jumpGroup(scope.row)">
+            <div class="en-text">{{ scope.row.campaignName }}</div>
+          </el-link>
+        </el-tooltip>
+      </template>
+      <!-- 类型 -->
+      <template #cell_resolvedExpression="scope">
+        <div>
+          {{ sdTypeMap[scope.row.resolvedExpression[0].type as string] }}
+        </div>
+      </template>
+
+      <template #cell_adGroupName="scope">
+        <el-tooltip effect="dark" :content="scope.row.adGroupName" placement="top">
+          <el-link type="primary" :underline="false" @click="jumpGroup(scope.row)">
+            <div class="en-text">{{ scope.row.adGroupName }}</div>
+          </el-link>
+        </el-tooltip>
+      </template>
+      <template #cell_suggestedBid="scope">
+        <div>{{ scope.row.suggestedBid ? `$${scope.row.suggestedBid}` : '--' }}</div>
+        <div class="text-range">
+          {{ scope.row.suggestedBid_lower ? `$${scope.row.suggestedBid_lower}` : '--' }} ~
+          {{ scope.row.suggestedBid_upper ? `$${scope.row.suggestedBid_upper}` : '--' }}
+        </div>
+      </template>
+      <template #cell_MissedImpressions="scope">
+        {{ scope.row.MissedImpressionsLower ?? '0' }} ~ {{ scope.row.MissedImpressionsUpper ?? '0' }}
+      </template>
+      <template #cell_MissedClicks="scope"> {{ scope.row.MissedClicksLower ?? '0' }} ~ {{ scope.row.MissedClicksUpper ?? '0' }}</template>
+      <template #cell_MissedSales="scope"> {{ scope.row.MissedSalesLower ?? '0' }} ~ {{ scope.row.MissedSalesUpper ?? '0' }}</template>
+
+      <template v-for="field of Object.keys(SdBaseColumn)" #[`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 { nextTick, onMounted, ref, watch } from 'vue'
+import { FsPage, useFs } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+import { useRoute, useRouter } from 'vue-router'
+import DataTendencyChart from '/@/views/adManage/sd/chartComponents/dataTendency.vue'
+import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import AdStructChart from './chartComponents/adStruct.vue'
+import { getCardData, getLineData, getLineMonthData, getLineWeekData } from './api'
+import { storeToRefs } from 'pinia'
+import { SdBaseColumn } from '/@/views/adManage/utils/commonTabColumn'
+import DataCompare from '/@/components/dataCompare/index.vue'
+import { sdTypeMap, sdtargetMap } from '/@/views/adManage/utils/enum'
+
+const tabActiveName = ref('dataTendency')
+const shopInfo = useShopInfo()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+const { profile } = storeToRefs(shopInfo)
+const queryParams = ref({
+  profileId: profile.value.profile_id,
+  dateRange,
+})
+
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+const route = useRoute()
+const router = useRouter()
+const adStructChartRef = ref()
+const dataTendencyRef = ref()
+const showCompare = ref(false)
+
+onMounted(() => {
+  crudExpose.doRefresh()
+})
+
+const jumpGroup = (row: any) => {
+  router.push({
+    name: 'CampaignDetail',
+    query: { campaignId: row.campaignId, tagsViewName: row.campaignName },
+  })
+}
+
+const resizeTabChart = () => {
+  if (tabActiveName.value === 'dataTendency') {
+    dataTendencyRef.value.resizeChart()
+  } else if (tabActiveName.value === 'adStruct') {
+    adStructChartRef.value.resizeChart()
+  }
+}
+const changeTab = () => {
+  nextTick(() => {
+    resizeTabChart()
+  })
+}
+defineExpose({ resizeTabChart })
+
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style lang="scss" scoped>
+.campare-switch {
+  flex: none;
+}
+
+::v-deep(.el-table__footer-wrapper td.el-table__cell:nth-child(n + 3):nth-child(-n + 6) .cell) {
+  display: none;
+}
+
+.en-text {
+  max-width: 100%;
+  font-size: 13px;
+  font-weight: 420;
+  word-break: break-word;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: normal;
+  display: -webkit-box;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+}
+
+::v-deep(.el-table__footer-wrapper) {
+  border: 0;
+}
+
+::v-deep(.el-table .el-table__footer-wrapper .cell) {
+  font-weight: 600;
+}
+
+.text-range {
+  color: #808184;
+}
+
+.ellipsis {
+  max-width: 100%;
+  font-size: 13px;
+  font-weight: 420;
+  word-break: break-word;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: normal;
+  display: -webkit-box;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+}
+
+.ellipsis-inline {
+  max-width: 100%;
+  font-size: 13px;
+  font-weight: 420;
+  word-break: break-word;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: normal;
+  display: inline;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+}
+
+::v-deep(span.el-link__inner) {
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  display: inline;
+}
+</style>

+ 1 - 1
src/views/adManage/sd/chartComponents/dataTendency.vue

@@ -2,7 +2,7 @@
   <div v-loading="loading">
     <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
     <el-radio-group v-model="statDim" class="chart-button-group" @change="changeStatDim">
-      <el-radio-button label="hour">时</el-radio-button>
+      <el-radio-button label="hour" :disabled="!props.fetchLineHour">时</el-radio-button>
       <el-radio-button label="day">日</el-radio-button>
       <el-radio-button label="week" :disabled="!props.fetchLineWeek">周</el-radio-button>
       <el-radio-button label="month" :disabled="!props.fetchLineWeek">月</el-radio-button>

+ 3 - 3
src/views/adManage/sd/index.vue

@@ -32,7 +32,7 @@ import { storeToRefs } from 'pinia'
 import { GetAllPortfolios } from '/@/views/adManage/portfolios/api'
 import DateRangePicker from '/@/components/DateRangePicker/index.vue'
 import Campaigns from '/@/views/adManage/sd/campaigns/index.vue'
-import Keywords from '/@/views/adManage/sb/keywords/index.vue'
+import Audiences from '/@/views/adManage/sd/audiences/index.vue'
 import Targets from '/@/views/adManage/sd/targets/index.vue'
 import SearchTerm from '/@/views/adManage/sb/searchTerm/index.vue'
 import AdvertisedProducts from './advertisedProducts/index.vue'
@@ -47,8 +47,8 @@ const { dateRange } = storeToRefs(publicData)
 const tabActiveName = ref('Campaigns')
 const tabsComponents: any = {
   Campaigns,
-  // Keywords,
   Targets,
+  Audiences,
   // AdvertisedProducts,
   // PurchasedOtherProducts,
   // SearchTerm,
@@ -56,8 +56,8 @@ const tabsComponents: any = {
 }
 const tabs = [
   { label: '广告活动', name: 'Campaigns' },
-  // { label: '关键词', name: 'Keywords' },
   { label: '商品投放', name: 'Targets' },
+  { label: '受众', name: 'Audiences' },
   // { label: '推广商品', name: 'AdvertisedProducts' },
   // { label: '购买的其他商品', name: 'PurchasedOtherProducts' },
   // { label: '搜索词', name: 'SearchTerm' },

+ 1 - 1
src/views/adManage/sd/targets/index.vue

@@ -93,7 +93,7 @@ import {nextTick, onMounted, ref, watch} from 'vue'
 import {FsPage, useFs} from '@fast-crud/fast-crud'
 import {createCrudOptions} from './crud'
 import {useRoute, useRouter} from 'vue-router'
-import DataTendencyChart from '/@/views/adManage/sb/chartComponents/dataTendency.vue'
+import DataTendencyChart from '/@/views/adManage/sd/chartComponents/dataTendency.vue'
 import {useShopInfo} from '/@/stores/shopInfo'
 import {usePublicData} from '/@/stores/publicData'
 import AdStructChart from './chartComponents/adStruct.vue'

+ 10 - 8
src/views/adManage/utils/commonTabColumn.tsx

@@ -1,3 +1,5 @@
+import  { InfoFilled } from '@element-plus/icons-vue'
+
 export const BaseColumn = {
   Impression: {
     title: '曝光量',
@@ -17,7 +19,7 @@ export const BaseColumn = {
             </span>
         )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -362,7 +364,7 @@ export const SbBaseColumn = {
             </span>
       )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -672,7 +674,7 @@ export const SbBaseColumn = {
             </span>
       )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -724,7 +726,7 @@ export const SdBaseColumn = {
             </span>
         )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -988,7 +990,7 @@ export const SdBaseColumn = {
             </span>
         )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -1017,7 +1019,7 @@ export const SdBaseColumn = {
             </span>
         )
       },
-      formatter: (row) => {
+      formatter: (row: any) => {
         if (row.value != null) {
           return row.value
         } else {
@@ -1050,8 +1052,8 @@ export const SdBaseColumn = {
   }
 }
 
-export function createMul(mul) {
-  return function(number) {
+export function createMul(mul: any) {
+  return function(number: any) {
     return number * mul
   }
 }

+ 32 - 1
src/views/adManage/utils/enum.ts

@@ -262,6 +262,38 @@ export const metricMap = {
   'Click': '点击量',
 }
 
+export const sdTypeMap: {[key: string]: string} = {
+  'views': '浏览再营销',
+  'purchases': '购买再营销',
+  'audience': '亚马逊受众'
+}
+
+export const sdtargetMap: {[key: string]: string} = {
+  'asinBrandSameAs': '品牌',
+  'asinSameAs': '商品',
+  'asinPriceGreaterThan': '商品价格>',
+  'asinPriceLESSThan': '商品价格<',
+  'asinReviewRatingGreaterThan': '评分>',
+  'asinReviewRatingLessThan': '评分<',
+  'asinPriceBetween': '商品价格',
+  'lookback': '回溯期'
+}
+export const targetEnum = [
+  { 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' },
+]
 
 export const sdBarOptionsMap = {
   'Spend': '花费',
@@ -594,7 +626,6 @@ export const barOptions2 = [
     label: '搜索结果顶部展示份额'
   },
 ]
-
 export const pieOptions = [
   {
     value: 'Spend',