|
|
@@ -0,0 +1,620 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container h-full flex flex-1 flex-col overflow-auto">
|
|
|
+ <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
|
|
+ <el-tab-pane label="平台数据" name="first">
|
|
|
+ <!-- 实时数据 -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="flex items-center gap-10px">
|
|
|
+ <div class="flex items-center gap-6px">
|
|
|
+ <span class="font-bold text-24px">实时数据</span>
|
|
|
+ <el-icon color="#4080ff" size="20" class="cursor-pointer" @click="refreshData">
|
|
|
+ <Refresh />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="font-400 text-14px text-#949494">{{ lastUpdateTime }} 更新</div>
|
|
|
+ </div>
|
|
|
+ <div class="mt-10px flex flex-wrap gap-16px">
|
|
|
+ <DataCard
|
|
|
+ v-for="item in totalDataList"
|
|
|
+ :key="item.type"
|
|
|
+ :label="item.label"
|
|
|
+ :icon="item.icon"
|
|
|
+ :value="item.value"
|
|
|
+ :unit="item.unit"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div class="h-16px"></div>
|
|
|
+
|
|
|
+ <!-- 今日实时数据 -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="flex items-center gap-10px">
|
|
|
+ <div class="flex items-center gap-6px">
|
|
|
+ <span class="font-bold text-24px">今日实时数据</span>
|
|
|
+ <el-icon color="#4080ff" size="20" class="cursor-pointer" @click="refreshData">
|
|
|
+ <Refresh />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="font-400 text-14px text-#949494">{{ lastUpdateTime }} 更新</div>
|
|
|
+ </div>
|
|
|
+ <div class="mt-10px flex flex-wrap gap-16px">
|
|
|
+ <DataCard
|
|
|
+ v-for="item in todayDataList"
|
|
|
+ :key="item.type"
|
|
|
+ :label="item.label"
|
|
|
+ :icon="item.icon"
|
|
|
+ :value="item.value"
|
|
|
+ :unit="item.unit"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div class="h-16px"></div>
|
|
|
+
|
|
|
+ <!-- 所有站点充电度数 日时段对比趋势 -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="flex items-center gap-16px mb-16px">
|
|
|
+ <span class="font-bold text-18px">所有站点充电度数 日时段对比趋势</span>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-24px mb-16px">
|
|
|
+ <div class="flex items-center gap-8px">
|
|
|
+ <span class="w-20px h-20px bg-#4868FE rounded"></span>
|
|
|
+ <span class="text-14px">今日</span>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-8px">
|
|
|
+ <span class="w-20px h-20px bg-#0CD489 rounded"></span>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="selectedCompareDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="选择对比日期"
|
|
|
+ format="MM.DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ size="small"
|
|
|
+ style="width: 120px"
|
|
|
+ :disabled-date="disableCompareDate"
|
|
|
+ :clearable="false"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <ECharts :options="hourlyTrendChartOptions" height="350px" />
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div class="h-16px"></div>
|
|
|
+
|
|
|
+ <!-- 历史营业数据(面积图) -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="flex items-center justify-between mb-16px">
|
|
|
+ <span class="font-bold text-18px">历史营业数据</span>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center justify-between flex-wrap gap-16px mb-16px">
|
|
|
+ <div class="flex items-center gap-16px">
|
|
|
+ <div class="flex items-center gap-8px">
|
|
|
+ <span class="text-14px text-#666">时间维度</span>
|
|
|
+ <el-select v-model="historyTimeType" style="width: 100px" size="small">
|
|
|
+ <el-option label="日趋势" value="day" />
|
|
|
+ <el-option label="月趋势" value="month" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="historyDateValue"
|
|
|
+ :type="historyTimeType === 'day' ? 'month' : 'year'"
|
|
|
+ :placeholder="historyTimeType === 'day' ? '选择月份' : '选择年份'"
|
|
|
+ :format="historyTimeType === 'day' ? 'YYYY-MM' : 'YYYY'"
|
|
|
+ :value-format="historyTimeType === 'day' ? 'YYYY-MM' : 'YYYY'"
|
|
|
+ size="small"
|
|
|
+ style="width: 140px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-8px">
|
|
|
+ <span class="text-14px text-#666">指标筛选</span>
|
|
|
+ <el-radio-group v-model="historyMetric" size="small">
|
|
|
+ <el-radio-button value="chargeDegree">充电度数</el-radio-button>
|
|
|
+ <el-radio-button value="chargeAmount">充电金额</el-radio-button>
|
|
|
+ <el-radio-button value="validOrder">有效订单</el-radio-button>
|
|
|
+ <el-radio-button value="registerUser">注册用户</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <ECharts :options="historyAreaChartOptions" height="350px" />
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div class="h-16px"></div>
|
|
|
+
|
|
|
+ <!-- 历史营业数据(表格) -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="flex items-center justify-between flex-wrap gap-16px mb-16px">
|
|
|
+ <div class="flex items-center gap-8px">
|
|
|
+ <el-radio-group v-model="tableDataType" size="small">
|
|
|
+ <el-radio-button value="degree">按充电度数</el-radio-button>
|
|
|
+ <el-radio-button value="order">按有效订单</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-8px flex-wrap">
|
|
|
+ <el-radio-group v-model="tableDateRange" size="small">
|
|
|
+ <el-radio-button value="yesterday">昨天</el-radio-button>
|
|
|
+ <el-radio-button value="week">近七天</el-radio-button>
|
|
|
+ <el-radio-button value="month">近30天</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="tableStartDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="开始日期"
|
|
|
+ format="YYYY-MM-DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ size="small"
|
|
|
+ style="width: 130px"
|
|
|
+ />
|
|
|
+ <span class="text-#999">至</span>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="tableEndDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="结束日期"
|
|
|
+ format="YYYY-MM-DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ size="small"
|
|
|
+ style="width: 130px"
|
|
|
+ />
|
|
|
+ <span class="text-14px text-#999 ml-16px">已选时间: {{ selectedDateRange }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex gap-24px flex-wrap">
|
|
|
+ <!-- 热门充电站 -->
|
|
|
+ <div class="flex-1 min-w-400px">
|
|
|
+ <div class="font-bold text-16px mb-12px">热门充电站</div>
|
|
|
+ <el-table :data="hotStationData" border stripe size="small">
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
+ <el-table-column prop="name" label="电站名称" min-width="180" />
|
|
|
+ <el-table-column
|
|
|
+ prop="value"
|
|
|
+ :label="tableDataType === 'degree' ? '充电度数' : '有效订单'"
|
|
|
+ width="120"
|
|
|
+ align="right"
|
|
|
+ />
|
|
|
+ </el-table>
|
|
|
+ <div class="flex items-center justify-between mt-12px">
|
|
|
+ <span class="text-12px text-#999">共{{ hotStationTotal }}条</span>
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="hotCurrentPage"
|
|
|
+ v-model:page-size="hotPageSize"
|
|
|
+ :page-sizes="[5, 10, 20]"
|
|
|
+ :total="hotStationTotal"
|
|
|
+ size="small"
|
|
|
+ layout="sizes, prev, pager, next, jumper"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 波动充电站 -->
|
|
|
+ <div class="flex-1 min-w-400px">
|
|
|
+ <div class="font-bold text-16px mb-12px">波动充电站</div>
|
|
|
+ <el-table :data="fluctuationStationData" border stripe size="small">
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
+ <el-table-column prop="name" label="电站名称" min-width="180" />
|
|
|
+ <el-table-column prop="rate" label="涨幅 | 跌幅" width="120" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span :class="row.rate >= 0 ? 'text-#F56C6C' : 'text-#67C23A'">
|
|
|
+ <el-icon v-if="row.rate >= 0"><CaretTop /></el-icon>
|
|
|
+ <el-icon v-else><CaretBottom /></el-icon>
|
|
|
+ {{ Math.abs(row.rate).toFixed(2) }}%
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div class="flex items-center justify-between mt-12px">
|
|
|
+ <span class="text-12px text-#999">共{{ fluctuationStationTotal }}条</span>
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="fluctuationCurrentPage"
|
|
|
+ v-model:page-size="fluctuationPageSize"
|
|
|
+ :page-sizes="[5, 10, 20]"
|
|
|
+ :total="fluctuationStationTotal"
|
|
|
+ size="small"
|
|
|
+ layout="sizes, prev, pager, next, jumper"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div class="h-16px"></div>
|
|
|
+
|
|
|
+ <!-- 用户流失率 -->
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div class="font-bold text-18px mb-24px">用户流失率</div>
|
|
|
+ <div class="flex items-center gap-48px flex-wrap">
|
|
|
+ <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
|
|
|
+ <div>
|
|
|
+ <div class="text-14px text-#666 mb-8px">近七天 流失率</div>
|
|
|
+ <div class="font-bold text-32px text-#333">{{ lossRate.sevenDay }}%</div>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" link @click="downloadReport('sevenDay')">
|
|
|
+ <el-icon><Download /></el-icon>
|
|
|
+ 下载表格
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
|
|
|
+ <div>
|
|
|
+ <div class="text-14px text-#666 mb-8px">近1个月 流失率</div>
|
|
|
+ <div class="font-bold text-32px text-#333">{{ lossRate.oneMonth }}%</div>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" link @click="downloadReport('oneMonth')">
|
|
|
+ <el-icon><Download /></el-icon>
|
|
|
+ 下载表格
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
|
|
|
+ <div>
|
|
|
+ <div class="text-14px text-#666 mb-8px">近3个月 流失率</div>
|
|
|
+ <div class="font-bold text-32px text-#333">{{ lossRate.threeMonth }}%</div>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" link @click="downloadReport('threeMonth')">
|
|
|
+ <el-icon><Download /></el-icon>
|
|
|
+ 下载表格
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="场站方数据" name="second">场站方数据内容</el-tab-pane>
|
|
|
+ <el-tab-pane label="销售方数据" name="third">销售方数据内容</el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed, onMounted } from "vue";
|
|
|
+import { dayjs, ElMessage } from "element-plus";
|
|
|
+import { CaretTop, CaretBottom, Download, Refresh } from "@element-plus/icons-vue";
|
|
|
+import type { TabsPaneContext } from "element-plus";
|
|
|
+import DataCard from "./components/DataCard.vue";
|
|
|
+
|
|
|
+const activeName = ref("first");
|
|
|
+
|
|
|
+// 最后更新时间
|
|
|
+const lastUpdateTime = ref(dayjs().format("MM-DD HH:mm"));
|
|
|
+
|
|
|
+// 对比日期选择
|
|
|
+const selectedCompareDate = ref(dayjs().subtract(1, "day").format("YYYY-MM-DD"));
|
|
|
+// 对比日期显示格式
|
|
|
+const compareDate = computed(() => dayjs(selectedCompareDate.value).format("MM.DD"));
|
|
|
+
|
|
|
+// 禁用日期:只允许选择昨天之前的15天(含昨天)
|
|
|
+const disableCompareDate = (date: Date) => {
|
|
|
+ const today = dayjs().startOf("day");
|
|
|
+ const yesterday = today.subtract(1, "day");
|
|
|
+ const fifteenDaysAgo = today.subtract(15, "day");
|
|
|
+ const currentDate = dayjs(date);
|
|
|
+ // 禁用今天及之后的日期,以及15天之前的日期
|
|
|
+ return currentDate.isAfter(yesterday) || currentDate.isBefore(fifteenDaysAgo);
|
|
|
+};
|
|
|
+
|
|
|
+// 刷新数据
|
|
|
+const refreshData = () => {
|
|
|
+ lastUpdateTime.value = dayjs().format("MM-DD HH:mm");
|
|
|
+ ElMessage.success("数据已刷新");
|
|
|
+};
|
|
|
+
|
|
|
+// 数据指标配置
|
|
|
+const dataMetrics = [
|
|
|
+ { type: "cdds", label: "充电度数", icon: "cdds.png", unit: "度" },
|
|
|
+ { type: "cdje", label: "充电金额", icon: "cdje.png", unit: "元" },
|
|
|
+ { type: "dkqje", label: "抵扣券金额", icon: "dkqje.png", unit: "元" },
|
|
|
+ { type: "fwfje", label: "服务费金额", icon: "fwfje.png", unit: "元" },
|
|
|
+ { type: "tkje", label: "退款金额", icon: "tkje.png", unit: "元" },
|
|
|
+ { type: "sfje", label: "实付金额", icon: "sfje.png", unit: "元" },
|
|
|
+ { type: "sdje", label: "首单金额", icon: "sdje.png", unit: "元" },
|
|
|
+ { type: "yhqjm", label: "优惠券减免", icon: "yhqjm.png", unit: "元" },
|
|
|
+ { type: "qyzxjj", label: "企业专享价减", icon: "qyzxjj.png", unit: "元" },
|
|
|
+ { type: "fxyj", label: "分销佣金", icon: "fxyj.png", unit: "元" },
|
|
|
+];
|
|
|
+
|
|
|
+// 总数据列表
|
|
|
+const totalDataList = computed(() =>
|
|
|
+ dataMetrics.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ label: `总${item.label}`,
|
|
|
+ value: "198,708.45",
|
|
|
+ }))
|
|
|
+);
|
|
|
+
|
|
|
+// 今日数据列表
|
|
|
+const todayDataList = computed(() =>
|
|
|
+ dataMetrics.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ label: `今日${item.label}`,
|
|
|
+ value: "198,708.45",
|
|
|
+ }))
|
|
|
+);
|
|
|
+
|
|
|
+// ==================== 日时段对比趋势图表 ====================
|
|
|
+const hourlyTrendChartOptions = computed(() => {
|
|
|
+ const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
|
+ // 模拟今日数据
|
|
|
+ const todayData = [
|
|
|
+ 50, 80, 120, 150, 200, 280, 350, 450, 520, 580, 620, 580, 520, 480, 450, 420, 400, 450, 500,
|
|
|
+ 480, 420, 350, 200, 100,
|
|
|
+ ];
|
|
|
+ // 模拟昨日数据
|
|
|
+ const yesterdayData = [
|
|
|
+ 40, 70, 100, 130, 180, 250, 320, 420, 480, 550, 600, 560, 500, 460, 430, 400, 380, 420, 470,
|
|
|
+ 450, 400, 320, 180, 80,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: {
|
|
|
+ type: "cross",
|
|
|
+ },
|
|
|
+ formatter: (params: any) => {
|
|
|
+ let result = `时间: ${params[0].axisValue}:00:00<br/>`;
|
|
|
+ params.forEach((item: any) => {
|
|
|
+ result += `${item.marker} ${item.seriesName}: ${item.value}<br/>`;
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: "3%",
|
|
|
+ right: "4%",
|
|
|
+ bottom: "3%",
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ data: hours,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: "{value}",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: "dashed",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "今日",
|
|
|
+ type: "line",
|
|
|
+ data: todayData,
|
|
|
+ smooth: true,
|
|
|
+ symbol: "circle",
|
|
|
+ symbolSize: 6,
|
|
|
+ areaStyle: {
|
|
|
+ color: {
|
|
|
+ type: "linear",
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ x2: 0,
|
|
|
+ y2: 1,
|
|
|
+ colorStops: [
|
|
|
+ { offset: 0, color: "rgba(72, 104, 254, 0.3)" },
|
|
|
+ { offset: 1, color: "rgba(72, 104, 254, 0.05)" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: "#4868FE",
|
|
|
+ },
|
|
|
+ lineStyle: {
|
|
|
+ color: "#4868FE",
|
|
|
+ width: 2,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: compareDate.value,
|
|
|
+ type: "line",
|
|
|
+ data: yesterdayData,
|
|
|
+ smooth: true,
|
|
|
+ symbol: "circle",
|
|
|
+ symbolSize: 6,
|
|
|
+ areaStyle: {
|
|
|
+ color: {
|
|
|
+ type: "linear",
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ x2: 0,
|
|
|
+ y2: 1,
|
|
|
+ colorStops: [
|
|
|
+ { offset: 0, color: "rgba(12, 212, 137, 0.3)" },
|
|
|
+ { offset: 1, color: "rgba(12, 212, 137, 0.05)" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: "#0CD489",
|
|
|
+ },
|
|
|
+ lineStyle: {
|
|
|
+ color: "#0CD489",
|
|
|
+ width: 2,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+// ==================== 历史营业数据(面积图) ====================
|
|
|
+const historyTimeType = ref("day");
|
|
|
+const historyDateValue = ref(dayjs().format("YYYY-MM"));
|
|
|
+const historyMetric = ref("chargeDegree");
|
|
|
+
|
|
|
+// 监听时间维度变化,重置日期值
|
|
|
+watch(historyTimeType, (newType) => {
|
|
|
+ if (newType === "day") {
|
|
|
+ historyDateValue.value = dayjs().format("YYYY-MM");
|
|
|
+ } else {
|
|
|
+ historyDateValue.value = dayjs().format("YYYY");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const historyAreaChartOptions = computed(() => {
|
|
|
+ let dates: string[];
|
|
|
+ let mockData: number[];
|
|
|
+
|
|
|
+ if (historyTimeType.value === "day") {
|
|
|
+ // 日趋势:显示月份中的每一天
|
|
|
+ const daysInMonth = dayjs(historyDateValue.value).daysInMonth();
|
|
|
+ dates = Array.from({ length: daysInMonth }, (_, i) =>
|
|
|
+ dayjs(historyDateValue.value).startOf("month").add(i, "day").format("MM.DD")
|
|
|
+ );
|
|
|
+ mockData = Array.from({ length: daysInMonth }, () => Math.floor(Math.random() * 5000) + 2000);
|
|
|
+ } else {
|
|
|
+ // 月趋势:显示年份中的12个月
|
|
|
+ dates = Array.from({ length: 12 }, (_, i) => `${i + 1}月`);
|
|
|
+ mockData = Array.from({ length: 12 }, () => Math.floor(Math.random() * 50000) + 20000);
|
|
|
+ }
|
|
|
+
|
|
|
+ const metricLabels: Record<string, string> = {
|
|
|
+ chargeDegree: "充电度数",
|
|
|
+ chargeAmount: "充电金额",
|
|
|
+ validOrder: "有效订单",
|
|
|
+ registerUser: "注册用户",
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: {
|
|
|
+ type: "cross",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: "3%",
|
|
|
+ right: "4%",
|
|
|
+ bottom: "3%",
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ data: dates,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: "dashed",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: metricLabels[historyMetric.value],
|
|
|
+ type: "line",
|
|
|
+ data: mockData,
|
|
|
+ smooth: true,
|
|
|
+ areaStyle: {
|
|
|
+ color: {
|
|
|
+ type: "linear",
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ x2: 0,
|
|
|
+ y2: 1,
|
|
|
+ colorStops: [
|
|
|
+ { offset: 0, color: "rgba(64, 158, 255, 0.3)" },
|
|
|
+ { offset: 1, color: "rgba(64, 158, 255, 0.05)" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: "#409EFF",
|
|
|
+ },
|
|
|
+ lineStyle: {
|
|
|
+ color: "#409EFF",
|
|
|
+ width: 2,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+// ==================== 历史营业数据(表格) ====================
|
|
|
+const tableDataType = ref("degree");
|
|
|
+const tableDateRange = ref("week");
|
|
|
+const tableStartDate = ref("");
|
|
|
+const tableEndDate = ref("");
|
|
|
+
|
|
|
+// 热门充电站分页
|
|
|
+const hotCurrentPage = ref(1);
|
|
|
+const hotPageSize = ref(5);
|
|
|
+const hotStationTotal = ref(50);
|
|
|
+
|
|
|
+// 波动充电站分页
|
|
|
+const fluctuationCurrentPage = ref(1);
|
|
|
+const fluctuationPageSize = ref(5);
|
|
|
+const fluctuationStationTotal = ref(50);
|
|
|
+
|
|
|
+// 选中的日期范围显示
|
|
|
+const selectedDateRange = computed(() => {
|
|
|
+ if (tableStartDate.value && tableEndDate.value) {
|
|
|
+ return `${tableStartDate.value} 至 ${tableEndDate.value}`;
|
|
|
+ }
|
|
|
+ const today = dayjs();
|
|
|
+ switch (tableDateRange.value) {
|
|
|
+ case "yesterday":
|
|
|
+ return today.subtract(1, "day").format("YYYY-MM-DD");
|
|
|
+ case "week":
|
|
|
+ return `${today.subtract(7, "day").format("YYYY-MM-DD")} 至 ${today.format("YYYY-MM-DD")}`;
|
|
|
+ case "month":
|
|
|
+ return `${today.subtract(30, "day").format("YYYY-MM-DD")} 至 ${today.format("YYYY-MM-DD")}`;
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 热门充电站数据
|
|
|
+const hotStationData = computed(() => [
|
|
|
+ { name: "小关停车场B区-小站", value: 100.01 },
|
|
|
+ { name: "延安西路智慧停车楼", value: 99.01 },
|
|
|
+ { name: "花果园立交桥南广场停车场", value: 97.01 },
|
|
|
+ { name: "花果园立交北广场停车场", value: 97 },
|
|
|
+ { name: "轩宇智慧停车场", value: 96 },
|
|
|
+]);
|
|
|
+
|
|
|
+// 波动充电站数据
|
|
|
+const fluctuationStationData = computed(() => [
|
|
|
+ { name: "小关停车场B区-小站", rate: 6.29 },
|
|
|
+ { name: "延安西路智慧停车楼", rate: 5.34 },
|
|
|
+ { name: "花果园立交桥南广场停车场", rate: 4.21 },
|
|
|
+ { name: "花果园立交北广场停车场", rate: -3.52 },
|
|
|
+ { name: "轩宇智慧停车场", rate: -1.98 },
|
|
|
+]);
|
|
|
+
|
|
|
+// ==================== 用户流失率 ====================
|
|
|
+const lossRate = ref({
|
|
|
+ sevenDay: "59.99",
|
|
|
+ oneMonth: "70.12",
|
|
|
+ threeMonth: "70.12",
|
|
|
+});
|
|
|
+
|
|
|
+// 下载报表
|
|
|
+const downloadReport = (type: string) => {
|
|
|
+ const typeLabels: Record<string, string> = {
|
|
|
+ sevenDay: "近七天",
|
|
|
+ oneMonth: "近1个月",
|
|
|
+ threeMonth: "近3个月",
|
|
|
+ };
|
|
|
+ ElMessage.success(`正在下载${typeLabels[type]}流失率报表...`);
|
|
|
+};
|
|
|
+
|
|
|
+const handleClick = (tab: TabsPaneContext, event: Event) => {
|
|
|
+ console.log(tab, event);
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // 初始化数据
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.demo-tabs {
|
|
|
+ :deep(.el-tabs__content) {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|