|
|
|
|
@ -47,9 +47,6 @@
|
|
|
|
|
{{ getNodeTypeLabel(data.nodeType) }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<template v-if="data.type === 'device'">
|
|
|
|
|
<span class="device-mgmt__treeNodeMeta">{{ data.code }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="device-mgmt__treeNodeActions">
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="canCreateChild(data)"
|
|
|
|
|
@ -133,7 +130,7 @@
|
|
|
|
|
<el-card shadow="never" class="device-mgmt__panel device-mgmt__header">
|
|
|
|
|
<div class="device-mgmt__headerMain">
|
|
|
|
|
<div class="device-mgmt__titleRow">
|
|
|
|
|
<el-button link type="primary" @click="backToList">返回列表</el-button>
|
|
|
|
|
<el-button :icon="Back" @click="backToList">返回</el-button>
|
|
|
|
|
<span
|
|
|
|
|
class="device-mgmt__dot device-mgmt__dot--lg"
|
|
|
|
|
:class="
|
|
|
|
|
@ -312,35 +309,39 @@
|
|
|
|
|
<el-card shadow="never" class="device-mgmt__panel" v-loading="historyLoading">
|
|
|
|
|
<div class="device-mgmt__filters">
|
|
|
|
|
<div class="device-mgmt__filter">
|
|
|
|
|
<div class="device-mgmt__filterLabel">参数选择</div>
|
|
|
|
|
<el-select v-model="historyMetricKey" class="!w-160px" clearable>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="m in historyMetricOptions"
|
|
|
|
|
:key="m.id"
|
|
|
|
|
:label="m.attributeName"
|
|
|
|
|
:value="m.id"
|
|
|
|
|
<div class="device-mgmt__filter">
|
|
|
|
|
<div class="device-mgmt__filterLabel">参数选择</div>
|
|
|
|
|
<el-select v-model="historyMetricKey" class="!w-160px" clearable>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="m in historyMetricOptions"
|
|
|
|
|
:key="m.id"
|
|
|
|
|
:label="m.attributeName"
|
|
|
|
|
:value="m.id"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="device-mgmt__filter">
|
|
|
|
|
<div class="device-mgmt__filterLabel">时间范围</div>
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="historyTimeRange"
|
|
|
|
|
type="datetimerange"
|
|
|
|
|
range-separator="-"
|
|
|
|
|
start-placeholder="开始时间"
|
|
|
|
|
end-placeholder="结束时间"
|
|
|
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
:disabled-date="historyDisabledDate"
|
|
|
|
|
@change="handleHistoryRangeChange"
|
|
|
|
|
class="!w-320px"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="device-mgmt__filter">
|
|
|
|
|
<div class="device-mgmt__filterLabel">时间范围</div>
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="historyTimeRange"
|
|
|
|
|
type="datetimerange"
|
|
|
|
|
range-separator="-"
|
|
|
|
|
start-placeholder="开始时间"
|
|
|
|
|
end-placeholder="结束时间"
|
|
|
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
class="!w-320px"
|
|
|
|
|
/>
|
|
|
|
|
<el-button type="primary" @click="loadHistoryData">查询</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<el-button type="primary" @click="loadHistoryData">查询</el-button>
|
|
|
|
|
|
|
|
|
|
<div class="device-mgmt__filterActions">
|
|
|
|
|
<el-radio-group v-model="historyViewType" class="ml-16px">
|
|
|
|
|
<el-radio-button label="chart">曲线</el-radio-button>
|
|
|
|
|
<el-radio-button label="list">列表</el-radio-button>
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
<el-button class="ml-16px">导出</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -403,13 +404,7 @@
|
|
|
|
|
: 'info'
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
{{
|
|
|
|
|
String(row.alarmLevel) === '1'
|
|
|
|
|
? '高级'
|
|
|
|
|
: String(row.alarmLevel) === '2'
|
|
|
|
|
? '中级'
|
|
|
|
|
: '低级'
|
|
|
|
|
}}
|
|
|
|
|
{{ getAlarmLevelLabel(row.alarmLevel) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
@ -449,7 +444,8 @@ import { useRoute } from 'vue-router'
|
|
|
|
|
import NodeForm from './NodeForm.vue'
|
|
|
|
|
import DeviceForm from '../DeviceForm.vue'
|
|
|
|
|
import { formatDate } from '@/utils/formatTime'
|
|
|
|
|
import { OfficeBuilding, Monitor, Refresh } from '@element-plus/icons-vue'
|
|
|
|
|
import { getIntDictOptions } from '@/utils/dict'
|
|
|
|
|
import { OfficeBuilding, Monitor, Refresh, Back } from '@element-plus/icons-vue'
|
|
|
|
|
defineOptions({ name: 'IoTDeviceManagement' })
|
|
|
|
|
|
|
|
|
|
type TreeNodeType = 'company' | 'line' | 'device'
|
|
|
|
|
@ -661,6 +657,13 @@ const getOperatingStatusLabel = (status?: string | number) => {
|
|
|
|
|
return String(status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const alarmLevelOptions = computed(() => getIntDictOptions('alarm_registration'))
|
|
|
|
|
|
|
|
|
|
const getAlarmLevelLabel = (value?: string | number) => {
|
|
|
|
|
const item = alarmLevelOptions.value.find((option) => String(option.value) === String(value))
|
|
|
|
|
return item?.label || String(value ?? '-')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loadNodeDeviceList = async () => {
|
|
|
|
|
if (!selectedNode.value) return
|
|
|
|
|
nodeDeviceLoading.value = true
|
|
|
|
|
@ -837,10 +840,57 @@ watch([() => activeTab.value, () => selectedDeviceDetail.value.id], ([tab, devic
|
|
|
|
|
|
|
|
|
|
const historyMetricOptions = ref<any[]>([])
|
|
|
|
|
const historyMetricKey = ref<string | number>('')
|
|
|
|
|
const historyTimeRange = ref<string[]>([])
|
|
|
|
|
const historyViewType = ref<'chart' | 'list'>('chart')
|
|
|
|
|
const historyList = ref<any[]>([])
|
|
|
|
|
const historyLoading = ref(false)
|
|
|
|
|
const HISTORY_MAX_RANGE_MS = 8 * 60 * 60 * 1000
|
|
|
|
|
|
|
|
|
|
const formatDateTimeText = (date: Date) => {
|
|
|
|
|
const year = date.getFullYear()
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
const hours = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const buildDefaultHistoryRange = () => {
|
|
|
|
|
const end = new Date()
|
|
|
|
|
const start = new Date(end.getTime() - 2 * 60 * 60 * 1000)
|
|
|
|
|
return [formatDateTimeText(start), formatDateTimeText(end)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const historyTimeRange = ref<string[]>(buildDefaultHistoryRange())
|
|
|
|
|
|
|
|
|
|
const toTimeStamp = (value?: string) => {
|
|
|
|
|
if (!value) return 0
|
|
|
|
|
const ts = new Date(value).getTime()
|
|
|
|
|
return Number.isNaN(ts) ? 0 : ts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const historyDisabledDate = (time: Date) => {
|
|
|
|
|
const endOfDay = new Date(time)
|
|
|
|
|
endOfDay.setHours(23, 59, 59, 999)
|
|
|
|
|
return endOfDay.getTime() > Date.now()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleHistoryRangeChange = (range?: string[]) => {
|
|
|
|
|
if (!range || range.length !== 2) return
|
|
|
|
|
const startTime = toTimeStamp(range[0])
|
|
|
|
|
const endTime = Math.min(toTimeStamp(range[1]), Date.now())
|
|
|
|
|
if (!startTime || !endTime) return
|
|
|
|
|
if (endTime - startTime > HISTORY_MAX_RANGE_MS) {
|
|
|
|
|
const fixedStart = formatDateTimeText(new Date(endTime - HISTORY_MAX_RANGE_MS))
|
|
|
|
|
const fixedEnd = formatDateTimeText(new Date(endTime))
|
|
|
|
|
historyTimeRange.value = [fixedStart, fixedEnd]
|
|
|
|
|
message.warning('时间范围最多 8 小时,已自动调整')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (endTime !== toTimeStamp(range[1])) {
|
|
|
|
|
historyTimeRange.value = [range[0], formatDateTimeText(new Date(endTime))]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parseHistoryTime = (timeText?: string) => {
|
|
|
|
|
if (!timeText) return { display: '-', timestamp: 0 }
|
|
|
|
|
@ -888,6 +938,7 @@ const loadHistoryData = async () => {
|
|
|
|
|
modelId: Number(historyMetricKey.value)
|
|
|
|
|
}
|
|
|
|
|
if (historyTimeRange.value && historyTimeRange.value.length === 2) {
|
|
|
|
|
handleHistoryRangeChange(historyTimeRange.value)
|
|
|
|
|
params.collectionStartTime = historyTimeRange.value[0]
|
|
|
|
|
params.collectionEndTime = historyTimeRange.value[1]
|
|
|
|
|
}
|
|
|
|
|
@ -986,6 +1037,9 @@ watch([() => activeTab.value, () => selectedDeviceDetail.value.id], ([tab, devic
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
:deep(.el-tree-node__content) {
|
|
|
|
|
margin: 2px 0;
|
|
|
|
|
}
|
|
|
|
|
.device-mgmt {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|