|
@@ -1,279 +1,135 @@
|
|
|
<template>
|
|
|
- <div class="eventStat">
|
|
|
- <div class="eventStat-header">
|
|
|
- <el-card>
|
|
|
- <template #header>
|
|
|
- <div class="event-category">
|
|
|
- <span>监控点位违反统计</span>
|
|
|
- <el-radio-group v-model="eventDateType" @change="getBarData" size="small">
|
|
|
- <el-radio-button label="今天" value="1" />
|
|
|
- <el-radio-button label="近7天" value="7" />
|
|
|
- <el-radio-button label="近30天" value="30" />
|
|
|
- </el-radio-group>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <BaseChart width="100%" height="250px" :option="barOptions" />
|
|
|
- </el-card>
|
|
|
- <el-card>
|
|
|
- <template #header>
|
|
|
- <div class="event-category">
|
|
|
- <span>区域违法分布</span>
|
|
|
- <el-radio-group v-model="regionDateType" @change="getPieData" size="small">
|
|
|
- <el-radio-button label="今天" value="1" />
|
|
|
- <el-radio-button label="近7天" value="7" />
|
|
|
- <el-radio-button label="近30天" value="30" />
|
|
|
- </el-radio-group>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <BaseChart width="100%" height="250px" :option="pieOptions" />
|
|
|
- </el-card>
|
|
|
+ <div class="p-2">
|
|
|
+ <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
|
|
+ :leave-active-class="proxy?.animate.searchAnimate.leave">
|
|
|
+ <div v-show="showSearch" class="mb-[10px]">
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="90px">
|
|
|
+ <el-form-item label="车牌号" prop="objectId">
|
|
|
+ <el-input v-model="queryParams.objectId" placeholder="请输入车牌号" clearable @keyup.enter="handleQuery" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="时间范围" prop="params">
|
|
|
+ <el-date-picker v-model="queryParams.params.date" value-format="YYYY-MM-DD" type="daterange"
|
|
|
+ range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" unlink-panels :shortcuts="shortcuts"
|
|
|
+ @change="handleQuery" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
|
|
|
- </div>
|
|
|
- <el-card style="margin-top: 10px;">
|
|
|
+ <el-card shadow="never">
|
|
|
<template #header>
|
|
|
- <div class="event-category">
|
|
|
- <span>每分钟{{ formatDict(eventType) }}统计</span>
|
|
|
- <el-select v-model="eventType" @change="getLineData">
|
|
|
- <el-option v-for="item in eventTypeOptions" :key="item.dictValue" :label="item.dictLabel"
|
|
|
- :value="item.dictValue" />
|
|
|
- </el-select>
|
|
|
- </div>
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
+ </el-row>
|
|
|
</template>
|
|
|
- <BaseChart width="100%" height="250px" :option="lineOptions" />
|
|
|
- </el-card>
|
|
|
-
|
|
|
|
|
|
+ <el-table v-loading="loading" :data="cyclingList">
|
|
|
+ <el-table-column label="车牌" align="center" width="150" show-overflow-tooltip fixed="left" prop="objectId" />
|
|
|
+ <el-table-column label="品牌" align="center" width="100" prop="objectAttr" />
|
|
|
+ <el-table-column label="车辆类型" align="center" prop="objectType" />
|
|
|
+ <el-table-column label="违法变道" align="center" prop="eventCount5" />
|
|
|
+ <el-table-column label="压线行驶" align="center" prop="eventCount6" />
|
|
|
+ <el-table-column label="机动车逆行" align="center"width="100" prop="eventCount7" />
|
|
|
+ <el-table-column label="车辆闯禁" align="center" prop="eventCount8" />
|
|
|
+ <el-table-column label="违法停车" align="center" prop="eventCount9" />
|
|
|
+ <el-table-column label="占用非机动车道" align="center" width="120" prop="eventCount10" />
|
|
|
+ <el-table-column label="不按规定车道行驶" align="center" width="130" prop="eventCount11" />
|
|
|
+ <el-table-column label="货车右转必停" align="center" width="120" prop="eventCount12" />
|
|
|
+ </el-table>
|
|
|
|
|
|
+ <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
|
|
+ v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
+ </el-card>
|
|
|
</div>
|
|
|
</template>
|
|
|
-<script setup lang="ts">
|
|
|
-import BaseChart from '@/components/BaseChart/index.vue'
|
|
|
-import { pointEventsStat, regionEventsStat, minuteEventsStat } from '@/api/sense/events/index'
|
|
|
-import { getDicts } from '@/api/system/dict/data/index'
|
|
|
-const pieData = ref([])
|
|
|
-const barData = ref([])
|
|
|
-const lineData = ref([])
|
|
|
-const eventTypeOptions = ref([])
|
|
|
-const eventType = ref('1')
|
|
|
-const eventDateType = ref('1')
|
|
|
-const regionDateType = ref('1')
|
|
|
-const pieOptions = computed(() => {
|
|
|
- let options = {
|
|
|
- tooltip: {
|
|
|
- trigger: 'item'
|
|
|
- },
|
|
|
- legend: {
|
|
|
- orient: 'vertical',
|
|
|
- top: '8%',
|
|
|
- right: 0,
|
|
|
- itemWidth: 10,
|
|
|
- itemHeight: 10,
|
|
|
- itemGap: 10
|
|
|
- },
|
|
|
- grid: {
|
|
|
- },
|
|
|
- series: [
|
|
|
- {
|
|
|
- type: 'pie',
|
|
|
- radius: '50%',
|
|
|
- data: pieData.value,
|
|
|
- center: ['40%', '50%'],
|
|
|
- emphasis: {
|
|
|
- itemStyle: {
|
|
|
- shadowBlur: 10,
|
|
|
- shadowOffsetX: 0,
|
|
|
- shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- ]
|
|
|
- };
|
|
|
- return options
|
|
|
-})
|
|
|
-const barOptions = computed(() => {
|
|
|
- const colors = [
|
|
|
- "#FF5733",
|
|
|
- "#7B3F00",
|
|
|
- "#FFC300",
|
|
|
- "#DAF7A6",
|
|
|
- "#FF5733",
|
|
|
- "#008080",
|
|
|
- "#E8EAF6",
|
|
|
- "#FFEEC1",
|
|
|
- "#B39DDB",
|
|
|
- "#4CAF50"
|
|
|
- ];
|
|
|
- const barObj = {}
|
|
|
- barData.value.forEach(item => {
|
|
|
- barObj[item.area_name] = item.event_count
|
|
|
- })
|
|
|
- let options = {
|
|
|
- tooltip: {
|
|
|
- trigger: 'axis',
|
|
|
- axisPointer: {
|
|
|
- type: 'shadow'
|
|
|
- }
|
|
|
- },
|
|
|
- grid: {
|
|
|
- left: '5%',
|
|
|
- right: '3%',
|
|
|
- bottom: 0,
|
|
|
- top: '6%',
|
|
|
- containLabel: true
|
|
|
- },
|
|
|
- xAxis: {
|
|
|
- type: 'category',
|
|
|
- data: Object.keys(barObj),
|
|
|
- axisLabel: {
|
|
|
- interval: 0,
|
|
|
- // rotate: 15 // 表示倾斜的角度
|
|
|
- }
|
|
|
- },
|
|
|
- yAxis: {
|
|
|
- type: 'value'
|
|
|
- },
|
|
|
- series: [
|
|
|
- {
|
|
|
- data: Object.values(barObj),
|
|
|
- type: 'bar',
|
|
|
- barWidth: 20,
|
|
|
- itemStyle: {
|
|
|
- color: (params) => {
|
|
|
- return colors[params.dataIndex]
|
|
|
- }
|
|
|
- },
|
|
|
- label: {
|
|
|
- show: true, // 显示数值
|
|
|
- position: 'top' // 在顶部显示
|
|
|
- }
|
|
|
|
|
|
- }
|
|
|
- ]
|
|
|
- };
|
|
|
- return options
|
|
|
-})
|
|
|
-const lineOptions = computed(() => {
|
|
|
- const xData = lineData.value.map(item => item.time_format)
|
|
|
- const yData = lineData.value.map(item => item.count)
|
|
|
- const options = {
|
|
|
- tooltip: {
|
|
|
- trigger: 'axis',
|
|
|
- appendToBody: true
|
|
|
- },
|
|
|
- grid: {
|
|
|
- left: '1%',
|
|
|
- right: '3%',
|
|
|
- bottom: 0,
|
|
|
- top: '22%',
|
|
|
- containLabel: true
|
|
|
+<script setup name="cycling" lang="ts">
|
|
|
+import { keyVehicleEventStat } from '@/api/sense/events';
|
|
|
+
|
|
|
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const cyclingList = ref([])
|
|
|
+const loading = ref(true);
|
|
|
+const showSearch = ref(true);
|
|
|
+const total = ref(0);
|
|
|
+
|
|
|
+const queryFormRef = ref<ElFormInstance>();
|
|
|
+const shortcuts = [
|
|
|
+ {
|
|
|
+ text: '上周',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
|
|
+ return [start, end]
|
|
|
},
|
|
|
- xAxis: {
|
|
|
- type: 'category',
|
|
|
- axisLabel: {
|
|
|
- show: true,
|
|
|
- interval: 0,//横轴信息全部显示
|
|
|
- // margin: 5, //刻度标签与轴线之间的距离
|
|
|
- },
|
|
|
- data: xData
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '上个月',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
|
|
+ return [start, end]
|
|
|
},
|
|
|
- yAxis: {
|
|
|
- type: 'value'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '前三个月',
|
|
|
+ value: () => {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
|
|
|
+ return [start, end]
|
|
|
},
|
|
|
- series: [
|
|
|
- // {
|
|
|
- // name: '',
|
|
|
- // type: 'line',
|
|
|
- // symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆
|
|
|
- // showAllSymbol: true,
|
|
|
- // symbolSize: 0,
|
|
|
- // data: yData
|
|
|
- // }
|
|
|
- {
|
|
|
- name: '',
|
|
|
- data: yData,
|
|
|
- type: 'pictorialBar',
|
|
|
- barMinHeight: 50,
|
|
|
- barCategoryGap: '80%',
|
|
|
- symbol: 'path://M0,10 L10,10 C5.5,10 5.5,5 5,0 C4.5,5 4.5,10 0,10 z',
|
|
|
- itemStyle: {
|
|
|
- normal: {
|
|
|
- color: '#65B1E9'
|
|
|
- }
|
|
|
- },
|
|
|
- },
|
|
|
- ]
|
|
|
- };
|
|
|
- return options
|
|
|
-})
|
|
|
-onMounted(() => {
|
|
|
- getPieData()
|
|
|
- getBarData()
|
|
|
- getLineData()
|
|
|
- getDicts('event_type').then(({ code, data }) => {
|
|
|
- if (code === 200) {
|
|
|
- eventTypeOptions.value = data || []
|
|
|
- }
|
|
|
- })
|
|
|
+ },
|
|
|
+]
|
|
|
+const queryParams = ref({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ objectId: undefined,
|
|
|
+ params: {
|
|
|
+ date: [],
|
|
|
+ startDate: '',
|
|
|
+ endDate: ''
|
|
|
+ }
|
|
|
})
|
|
|
-const getPieData = () => {
|
|
|
- regionEventsStat({ dateType: regionDateType.value }).then(({ code, data }) => {
|
|
|
- if (code === 200) {
|
|
|
- pieData.value = data || []
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-const getBarData = () => {
|
|
|
- pointEventsStat({ dateType: eventDateType.value }).then(({ code, data }) => {
|
|
|
- if (code === 200) {
|
|
|
- barData.value = data || []
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-const getLineData = () => {
|
|
|
- minuteEventsStat({ eventType: eventType.value }).then(({ code, data }) => {
|
|
|
- if (code === 200) {
|
|
|
- lineData.value = data || {}
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-const formatDict = (val) => {
|
|
|
- let name = ''
|
|
|
- eventTypeOptions.value.forEach(item => {
|
|
|
- if (item.dictValue === val) {
|
|
|
- name = item.dictLabel
|
|
|
- }
|
|
|
- })
|
|
|
- return name
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ queryParams.value.params.startDate = ''
|
|
|
+ queryParams.value.params.endDate = ''
|
|
|
+ if (queryParams.value.params.date && queryParams.value.params.date.length) {
|
|
|
+ const [startDate, endDate] = queryParams.value.params.date
|
|
|
+ queryParams.value.params.startDate = startDate
|
|
|
+ queryParams.value.params.endDate = endDate
|
|
|
+ }
|
|
|
+ const res = await keyVehicleEventStat(queryParams.value);
|
|
|
+ cyclingList.value = res.rows;
|
|
|
+ total.value = res.total;
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
|
|
|
-</script>
|
|
|
-<style lang="scss" scoped>
|
|
|
-.eventStat {
|
|
|
- padding: 10px;
|
|
|
- height: 100%;
|
|
|
- background-color: #F0F2F5;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
|
|
|
-.event-category {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
|
- .el-select {
|
|
|
- width: 250px;
|
|
|
- }
|
|
|
+/** 搜索按钮操作 */
|
|
|
+const handleQuery = () => {
|
|
|
+ queryParams.value.pageNum = 1;
|
|
|
+ getList();
|
|
|
}
|
|
|
|
|
|
-.eventStat-header {
|
|
|
- display: flex;
|
|
|
+/** 重置按钮操作 */
|
|
|
+const resetQuery = () => {
|
|
|
+ queryFormRef.value?.resetFields();
|
|
|
+ handleQuery();
|
|
|
+}
|
|
|
|
|
|
- .el-card {
|
|
|
- flex: 1;
|
|
|
|
|
|
- &:not(:first-child) {
|
|
|
- margin-left: 10px;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getList();
|
|
|
+});
|
|
|
+</script>
|