Sfoglia il codice sorgente

Merge branch 'refs/heads/xinyan' into test

xinyan 1 anno fa
parent
commit
6867ccae8b

+ 0 - 3
src/main.ts

@@ -39,7 +39,6 @@ import Timezon from 'dayjs/plugin/timezone'
 import IsSameOrBefore from 'dayjs/plugin/isSameOrBefore'
 import 'dayjs/locale/zh-cn'
 
-
 dayjs.extend(UTC)
 dayjs.extend(Timezon)
 dayjs.extend(IsSameOrBefore)
@@ -50,7 +49,6 @@ iconList.addIcon(forIconfont.list) // 添加iconfont dvadmin3的icon
 iconList.addIcon(elementPlus) // 添加element plus的图标
 iconList.addIcon(fontAwesome470) // 添加fontAwesome 470版本的图标
 
-
 let app = createApp(App)
 
 scanAndInstallPlugins(app)
@@ -65,7 +63,6 @@ pinia.use(piniaPersist)
 directive(app)
 other.elSvg(app)
 
-
 app.use(VXETable)
 app.use(permission)
 // @ts-ignore

+ 40 - 0
src/views/reportManage/dataCenter/api.ts

@@ -0,0 +1,40 @@
+import { request } from '/@/utils/service'
+import {UserPageQuery} from "@fast-crud/fast-crud";
+
+
+
+
+export const apiPrefix = '/api/sellers/product/trend/daily/'
+
+export function getCardData(query: UserPageQuery) {
+    return request({
+        url: '/api/sellers/home/total/',//需要修改
+        method: 'GET',
+        params: query,
+    })
+}
+export function getLineData(query: UserPageQuery) {
+    query['dateRangeType'] = 'D'
+    return request({
+        url: apiPrefix,
+        method: 'GET',
+        params: query,
+    })
+}
+
+export function getLineWeekData(query: UserPageQuery) {
+    query['dateRangeType'] = 'W'
+    return request({
+        url: apiPrefix,
+        method: 'GET',
+        params: query,
+    })
+}
+export function getLineMonthData(query: UserPageQuery) {
+    query['dateRangeType'] = 'M'
+    return request({
+        url: apiPrefix,
+        method: 'GET',
+        params: query,
+    })
+}

+ 42 - 0
src/views/reportManage/dataCenter/components/CompareData.vue

@@ -0,0 +1,42 @@
+<!--<script setup lang="ts">-->
+<!--import { ref } from 'vue'-->
+<!--// 筛选条件-->
+<!--const value1 = ref([]) // 平台编号-->
+<!--const value2 = ref([])-->
+<!--const value3 = ref([])-->
+<!--const value4 = ref([])-->
+<!--const options = [-->
+<!--  {-->
+<!--    value: 'Option1',-->
+<!--    label: 'Option1',-->
+<!--  },-->
+<!--  {-->
+<!--    value: 'Option2',-->
+<!--    label: 'Option2',-->
+<!--  },-->
+<!--]-->
+<!--</script>-->
+
+<!--<template>-->
+<!--  <div class="flex gap-1.5 justify-between">-->
+<!--    <div class="flex gap-1.5">-->
+<!--      <el-select v-model="value1" multiple placeholder="平台编号" style="width: 160px">-->
+<!--        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />-->
+<!--      </el-select>-->
+<!--      <el-select v-model="value2" multiple placeholder="平台名称" style="width: 160px">-->
+<!--        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />-->
+<!--      </el-select>-->
+<!--      <el-select v-model="value3" multiple placeholder="运营" style="width: 160px">-->
+<!--        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />-->
+<!--      </el-select>-->
+<!--      <el-select v-model="value4" multiple placeholder="国家" style="width: 160px">-->
+<!--        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />-->
+<!--      </el-select>-->
+<!--      <el-select v-model="value4" multiple placeholder="品牌" style="width: 160px">-->
+<!--        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />-->
+<!--      </el-select>-->
+<!--    </div>-->
+<!--  </div>-->
+<!--</template>-->
+
+<!--<style scoped></style>-->

+ 173 - 0
src/views/reportManage/dataCenter/components/DataExport.vue

@@ -0,0 +1,173 @@
+<template>
+  <div>
+    <vxe-grid ref="xGrid" v-bind="gridOptions" v-on="gridEvents">
+      <template #operate="{ row }">
+        <template v-if="hasActiveEditRow(row)">
+          <vxe-button content="取消" @click="clearRowEvent"></vxe-button>
+          <vxe-button status="primary" content="保存" @click="saveRowEvent(row)"></vxe-button>
+        </template>
+        <template v-else>
+          <vxe-button content="编辑" @click="editRowEvent(row)"></vxe-button>
+        </template>
+        <vxe-button status="danger" content="删除" @click="removeRowEvent(row)"></vxe-button>
+      </template>
+
+      <template #Name_edit="{ row }">
+        <vxe-input v-model="row.Name"></vxe-input>
+      </template>
+      <template #DailyDataHeader_edit="{ row }">
+        <vxe-input v-model="row.DailyDataHeader"></vxe-input>
+      </template>
+      <template #WeeklyDataHeader_edit="{ row }">
+        <vxe-input v-model="row.WeeklyDataHeader"></vxe-input>
+      </template>
+      <template #MonthlyDataHeader_edit="{ row }">
+        <vxe-input v-model="row.MonthlyDataHeader"></vxe-input>
+      </template>
+      <template #CreationTime_edit="{ row }">
+        <vxe-input v-model="row.CreationTime"></vxe-input>
+      </template>
+    </vxe-grid>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import { VXETable, VxeGridInstance, VxeGridProps, VxeGridListeners } from 'vxe-table'
+
+interface RowVO {
+  Name: string
+  DailyDataHeader: string
+  WeeklyDataHeader: string
+  MonthlyDataHeader: string
+  CreationTime: number
+}
+
+const xGrid = ref<VxeGridInstance<RowVO>>()
+
+const gridOptions = reactive<VxeGridProps<RowVO>>({
+  border: true,
+  keepSource: true,
+  showOverflow: true,
+  height: 530,
+  loading: false,
+  columnConfig: {
+    resizable: true
+  },
+  pagerConfig: {
+    enabled: true,
+    total: 0,
+    currentPage: 1,
+    pageSize: 10,
+    pageSizes: [10, 20, 50, 100, 200, 500]
+  },
+  editConfig: {
+    trigger: 'manual',
+    mode: 'row',
+    showStatus: true
+  },
+  columns: [
+    { field: 'Name', title: '模板名称', editRender: { autofocus: '.vxe-input--inner' }, slots: { edit: 'Name_edit' } },
+    { field: 'DailyDataHeader', title: '日数据表头', editRender: { autofocus: '.vxe-input--inner' }, slots: { edit: 'DailyDataHeader_edit' } },
+    { field: 'WeeklyDataHeader', title: '周数据表头', editRender: {}, slots: { edit: 'WeeklyDataHeader_edit' } },
+    { field: 'MonthlyDataHeader', title: '月数据表头', editRender: {}, slots: { edit: 'MonthlyDataHeader_edit' } },
+    { field: 'CreationTime', title: '创建时间', editRender: {}, slots: { edit: 'CreationTime_edit' } },
+    { title: '操作', width: 300, slots: { default: 'operate' } }
+  ],
+  data: []
+})
+
+
+const findList = () => {
+  gridOptions.loading = true
+  setTimeout(() => {
+    gridOptions.loading = false
+    if (gridOptions.pagerConfig) {
+      gridOptions.pagerConfig.total = 10
+    }
+    gridOptions.data = [
+      {
+        Name: 'John Brown',
+        DailyDataHeader: 'New York No. 1 Lake Park',
+        WeeklyDataHeader: 'New York No. 1 Lake Park',
+        MonthlyDataHeader: 'New York No. 1 Lake Park',
+        'CreationTime': '2016-10-03',
+      },
+        {
+          Name: 'Jim Green',
+          DailyDataHeader: 'London No. 1 Lake Park',
+          WeeklyDataHeader: 'London No. 1 Lake Park',
+          MonthlyDataHeader: 'London No. 1 Lake Park',
+          'CreationTime': '2016-10-01',
+        },
+    ]
+  }, 300)
+}
+
+const gridEvents: VxeGridListeners<RowVO> = {
+  pageChange ({ currentPage, pageSize }) {
+    if (gridOptions.pagerConfig) {
+      gridOptions.pagerConfig.currentPage = currentPage
+      gridOptions.pagerConfig.pageSize = pageSize
+    }
+    findList()
+  }
+}
+
+const formatSex = (value: string) => {
+  if (value === '1') {
+    return '男'
+  }
+  if (value === '0') {
+    return '女'
+  }
+  return ''
+}
+
+const hasActiveEditRow = (row: RowVO) => {
+  const $grid = xGrid.value
+  if ($grid) {
+    return $grid.isEditByRow(row)
+  }
+  return false
+}
+
+const editRowEvent = (row: RowVO) => {
+  const $grid = xGrid.value
+  if ($grid) {
+    $grid.setEditRow(row)
+  }
+}
+
+const clearRowEvent = () => {
+  const $grid = xGrid.value
+  if ($grid) {
+    $grid.clearEdit()
+  }
+}
+
+const saveRowEvent = async (row: RowVO) => {
+  const $grid = xGrid.value
+  if ($grid) {
+    await $grid.clearEdit()
+    gridOptions.loading = true
+    // 模拟异步保存
+    setTimeout(() => {
+      gridOptions.loading = false
+      VXETable.modal.message({ content: `${JSON.stringify(row)}`, status: 'success' })
+    }, 300)
+  }
+}
+
+const removeRowEvent = async (row: RowVO) => {
+  const type = await VXETable.modal.confirm('您确定要删除该数据?')
+  const $grid = xGrid.value
+  if ($grid) {
+    if (type === 'confirm') {
+      await $grid.remove(row)
+    }
+  }
+}
+
+findList()
+</script>

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

@@ -0,0 +1,330 @@
+<template>
+  <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="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>
+    </el-radio-group>
+    <div style="height: 350px" ref="chartRef"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import * as echarts from 'echarts'
+import { Ref, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
+import { dataCenterMetricsEnum } from '/@/views/reportManage/dataCenter/utils/enum'
+// import MetricsCards from '/@/components/MetricsCards/index.vue'
+import XEUtils from 'xe-utils'
+import MetricsCards from '../MetricsCards/index.vue'
+import emitter from '/@/utils/emitter'
+import { buildChartOpt, parseQueryParams } from '/@/views/adManage/utils/tools.js'
+
+defineOptions({
+  name: 'DataTendencyChart',
+})
+
+interface Props {
+  fetchCard: Function
+  fetchLine: Function
+  fetchLineMonth?: Function
+  fetchLineWeek?: Function
+  query: { [key: string]: any }
+  initMetric?: ShowMetric[]
+  metricEnum?: { [key: string]: string }[]
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  initMetric: () => [
+    { metric: 'Impression', color: '#0085ff', label: '曝光量' },
+    { metric: 'Click', color: '#3fd4cf', label: '点击量' },
+    { metric: 'Spend', color: '#ff9500', label: '花费' },
+  ],
+  metricEnum: () => dataCenterMetricsEnum,
+})
+
+const metrics = ref(props.initMetric)
+
+const metricsItems: Ref<MetricData[]> = ref([])
+let chartObj: any
+const chartRef = ref()
+const statDim = ref('day')
+const option: any = {
+  dataset: {
+    source: [],
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      label: {
+        backgroundColor: '#6a7985',
+      },
+    },
+  },
+  legend: {
+    selected: {}, // 控制显隐
+    show: false,
+  },
+  grid: {
+    top: 50,
+    right: 150,
+    bottom: 30,
+    left: 65,
+  },
+  xAxis: {
+    type: 'category',
+  },
+  yAxis: [
+    {
+      id: 0,
+      type: 'value',
+      name: '',
+      splitLine: {
+        show: true, // 设置显示分割线
+      },
+      axisLine: {
+        show: true,
+        lineStyle: { color: '' },
+      },
+      show: true,
+    },
+    {
+      id: 1,
+      type: 'value',
+      name: '',
+      position: 'right',
+      splitLine: {
+        show: false,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '',
+        },
+      },
+      show: true,
+    },
+    {
+      id: 2,
+      type: 'value',
+      position: 'right',
+      offset: 90,
+      name: '',
+      splitLine: {
+        show: false,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '',
+        },
+      },
+      show: true,
+    },
+  ],
+  series: [
+    {
+      id: 0,
+      name: '',
+      type: 'bar',
+      encode: {
+        x: 'Name',
+        y: '',
+      },
+      barWidth: '18px',
+      yAxisIndex: 0,
+      itemStyle: {
+        color: '',
+        borderRadius: 4,
+      },
+    },
+    {
+      id: 1,
+      name: '',
+      type: 'line',
+      encode: {
+        x: 'Name',
+        y: '',
+      },
+      symbolSize: 6,
+      symbol: 'circle',
+      smooth: true,
+      yAxisIndex: 1,
+      itemStyle: {
+        // color: '#ff9500',
+        // borderColor: '#ff9500'
+      },
+      areaStyle: {
+        // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+        //   { offset: 0, color: '#3fd4cf53' },
+        //   { offset: 1, color: '#3fd4cf03' },
+        // ]),
+      },
+      emphasis: {
+        focus: 'series',
+      },
+    },
+    {
+      id: 2,
+      name: '',
+      type: 'line',
+      encode: {
+        x: 'Name',
+        y: '',
+      },
+      symbolSize: 6,
+      symbol: 'circle',
+      smooth: true,
+      yAxisIndex: 2,
+      itemStyle: {},
+      areaStyle: {},
+      emphasis: {
+        focus: 'series',
+      },
+    },
+  ],
+}
+const loading = ref(true)
+const queryParams = computed(() => parseQueryParams(props.query))
+
+onMounted(() => {
+  getMetricsItems()
+  addResize()
+  // initLine()
+  setTimeout(() => {
+    initLine()
+  }, 0)
+})
+onBeforeUnmount(() => {
+  if (chartObj) {
+    chartObj.dispose()
+    chartObj = null
+  }
+  removeResize()
+})
+
+const initLine = async () => {
+  chartObj = echarts.init(chartRef.value)
+  const items = await getDataset()
+  option.dataset.source = items
+
+  XEUtils.arrayEach(option.series, (info: any, index) => {
+    const color = metrics.value[index].color
+    info.name = metrics.value[index].label
+    info.encode.y = metrics.value[index].metric
+    if (info.type === 'bar') {
+      info.itemStyle.color = color
+    } else {
+      info.itemStyle = { color: color, borderColor: color }
+      info.areaStyle = {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          { offset: 0, color: color + '53' },
+          { offset: 1, color: color + '03' },
+        ]),
+      }
+    }
+  })
+  XEUtils.arrayEach(option.yAxis, (info: any, index) => {
+    info.name = metrics.value[index].label
+    info.axisLine.lineStyle.color = metrics.value[index].color
+  })
+
+  XEUtils.arrayEach(props.metricEnum, (info) => {
+    option.legend.selected[info.label] = false
+  })
+  for (const info of metrics.value) {
+    option.legend.selected[info.label] = true
+  }
+  // console.log(option)
+  chartObj.setOption(option)
+  loading.value = false
+}
+
+const getDataset = async () => {
+  if (statDim.value === 'week') {
+    if (props.fetchLineWeek) {
+      const resp = await props.fetchLineWeek(queryParams.value)
+      return resp.data
+    }
+  } else if (statDim.value === 'month') {
+    if (props.fetchLineMonth) {
+      const resp = await props.fetchLineMonth(queryParams.value)
+      return resp.data
+    }
+  } else {
+    const resp = await props.fetchLine(queryParams.value)
+    return resp.data
+  }
+}
+
+const getMetricsItems = async () => {
+  const resp = await props.fetchCard(queryParams.value)
+  const data = resp.data
+  metricsItems.value.length = 0
+  XEUtils.arrayEach(props.metricEnum, (info) => {
+    const tmp: MetricData = {
+      label: info.label,
+      value: info.value,
+      metricVal: data[info.value],
+      gapVal: data[`gap${info.value}`],
+      preVal: data[`prev${info.value}`],
+    }
+    metricsItems.value.push(tmp)
+  })
+}
+
+const changeMetric = () => {
+  const opt = buildChartOpt(option, metrics.value)
+  chartObj.setOption(opt)
+}
+
+const emit = defineEmits(['changeStatDim'])
+const changeStatDim = async () => {
+  emitter.emit('DateTendency-changeStatDim') // 触发DataTable的loading
+  loading.value = true
+  let source = await getDataset()
+  if (source.length > 0) {
+    chartObj.setOption({ dataset: { source: source } })
+  }
+  loading.value = false
+  emit('changeStatDim', source) // 向父组件传递数据后再传递给DataTable
+}
+
+watch(props.query, async () => {
+  // console.log("------watch-----queryParams", props.query)
+  emitter.emit('DateTendency-changeStatDim') // 触发DataTable的loading
+  loading.value = true
+  await getMetricsItems()
+  const items = await getDataset()
+  emit('changeStatDim', items) // 向父组件传递数据后再传递给DataTable
+  const opt = { dataset: { source: items } }
+  chartObj.setOption(opt)
+  loading.value = false
+})
+
+const resizeChart = () => {
+  chartObj.resize()
+}
+const addResize = () => {
+  window.addEventListener('resize', resizeChart)
+}
+const removeResize = () => {
+  window.removeEventListener('resize', resizeChart)
+}
+</script>
+
+<style scoped>
+.metrics-cards {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 12px;
+  width: 100%;
+}
+
+.chart-button-group {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 5px;
+}
+</style>

+ 149 - 0
src/views/reportManage/dataCenter/components/MetricsCards/index.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="metrics-cards">
+    <MCard
+     v-model="info.metric"
+     :metric-items="props.metricItems"
+     :color="info.color"
+     v-for="info in displayMetrics"
+     @change-metric="changedMetric"
+     @click="clickCard(info.metric)"/>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, Ref, onBeforeMount, watch, onMounted, computed } from 'vue'
+import MCard from './mCard.vue'
+import XEUtils from 'xe-utils';
+
+interface Props {
+  modelValue: ShowMetric[],
+  metricItems: MetricData[],
+}
+const colorsMap: { [key: string]: boolean } = {}
+const props = defineProps<Props>()
+const emits = defineEmits(['change', 'update:modelValue'])
+// const allMetricItems = ref(props.metricItems)
+const selectedMetric = ref(props.modelValue)
+const displayMetrics: Ref<{metric:string, color?: string}[]> = ref([])
+
+const metricMap = computed(():{[key: string]: string} => {
+  const tmp:{[key: string]: string} = {}
+  for (const info of props.metricItems) {
+    tmp[info.value] = info.label
+  }
+  return tmp
+})
+onBeforeMount(()=> {
+  const dup:{[key: string]: boolean} = {}
+  // 初始显示图线的三个维度
+  for (const info of selectedMetric.value) {
+    displayMetrics.value.push({ metric: info.metric, color: info.color })
+    dup[info.metric] = true
+    if (info.color) { colorsMap[info.color] = true }
+  }
+})
+
+const getColor = () => {
+  for (const [k,v] of Object.entries(colorsMap)) {
+    if (!v) {
+      colorsMap[k] = true
+      return k
+    }
+  }
+  return ""
+}
+const unsetColor = (color: string ) => {
+  if (XEUtils.has(colorsMap, color)) {
+    colorsMap[color] = false
+  }
+}
+const changedMetric = (newVal: string, oldVal: string) => {
+  for (const info of props.metricItems) {
+    if (info.value === newVal) {
+      info.disabled = true 
+    } else if (info.value === oldVal) {
+      info.disabled = false
+    }
+  }
+  const index = selectedMetric.value.findIndex( info => info.metric === oldVal)
+  if (index > -1) {
+    selectedMetric.value[index].metric = newVal
+    selectedMetric.value[index].label = metricMap.value[newVal]
+    emits('update:modelValue', selectedMetric.value)
+    emits('change', selectedMetric.value)
+  }
+}
+const clickCard = (metric: string) => {
+  const index = selectedMetric.value.findIndex( info => info.metric === metric)
+  if (index > -1) {  // 已存在则删除
+    if (selectedMetric.value.length <= 1 ) return
+    const tmp = selectedMetric.value[index]
+    selectedMetric.value.splice(index, 1)
+    unsetColor(tmp.color)
+    emits('update:modelValue', selectedMetric.value)
+    emits('change', selectedMetric.value)
+  } else {  // 不存在则添加
+    if (selectedMetric.value.length === 3) { 
+      selectedMetric.value[2].metric = metric
+      selectedMetric.value[2].label = metricMap.value[metric]
+    } else {
+      const color = getColor()
+      selectedMetric.value.push({ metric: metric, color: color, label: metricMap.value[metric]})
+    }
+    emits('update:modelValue', selectedMetric.value)
+    emits('change', selectedMetric.value)
+  }
+}
+watch(selectedMetric.value, () => {
+  const cache:{ [key: string]: string } = {}
+  for (const info of selectedMetric.value) {
+    cache[info.metric] = info.color
+  }
+  for (const info of displayMetrics.value) {
+    const color = cache[info.metric]
+    if (color) {
+      info.color = color
+    } else {
+      info.color = undefined
+    }
+  }
+})
+
+watch(
+  props.metricItems,
+  () => {
+    const dup:{[key: string]: boolean} = {}
+    for (const info of displayMetrics.value) { dup[info.metric] = true }
+    let needNum = 6 - displayMetrics.value.length
+    if (needNum > 0) {  
+      // 从所有维度中选择剩余
+      for (const info of props.metricItems) {
+        if (!dup[info.value]) {
+          displayMetrics.value.push({ metric: info.value })
+          dup[info.value] = true
+          needNum --
+          if (needNum === 0) break
+        }
+      }
+    }
+    for (const info of props.metricItems) {
+      if (dup[info.value]) {
+        info.disabled = true
+      } else {
+        info.disabled = false
+      }  
+    }
+  }
+)
+
+</script>
+
+<style scoped>
+.metrics-cards {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 12px;
+  width: 100%;
+}
+</style>

+ 100 - 0
src/views/reportManage/dataCenter/components/MetricsCards/mCard.vue

@@ -0,0 +1,100 @@
+<template>
+  <el-card class="metric-card">
+    <div class="metric-card__color" :style="boardTopStyle"></div>
+    <TextSelector v-model="metric" :options="props.metricItems" @change="changeMetric"></TextSelector>
+    <div class="metric-value">{{ selectedData?.metricVal }}</div>
+    <div class="metric-pre">
+      <span>{{ selectedData?.preVal }}&nbsp;&nbsp;</span>
+      <el-icon v-show="selectedData?.gapVal" style="display: inline-block; padding-top: 2px">
+        <Top :class="colorClass" v-if="isBoost"/>
+        <Bottom :class="colorClass" v-else/>
+      </el-icon>
+      <span :class="colorClass">{{ selectedData?.gapVal ? selectedData?.gapVal + '%' : '' }}</span>
+    </div>
+  </el-card>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+import TextSelector from '/@/components/TextSelector/index.vue'
+
+defineOptions({
+  name: 'MCard'
+})
+
+interface Props {
+  modelValue: string,
+  metricItems: MetricData[],
+  color?: string,
+}
+const props = defineProps<Props>()
+const emits = defineEmits(["update:modelValue", "change-metric"])
+const metric = ref(props.modelValue)
+const changeMetric = ( newVal: string, oldVal: string) => {
+  emits('update:modelValue', newVal)
+  emits('change-metric', newVal, oldVal)
+}
+const selectedData = computed(():MetricData|null => {
+  const info = props.metricItems.find(item => item.value === metric.value)
+  if(!info) return null
+  return info
+})
+const boardTopStyle = computed(() => {
+  const style_ = { "border-top-color": "rgb(232, 244, 255)" }
+  if (props.color) { style_["border-top-color"] = props.color }
+  return style_
+})
+const isBoost = computed(():boolean => {
+  return (selectedData.value?.gapVal ?? -1) > 0
+})
+const colorClass = computed((): "green"|"red" => isBoost.value ? "green": "red")
+
+</script>
+
+<style scoped>
+:deep(.el-card__body) {
+  padding: 0;
+}
+.metric-card {
+  padding: 12px 8px;
+  height: 100px;
+
+  position: relative;
+  min-width: 150px;
+  overflow-y: hidden;
+  line-height: 1.4;
+  background-color: #fff;
+  border-radius: 10px;
+  box-shadow: 0 0 12px rgba(51,89,181,.1607843137254902);
+  cursor: pointer;
+  flex-grow: 1;
+}
+.metric-card__color {
+  position: absolute;
+  top: 0;
+  left: 8px;
+  width: calc(100% - 16px);
+  height: 0;
+  border-top: 4px solid #86909c;
+  border-left: 2px solid transparent;
+  border-right: 2px solid transparent;
+}
+.metric-value {
+  padding: 8px 0;
+  font-size: 18px;
+  font-weight: 700;
+  line-height: 25px;
+}
+
+.metric-pre {
+  color: #6b7785;
+  font-size: 12px;
+  white-space: nowrap;
+}
+.red {
+  color: red;
+}
+.green {
+  color: #1cbc0e;
+}
+</style>

+ 155 - 0
src/views/reportManage/dataCenter/components/NormalDisplay.vue

@@ -0,0 +1,155 @@
+<script setup lang="ts">
+import { dataCenterMetricsEnum } from '/@/views/reportManage/dataCenter/utils/enum'
+import { getCardData, getLineData, getLineMonthData, getLineWeekData } from '/@/views/reportManage/dataCenter/api'
+import DataTendencyChart from '/@/views/reportManage/dataCenter/components/DateTendency/index.vue'
+import { reactive, ref } from 'vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+import { storeToRefs } from 'pinia'
+import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+// import router from "/@/router";
+import DataEntry from "/src/views/reportManage/dataCenter/components/EntryDetail.vue";
+// 店铺信息
+const shopInfo = useShopInfo()
+const { profile } = storeToRefs(shopInfo)
+//公共数据
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+
+const queryParams = ref({
+  profileId: profile.value.profile_id,
+  dateRange,
+  // parentAsin,
+  // childAsin,
+  // sku,
+})
+// 筛选条件
+const value1 = ref([]) // 平台编号
+const value2 = ref([])
+const value3 = ref([])
+const value4 = ref([])
+const options = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+]
+const tableColumns = [
+  { type: 'seq', title: '平台编号' },
+  { field: 'Name', title: '平台名称' },
+  { field: 'Operation', title: '运营' },
+  { field: 'Country', title: '国家' },
+  { field: 'Brand', title: '品牌' },
+  { field: 'Sales', title: '销售额' },
+  { field: 'YearOnYear', title: '期末同比变化' },
+  { field: 'MonthOnMonth', title: '期末环比变化' },
+  { field: 'AdSpend', title: '广告花费' },
+  { field: 'TotalAdSales', title: '广告销售额' },
+  { field: 'ROI', title: '广告ROI' },
+  { field: 'ACOS', title: '广告ACOS' },
+  { field: 'ROAS', title: '广告ROAS' },
+  { field: 'MonthSalesCompletionRate', title: '月销售额完成占比' },
+]
+
+//表格
+const gridOptions = reactive({
+  border: true,
+  height: 300,
+  align: null,
+  round: true,
+  columnConfig: {
+    resizable: true,
+  },
+  columns: tableColumns,
+  toolbarConfig: {
+    slots: {
+      buttons: 'toolbar_buttons',
+    },
+  },
+  data: [
+    {
+      id: 10001,
+      Name: 'Test1',
+      Operation: 'T1',
+      Country: 'Develop',
+      Brand: 'Man',
+      Sales: 28,
+      YearOnYear: 11,
+      MonthOnMonth: 22,
+      AdSpend: 33,
+      TotalAdSales: 100,
+      ROI: 11,
+      ACOS: 11,
+      ROAS: 11,
+      MonthSalesCompletionRate: 11,
+    },
+  ],
+})
+
+//数据导入
+const handleImport = (row: any) => {
+  router.push({
+    name: 'EntryDetail',
+  })
+}
+
+
+</script>
+
+<template>
+  <div>
+    <div class="flex gap-1.5 justify-between">
+      <div class="flex gap-1.5">
+        <el-select v-model="value1" multiple placeholder="平台编号" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value2" multiple placeholder="平台名称" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value3" multiple placeholder="运营" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value4" multiple placeholder="国家" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value4" multiple placeholder="品牌" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </div>
+      <DateRangePicker v-model="dateRange"></DateRangePicker>
+    </div>
+    <el-card class="mb-1.5">
+      <DataTendencyChart
+        :metricEnum="dataCenterMetricsEnum"
+        :query="queryParams"
+        :fetchCard="getCardData"
+        :fetchLine="getLineData"
+        :fetch-line-month="getLineMonthData"
+        :fetch-line-week="getLineWeekData"
+        >np
+      </DataTendencyChart>
+    </el-card>
+    </div>
+    <el-card>
+      <vxe-grid v-bind="gridOptions">
+        <template #toolbar_buttons>
+          <!--TODO: 数据录入-->
+          <vxe-button @click="handleImport">数据录入</vxe-button>
+          <vxe-button @click="">任务列表</vxe-button>
+        </template>
+      </vxe-grid>
+    </el-card>
+</template>
+
+<style scoped>
+.vxe-grid {
+  border-radius: 8px; /* 更改这个值以设置你想要的圆角大小 */
+}
+</style>

+ 152 - 0
src/views/reportManage/dataCenter/components/TableBrowsing.vue

@@ -0,0 +1,152 @@
+<script setup lang="ts">
+import { dataCenterMetricsEnum } from '/@/views/reportManage/dataCenter/utils/enum'
+import { getCardData, getLineData, getLineMonthData, getLineWeekData } from '/@/views/reportManage/dataCenter/api'
+import DataTendencyChart from '/@/views/reportManage/dataCenter/components/DateTendency/index.vue'
+import { reactive, ref } from 'vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+import { storeToRefs } from 'pinia'
+import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+
+// 店铺信息
+const shopInfo = useShopInfo()
+const { profile } = storeToRefs(shopInfo)
+//公共数据
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+
+const queryParams = ref({
+  profileId: profile.value.profile_id,
+  dateRange,
+  // parentAsin,
+  // childAsin,
+  // sku,
+})
+// 筛选条件
+const value1 = ref([]) // 平台编号
+const value2 = ref([])
+const value3 = ref([])
+const value4 = ref([])
+const options = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+]
+const tableColumns = [
+  { type: 'seq', title: '平台编号' },
+  { field: 'Name', title: '平台名称' },
+  { field: 'Operation', title: '运营' },
+  { field: 'Country', title: '国家' },
+  { field: 'Brand', title: '品牌' },
+  { field: 'Sales', title: '销售额' },
+  { field: 'YearOnYear', title: '期末同比变化' },
+  { field: 'MonthOnMonth', title: '期末环比变化' },
+  { field: 'AdSpend', title: '广告花费' },
+  { field: 'TotalAdSales', title: '广告销售额' },
+  { field: 'ROI', title: '广告ROI' },
+  { field: 'ACOS', title: '广告ACOS' },
+  { field: 'ROAS', title: '广告ROAS' },
+  { field: 'AdExposure', title: '广告曝光' },
+  { field: 'AdOrder', title: '广告订单' },
+  { field: 'AdClick', title: '广告点击' },
+  { field: 'AdConversionRate', title: '广告转化率' },
+  { field: 'CumulativeSales', title: '累计销售额' },
+  { field: 'SalesCompletion', title: '销售额完成情况' },
+  { field: 'MonthOnMonthGrowthRate', title: '环比上周增长率' },
+]
+
+//表格
+const gridOptions = reactive({
+  border: true,
+  height: 300,
+  align: null,
+  round: true,
+  columnConfig: {
+    resizable: true,
+  },
+  columns: tableColumns,
+  toolbarConfig: {
+    slots: {
+      buttons: 'toolbar_buttons',
+    },
+  },
+  data: [
+    {
+      id: 10001,
+      npmName: 'Test1',
+      Operation: 'T1',
+      Country: 'Develop',
+      Brand: 'Man',
+      Sales: 28,
+      YearOnYear: 11,
+      MonthOnMonth: 22,
+      AdSpend: 33,
+      TotalAdSales: 100,
+      ROI: 11,
+      ACOS: 11,
+      ROAS: 11,
+      MonthSalesCompletionRate: 11,
+      AdExposure: 11,
+      AdOrder: 11,
+      AdClick: 11,
+      AdConversionRate: 11,
+      CumulativeSales: 11,
+      SalesCompletion: 11,
+      MonthOnMonthGrowthRate: 11,
+    },
+  ],
+})
+</script>
+
+<template>
+  <div>
+    <div class="flex gap-1.5 justify-between">
+      <div class="flex gap-1.5">
+        <el-select v-model="value1" multiple placeholder="平台编号" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value2" multiple placeholder="平台名称" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value3" multiple placeholder="运营" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value4" multiple placeholder="国家" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+        <el-select v-model="value4" multiple placeholder="品牌" style="width: 160px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </div>
+      <DateRangePicker v-model="dateRange"></DateRangePicker>
+    </div>
+
+    <el-card class="mb-1.5">
+      <DataTendencyChart
+        :metricEnum="dataCenterMetricsEnum"
+        :query="queryParams"
+        :fetchCard="getCardData"
+        :fetchLine="getLineData"
+        :fetch-line-month="getLineMonthData"
+        :fetch-line-week="getLineWeekData"
+        >np
+      </DataTendencyChart>
+    </el-card>
+    <el-card>
+      <vxe-grid v-bind="gridOptions">
+        <template #toolbar_buttons>
+          <!--TODO: 主数据-->
+          <vxe-button @click="">主数据</vxe-button>
+          <vxe-button @click="">月对比数据</vxe-button>
+        </template>
+      </vxe-grid>
+    </el-card>
+  </div>
+</template>
+
+<style scoped></style>

+ 49 - 0
src/views/reportManage/dataCenter/index.vue

@@ -0,0 +1,49 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import type { TabsPaneContext } from 'element-plus'
+import TableBrowsing from '/@/views/reportManage/dataCenter/components/TableBrowsing.vue'
+import DataExport from '/@/views/reportManage/dataCenter/components/DataExport.vue'
+import CompareData from '/@/views/reportManage/dataCenter/components/CompareData.vue'
+import NormalDisplay from '/@/views/reportManage/dataCenter/components/NormalDisplay.vue'
+
+const activeName = ref('NormalDisplay')
+const tabsComponents: any = {
+  NormalDisplay,
+  TableBrowsing,
+  // CompareData,
+  DataExport,
+}
+const panes = [
+  { label: '普通展示', name: 'NormalDisplay' },
+  { label: '表格浏览', name: 'TableBrowsing' },
+  // { label: '对比数据', name: 'CompareData' },
+  { label: '数据导出', name: 'DataExport' },
+]
+
+/**
+ *
+ * @param tab
+ * @param event
+ */
+function handleTabClick(tab: TabsPaneContext, event: Event) {
+  return 'test name'
+}
+</script>
+
+<template>
+  <div class="px-2">
+    <el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleTabClick">
+      <el-tab-pane v-for="pane in panes" :key="pane.name" :label="pane.label" :name="pane.name"></el-tab-pane>
+      <component :is="tabsComponents[activeName]"></component>
+    </el-tabs>
+  </div>
+</template>
+
+<style scoped>
+.demo-tabs > .el-tabs__content {
+  padding: 32px;
+  color: #6b778c;
+  font-size: 32px;
+  font-weight: 600;
+}
+</style>

+ 229 - 0
src/views/reportManage/dataCenter/utils/columns.ts

@@ -0,0 +1,229 @@
+const universal = [
+  {
+    field: 'TotalSales',
+    title: '总销售额',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalSales_default', footer: 'TotalSales_footer' },
+  },
+  {
+    field: 'TotalOrderItems',
+    title: '总订单数',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalOrderItems_default', footer: 'TotalOrderItems_footer' },
+  },
+  {
+    field: 'TotalUnitOrdered',
+    title: '总销量',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalUnitOrdered_default', footer: 'TotalUnitOrdered_footer' },
+  },
+  { field: 'SAP', title: '单均价', align: 'right', width: 130, sortable: true, slots: { default: 'SAP_default', footer: 'SAP_footer' } },
+  {
+    field: 'TotalAdSales',
+    title: '广告销售额',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalAdSales_default', footer: 'TotalAdSales_footer' },
+  },
+  {
+    field: 'TotalAdSalesSameSKU',
+    title: '本商品广告销售额',
+    align: 'right',
+    width: 180,
+    sortable: true,
+    slots: { default: 'TotalAdSalesSameSKU_default', footer: 'TotalAdSalesSameSKU_footer' },
+  },
+  {
+    field: 'TotalAdPurchases',
+    title: '广告订单数',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalAdPurchases_default', footer: 'TotalAdPurchases_footer' },
+  },
+  {
+    field: 'TotalAdPurchasesSameSKU',
+    title: '本商品广告订单数',
+    align: 'right',
+    width: 180,
+    sortable: true,
+    slots: { default: 'TotalAdPurchasesSameSKU_default', footer: 'TotalAdPurchasesSameSKU_footer' },
+  },
+  {
+    field: 'TotalAdUnitOrdered',
+    title: '广告销量',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'TotalAdUnitOrdered_default', footer: 'TotalAdUnitOrdered_footer' },
+  },
+  {
+    field: 'TotalAdUnitOrderedSameSKU',
+    title: '本商品广告销量',
+    align: 'right',
+    width: 180,
+    sortable: true,
+    slots: { default: 'TotalAdUnitOrderedSameSKU_default', footer: 'TotalAdUnitOrderedSameSKU_footer' },
+  },
+  {
+    field: 'Spend',
+    title: '花费',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    showOverflow: true,
+    slots: { default: 'Spend_default', footer: 'Spend_footer' },
+  },
+  { field: 'ACOS', title: 'ACOS', align: 'right', width: 130, sortable: true, slots: { default: 'ACOS_default', footer: 'ACOS_footer' } },
+  { field: 'ROAS', title: 'ROAS', align: 'right', width: 130, sortable: true, slots: { default: 'ROAS_default', footer: 'ROAS_footer' } },
+  { field: 'TACOS', title: 'TACOS', align: 'right', width: 130, sortable: true, slots: { default: 'TACOS_default', footer: 'TACOS_footer' } },
+  { field: 'CR', title: '转化率', align: 'right', width: 130, sortable: true, slots: { default: 'CR_default', footer: 'CR_footer' } },
+  { field: 'CTR', title: '点击率', align: 'right', width: 130, sortable: true, slots: { default: 'CTR_default', footer: 'CTR_footer' } },
+  { field: 'CPC', title: '点击成本', align: 'right', width: 130, sortable: true, slots: { default: 'CPC_default', footer: 'CPC_footer' } },
+  { field: 'CPO', title: '总订单成本', align: 'right', width: 130, sortable: true, slots: { default: 'CPO_default', footer: 'CPO_footer' } },
+  { field: 'CPA', title: '广告订单成本', align: 'right', width: 180, sortable: true, slots: { default: 'CPA_default', footer: 'CPA_footer' } },
+  {
+    field: 'Impression',
+    title: '曝光量',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'Impression_default', footer: 'Impression_footer' },
+  },
+  { field: 'Click', title: '点击量', align: 'right', width: 130, sortable: true, slots: { default: 'Click_default', footer: 'Click_footer' } },
+  {
+    field: 'Sessions',
+    title: '会话次数',
+    align: 'right',
+    width: 150,
+    sortable: true,
+    slots: { default: 'Sessions_default', footer: 'Sessions_footer' },
+  },
+  {
+    field: 'ProductCr',
+    title: '商品会话百分比',
+    align: 'right',
+    width: 150,
+    sortable: true,
+    slots: { default: 'ProductCr_default', footer: 'ProductCr_footer' },
+  },
+  {
+    field: 'PageViews',
+    title: '页面浏览量',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    slots: { default: 'PageViews_default', footer: 'PageViews_footer' },
+  },
+  {
+    field: 'BuyBoxPercentage',
+    title: '推荐报价(购买按钮)百分比',
+    align: 'right',
+    width: 180,
+    sortable: true,
+    showHeaderOverflow: true,
+    slots: { default: 'BuyBoxPercentage_default', footer: 'BuyBoxPercentage_footer' },
+  },
+  { field: 'FBAQuantity', title: 'FBA库存', align: 'right', width: 130, sortable: true, slots: { footer: 'FBAQuantity_footer' } },
+  {
+    field: 'FBMQuantity',
+    title: 'FBM库存',
+    align: 'right',
+    width: 130,
+    sortable: true,
+    showHeaderOverflow: true,
+    slots: { footer: 'FBMQuantity_footer' },
+  },
+]
+
+export const productLineColumns = [
+  {
+    field: 'productlineName',
+    title: '产品线',
+    align: 'left',
+    fixed: 'left',
+    width: 180,
+    sortable: true,
+    slots: { default: 'productlineName_default', footer: 'productlineName_footer' },
+  },
+  ...universal,
+]
+
+export const parentAsinColumns = [
+  {
+    field: 'parentAsin',
+    title: '父ASIN',
+    align: 'left',
+    fixed: 'left',
+    width: 180,
+    sortable: true,
+    slots: { default: 'parentAsin_default', footer: 'parentAsin_footer' },
+  },
+  { field: 'bestSku', title: '最佳SKU', align: 'left', width: 300, sortable: true, slots: { default: 'bestSku_default', footer: 'bestSku_footer' } },
+  // { field: 'TACOS', title: '预警TACOS', align: 'right', width: 130, slots: { default: 'TACOS_default', footer: 'TACOS_footer' } },
+  ...universal,
+]
+
+export const asinColumns = [
+  {
+    field: 'Asin',
+    title: 'ASIN',
+    align: 'left',
+    fixed: 'left',
+    width: 300,
+    sortable: true,
+    slots: { default: 'Asin_default', footer: 'Asin_footer' },
+  },
+  {
+    field: 'productlineName',
+    title: '产品线',
+    align: 'left',
+    fixed: 'left',
+    width: 180,
+    sortable: true,
+    slots: { default: 'productlineName_default', footer: 'productlineName_footer' },
+  },
+  {
+    field: 'rank',
+    title: '排名',
+    align: 'left',
+    fixed: 'left',
+    width: 120,
+    sortable: true,
+    slots: { default: 'rank_default', footer: 'rank_footer' },
+  },
+  // { field: 'TACOS', title: '预警TACOS', align: 'right', width: 130, slots: { default: 'TACOS_default', footer: 'TACOS_footer' } },
+  ...universal,
+]
+
+export const skuColumns = [
+  { field: 'sku', title: 'SKU', align: 'left', fixed: 'left', width: 300, slots: { default: 'sku_default', footer: 'sku_footer' } },
+  {
+    field: 'productlineName',
+    title: '产品线',
+    align: 'left',
+    width: 180,
+    slots: { default: 'productlineName_default', footer: 'productlineName_footer' },
+  },
+  { field: 'status', title: '商品状态', align: 'center', width: 80, slots: { default: 'status_default', footer: 'status_footer' } },
+  { field: 'rank', title: '排名', align: 'left', width: 130, slots: { default: 'rank_default', footer: 'rank_footer' } },
+  { field: 'parentAsin', title: '父ASIN', align: 'left', width: 130, slots: { default: 'parentAsin_default', footer: 'parentAsin_footer' } },
+  {
+    field: 'launchDatetime',
+    title: '上架时间',
+    align: 'center',
+    width: 120,
+    sortable: true,
+    slots: { default: 'launchDatetime_default', footer: 'launchDatetime_footer' },
+  },
+  ...universal,
+  // { field: 'ABP', title: '异常推广', align: 'right', width: 130, slots: { default: 'ABP_default', footer: 'ABP_footer' } },
+  // { field: 'TACOS', title: '预警TACOS', align: 'right', width: 130, slots: { default: 'TACOS_default', footer: 'TACOS_footer' } },
+]

+ 23 - 0
src/views/reportManage/dataCenter/utils/enum.ts

@@ -0,0 +1,23 @@
+export const dataCenterMetricsEnum = [
+  {label: '曝光量', value: 'Impression'},
+  {label: '点击量', value: 'Click'},
+  {label: '花费', value: 'Spend'},
+  {label: '总订单数', value: 'TotalOrderItems'},
+  {label: '总销售额', value: 'TotalSales'},
+  {label: '单均价', value: 'SAP'},
+  {label: '广告订单数', value: 'TotalAdPurchases'},
+  {label: '广告销售额', value: 'TotalAdSales'},
+  {label: '广告销量', value: 'TotalAdUnitOrdered'},
+  {label: '本商品广告销售额', value: 'TotalAdSalesSameSKU'},
+  {label: '本商品广告订单数', value: 'TotalAdPurchasesSameSKU'},
+  {label: '本商品广告销量', value: 'TotalAdUnitOrderedSameSKU'},
+  // {label: 'SAP', value: 'SAP'},
+  {label: 'ACOS', value: 'ACOS'},
+  {label: 'ROAS', value: 'ROAS'},
+  {label: 'TACOS', value: 'TACOS'},
+  {label: '转化率', value: 'CR'},
+  {label: '点击率', value: 'CTR'},
+  {label: '点击成本', value: 'CPC'},
+  {label: '总订单成本', value: 'CPO'},
+  {label: '广告订单成本', value: 'CPA'},
+]

+ 11 - 0
src/views/reportManage/index.vue

@@ -0,0 +1,11 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+
+</template>
+
+<style scoped>
+
+</style>