|
@@ -1,14 +1,279 @@
|
|
|
<template>
|
|
|
- <ContentWrap>
|
|
|
- <div>
|
|
|
- 资产编号:
|
|
|
- </div>
|
|
|
+ <ContentWrap v-loading="formLoading">
|
|
|
+ <ContentWrap>
|
|
|
+ <el-form>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="资产编码:" prop="deviceCode"
|
|
|
+ >{{ formData.deviceCode }}
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="设备类别:" prop="deviceName"
|
|
|
+ >{{ formData.deviceName }}
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="是否在线:" prop="ifInline"
|
|
|
+ >{{ getDictLabel(DICT_TYPE.IOT_DEVICE_STATUS, formData.ifInline) }}
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="最后在线时间:" prop="lastInlineTime"
|
|
|
+ >{{ formData.lastInlineTime }}
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+ <ContentWrap>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <TdDeviceLabel :tags="specs" tag-width="24%" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </ContentWrap>
|
|
|
+ <ContentWrap>
|
|
|
+ <div class="chart-container">
|
|
|
+ <!-- 日期选择区域 -->
|
|
|
+ <div class="date-controls">
|
|
|
+ <input
|
|
|
+ type="datetime-local"
|
|
|
+ v-model="startTime"
|
|
|
+ :max="endTime"
|
|
|
+ @change="handleDateChange"
|
|
|
+ />
|
|
|
+ <span class="separator">至</span>
|
|
|
+ <input
|
|
|
+ type="datetime-local"
|
|
|
+ v-model="endTime"
|
|
|
+ :min="startTime"
|
|
|
+ @change="handleDateChange"
|
|
|
+ />
|
|
|
+ <button class="query-btn" @click="fetchData">查询</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 图表容器 -->
|
|
|
+ <div v-loading="loading" class="chart" ref="chartRef"></div>
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
</ContentWrap>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
+import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
|
|
+import TdDeviceLabel from '@/views/pms/device/monitor/TdDeviceLabel.vue'
|
|
|
+import {IotDeviceApi} from "@/api/pms/device";
|
|
|
+
|
|
|
+const { params, name } = useRoute() // 查询参数
|
|
|
+const info = ref({})
|
|
|
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
|
+const id = params.id
|
|
|
+defineOptions({ name: 'TdDeviceDetail' })
|
|
|
+const formData = ref({
|
|
|
+ deviceCode: '',
|
|
|
+ deviceName: '',
|
|
|
+ ifInline: undefined,
|
|
|
+ lastInlineTime: ''
|
|
|
+})
|
|
|
+const specs = ref([])
|
|
|
+onMounted(async () => {
|
|
|
+ formLoading.value = true
|
|
|
+ formData.value.deviceCode = params.code
|
|
|
+ formData.value.deviceName = params.name
|
|
|
+ formData.value.lastInlineTime = params.time
|
|
|
+ formData.value.ifInline = params.ifInline
|
|
|
+ IotDeviceApi.getIotDeviceTds(id).then(res => {
|
|
|
+ specs.value = res
|
|
|
+ specs.value = specs.value.sort((a, b) => {
|
|
|
+ return b.modelOrder - a.modelOrder
|
|
|
+ })
|
|
|
+ formLoading.value = false
|
|
|
+ })
|
|
|
+ initDefaultDate()
|
|
|
+ initChart()
|
|
|
+ await fetchData()
|
|
|
+})
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import * as echarts from 'echarts'
|
|
|
+import dayjs from 'dayjs'
|
|
|
+
|
|
|
+// ECharts 实例
|
|
|
+const chartRef = ref(null)
|
|
|
+let chartInstance = null
|
|
|
+
|
|
|
+// 响应式数据
|
|
|
+const startTime = ref('')
|
|
|
+const endTime = ref('')
|
|
|
+const chartData = ref([])
|
|
|
+const loading = ref(false)
|
|
|
+
|
|
|
+// 初始化默认时间范围
|
|
|
+const initDefaultDate = () => {
|
|
|
+ const now = dayjs()
|
|
|
+ startTime.value = now.subtract(1, 'day').format('YYYY-MM-DDTHH:mm')
|
|
|
+ endTime.value = now.format('YYYY-MM-DDTHH:mm')
|
|
|
+}
|
|
|
+
|
|
|
+// 空数据占位处理
|
|
|
+const displayData = computed(() => {
|
|
|
+ return chartData.value.length > 0
|
|
|
+ ? chartData.value
|
|
|
+ : [{ time: startTime.value, value: 0 }, { time: endTime.value, value: 0 }]
|
|
|
+})
|
|
|
+
|
|
|
+// 初始化图表配置
|
|
|
+const initChart = () => {
|
|
|
+ chartInstance = echarts.init(chartRef.value)
|
|
|
+
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: '数据趋势图',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter: params => {
|
|
|
+ if (chartData.value.length === 0) return '暂无数据'
|
|
|
+ return `${params[0].axisValue}<br/>${params[0].marker} ${params[0].value}`
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'time',
|
|
|
+ boundaryGap: false,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: value => dayjs(value).format('MM-DD HH:mm')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ min: value => Math.max(0, value.min - 5)
|
|
|
+ },
|
|
|
+ series: [{
|
|
|
+ name: '数据',
|
|
|
+ type: 'line',
|
|
|
+ showSymbol: chartData.value.length > 0,
|
|
|
+ data: displayData.value.map(item => [item.time, item.value]),
|
|
|
+ areaStyle: chartData.value.length > 0 ? {} : null
|
|
|
+ }],
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ chartInstance.setOption(option)
|
|
|
+ window.addEventListener('resize', () => chartInstance.resize())
|
|
|
+}
|
|
|
|
|
|
+// 获取数据(示例)
|
|
|
+const fetchData = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ // 模拟API请求
|
|
|
+ const mockData = [] // 测试空数据时设置为空数组
|
|
|
+
|
|
|
+ // 实际应替换为:
|
|
|
+ // const { data } = await api.getData({
|
|
|
+ // start: dayjs(startTime.value).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ // end: dayjs(endTime.value).format('YYYY-MM-DD HH:mm:ss')
|
|
|
+ // })
|
|
|
+
|
|
|
+ chartData.value = mockData
|
|
|
+ updateChart()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('数据获取失败:', error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 更新图表
|
|
|
+const updateChart = () => {
|
|
|
+ const options = {
|
|
|
+ series: [{
|
|
|
+ data: displayData.value.map(item => [item.time, item.value]),
|
|
|
+ showSymbol: chartData.value.length > 0,
|
|
|
+ areaStyle: chartData.value.length > 0 ? {} : null
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ chartInstance.setOption(options)
|
|
|
+}
|
|
|
+
|
|
|
+// 日期变更处理
|
|
|
+const handleDateChange = () => {
|
|
|
+ if (dayjs(startTime.value).isAfter(endTime.value)) {
|
|
|
+ endTime.value = startTime.value
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', () => chartInstance.resize())
|
|
|
+ chartInstance.dispose()
|
|
|
+})
|
|
|
</script>
|
|
|
<style scoped lang="scss">
|
|
|
+.container {
|
|
|
+ width: 100%;
|
|
|
+ margin: 20px auto;
|
|
|
+ padding: 24px;
|
|
|
+ //background: #f8f9fa;
|
|
|
+ border-radius: 12px;
|
|
|
+}
|
|
|
+.chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 600px;
|
|
|
+ padding: 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.date-controls {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+input[type="datetime-local"] {
|
|
|
+ padding: 8px 12px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: border-color 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+input[type="datetime-local"]:focus {
|
|
|
+ border-color: #409eff;
|
|
|
+ outline: none;
|
|
|
+}
|
|
|
+
|
|
|
+.separator {
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+.query-btn {
|
|
|
+ padding: 8px 20px;
|
|
|
+ background: #409eff;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: opacity 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.query-btn:hover {
|
|
|
+ opacity: 0.8;
|
|
|
+}
|
|
|
|
|
|
+.chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 500px;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
</style>
|