|
|
@@ -25,125 +25,196 @@
|
|
|
}
|
|
|
</style>
|
|
|
<template>
|
|
|
- <div class="system-container layout-padding">
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
-
|
|
|
- <el-form
|
|
|
- :model="state.formQuery"
|
|
|
- ref="queryRef"
|
|
|
- size="default" label-width="0px" class="mt5 mb5">
|
|
|
+ <div class="system-container1 layout-padding">
|
|
|
+
|
|
|
+ <el-form
|
|
|
+ inline
|
|
|
+ :model="state.formQuery"
|
|
|
+ ref="queryRef"
|
|
|
+ size="default" label-width="0px" class="mt5 mb5">
|
|
|
+ <el-form-item>
|
|
|
<ext-select
|
|
|
v-model="state.formQuery.stationIdList"
|
|
|
- placeholder="活动名称"
|
|
|
+ placeholder="站点"
|
|
|
+ value-key="stationId"
|
|
|
+ label-key="stationName"
|
|
|
clearable
|
|
|
multiple
|
|
|
:data-list="state.stationList"
|
|
|
- @blur="loadData(true)"
|
|
|
- class="wd150 mr10">
|
|
|
+ style="min-width: 150px;"
|
|
|
+ class=" mr10">
|
|
|
</ext-select>
|
|
|
-
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
+ <el-radio-group v-model="state.formQuery.type" size="default" @change="handleQueryTypeChange">
|
|
|
+ <el-radio-button label="day">天</el-radio-button>
|
|
|
+ <el-radio-button label="month">月</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <el-date-picker
|
|
|
+ :value-format="state.dateFormat"
|
|
|
+ v-model="state.formQuery.dateRange"
|
|
|
+ :type="state.dateType"
|
|
|
+ unlink-panels
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ :shortcuts="shortcuts"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
<el-button class="ml10" plain size="default" type="success" @click="loadData()">
|
|
|
<SvgIcon name="ele-Search"/>
|
|
|
查询
|
|
|
</el-button>
|
|
|
- </el-form>
|
|
|
-
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card>
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>充电用户数</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>有效订单数</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>总电量</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>总金额</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>总电费</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-scrollbar>
|
|
|
+ <el-card>
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>充电用户数</template>
|
|
|
+ <template #default>
|
|
|
+ <div style="min-height: 500px;padding: 20px;" ref="chartOneRef"></div>
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>有效订单数</template>
|
|
|
+ <template #default>
|
|
|
+ <div style="min-height: 300px;padding: 20px;" ref="chartTwoRef"></div>
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>总电量</template>
|
|
|
+ <template #default>
|
|
|
+ <div style="min-height: 300px;padding: 20px;" ref="chartThreeRef"></div>
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!--
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>总金额</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>总电费</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>服务费</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>平均电量</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>平均订单费用</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+ <template #header>平均充电用电量</template>
|
|
|
+ <template #default>
|
|
|
+ XXXx
|
|
|
+ </template>
|
|
|
+ </el-card>-->
|
|
|
</el-card>
|
|
|
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>服务费</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>平均电量</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>平均订单费用</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
-
|
|
|
-
|
|
|
- <el-card shadow="hover" class="layout-padding-auto">
|
|
|
- <template #header>平均充电用电量</template>
|
|
|
- <template #default>
|
|
|
- XXXx
|
|
|
- </template>
|
|
|
- </el-card>
|
|
|
- </el-card>
|
|
|
+ </el-scrollbar>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script setup lang="ts" name="ActivityList">
|
|
|
-import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance, nextTick, onBeforeUnmount} from 'vue';
|
|
|
+<script setup lang="ts" name="KanbanList">
|
|
|
+import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance, nextTick, onUnmounted} from 'vue';
|
|
|
import {$body, $get} from "/@/utils/request";
|
|
|
import {Msg} from "/@/utils/message";
|
|
|
-
|
|
|
+import * as echarts from 'echarts';
|
|
|
|
|
|
import mittBus from '/@/utils/mitt';
|
|
|
-import ExtDSelect from "/@/components/form/ExtDSelect.vue";
|
|
|
-import ExtDatePicker from "/@/components/form/ExtDatePicker.vue";
|
|
|
import ExtSelect from "/@/components/form/ExtSelect.vue";
|
|
|
+import u from "/@/utils/u";
|
|
|
|
|
|
//定义引用
|
|
|
const queryRef = ref();
|
|
|
+const chartOneRef = ref();
|
|
|
+const chartTwoRef = ref();
|
|
|
+const chartThreeRef = ref();
|
|
|
+
|
|
|
+const end = new Date()
|
|
|
+const start = new Date()
|
|
|
+start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
|
|
+
|
|
|
|
|
|
//定义变量
|
|
|
const state = reactive({
|
|
|
formQuery: {
|
|
|
- stationIdList: []
|
|
|
+ stationIdList: [],
|
|
|
+ dateRange: [u.date.format(start, "YYYY-MM-DD"), u.date.format(end, "YYYY-MM-DD")],
|
|
|
+ type: 'day'
|
|
|
},
|
|
|
- stationList: []
|
|
|
+ stationList: [],
|
|
|
+ chartOne: null as any,
|
|
|
+ chartTwo: null as any,
|
|
|
+ chartThree: null as any,
|
|
|
+ chartData: {},
|
|
|
+ theme: '',
|
|
|
+ dateType: 'daterange',
|
|
|
+ dateFormat: 'YYYY-MM-DD'
|
|
|
})
|
|
|
|
|
|
|
|
|
+const shortcuts = [
|
|
|
+ {
|
|
|
+ text: '近7天',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
|
|
+ return [start, end]
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '近30天',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
|
|
+ return [start, end]
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '近90天',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
|
|
|
+ return [start, end]
|
|
|
+ },
|
|
|
+ },
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
// 监听双向绑定 modelValue 的变化
|
|
|
// watch(
|
|
|
// () => state.pageIndex,
|
|
|
@@ -161,12 +232,38 @@ onMounted(() => {
|
|
|
loadData();
|
|
|
});
|
|
|
|
|
|
+onUnmounted(() => {
|
|
|
+ state.chartOne?.dispose();
|
|
|
+ state.chartTwo?.dispose();
|
|
|
+ state.chartThree?.dispose();
|
|
|
+})
|
|
|
+
|
|
|
+window.onresize = function () {
|
|
|
+ //自适应大小
|
|
|
+ state.chartOne.resize();
|
|
|
+};
|
|
|
|
|
|
//region 方法区
|
|
|
|
|
|
+const handleQueryTypeChange = (type: string) => {
|
|
|
+ console.log(type)
|
|
|
+ if (type === 'day') {
|
|
|
+ state.dateType = 'daterange'
|
|
|
+ state.dateFormat = 'YYYY-MM-DD'
|
|
|
+ } else {
|
|
|
+ state.dateType = 'monthrange'
|
|
|
+ state.dateFormat = 'YYYY-MM-DD'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const loadStationList = () => {
|
|
|
$get(`/station/listStation`, {pageNum: 1024}).then((res: any) => {
|
|
|
state.stationList = res;
|
|
|
+ state.formQuery.stationIdList = res.map(k=>k.stationId);
|
|
|
+ console.log(res)
|
|
|
+ setTimeout(()=>{
|
|
|
+ loadData();
|
|
|
+ },200)
|
|
|
}).catch(e => {
|
|
|
console.error(e)
|
|
|
})
|
|
|
@@ -174,8 +271,175 @@ const loadStationList = () => {
|
|
|
|
|
|
// 初始化表格数据
|
|
|
const loadData = () => {
|
|
|
+ console.log(state.formQuery)
|
|
|
+ let params = {
|
|
|
+ stationIds: state.formQuery.stationIdList,
|
|
|
+ startTime: state.formQuery.dateRange[0],
|
|
|
+ endTime: state.formQuery.dateRange[1],
|
|
|
+ type: state.formQuery.type
|
|
|
+ }
|
|
|
+ for (let i = 0; i < Object.keys(params).length; i++) {
|
|
|
+ if (u.isEmptyOrNull(params[Object.keys(params)[i]])) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $get(`/stat/stationStatDetail`, params).then(res => {
|
|
|
+ console.log(res)
|
|
|
+ state.chartData = res;
|
|
|
+ buildOrderChart()
|
|
|
+ })
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * 充电人数、有限订单数
|
|
|
+ */
|
|
|
+const buildOrderChart = () => {
|
|
|
+ if (state.chartOne) {
|
|
|
+ state.chartOne.dispose();
|
|
|
+ }
|
|
|
+ state.chartOne = echarts.init(chartOneRef.value, state.theme);
|
|
|
+ let {type} = state.formQuery
|
|
|
+ let stationIdList = Object.keys(state.chartData);
|
|
|
+ if (u.isEmptyOrNull(stationIdList)) {
|
|
|
+ Msg.message('未查询到统计数据', 'error')
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let xAxis = state.chartData[`${stationIdList[0]}`].map((k: any) => k.statDay);
|
|
|
+ let legends:Array<string> =[];
|
|
|
+ state.stationList.filter((k: any) => stationIdList.includes(k.stationId)).forEach(item=>{
|
|
|
+ legends.push(item.stationName+'-充电人数')
|
|
|
+ legends.push(item.stationName+'-订单数')
|
|
|
+ })
|
|
|
+
|
|
|
+ let y1Max = 0, y2Max = 0;
|
|
|
+ let series: Array<any> = [];
|
|
|
+ stationIdList.forEach((stationId: string) => {
|
|
|
+ let tmpUserMax = Math.max(...state.chartData[`${stationId}`].map((k: any) => k.chargeUsers))
|
|
|
+ y1Max = Math.max(y1Max, tmpUserMax)
|
|
|
+
|
|
|
+ let tmpOrderMax = Math.max(...state.chartData[`${stationId}`].map((k: any) => k.validOrders))
|
|
|
+ y2Max = Math.max(y2Max, tmpOrderMax)
|
|
|
+
|
|
|
+ let station = state.stationList.find((k: any) => k.stationId == stationId);
|
|
|
+ let {stationName} = station;
|
|
|
+ series.push({
|
|
|
+ name: stationName+'-充电人数',
|
|
|
+ type: 'bar',
|
|
|
+ symbolSize: 6,
|
|
|
+ symbol: 'circle',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { barBorderRadius: 5},
|
|
|
+ tooltip: {
|
|
|
+ trigger:'axis',
|
|
|
+ formatter: ' {a0} {b}<br />{a} : {c}个 '
|
|
|
+ },
|
|
|
+ data: state.chartData[stationId].map((k: any) => k.chargeUsers)
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ series.push({
|
|
|
+ name: stationName+'-订单数',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ yAxisIndex: 1,
|
|
|
+ tooltip: {
|
|
|
+ trigger:'item',
|
|
|
+ formatter: '{a0} {b}<br />{a} : {c}个 '
|
|
|
+ },
|
|
|
+ // tooltip: {
|
|
|
+ // valueFormatter: function (value) {
|
|
|
+ // console.log(value)
|
|
|
+ // return value + ' 人';
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ data: state.chartData[stationId].map((k: any) => k.validOrders)
|
|
|
+ })
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ y1Max = Math.max(y1Max, 100)
|
|
|
+ y2Max = Math.max(y2Max, 100)
|
|
|
+
|
|
|
+ let y1Interval = Math.ceil(y1Max / 5)
|
|
|
+ let y2Interval = Math.ceil(y2Max / 5)
|
|
|
+
|
|
|
+
|
|
|
+ let option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'cross',
|
|
|
+ crossStyle: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ toolbox: {
|
|
|
+ feature: {
|
|
|
+ // dataView: {show: true, readOnly: false},
|
|
|
+ // magicType: {show: true, type: ['line', 'bar']},
|
|
|
+ // restore: {show: true},
|
|
|
+ // saveAsImage: {show: true}
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ // data: ['Evaporation', 'Precipitation', 'Temperature']
|
|
|
+ data: legends
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: 'category',
|
|
|
+ data: xAxis,
|
|
|
+ // data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '人数',
|
|
|
+ min: 0,
|
|
|
+ max: y1Max,
|
|
|
+ interval: y1Interval,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value} 人'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '订单数',
|
|
|
+ min: 0,
|
|
|
+ max: y2Max,
|
|
|
+ interval: y2Interval,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value} 个'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: series
|
|
|
+ };
|
|
|
+ console.log(option)
|
|
|
+ state.chartOne.setOption(option)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 充电量
|
|
|
+ */
|
|
|
+const buildElectricChart = () => {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 充电金额
|
|
|
+ */
|
|
|
+const buildMoneyChart = () => {
|
|
|
+
|
|
|
+}
|
|
|
|
|
|
//endregion
|
|
|
|