Просмотр исходного кода

广告活动添加数据对比;组件化数据趋势图

guojing_wu 1 год назад
Родитель
Сommit
9a6f154729

+ 3 - 1
src/views/adManage/sp/adGroups/index.vue

@@ -17,11 +17,13 @@
 <script lang="ts" setup>
 import { Ref, ref, onMounted } from 'vue'
 import { useFs, FsPage } from '@fast-crud/fast-crud';
-import DateRangePicker from '/@/components/DateRangePicker/index.vue'
 import { createCrudOptions } from './crud'
 import { useRoute, useRouter, LocationQueryValue } from 'vue-router'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
 import AdGroupChart from './chartComponents/adGroupChart.vue'
 import { usePublicData } from '/@/stores/publicData'
+// import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
+// import { getCardData, getLineData } from './api'
 
 const publicData = usePublicData()
 const router = useRouter()

+ 1 - 1
src/views/adManage/sp/campaigns/api.ts

@@ -57,7 +57,7 @@ export function getLineData(query: UserPageQuery) {
     })
 }
 
-export function getAdStructureData(query) {
+export function getAdStructureData(query: UserPageQuery) {
     return request({
         url: apiPrefix + "structure/",
         method: 'GET',

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

@@ -13,9 +13,9 @@
         <span>竞价策略:{{ campaignInfo.dynBidStrategy }}</span>
       </div>
     </div>
-    <el-tabs type="border-card" class="asj-detail-tabs">
-      <el-tab-pane label="广告组">
-        <AdGroups :campaignId="route.query.campaignId"></AdGroups>
+    <el-tabs type="border-card" class="asj-detail-tabs" v-model="tabActiveName">
+      <el-tab-pane label="广告组" name="adGroup">
+        <AdGroups :campaignId="route.query.campaignId" v-if="tabActiveName==='adGroup'"></AdGroups>
       </el-tab-pane>
       <el-tab-pane label="预算" :lazy="true">
         预算
@@ -23,8 +23,8 @@
       <el-tab-pane label="自动化" :lazy="true">
         自动化
       </el-tab-pane>
-      <el-tab-pane label="广告位" :lazy="true">
-        <Placement :campaignId="route.query.campaignId" />
+      <el-tab-pane label="广告位" name="placement">
+        <Placement :campaignId="route.query.campaignId" v-if="tabActiveName==='placement'"/>
       </el-tab-pane>
       <el-tab-pane label="否定投放" :lazy="true">
         否定投放
@@ -45,6 +45,7 @@ import { GetObj } from '../api'
 const router = useRouter()
 const route = useRoute()
 const campaignInfo: Ref<SpCampaign> = ref({})
+const tabActiveName = ref('adGroup')
 
 onMounted(async () => {
   const resp = await GetObj(route.query.id)

+ 0 - 1
src/views/adManage/sp/campaigns/campaignDetail/placement/index.vue

@@ -7,7 +7,6 @@
       <template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
         <p>{{ scope.row[field] }}</p>
         <el-popover
-          class="box-item"
           effect="dark"
           :width="260">
           <template #reference>

+ 7 - 4
src/views/adManage/sp/campaigns/crud.tsx

@@ -7,7 +7,9 @@ import { dynBidStrategyEnum } from '/@/views/adManage/utils/enum.js'
 
 export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
 	const pageRequest = async (query: UserPageQuery) => {
-		query["profile"] = context["profileId"]
+		query["profile"] = context.profile.value.profile_id
+		query["start"] = context.dateRange.value[0]
+		query["end"] = context.dateRange.value[1]
 		return await api.GetList(query);
 	};
 	const editRequest = async ({ form, row }: EditReq) => {
@@ -81,9 +83,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 						// show: hasPermissions('dictionary:Update'),
 					},
 					remove: {
-						iconRight: 'Delete',
-						type: 'text',
-            text: null
+						show: false
+						// iconRight: 'Delete',
+						// type: 'text',
+            // text: null
 						// show: hasPermissions('dictionary:Delete'),
 					},
 				},

+ 50 - 5
src/views/adManage/sp/campaigns/index.vue

@@ -4,7 +4,7 @@
 			<template #header-middle>
 				<el-tabs v-model="tabActiveName" class="chart-tabs" type="border-card">
 					<el-tab-pane label="数据趋势" name="dataTendency">
-						<DataTendencyChart v-if="tabActiveName === 'dataTendency'"></DataTendencyChart>
+						<DataTendencyChart v-if="tabActiveName === 'dataTendency'" :fetchCard="getCardData" :fetchLine="getLineData"></DataTendencyChart>
 					</el-tab-pane>
 					<el-tab-pane label="广告结构" name="adStruct" >
 						<AdStructChart v-if="tabActiveName === 'adStruct'"/>
@@ -29,23 +29,58 @@
 			<template #cell_MissedSales="scope">
         {{ scope.row.MissedSalesLower ?? '0' }} ~ {{ scope.row.MissedSalesUpper ?? '0' }}
       </template>
+
+			<template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <p>{{ scope.row[field] }}</p>
+        <el-popover
+          effect="dark"
+          :width="260">
+          <template #reference>
+            <p :style="{color: scope.row[`gap${field}`] > 0 ? 'green' : 'red'}" v-show="showCompare">
+              <el-icon v-show="scope.row[field]">
+                <Top v-if="scope.row[`gap${field}`] > 0"/>
+                <Bottom v-else/>
+              </el-icon>
+              <span>{{ scope.row[`gap${field}`] }}</span>
+            </p>
+          </template>
+          <p>对比周期:</p>
+          <p>对比值:{{ scope.row[`prev${field}`] }}</p>
+        </el-popover>
+      </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, Ref, nextTick, onBeforeMount} from 'vue'
+import {ref, onMounted, Ref, 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 { useRoute, useRouter } from 'vue-router'
 import AdStructChart from './chartComponents/adStruct.vue'
-import DataTendencyChart from './chartComponents/dataTendency.vue'
+// import DataTendencyChart from './chartComponents/dataTendency.vue'
+import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
+import { getCardData, getLineData } from './api'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
 
 const tabActiveName = ref("dataTendency")
 const shopInfo = useShopInfo()
-const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { profileId: shopInfo.profile.profile_id } })
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+const { profile } = storeToRefs(shopInfo)
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { profile, dateRange } })
 const router = useRouter()
+const showCompare = ref(false)
 
 onMounted(async () => {
 	crudExpose.doRefresh()
@@ -57,7 +92,17 @@ const jumpGroup = (row: any) => {
 		query: { id: row.id, campaignId: row.campaignId, tagsViewName: row.campaignName },
 	})
 }
+
+watch(
+	[dateRange, profile], 
+	async () => {
+		crudExpose.doRefresh()
+	}
+)
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+.campare-switch {
+	flex: none;
+}
 </style>

+ 61 - 25
src/views/adManage/sp/keywords/chartComponents/dataTendency.vue → src/views/adManage/sp/chartComponents/dataTendency.vue

@@ -1,37 +1,33 @@
 <template>
-	<MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
-  <div style="height: 350px;" ref="chartRef"></div>
+  <div v-loading="loading">
+    <MetricsCards v-model="metrics" :metric-items="metricsItems" @change="changeMetric"></MetricsCards>
+    <div style="height: 350px;" ref="chartRef"></div>
+</div>
 </template>
 
 <script lang="ts" setup>
 import { ref,onMounted, onBeforeUnmount, Ref, onBeforeMount, watch, computed } from 'vue'
 import * as echarts from 'echarts'
 import { useShopInfo } from '/@/stores/shopInfo'
-import { getLineData, getCardData } from '../api'
 import { spCampaignMetricsEnum } from '/@/views/adManage/utils/enum.js'
 import MetricsCards from '/@/components/MetricsCards/index.vue'
 import XEUtils from 'xe-utils'
 import { buildChartOpt } from '/@/views/adManage/utils/tools.js'
+import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
 
 defineOptions({
   name: "DataTendencyChart"
 })
 
-onBeforeMount(async () => {
-	await getMetricsItems()
-})
-onMounted(() => {
-	setTimeout(() => { initLine() }, 0)
-	addResize()
-});
-onBeforeUnmount(() => {
-	if(chartObj) {
-		chartObj.dispose()
-    chartObj = null
-	}
-  removeResize()
-})
+interface Props {
+  fetchCard: Function,
+  fetchLine: Function,
+  query?: {[key: string]: any}
+}
 
+const props = defineProps<Props>()
+const publicData = usePublicData()
 const metrics = ref([
   {metric: 'Impression', color: '#0085ff', 'label': '曝光量'},
   {metric: 'Click', color: '#3fd4cf', 'label': '点击量'},
@@ -177,33 +173,61 @@ const option: any = {
     }
   ]
 }
-const getDataset = async () => {
-	const resp = await getLineData({profile: shopInfo.profile.profile_id, start: '2023-11-01', end: '2023-11-04'})
-	return resp.data
-}
+const { dateRange } = storeToRefs(publicData)
+const loading = ref(true)
+const queryParams = computed(() => {
+  return {
+    profile: shopInfo.profile.profile_id,
+    start: dateRange.value[0],
+    end: dateRange.value[1],
+   ...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(metricsItems.value, info => {
+    option.legend.selected[info.label] = false
+  })
   for(const info of metrics.value) {
     option.legend.selected[info.label] = true
   }
 	chartObj.setOption(option)
+  loading.value = false
+}
+const getDataset = async () => {
+	const resp = await props.fetchLine(queryParams.value)
+	return resp.data
 }
-
 const getMetricsItems = async () => {
-	const resp = await getCardData({start: '2023-11-01', end: '2023-11-04', profile: shopInfo.profile.profile_id})
+	const resp = await props.fetchCard(queryParams.value)
 	const data = resp.data
+  metricsItems.value.length = 0
 	XEUtils.arrayEach(spCampaignMetricsEnum, info => {
 		const tmp:MetricData = {
 			label: info.label,
 			value: info.value,
 			metricVal: data[info.value],
 			gapVal: data[`gap${info.value}`],
-			preVal: data[`prev${info.value}`]
+			preVal: data[`prev${info.value}`],
 		}
 		metricsItems.value.push(tmp)
-    option.legend.selected[info.label] = false
 	})
 }
 
@@ -212,6 +236,18 @@ const changeMetric = () => {
   chartObj.setOption(opt)
 }
 
+watch(
+  queryParams, 
+  async () => {
+    loading.value = true
+    await getMetricsItems()
+    const items = await getDataset()
+    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) }

+ 4 - 4
src/views/adManage/sp/index.vue

@@ -29,8 +29,8 @@
 import { ref, onBeforeMount, Ref, watch, provide } from 'vue'
 import { useShopInfo } from '/@/stores/shopInfo'
 import { usePublicData } from '/@/stores/publicData'
-import { GetList } from '/@/views/adManage/portfolios/api'
 import { storeToRefs } from 'pinia'
+import { GetList } from '/@/views/adManage/portfolios/api'
 import DateRangePicker from '/@/components/DateRangePicker/index.vue'
 import { recentDaysRange } from '/@/views/adManage/utils/tools'
 import Campaigns from './campaigns/index.vue'
@@ -38,7 +38,7 @@ import Keywords from './keywords/index.vue'
 import Targets from './targets/index.vue'
 import SearchTerm from './searchTerm/index.vue'
 import AdvertisedProducts from './advertisedProducts/index.vue'
-import PurchasedProducts from './purchasedProducts/index.vue'
+import PurchasedOtherProducts from './purchasedProducts/index.vue'
 import Placement from './placement/index.vue'
 
 const shopInfo = useShopInfo()
@@ -52,7 +52,7 @@ const tabs = [
 	{ label: '关键词投放', name: 'Keywords' },
 	{ label: '商品投放', name: 'Targets' },
 	{ label: '推广商品', name: 'AdvertisedProducts' },
-	{ label: '购买的其他商品', name: 'PurchasedProducts' },
+	{ label: '购买的其他商品', name: 'PurchasedOtherProducts' },
 	{ label: '搜索词', name: 'SearchTerm' },
 	{ label: '广告位', name: 'Placement' }
 ]
@@ -61,7 +61,7 @@ const tabsComponents: any = {
 	Keywords,
 	Targets,
 	AdvertisedProducts,
-	PurchasedProducts,
+	PurchasedOtherProducts,
 	SearchTerm,
 	Placement
 }

+ 1 - 1
src/views/adManage/sp/keywords/api.ts

@@ -57,7 +57,7 @@ export function getLineData(query: UserPageQuery) {
     })
 }
 
-export function getAdStructureData(query) {
+export function getAdStructureData(query: UserPageQuery) {
     return request({
         url: apiPrefix + "structure/",
         method: 'GET',

+ 3 - 2
src/views/adManage/sp/keywords/index.vue

@@ -4,7 +4,7 @@
 			<template #header-middle>
 				<el-tabs v-model="tabActiveName" class="chart-tabs" type="border-card">
 					<el-tab-pane label="数据趋势" name="dataTendency">
-						<DataTendencyChart v-if="tabActiveName === 'dataTendency'" />
+						<DataTendencyChart v-if="tabActiveName === 'dataTendency'" :fetch-card="getCardData" :fetch-line="getLineData" />
 					</el-tab-pane>
 					<el-tab-pane label="广告结构" name="adStruct">
 						<AdStructChart v-if="tabActiveName === 'adStruct'" />
@@ -20,9 +20,10 @@
 import { ref, onMounted, inject } from 'vue'
 import { useFs, FsPage } from '@fast-crud/fast-crud'
 import { createCrudOptions } from './crud'
-import DataTendencyChart from './chartComponents/dataTendency.vue'
+import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
 import { useShopInfo } from '/@/stores/shopInfo'
 import AdStructChart from './chartComponents/adStruct.vue'
+import { getCardData, getLineData } from './api'
 
 const tabActiveName = ref('dataTendency')
 const shopInfo = useShopInfo()

+ 41 - 0
src/views/adManage/sp/placement/api.ts

@@ -0,0 +1,41 @@
+import { request } from '/@/utils/service'
+import { UserPageQuery } from '@fast-crud/fast-crud'
+
+export const apiPrefix = '/api/ad_manage/spcampaigns/'
+export function GetList(query: UserPageQuery) {
+	return request({
+		url: apiPrefix,
+		method: 'get',
+		params: query,
+	})
+}
+export function GetObj(id: any) {
+	return request({
+		url: apiPrefix + id + '/',
+		method: 'get',
+	})
+}
+
+export function getCardData(query: UserPageQuery) {
+	return request({
+		url: apiPrefix + 'total/',
+		method: 'GET',
+		params: query,
+	})
+}
+
+export function getLineData(query: UserPageQuery) {
+	return request({
+		url: apiPrefix + 'daily/',
+		method: 'GET',
+		params: query,
+	})
+}
+
+export function getAdStructureData(query: UserPageQuery) {
+	return request({
+		url: apiPrefix + 'structure/',
+		method: 'GET',
+		params: query,
+	})
+}

+ 91 - 0
src/views/adManage/sp/placement/crud.tsx

@@ -0,0 +1,91 @@
+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 { dynBidStrategyEnum } from '/@/views/adManage/utils/enum.js'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+	const pageRequest = async (query: UserPageQuery) => {
+		query['profile'] = context['profileId']
+		return await api.GetList(query)
+	}
+
+	//权限判定
+	const hasPermissions = inject('$hasPermissions')
+
+	return {
+		crudOptions: {
+			table: {
+				height: 800,
+			},
+			container: {
+				fixedHeight: false,
+			},
+			actionbar: {
+				show: false
+			},
+			search: {
+				show: false,
+			},
+			toolbar: {
+				buttons: {
+					search: {
+						show: true,
+					},
+					compact: {
+						show: false,
+					},
+				},
+			},
+			request: {
+				pageRequest,
+			},
+			rowHandle: {
+				show: false,
+			},
+			columns: {
+				id: {
+					title: 'ID',
+					column: {
+						show: false,
+					},
+					form: {
+						show: false,
+					},
+				},
+				campaignName: {
+					title: '广告活动',
+					column: {
+						width: '200px',
+						fixed: 'left',
+					},
+					search: {
+						show: true,
+						component: {
+							props: {
+								clearable: true,
+							},
+						},
+					},
+				},
+				placement: {
+					title: '广告位',
+				},
+				dynBidStrategy: {
+					title: '竞价策略',
+					form: {
+						show: false,
+					},
+					column: {
+						width: '160px',
+					},
+					type: 'dict-select',
+					dict: dict({
+						data: dynBidStrategyEnum,
+					}),
+				},
+				...BaseColumn,
+			},
+		},
+	}
+}

+ 31 - 5
src/views/adManage/sp/placement/index.vue

@@ -1,12 +1,38 @@
 <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">
+					<el-tab-pane label="数据趋势" name="dataTendency">
+						<DataTendencyChart v-if="tabActiveName === 'dataTendency'" :fetchCard="getCardData" :fetchLine="getLineData" />
+					</el-tab-pane>
+					<el-tab-pane label="广告结构" name="adStruct">
+						<AdStructChart v-if="tabActiveName === 'adStruct'" />
+					</el-tab-pane>
+					<el-tab-pane label="散点视图" name="scatterView">
+						<div v-if="tabActiveName === 'adStruct'">散点视图</div>
+					</el-tab-pane>
+				</el-tabs>
+			</template>
+		</fs-crud>
+	</fs-page>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
+import { getCardData, getLineData } from './api'
+import { useShopInfo } from '/@/stores/shopInfo'
 
-</script>
+const tabActiveName = ref('dataTendency')
+const shopInfo = useShopInfo()
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { profileId: shopInfo.profile.profile_id } })
 
-<style scoped>
+onMounted(async () => {
+	crudExpose.doRefresh()
+})
+</script>
 
-</style>
+<style scoped></style>

+ 0 - 0
src/views/adManage/sp/purchasedProducts/index.vue → src/views/adManage/sp/purchasedOtherProducts/index.vue


+ 9 - 0
src/views/adManage/utils/commonTabColumn.ts

@@ -71,6 +71,15 @@ export const BaseColumn = {
       align: 'right'
     }
   },
+  PurchasesRate: {
+    title: '转化率',
+    form: {
+      show: false
+    },
+    column:{
+      align: 'right'
+    }
+  },
   ACOS: {
     title: 'ACOS',
     form: {