|
@@ -1,89 +1,146 @@
|
|
|
<script setup lang="ts">
|
|
|
+import type { overviewAdvertisingData } from '@/types/HomeTypes'
|
|
|
+import type { BaseMenu } from '@/types/Promotion/Menu'
|
|
|
import type {
|
|
|
- overviewAdvertisingData,
|
|
|
- FilterSelect,
|
|
|
- MediaSelect,
|
|
|
- DateSelect,
|
|
|
- CustomIndicatorSelect,
|
|
|
-} from '@/types/HomeTypes'
|
|
|
+ homeAnalysisChartData,
|
|
|
+ LegendInfo,
|
|
|
+} from '@/types/echarts/homeAnalysisChart'
|
|
|
|
|
|
-import { reactive } from 'vue'
|
|
|
+import { computed, reactive, ref, watch } from 'vue'
|
|
|
+import { useRequest } from '@/hooks/useRequest'
|
|
|
|
|
|
+import Menu from '@/components/navigation/Menu.vue'
|
|
|
+import CSelect from '@/components/form/CSelect.vue'
|
|
|
+import HomeAnalysisLine from '@/components/echarts/HomeAnalysisLine.vue'
|
|
|
import '@/assets/css/statistic.css'
|
|
|
|
|
|
+import { useHomeSelect } from '@/hooks/HomeSelect/useHomeSelect'
|
|
|
+
|
|
|
+import axiosInstance from '@/utils/axios/axiosInstance'
|
|
|
+import type { ResponseInfo } from '@/types/axios'
|
|
|
+
|
|
|
+const { AllApi } = useRequest()
|
|
|
+
|
|
|
+const {
|
|
|
+ overviewSelectInfo,
|
|
|
+ analysisSelectInfo,
|
|
|
+ legendSelectInfo,
|
|
|
+ leftToolsSelectInfo,
|
|
|
+ rightToolsSelectInfo,
|
|
|
+} = useHomeSelect()
|
|
|
+
|
|
|
+// 广告数据
|
|
|
const overviewAdvertisingData = reactive<Array<overviewAdvertisingData>>([
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce1',
|
|
|
+ title: '消耗(元)11',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce2',
|
|
|
+ title: '消耗(元)22',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce3',
|
|
|
+ title: '消耗(元)33',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce4',
|
|
|
+ title: '消耗(元)44',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce5',
|
|
|
+ title: '消耗(元)55',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
{
|
|
|
- title: '消耗(元)',
|
|
|
+ name: 'ce6',
|
|
|
+ title: '消耗(元)66',
|
|
|
value: 98500.03,
|
|
|
rate: 24,
|
|
|
},
|
|
|
])
|
|
|
|
|
|
-const mediaSelectList = reactive<Array<MediaSelect>>([
|
|
|
+// 分析栏导航菜单
|
|
|
+const dataAnalysisMenuList = reactive<Array<BaseMenu>>([
|
|
|
{
|
|
|
- label: '全部媒体',
|
|
|
- value: 'all',
|
|
|
+ name: 'projAnalysis',
|
|
|
+ title: '项目分析',
|
|
|
},
|
|
|
-])
|
|
|
-
|
|
|
-const dateSelectList = reactive<Array<DateSelect>>([
|
|
|
{
|
|
|
- label: '今天',
|
|
|
- value: new Date(),
|
|
|
+ name: 'productAnalysis',
|
|
|
+ title: '产品分析',
|
|
|
},
|
|
|
])
|
|
|
|
|
|
-const customIndicatorSelectList = reactive<Array<CustomIndicatorSelect>>([
|
|
|
- {
|
|
|
- label: '自定义指标',
|
|
|
- value: 'custom',
|
|
|
- },
|
|
|
-])
|
|
|
+const analysisiMenuActive = ref<string>('projAnalysis')
|
|
|
|
|
|
-const overviewSelectInfo = reactive<{
|
|
|
- [key: string]: {
|
|
|
- selected: Date | string
|
|
|
- list: Array<FilterSelect>
|
|
|
+/**
|
|
|
+ * @description: 根据已选择自定义指标动态的加入广告数据概况
|
|
|
+ * @return {*}
|
|
|
+ */
|
|
|
+const overviewDataSelected = computed(() => {
|
|
|
+ // 这里这样搞是为了做出动态加入的效果,而不是始终在原位置显示
|
|
|
+ let result: Array<overviewAdvertisingData> = []
|
|
|
+ overviewSelectInfo.customIndicatorSelect.value.forEach((item: string) => {
|
|
|
+ let finded = overviewAdvertisingData.find(data => data.name === item)
|
|
|
+ if (finded) {
|
|
|
+ result.push(finded)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return result
|
|
|
+})
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description: 数据分析栏菜单切换
|
|
|
+ * @param {*} newMenu 新的菜单值
|
|
|
+ * @return {*}
|
|
|
+ */
|
|
|
+const analysisiMenuChange = (newMenu: string) => {
|
|
|
+ analysisiMenuActive.value = newMenu
|
|
|
+}
|
|
|
+
|
|
|
+const chartLoading = ref<boolean>(true)
|
|
|
+
|
|
|
+const chartData = reactive<Array<homeAnalysisChartData>>([])
|
|
|
+
|
|
|
+const getChartData = async () => {
|
|
|
+ try {
|
|
|
+ chartLoading.value = true
|
|
|
+ const res = (await axiosInstance.get(AllApi.mockChart)) as ResponseInfo
|
|
|
+ if (res.code !== 0) throw new Error('获取图表数据失败')
|
|
|
+ chartData.splice(0, chartData.length, ...res.data)
|
|
|
+ } catch (err) {
|
|
|
+ console.log(err)
|
|
|
+ } finally {
|
|
|
+ chartLoading.value = false
|
|
|
}
|
|
|
-}>({
|
|
|
- mediaSelect: {
|
|
|
- selected: mediaSelectList[0].value,
|
|
|
- list: mediaSelectList,
|
|
|
- },
|
|
|
- dateSelect: {
|
|
|
- selected: dateSelectList[0].value,
|
|
|
- list: dateSelectList,
|
|
|
- },
|
|
|
- customIndicatorSelect: {
|
|
|
- selected: customIndicatorSelectList[0].value,
|
|
|
- list: customIndicatorSelectList,
|
|
|
- },
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description: 计算图例所需要的信息
|
|
|
+ * @return {*}
|
|
|
+ */
|
|
|
+const analysisLegendInfo = computed<Array<LegendInfo>>(() => {
|
|
|
+ let result: Array<LegendInfo> = []
|
|
|
+ for (let [_k, v] of Object.entries(legendSelectInfo)) {
|
|
|
+ result.push({
|
|
|
+ value: v.value,
|
|
|
+ color: v.color!,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return result
|
|
|
})
|
|
|
+
|
|
|
+getChartData()
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -95,50 +152,95 @@ const overviewSelectInfo = reactive<{
|
|
|
<span class="getDetail">查看详情</span>
|
|
|
</div>
|
|
|
<div class="overviewFilterBox">
|
|
|
- <el-select
|
|
|
- v-for="selectItem in overviewSelectInfo"
|
|
|
- v-model="selectItem.selected"
|
|
|
- placeholder="Select"
|
|
|
- size="small"
|
|
|
- style="width: 130px; margin-right: 10px"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="listItem in selectItem.list"
|
|
|
- :key="listItem.value"
|
|
|
- :label="listItem.label"
|
|
|
- :value="listItem.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
+ <CSelect
|
|
|
+ :multiple-limit="6"
|
|
|
+ size="default"
|
|
|
+ :select-info="overviewSelectInfo"
|
|
|
+ ></CSelect>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="dataOverview">
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :span="4" v-for="item in overviewAdvertisingData">
|
|
|
- <div class="statistic-card" html>
|
|
|
- <el-statistic :value="item.value" :precision="2">
|
|
|
- <template #title>
|
|
|
- <div style="display: inline-flex; align-items: center">
|
|
|
- {{ item.title }}
|
|
|
+ <el-col :span="4" v-for="item in overviewDataSelected">
|
|
|
+ <template
|
|
|
+ v-if="
|
|
|
+ overviewSelectInfo.customIndicatorSelect.value.includes(
|
|
|
+ item.name,
|
|
|
+ )
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div class="statistic-card" html>
|
|
|
+ <el-statistic :value="item.value" :precision="2">
|
|
|
+ <template #title>
|
|
|
+ <div style="display: inline-flex; align-items: center">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-statistic>
|
|
|
+ <div class="statistic-footer">
|
|
|
+ <div class="footer-item">
|
|
|
+ <span>环比</span>
|
|
|
+ <span class="green">
|
|
|
+ {{ item.rate }}%
|
|
|
+ <el-icon>
|
|
|
+ <CaretTop />
|
|
|
+ </el-icon>
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-statistic>
|
|
|
- <div class="statistic-footer">
|
|
|
- <div class="footer-item">
|
|
|
- <span>环比</span>
|
|
|
- <span class="green">
|
|
|
- {{ item.rate }}%
|
|
|
- <el-icon>
|
|
|
- <CaretTop />
|
|
|
- </el-icon>
|
|
|
- </span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="advertisingDataAnalysis"></div>
|
|
|
+ <div class="advertisingDataAnalysis">
|
|
|
+ <div class="analysisHeader">
|
|
|
+ <div class="headerMeun">
|
|
|
+ <Menu
|
|
|
+ mode="horizontal"
|
|
|
+ default-active="projAnalysis"
|
|
|
+ :menu-list="dataAnalysisMenuList"
|
|
|
+ @active-change="analysisiMenuChange"
|
|
|
+ ></Menu>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="analysisContent">
|
|
|
+ <div class="lineChartContainer">
|
|
|
+ <div class="chartBoxHeader">
|
|
|
+ <div class="layoutItem"></div>
|
|
|
+ <CSelect :select-info="analysisSelectInfo"></CSelect>
|
|
|
+ </div>
|
|
|
+ <div class="chartBoxTools">
|
|
|
+ <div
|
|
|
+ class="dataTools"
|
|
|
+ v-if="analysisiMenuActive !== 'projAnalysis'"
|
|
|
+ >
|
|
|
+ <div class="dataSource">
|
|
|
+ <CSelect :select-info="leftToolsSelectInfo"></CSelect>
|
|
|
+ </div>
|
|
|
+ <div class="dataCategory">
|
|
|
+ <CSelect :select-info="rightToolsSelectInfo"></CSelect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="legendTools" v-else>
|
|
|
+ <CSelect
|
|
|
+ :select-style="`width:100px;margin-right:15px;`"
|
|
|
+ size="small"
|
|
|
+ :select-info="legendSelectInfo"
|
|
|
+ ></CSelect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="chartContent">
|
|
|
+ <HomeAnalysisLine
|
|
|
+ :legend="analysisLegendInfo"
|
|
|
+ :data="chartData"
|
|
|
+ :loading="chartLoading"
|
|
|
+ ></HomeAnalysisLine>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div class="topArea"></div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -183,4 +285,61 @@ const overviewSelectInfo = reactive<{
|
|
|
.dataOverview {
|
|
|
width: 100%;
|
|
|
}
|
|
|
+
|
|
|
+.advertisingDataAnalysis {
|
|
|
+ position: relative;
|
|
|
+ width: 85%;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 16px 16px 24px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 2px;
|
|
|
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.analysisHeader {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ position: absolute;
|
|
|
+ top: 16px;
|
|
|
+ z-index: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.headerMeun {
|
|
|
+ width: 50%;
|
|
|
+}
|
|
|
+
|
|
|
+.analysisContent {
|
|
|
+ width: 100%;
|
|
|
+ height: 500px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.lineChartContainer {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ padding: 16px 16px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.chartBoxHeader {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.chartBoxTools {
|
|
|
+ margin-top: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.legendTools {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.dataTools {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
</style>
|