first commit
This commit is contained in:
223
src/views/iot/devicedata/DeviceDataForm.vue
Normal file
223
src/views/iot/devicedata/DeviceDataForm.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="historyDialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="900px"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="history-toolbar">
|
||||
<el-form inline label-width="0">
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="historyRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
class="history-range-picker"
|
||||
@change="onHistorySearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="fetchHistoryData">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="history-chart" v-loading="historyLoading">
|
||||
<template v-if="historyData.length">
|
||||
<Echart :key="echartKey" :options="historyChartOption" height="320px" />
|
||||
</template>
|
||||
<el-empty v-else description="暂无历史数据" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
|
||||
import { Echart } from '@/components/Echart'
|
||||
import { DeviceApi } from '@/api/iot/device/device/index'
|
||||
|
||||
interface LatestData {
|
||||
collectedAt?: number | string | null
|
||||
temperatureC?: number | string | null
|
||||
analog5?: number | string | null
|
||||
deviceType?: number | string | null
|
||||
}
|
||||
|
||||
interface ProductWithLatest {
|
||||
id: number
|
||||
name?: string
|
||||
latestData?: LatestData | null
|
||||
}
|
||||
|
||||
const historyDialogVisible = ref(false)
|
||||
const selectedDevice = ref<ProductWithLatest | null>(null)
|
||||
const historyLoading = ref(false)
|
||||
const historyData = ref<any[]>([])
|
||||
const historyRange = ref<[string, string]>(['', ''])
|
||||
const echartKey = ref(0) // 强制 Echart 重新渲染,避免二次打开不显示
|
||||
|
||||
const dialogTitle = computed(() =>
|
||||
selectedDevice.value ? `${selectedDevice.value.name || '设备'}历史数据` : '设备历史数据'
|
||||
)
|
||||
|
||||
const formatTimestamp = (value: number | string | null | undefined) => {
|
||||
if (value === null || value === undefined) return '-'
|
||||
const num = typeof value === 'number' ? value : Number(value)
|
||||
const ts = Number.isNaN(num) ? Date.parse(String(value)) : num < 1e12 ? num * 1000 : num
|
||||
if (!Number.isFinite(ts)) return '-'
|
||||
const date = dayjs(ts)
|
||||
return date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : '-'
|
||||
}
|
||||
|
||||
const toNumeric = (value: number | string | null | undefined) => {
|
||||
if (value === null || value === undefined || value === '') return null
|
||||
const num = typeof value === 'number' ? value : Number(value)
|
||||
return Number.isNaN(num) ? null : Number(num.toFixed(2))
|
||||
}
|
||||
|
||||
const toTimestamp = (value: number | string | null | undefined) => {
|
||||
if (value === null || value === undefined || value === '') return null
|
||||
const num = typeof value === 'number' ? value : Number(value)
|
||||
const ts = Number.isNaN(num) ? Date.parse(String(value)) : num < 1e12 ? num * 1000 : num
|
||||
return Number.isFinite(ts) ? ts : null
|
||||
}
|
||||
|
||||
// 按时间升序展示,让最新数据在图表右侧
|
||||
const sortedHistoryList = computed(() => {
|
||||
const list = historyData.value || []
|
||||
return [...list].sort((a, b) => {
|
||||
const ta = toTimestamp(a?.collectedAt) || 0
|
||||
const tb = toTimestamp(b?.collectedAt) || 0
|
||||
return ta - tb
|
||||
})
|
||||
})
|
||||
|
||||
const historyChartOption = computed<EChartsOption>(() => {
|
||||
const list = sortedHistoryList.value
|
||||
const times = list.map((item) => formatTimestamp(item.collectedAt))
|
||||
const temperatures = list.map((item) => toNumeric(item.temperatureC))
|
||||
const humidities = list.map((item) => toNumeric(item.analog5))
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['温度(℃)', '湿度(RH)']
|
||||
},
|
||||
grid: {
|
||||
left: 40,
|
||||
right: 40,
|
||||
top: 40,
|
||||
bottom: 40
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: times
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '温度(℃)'
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '湿度(RH)'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '温度(℃)',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: temperatures,
|
||||
showSymbol: false
|
||||
},
|
||||
{
|
||||
name: '湿度(RH)',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
yAxisIndex: 1,
|
||||
data: humidities,
|
||||
showSymbol: false
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const resetHistoryRange = () => {
|
||||
const end = dayjs()
|
||||
const start = end.subtract(1, 'day')
|
||||
historyRange.value = [start.format('YYYY-MM-DD 00:00:00'), end.format('YYYY-MM-DD 23:59:59')]
|
||||
}
|
||||
|
||||
const fetchHistoryData = async () => {
|
||||
if (!selectedDevice.value) return
|
||||
if (!historyRange.value?.[0] || !historyRange.value?.[1]) {
|
||||
ElMessage.warning('请选择时间范围')
|
||||
return
|
||||
}
|
||||
historyLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
deviceId: selectedDevice.value.id,
|
||||
startTime: historyRange.value[0],
|
||||
endTime: historyRange.value[1]
|
||||
}
|
||||
const res = await DeviceApi.getOeeBaseDataList(params)
|
||||
const data = (res as any)?.data ?? (res as any) ?? []
|
||||
historyData.value = Array.isArray(data) ? data : []
|
||||
echartKey.value += 1
|
||||
} catch (error) {
|
||||
historyData.value = []
|
||||
ElMessage.error('获取历史数据失败')
|
||||
} finally {
|
||||
historyLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onHistorySearch = () => {
|
||||
// 将结束时间调整为当天的 23:59:59(日期选择器默认是 00:00:00)
|
||||
if (historyRange.value?.[1]) {
|
||||
const end = dayjs(historyRange.value[1])
|
||||
// 如果结束时间是 00:00:00,说明是日期选择器默认值,需要调整为当天的 23:59:59
|
||||
if (end.format('HH:mm:ss') === '00:00:00') {
|
||||
historyRange.value[1] = end.format('YYYY-MM-DD') + ' 23:59:59'
|
||||
}
|
||||
}
|
||||
fetchHistoryData()
|
||||
}
|
||||
|
||||
const openHistoryDialog = (device: ProductWithLatest) => {
|
||||
selectedDevice.value = device
|
||||
resetHistoryRange()
|
||||
historyDialogVisible.value = true
|
||||
fetchHistoryData()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openHistoryDialog
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.history-toolbar {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.history-range-picker {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.history-chart {
|
||||
min-height: 320px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user