TdDeviceInfo.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <template>
  2. <ContentWrap v-loading="formLoading">
  3. <ContentWrap>
  4. <el-form style="height:89px;margin-left: 20px;">
  5. <el-row style="display: flex;flex-direction: row; ">
  6. <el-col :span="8">
  7. <el-form-item prop="deviceCode">
  8. <template #label>
  9. <span class="custom-label">资产编码:</span>
  10. </template>
  11. <span class="custom-label">{{ formData.deviceCode }}</span>
  12. </el-form-item>
  13. </el-col>
  14. <el-col :span="8">
  15. <el-form-item prop="deviceName">
  16. <template #label>
  17. <span class="custom-label">设备类别:</span>
  18. </template>
  19. <span class="custom-label">{{ formData.deviceName }}</span>
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="8">
  23. <el-form-item prop="dept">
  24. <template #label>
  25. <span class="custom-label">所在部门:</span>
  26. </template>
  27. <span class="custom-label">{{ formData.dept }}</span>
  28. </el-form-item>
  29. </el-col>
  30. <el-col :span="8">
  31. <el-form-item prop="ifInline">
  32. <template #label>
  33. <span class="custom-label">是否在线:</span>
  34. </template>
  35. <!-- <span class="custom-label">{{ getDictLabel(DICT_TYPE.IOT_DEVICE_STATUS, formData.ifInline) }}</span>-->
  36. <!-- <span class="custom-label">-->
  37. <!-- {{ getDictLabel(DICT_TYPE.IOT_DEVICE_STATUS, formData.ifInline) }}-->
  38. <template #default>
  39. <dict-tag :type="DICT_TYPE.IOT_DEVICE_STATUS" :value="formData.ifInline" />
  40. </template>
  41. <!-- </span>-->
  42. </el-form-item>
  43. </el-col>
  44. <el-col :span="8">
  45. <el-form-item prop="lastInlineTime">
  46. <template #label>
  47. <span class="custom-label">最后数据时间:</span>
  48. </template>
  49. <span class="custom-label">{{ formData.lastInlineTime }}</span>
  50. </el-form-item>
  51. </el-col>
  52. </el-row>
  53. </el-form>
  54. </ContentWrap>
  55. <ContentWrap>
  56. <el-row>
  57. <el-col :span="24">
  58. <TdDeviceLabel :tags="specs" @select="labelSelect" tag-width="24%" />
  59. </el-col>
  60. </el-row>
  61. </ContentWrap>
  62. <ContentWrap>
  63. <div class="chart-container">
  64. <!-- 日期选择区域 -->
  65. <!-- <div class="date-controls">-->
  66. <!-- <input-->
  67. <!-- type="datetime-local"-->
  68. <!-- v-model="startTime"-->
  69. <!-- :max="endTime"-->
  70. <!-- @change="handleDateChange"-->
  71. <!-- />-->
  72. <!-- <span class="separator">至</span>-->
  73. <!-- <input-->
  74. <!-- type="datetime-local"-->
  75. <!-- v-model="endTime"-->
  76. <!-- :min="startTime"-->
  77. <!-- @change="handleDateChange"-->
  78. <!-- />-->
  79. <!-- <button class="query-btn" @click="fetchData">查询</button>-->
  80. <!-- </div>-->
  81. <!-- 图表容器 -->
  82. <div v-loading="loading" style="height: 100%" ref="chartContainer"></div>
  83. </div>
  84. </ContentWrap>
  85. </ContentWrap>
  86. </template>
  87. <script setup lang="ts">
  88. import { DICT_TYPE, getDictLabel } from '@/utils/dict'
  89. import TdDeviceLabel from '@/views/pms/device/monitor/TdDeviceLabel.vue'
  90. import {IotDeviceApi} from "@/api/pms/device";
  91. import * as echarts from 'echarts'
  92. import dayjs from 'dayjs'
  93. import {IotStatApi} from "@/api/pms/stat";
  94. const { params, name } = useRoute() // 查询参数
  95. const info = ref({})
  96. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  97. const id = params.id
  98. defineOptions({ name: 'TdDeviceDetail' })
  99. const formData = ref({
  100. deviceCode: '',
  101. deviceName: '',
  102. ifInline: undefined,
  103. lastInlineTime: ''
  104. })
  105. const specs = ref([])
  106. // 响应式数据
  107. const startTime = ref('')
  108. const endTime = ref('')
  109. const topicName = ref([])
  110. const loading = ref(false)
  111. const topic = ref('')
  112. const labelSelect = (row) =>{
  113. topic.value = row.identifier
  114. topicName.value = row.modelName
  115. initChart()
  116. }
  117. const chartContainer = ref(null)
  118. let chartInstance = null
  119. // 时间格式化(HH:mm)
  120. const formatTime = timestamp => {
  121. return new Date(timestamp)
  122. .toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit',second:'2-digit' })
  123. .slice(0, 5)
  124. }
  125. // 初始化图表
  126. const initChart = async () => {
  127. if (!chartContainer.value) return
  128. // 销毁旧实例
  129. if (chartInstance) chartInstance.dispose()
  130. chartInstance = markRaw(echarts.init(chartContainer.value))
  131. const result = await IotStatApi.getDeviceInfoChart(params.code, topic.value)
  132. const option = {
  133. title:{
  134. text: topicName.value+'数据趋势',
  135. left:'center',
  136. },
  137. tooltip: { trigger: 'axis', },
  138. xAxis: {
  139. type: 'category',
  140. data: result.map(d => formatTime(d.timestamp)),
  141. axisLabel: { rotate: 45 } // X轴标签旋转防止重叠
  142. },
  143. yAxis: { type: 'value' },
  144. dataZoom: [{
  145. type: 'slider',
  146. xAxisIndex: 0,
  147. start: 0, // 初始显示范围开始位置
  148. end: 100 // 初始显示范围结束位置:ml-citation{ref="7" data="citationList"}
  149. }],
  150. series: [{
  151. data: result.map(d => d.value),
  152. type: 'line',
  153. smooth: true,
  154. areaStyle: {} // 显示区域填充
  155. }]
  156. }
  157. chartInstance.setOption(option)
  158. // 窗口自适应
  159. window.addEventListener('resize', () => chartInstance.resize())
  160. }
  161. onMounted(async () => {
  162. formLoading.value = true
  163. formData.value.deviceCode = params.code
  164. formData.value.deviceName = params.name
  165. formData.value.lastInlineTime = params.time
  166. formData.value.ifInline = params.ifInline
  167. formData.value.dept = params.dept
  168. await IotDeviceApi.getIotDeviceTds(id).then(res => {
  169. specs.value = res
  170. specs.value = specs.value.sort((a, b) => {
  171. return b.modelOrder - a.modelOrder
  172. })
  173. formLoading.value = false
  174. topic.value = specs.value[0].identifier
  175. topicName.value = specs.value[0].modelName
  176. })
  177. await initChart()
  178. })
  179. </script>
  180. <style scoped lang="scss">
  181. .container {
  182. width: 100%;
  183. margin: 20px auto;
  184. padding: 24px;
  185. //background: #f8f9fa;
  186. border-radius: 12px;
  187. }
  188. .chart-container {
  189. width: 100%;
  190. height: 600px;
  191. padding: 20px;
  192. background: #fff;
  193. border-radius: 8px;
  194. box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  195. }
  196. .date-controls {
  197. display: flex;
  198. align-items: center;
  199. gap: 15px;
  200. margin-bottom: 20px;
  201. }
  202. input[type="datetime-local"] {
  203. padding: 8px 12px;
  204. border: 1px solid #dcdfe6;
  205. border-radius: 4px;
  206. transition: border-color 0.2s;
  207. }
  208. input[type="datetime-local"]:focus {
  209. border-color: #409eff;
  210. outline: none;
  211. }
  212. .separator {
  213. color: #606266;
  214. }
  215. .query-btn {
  216. padding: 8px 20px;
  217. background: #409eff;
  218. color: white;
  219. border: none;
  220. border-radius: 4px;
  221. cursor: pointer;
  222. transition: opacity 0.2s;
  223. }
  224. .query-btn:hover {
  225. opacity: 0.8;
  226. }
  227. //.chart {
  228. // width: 100%;
  229. // height: 500px;
  230. // margin-top: 20px;
  231. //}
  232. .custom-label{
  233. font-size: 17px;
  234. font-weight: bold;
  235. }
  236. </style>