Browse Source

历史曲线

master
xh 7 hours ago
parent
commit
8353708704
  1. 316
      web/src/views/HandDevice/Home/components/HistoricalCurve.vue
  2. 1
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  3. 6
      web/src/views/HandDevice/Home/components/TrajectoryControls.vue
  4. 106
      web/src/views/HandDevice/Home/index.vue
  5. 8
      web/src/views/gas/fencealarm/index.vue

316
web/src/views/HandDevice/Home/components/HistoricalCurve.vue

@ -0,0 +1,316 @@
<template>
<el-drawer
v-model="drawer"
:direction="'btt'"
:before-close="handleClose"
:destroy-on-close="true"
resizable
size="550px"
>
<template #header>
<h4
>{{ currentMarker?.name }}手持表
<span class="gasChemical">{{ currentMarker?.gasChemical }}检测</span></h4
>
</template>
<template #default>
<!-- <div> 数据统计截止{{ currentMarker?.timeStr }} </div> -->
<div>
<div class="flex items-center justify-between">
<div class="chart-title"> {{ currentMarker?.gasChemical }}浓度与电量趋势 </div>
<div>
<el-date-picker
v-model="gasDateTimeRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
@change="handleDateChange"
/>
</div>
</div>
<div v-if="drawer && !loading">
<Echart :options="gasLineOptions" :height="400" />
</div>
</div>
<!-- <div>
<div class="flex items-center justify-between">
<div> 电量趋势 </div>
<div>
<el-date-picker
v-model="gasDateTimeRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</div>
</div>
<div v-if="drawer && !loading">
<Echart :options="batteryLineOptions" :height="300" />
</div>
</div> -->
</template>
<!-- <template #footer>
<div style="flex: auto">
<el-button @click="cancelClick">cancel</el-button>
<el-button type="primary" @click="confirmClick">confirm</el-button>
</div>
</template> -->
</el-drawer>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { MarkerData } from './types/map.types'
import { tdengineApi, tdStruct } from '@/api/gas/tdengine/index'
import { EChartsOption } from 'echarts'
import Echart from '@/components/Echart/src/Echart.vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'
const drawer = ref(false)
const loading = ref(false)
const currentMarker = ref<MarkerData | null>()
const historicalData = ref<tdStruct[]>([])
async function getData(row: MarkerData) {
try {
const data = await tdengineApi.getHistoricalSn({
sn: row.sn,
startTime: gasDateTimeRange.value[0],
endTime: gasDateTimeRange.value[1]
})
if (!data || data.length === 0) {
ElMessage.error('没有数据')
// drawer.value = false
return
}
historicalData.value = data.map((item) => ({
...item,
ts: dayjs(item.ts).format('YYYY-MM-DD HH:mm:ss')
}))
} catch (error) {
// ElMessage.error(error.message)
} finally {
loading.value = false
}
// console.log(data)
}
function openDrawer(row: MarkerData) {
reset()
historicalData.value = []
currentMarker.value = row
drawer.value = true
loading.value = true
getData(row)
}
const handleClose = (done) => {
reset()
done()
}
const reset = () => {
loading.value = false
historicalData.value = []
currentMarker.value = null
gasDateTimeRange.value = [
dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
]
}
// const battery = computed(() => {
// let value: number[] = []
// let time: string[] = []
// historicalData.value
// .filter((item) => item.battery !== '')
// .forEach((item) => {
// value.push(Number(item.battery))
// time.push(item.ts as string)
// })
// return {
// value,
// time
// }
// })
// const batteryLineOptions = computed<EChartsOption>(() => ({
// title: {
// text: '',
// left: 'center'
// },
// xAxis: {
// data: battery.value.time,
// boundaryGap: false,
// axisTick: {
// show: false
// }
// },
// grid: {
// left: 20,
// right: 20,
// bottom: 20,
// top: 10,
// containLabel: true
// },
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'cross'
// },
// padding: [5, 10]
// },
// yAxis: {
// axisTick: {
// show: false
// }
// },
// // legend: {
// // // data: [],
// // top: 50
// // },
// series: [
// {
// name: '',
// smooth: true,
// type: 'line',
// data: battery.value.value
// }
// ]
// }))
const gasDateTimeRange = ref(['', ''])
const handleDateChange = (dates: string[]) => {
// gasDateTimeRange.value = dates
if (!currentMarker.value) {
return
}
getData(currentMarker.value)
}
const gas = computed(() => {
var value: (number | null)[] = []
var battery: (number | null)[] = []
var time: string[] = []
historicalData.value
// .filter((item) => typeof item.value === 'number')
.forEach((item) => {
if (item.battery !== '') {
battery.push(Number(item.battery))
} else {
battery.push(null)
}
if (typeof item.value === 'number') {
value.push(Number(item.value))
} else {
value.push(null)
}
time.push(item.ts as string)
})
return {
value,
battery,
time
}
})
const gasLineOptions = computed<EChartsOption>(() => ({
title: {
text: '',
left: 'center'
},
xAxis: {
data: gas.value.time,
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: [
{
axisTick: {
show: false
}
},
{
axisTick: {
show: false
}
}
],
legend: {
// data: [],
top: 50
},
series: [
{
name: currentMarker.value?.gasChemical + '浓度',
smooth: true,
type: 'line',
data: gas.value.value,
animationDuration: 2800,
symbol: 'none',
animationEasing: 'cubicInOut'
},
{
name: '电池电量',
smooth: true,
type: 'line',
yAxisIndex: 1,
symbol: 'none',
data: gas.value.battery,
animationDuration: 2800,
animationEasing: 'cubicInOut'
}
]
}))
defineExpose({
openDrawer
})
</script>
<style scoped lang="scss">
.chart-title {
position: relative;
font-size: 14px;
font-weight: bold;
padding-left: 10px;
&::after {
content: '';
position: absolute;
left: 0px;
bottom: 0;
top: 0;
width: 4px;
height: 100%;
background-color: rgba(22, 119, 255, 1);
}
}
.gasChemical {
display: inline-block;
padding: 2px 6px;
font-size: 12px;
color: #f59a23;
background-color: rgba(245, 154, 35, 0.09);
}
</style>

1
web/src/views/HandDevice/Home/components/OpenLayerMap.vue

@ -263,6 +263,7 @@ const fitToMarkers = () => {
}
const setCenter = (coords: [number, number]) => {
console.log('setCenter', coords)
showMarkers.value = true
if (isMapInitialized) {
services.mapService?.setCenter(coords)

6
web/src/views/HandDevice/Home/components/TrajectoryControls.vue

@ -271,7 +271,7 @@ watch(
{ deep: true }
)
</script>
<style scoped>
<style scoped lang="scss">
.trajectory-controls {
position: absolute;
bottom: 10px;
@ -299,7 +299,7 @@ watch(
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 12px;
margin-bottom: 6px;
}
.play-controls {
@ -326,7 +326,7 @@ watch(
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
margin-bottom: 0px;
}
.time-display {

106
web/src/views/HandDevice/Home/index.vue

@ -26,7 +26,7 @@
<div class="flex-1 text-left font-500">
{{ item.name }}
</div>
<div class="text-gray-500 font-400 text-12px" :style="{ color: item.statusColor }">
<div class="text-gray-500 font-400 text-12px" :style="{ color: item.statusColor }">
{{ item.statusLabel }}
</div>
</div>
@ -47,41 +47,58 @@
><span>电池状态</span
>{{ getLabelWithTypeValue('batteryStatus', item.batteryStatus) }}</div
>
<div><span>电量</span>{{ item.battery }}</div>
<div><span>数值</span>{{ item.value }} {{ item.unit }}</div>
<div><span>时间</span>{{ item.timeStr }}</div>
<div style="margin-top: 10px">
<el-button
type="success"
plain
size="small"
v-show="item.latitude && item.longitude"
v-if="item.latitude && item.longitude"
@click="setCenter(item)"
>定位</el-button
>
<el-button v-show="item.latitude && item.longitude" v-hasPermi="['gas:hand-td:HistoricalSn']" type="success" size="small" @click="onClickTrajectory(item)"
<el-button
v-hasPermi="['gas:hand-td:HistoricalSn']"
plain
size="small"
@click="onClickTrajectory(item)"
>轨迹</el-button
>
<el-button
v-hasPermi="['gas:hand-td:HistoricalSn']"
plain
size="small"
@click="onClickHistoricalCurve(item)"
>历史曲线</el-button
>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-scrollbar>
</div>
<HistoricalCurve ref="historicalCurveRef" />
</div>
</template>
<script lang="ts" setup>
import OpenLayerMap from './components/OpenLayerMap.vue'
import TopPanel from './components/TopPanel.vue'
import HistoricalCurve from './components/HistoricalCurve.vue'
import { getLastDetectorData } from '@/api/gas'
import { HandDetectorData } from '@/api/gas/handdetector'
import type { HandDetectorData } from '@/api/gas/handdetector'
import { tdengineApi } from '@/api/gas/tdengine/index'
import { getLabelWithTypeValue, getHighestPriorityStatus,getStatusLabel,getStatusColor,getStatusPriority } from './components/utils/map.utils'
import {
getLabelWithTypeValue,
getHighestPriorityStatus,
getStatusLabel,
getStatusColor,
getStatusPriority
} from './components/utils/map.utils'
import { MarkerData, FenceData } from './components/types/map.types'
@ -97,7 +114,7 @@ const getDataTimer = ref<NodeJS.Timeout | null>(null)
const markers = ref<MarkerData[]>([])
const fences = ref<FenceData[]>([])
const mapRef = ref<typeof OpenLayerMap>()
const mapRef = ref<InstanceType<typeof OpenLayerMap>>()
const search = ref('')
watch(
() => search.value,
@ -129,32 +146,34 @@ const getMarkers = async () => {
console.log('getMarkers')
return await getLastDetectorData().then((res: HandDetectorData[]) => {
res = res.filter((i) => i.enableStatus === 1)
var res2 = res.map((i) => {
// console.log([i.longitude, i.latitude])
let statusStr = getHighestPriorityStatus({
gasStatus: i.gasStatus, //
batteryStatus: i.batteryStatus, //
fenceStatus: i.fenceStatus, //
onlineStatus: i.onlineStatus, //线
}) //
return {
...i,
coordinates: [i.longitude || 0, i.latitude || 0] as [number,number],
// data: [],
timeStr: i.time ? dayjs(i.time).format('YYYY-MM-DD HH:mm:ss') : '',
// value: i.value,
// unit: i.unit,
// gasStatus: i.gasStatus, //
// batteryStatus: i.batteryStatus, //
// fenceStatus: i.fenceStatus, //
// onlineStatus: i.onlineStatus, //线
// enableStatus: i.enableStatus, //
statusStr:statusStr, //
statusColor: getStatusColor(statusStr), //
statusLabel: getStatusLabel(statusStr), //
statusPriority: getStatusPriority(statusStr), //,
}
}).sort((a,b) => a.statusPriority - b.statusPriority)
var res2 = res
.map((i) => {
// console.log([i.longitude, i.latitude])
let statusStr = getHighestPriorityStatus({
gasStatus: i.gasStatus, //
batteryStatus: i.batteryStatus, //
fenceStatus: i.fenceStatus, //
onlineStatus: i.onlineStatus //线
}) //
return {
...i,
coordinates: [i.longitude || 0, i.latitude || 0] as [number, number],
// data: [],
timeStr: i.time ? dayjs(i.time).format('YYYY-MM-DD HH:mm:ss') : '',
// value: i.value,
// unit: i.unit,
// gasStatus: i.gasStatus, //
// batteryStatus: i.batteryStatus, //
// fenceStatus: i.fenceStatus, //
// onlineStatus: i.onlineStatus, //线
// enableStatus: i.enableStatus, //
statusStr: statusStr, //
statusColor: getStatusColor(statusStr), //
statusLabel: getStatusLabel(statusStr), //
statusPriority: getStatusPriority(statusStr) //,
}
})
.sort((a, b) => a.statusPriority - b.statusPriority)
markers.value = res2
})
}
@ -206,8 +225,12 @@ async function showTrajectory(item: MarkerData) {
// sn: '867989072729904'
})
if (!data || data.length === 0) {
ElMessage.error(`${dayjs(trajectoryTimeRange.value[0]).format('YYYY-MM-DD HH:mm:ss')}${dayjs(trajectoryTimeRange.value[1]).format('YYYY-MM-DD HH:mm:ss')}时段没有轨迹数据`)
// return
}
//
console.log('过滤前', data.length)
// console.log('', data.length)
//
var newData = data.filter((item, index, arr) => {
if (!item.longitude || !item.latitude) {
@ -222,7 +245,7 @@ async function showTrajectory(item: MarkerData) {
)
})
//
console.log('过滤后', newData.length)
// console.log('', newData.length)
item.data = newData
.filter((item, index, arr) => {
@ -282,6 +305,13 @@ async function showTrajectory(item: MarkerData) {
} finally {
}
}
const historicalCurveRef = ref<InstanceType<typeof HistoricalCurve>>()
// 线
function onClickHistoricalCurve(item: MarkerData) {
// console.log('onClickHistoricalCurve', item)
historicalCurveRef.value?.openDrawer(toRaw(item))
}
//
function onClickTrajectory(item: MarkerData) {
console.log('onClickTrajectory', item)

8
web/src/views/gas/fencealarm/index.vue

@ -153,14 +153,14 @@
<el-table-column
label="开始时间"
align="center"
prop="tAlarmStart"
prop="talarmStart"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column
label="结束时间"
align="center"
prop="tAlarmEnd"
prop="talarmEnd"
:formatter="dateFormatter"
width="180px"
/>
@ -170,13 +170,13 @@
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column
<!-- <el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
/> -->
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button

Loading…
Cancel
Save