Commit 9e0402d8043d2499aaef567fc85bd6c22b296961

Authored by xp.Huang
2 parents d4a291e0 7004bf55

Merge branch 'sqy_dev' into 'main'

feat地理位置---:历史记录

See merge request huang/yun-teng-iot-front!63
@@ -6,3 +6,27 @@ export const getDeviceProfile = () => { @@ -6,3 +6,27 @@ export const getDeviceProfile = () => {
6 url: '/deviceProfile/me/list', 6 url: '/deviceProfile/me/list',
7 }); 7 });
8 }; 8 };
  9 +
  10 +export const getDeviceHistoryInfo = (params) => {
  11 + return defHttp.get(
  12 + {
  13 + url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`,
  14 + params: { ...params, entityId: null, orderBy: 'ASC' },
  15 + },
  16 + {
  17 + joinPrefix: false,
  18 + }
  19 + );
  20 +};
  21 +
  22 +// 获取设备数据的keys
  23 +export const getDeviceDataKeys = (id: string) => {
  24 + return defHttp.get(
  25 + {
  26 + url: `/plugins/telemetry/DEVICE/${id}/keys/timeseries`,
  27 + },
  28 + {
  29 + joinPrefix: false,
  30 + }
  31 + );
  32 +};
@@ -14,7 +14,6 @@ import { setupStore } from '/@/store'; @@ -14,7 +14,6 @@ import { setupStore } from '/@/store';
14 import { setupGlobDirectives } from '/@/directives'; 14 import { setupGlobDirectives } from '/@/directives';
15 import { setupI18n } from '/@/locales/setupI18n'; 15 import { setupI18n } from '/@/locales/setupI18n';
16 import { registerGlobComp } from '/@/components/registerGlobComp'; 16 import { registerGlobComp } from '/@/components/registerGlobComp';
17 -  
18 // Do not introduce on-demand in local development? 17 // Do not introduce on-demand in local development?
19 // In the local development for introduce on-demand, the number of browser requests will increase by about 20%. 18 // In the local development for introduce on-demand, the number of browser requests will increase by about 20%.
20 // Which may slow down the browser refresh. 19 // Which may slow down the browser refresh.
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 import { BasicTable, useTable, TableAction } from '/@/components/Table'; 21 import { BasicTable, useTable, TableAction } from '/@/components/Table';
22 import { alarmColumns, alarmSearchSchemas } from './config/detail.config'; 22 import { alarmColumns, alarmSearchSchemas } from './config/detail.config';
23 import { getDeviceAlarm } from '/@/api/device/deviceManager'; 23 import { getDeviceAlarm } from '/@/api/device/deviceManager';
24 - import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue';  
25 import { useDrawer } from '/@/components/Drawer'; 24 import { useDrawer } from '/@/components/Drawer';
  25 + import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue';
26 export default defineComponent({ 26 export default defineComponent({
27 - name: 'DeviceManagement', 27 + name: 'AlarmCenter',
28 components: { 28 components: {
29 BasicTable, 29 BasicTable,
30 TableAction, 30 TableAction,
@@ -92,89 +92,328 @@ export const columns: BasicColumn[] = [ @@ -92,89 +92,328 @@ export const columns: BasicColumn[] = [
92 }, 92 },
93 ]; 93 ];
94 94
  95 +// 动态生成options
  96 +function generateOptions(value: number) {
  97 + if (value === 3600000) {
  98 + return [
  99 + {
  100 + label: '10秒',
  101 + value: 10000,
  102 + },
  103 + {
  104 + label: '15秒',
  105 + value: 15000,
  106 + },
  107 + {
  108 + label: '30秒',
  109 + value: 30000,
  110 + },
  111 + {
  112 + label: '1分钟',
  113 + value: 60000,
  114 + },
  115 + {
  116 + label: '2分钟',
  117 + value: 120000,
  118 + },
  119 + {
  120 + label: '5分钟',
  121 + value: 300000,
  122 + },
  123 + ];
  124 + } else if (value === 7200000) {
  125 + return [
  126 + {
  127 + label: '15秒',
  128 + value: 15000,
  129 + },
  130 + {
  131 + label: '30秒',
  132 + value: 30000,
  133 + },
  134 + {
  135 + label: '1分钟',
  136 + value: 60000,
  137 + },
  138 + {
  139 + label: '2分钟',
  140 + value: 120000,
  141 + },
  142 + {
  143 + label: '5分钟',
  144 + value: 300000,
  145 + },
  146 + {
  147 + label: '10分钟',
  148 + value: 600000,
  149 + },
  150 + {
  151 + label: '15分钟',
  152 + value: 900000,
  153 + },
  154 + ];
  155 + } else if (value === 18000000) {
  156 + return [
  157 + {
  158 + label: '1分钟',
  159 + value: 60000,
  160 + },
  161 + {
  162 + label: '2分钟',
  163 + value: 120000,
  164 + },
  165 + {
  166 + label: '5分钟',
  167 + value: 300000,
  168 + },
  169 + {
  170 + label: '10分钟',
  171 + value: 600000,
  172 + },
  173 + {
  174 + label: '15分钟',
  175 + value: 900000,
  176 + },
  177 + {
  178 + label: '30分钟',
  179 + value: 1800000,
  180 + },
  181 + ];
  182 + } else if (value === 36000000) {
  183 + return [
  184 + {
  185 + label: '2分钟',
  186 + value: 120000,
  187 + },
  188 + {
  189 + label: '5分钟',
  190 + value: 300000,
  191 + },
  192 + {
  193 + label: '10分钟',
  194 + value: 600000,
  195 + },
  196 + {
  197 + label: '15分钟',
  198 + value: 900000,
  199 + },
  200 + {
  201 + label: '30分钟',
  202 + value: 1800000,
  203 + },
  204 + {
  205 + label: '1小时',
  206 + value: 3600000,
  207 + },
  208 + ];
  209 + } else if (value === 43200000) {
  210 + return [
  211 + {
  212 + label: '2分钟',
  213 + value: 120000,
  214 + },
  215 + {
  216 + label: '5分钟',
  217 + value: 300000,
  218 + },
  219 + {
  220 + label: '10分钟',
  221 + value: 600000,
  222 + },
  223 + {
  224 + label: '15分钟',
  225 + value: 900000,
  226 + },
  227 + {
  228 + label: '30分钟',
  229 + value: 1800000,
  230 + },
  231 + {
  232 + label: '1小时',
  233 + value: 3600000,
  234 + },
  235 + ];
  236 + } else if (value === 86400000) {
  237 + return [
  238 + {
  239 + label: '5分钟',
  240 + value: 300000,
  241 + },
  242 + {
  243 + label: '10分钟',
  244 + value: 600000,
  245 + },
  246 + {
  247 + label: '15分钟',
  248 + value: 900000,
  249 + },
  250 + {
  251 + label: '30分钟',
  252 + value: 1800000,
  253 + },
  254 + {
  255 + label: '1小时',
  256 + value: 3600000,
  257 + },
  258 + {
  259 + label: '2小时',
  260 + value: 7200000,
  261 + },
  262 + ];
  263 + } else if (value === 604800000) {
  264 + return [
  265 + {
  266 + label: '30分钟',
  267 + value: 1800000,
  268 + },
  269 + {
  270 + label: '1小时',
  271 + value: 3600000,
  272 + },
  273 + {
  274 + label: '2小时',
  275 + value: 7200000,
  276 + },
  277 + {
  278 + label: '5小时',
  279 + value: 18000000,
  280 + },
  281 + {
  282 + label: '10小时',
  283 + value: 36000000,
  284 + },
  285 + {
  286 + label: '12小时',
  287 + value: 43200000,
  288 + },
  289 + {
  290 + label: '1天',
  291 + value: 86400000,
  292 + },
  293 + ];
  294 + } else {
  295 + return [
  296 + {
  297 + label: '2小时',
  298 + value: 7200000,
  299 + },
  300 + {
  301 + label: '5小时',
  302 + value: 18000000,
  303 + },
  304 + {
  305 + label: '10小时',
  306 + value: 36000000,
  307 + },
  308 + {
  309 + label: '12小时',
  310 + value: 43200000,
  311 + },
  312 + {
  313 + label: '1天',
  314 + value: 86400000,
  315 + },
  316 + ];
  317 + }
  318 +}
95 export const schemas: FormSchema[] = [ 319 export const schemas: FormSchema[] = [
96 { 320 {
97 - field: 'name', 321 + field: 'endTs',
98 label: '最后数据', 322 label: '最后数据',
99 component: 'Select', 323 component: 'Select',
100 - componentProps: {  
101 - options: [  
102 - {  
103 - label: '最近1小时',  
104 - value: '1',  
105 - },  
106 - {  
107 - label: '最近2小时',  
108 - value: '2',  
109 - },  
110 - {  
111 - label: '最近5小时',  
112 - value: '5', 324 + required: true,
  325 + componentProps({ formModel, formActionType }) {
  326 + return {
  327 + onChange(value) {
  328 + const { updateSchema } = formActionType;
  329 + formModel.interval = '';
  330 + updateSchema({
  331 + field: 'interval',
  332 + componentProps: {
  333 + placeholder: '请选择分组间隔',
  334 + options: generateOptions(value),
  335 + },
  336 + });
113 }, 337 },
114 - {  
115 - label: '最近12小时',  
116 - value: '12',  
117 - },  
118 - {  
119 - label: '最近1天',  
120 - value: '1天',  
121 - },  
122 - {  
123 - label: '最近7天',  
124 - value: '7天',  
125 - },  
126 - {  
127 - label: '最近30天',  
128 - value: '30天',  
129 - },  
130 - ], 338 + options: [
  339 + {
  340 + label: '最近1小时',
  341 + value: 3600000,
  342 + },
  343 + {
  344 + label: '最近2小时',
  345 + value: 7200000,
  346 + },
  347 + {
  348 + label: '最近5小时',
  349 + value: 18000000,
  350 + },
  351 + {
  352 + label: '最近10小时',
  353 + value: 36000000,
  354 + },
  355 + {
  356 + label: '最近12小时',
  357 + value: 43200000,
  358 + },
  359 + {
  360 + label: '最近1天',
  361 + value: 86400000,
  362 + },
  363 + {
  364 + label: '最近7天',
  365 + value: 604800000,
  366 + },
  367 + {
  368 + label: '最近30天',
  369 + value: 2592000000,
  370 + },
  371 + ],
  372 + };
131 }, 373 },
132 colProps: { 374 colProps: {
133 span: 6, 375 span: 6,
134 }, 376 },
135 }, 377 },
136 { 378 {
137 - field: 'name', 379 + field: 'interval',
138 label: '分组间隔', 380 label: '分组间隔',
139 component: 'Select', 381 component: 'Select',
  382 + colProps: {
  383 + span: 6,
  384 + },
140 componentProps: { 385 componentProps: {
  386 + placeholder: '请选择分组间隔',
141 options: [ 387 options: [
142 { 388 {
143 - label: '15秒',  
144 - value: '15',  
145 - },  
146 - {  
147 - label: '30秒',  
148 - value: '30', 389 + label: '5分钟',
  390 + value: 300000,
149 }, 391 },
150 { 392 {
151 - label: '1分钟',  
152 - value: '60', 393 + label: '10分钟',
  394 + value: 600000,
153 }, 395 },
154 { 396 {
155 - label: '2分钟',  
156 - value: '120', 397 + label: '15分钟',
  398 + value: 900000,
157 }, 399 },
158 { 400 {
159 - label: '5分钟',  
160 - value: '360', 401 + label: '30分钟',
  402 + value: 1800000,
161 }, 403 },
162 { 404 {
163 - label: '10分钟',  
164 - value: '600', 405 + label: '1小时',
  406 + value: 3600000,
165 }, 407 },
166 { 408 {
167 - label: '15分钟',  
168 - value: '900', 409 + label: '2小时',
  410 + value: 7200000,
169 }, 411 },
170 ], 412 ],
171 }, 413 },
172 - colProps: {  
173 - span: 6,  
174 - },  
175 }, 414 },
176 { 415 {
177 - field: 'name', 416 + field: 'agg',
178 label: '数据聚合功能', 417 label: '数据聚合功能',
179 component: 'Select', 418 component: 'Select',
180 componentProps: { 419 componentProps: {
1 <template> 1 <template>
2 - <div class="wrapper"> 2 + <div class="wrapper123">
3 <div ref="wrapRef" :style="{ height, width }"> </div> 3 <div ref="wrapRef" :style="{ height, width }"> </div>
4 <div class="right-wrap"> 4 <div class="right-wrap">
5 <BasicTable @register="registerTable" @rowClick="deviceRowClick"> 5 <BasicTable @register="registerTable" @rowClick="deviceRowClick">
@@ -25,14 +25,21 @@ @@ -25,14 +25,21 @@
25 </template> 25 </template>
26 </BasicTable> 26 </BasicTable>
27 </div> 27 </div>
28 - <a-button type="primary" @click="handleClick">打开弹窗</a-button>  
29 - <BasicModal @register="registerModal" title="历史数据" width="70%"> 28 + <BasicModal
  29 + @register="registerModal"
  30 + title="历史数据"
  31 + width="70%"
  32 + :footer="null"
  33 + @cancel="cancelHistoryModal"
  34 + :canFullscreen="false"
  35 + >
30 <BasicForm @register="registerForm" /> 36 <BasicForm @register="registerForm" />
  37 + <div ref="chartRef" :style="{ height: '600px', width }"></div>
31 </BasicModal> 38 </BasicModal>
32 </div> 39 </div>
33 </template> 40 </template>
34 <script lang="ts"> 41 <script lang="ts">
35 - import { defineComponent, ref, nextTick, unref, onMounted } from 'vue'; 42 + import { defineComponent, ref, nextTick, unref, onMounted, Ref } from 'vue';
36 import { useScript } from '/@/hooks/web/useScript'; 43 import { useScript } from '/@/hooks/web/useScript';
37 import { formSchema, columns } from './config.data'; 44 import { formSchema, columns } from './config.data';
38 import { BasicTable, useTable } from '/@/components/Table'; 45 import { BasicTable, useTable } from '/@/components/Table';
@@ -43,6 +50,9 @@ @@ -43,6 +50,9 @@
43 import { useModal, BasicModal } from '/@/components/Modal'; 50 import { useModal, BasicModal } from '/@/components/Modal';
44 import { BasicForm, useForm } from '/@/components/Form'; 51 import { BasicForm, useForm } from '/@/components/Form';
45 import { schemas } from './config.data'; 52 import { schemas } from './config.data';
  53 + import { useECharts } from '/@/hooks/web/useECharts';
  54 + import { getDeviceHistoryInfo, getDeviceDataKeys } from '/@/api/alarm/position';
  55 + import moment from 'moment';
46 export default defineComponent({ 56 export default defineComponent({
47 name: 'BaiduMap', 57 name: 'BaiduMap',
48 components: { 58 components: {
@@ -64,7 +74,7 @@ @@ -64,7 +74,7 @@
64 setup() { 74 setup() {
65 const wrapRef = ref<HTMLDivElement | null>(null); 75 const wrapRef = ref<HTMLDivElement | null>(null);
66 const { toPromise } = useScript({ src: BAI_DU_MAP_URL }); 76 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
67 - 77 + const entityId = ref('');
68 async function initMap() { 78 async function initMap() {
69 await toPromise(); 79 await toPromise();
70 await nextTick(); 80 await nextTick();
@@ -73,13 +83,9 @@ @@ -73,13 +83,9 @@
73 if (!wrapEl) return; 83 if (!wrapEl) return;
74 const map = new BMap.Map(wrapEl); 84 const map = new BMap.Map(wrapEl);
75 const point = new BMap.Point(104.04666605565338, 30.543516387560476); 85 const point = new BMap.Point(104.04666605565338, 30.543516387560476);
76 -  
77 map.centerAndZoom(point, 15); 86 map.centerAndZoom(point, 15);
78 map.enableScrollWheelZoom(true); 87 map.enableScrollWheelZoom(true);
79 } 88 }
80 - onMounted(() => {  
81 - initMap();  
82 - });  
83 89
84 const [registerTable] = useTable({ 90 const [registerTable] = useTable({
85 api: devicePage, 91 api: devicePage,
@@ -90,9 +96,13 @@ @@ -90,9 +96,13 @@
90 }, 96 },
91 showIndexColumn: false, 97 showIndexColumn: false,
92 useSearchForm: true, 98 useSearchForm: true,
  99 + pagination: {
  100 + showSizeChanger: false,
  101 + },
93 }); 102 });
94 // 点击表格某一行触发 103 // 点击表格某一行触发
95 const deviceRowClick = (record) => { 104 const deviceRowClick = (record) => {
  105 + entityId.value = record.tbDeviceId;
96 const BMap = (window as any).BMap; 106 const BMap = (window as any).BMap;
97 const wrapEl = unref(wrapRef); 107 const wrapEl = unref(wrapRef);
98 const map = new BMap.Map(wrapEl); 108 const map = new BMap.Map(wrapEl);
@@ -106,30 +116,32 @@ @@ -106,30 +116,32 @@
106 }; 116 };
107 map.centerAndZoom(point, 15); 117 map.centerAndZoom(point, 15);
108 map.enableScrollWheelZoom(true); 118 map.enableScrollWheelZoom(true);
  119 + // 创建信息窗口对象
109 let infoWindow = new BMap.InfoWindow( 120 let infoWindow = new BMap.InfoWindow(
110 ` 121 `
111 - <div style="display:flex;justify-content:space-between; margin:20px 0px;">  
112 - <div style="font-size:16px;font-weight:bold">${name}</div>  
113 - ${  
114 - deviceState === 'INACTIVE'  
115 - ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'  
116 - : deviceState === 'ONLINE'  
117 - ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'  
118 - : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'  
119 - }  
120 - </div>  
121 - <div>所属组织:${organizationDTO.name}</div>  
122 - <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>  
123 - <div style="margin-top:6px;">设备位置:${address}</div>  
124 - <div style="margin-top:6px;">下线时间:${updateTime}</div>  
125 - <div style="display:flex;justify-content:space-between; margin-top:10px">  
126 - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>  
127 - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>  
128 - <button onclick="historyClick()"style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>  
129 - </div>  
130 - `, 122 + <div style="display:flex;justify-content:space-between; margin:20px 0px;">
  123 + <div style="font-size:16px;font-weight:bold">${name}</div>
  124 + ${
  125 + deviceState === 'INACTIVE'
  126 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'
  127 + : deviceState === 'ONLINE'
  128 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'
  129 + : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'
  130 + }
  131 + </div>
  132 + <div>所属组织:${organizationDTO.name}</div>
  133 + <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
  134 + <div style="margin-top:6px;">设备位置:${address}</div>
  135 + <div style="margin-top:6px;">下线时间:${updateTime}</div>
  136 + <div style="display:flex;justify-content:space-between; margin-top:10px">
  137 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
  138 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
  139 + <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
  140 + </div>
  141 + `,
131 options 142 options
132 - ); // 创建信息窗口对象 143 + );
  144 +
133 map.openInfoWindow(infoWindow, map.getCenter()); 145 map.openInfoWindow(infoWindow, map.getCenter());
134 let preMarker = null; 146 let preMarker = null;
135 const rivet = 147 const rivet =
@@ -156,39 +168,190 @@ @@ -156,39 +168,190 @@
156 let infoWindow = new BMap.InfoWindow('该设备暂无地理位置', options); // 创建信息窗口对象 168 let infoWindow = new BMap.InfoWindow('该设备暂无地理位置', options); // 创建信息窗口对象
157 map.openInfoWindow(infoWindow, map.getCenter()); 169 map.openInfoWindow(infoWindow, map.getCenter());
158 } 170 }
159 -  
160 - console.log(record);  
161 }; 171 };
162 - 172 + let keys = [];
163 const [registerModal, { openModal }] = useModal(); 173 const [registerModal, { openModal }] = useModal();
164 - const [registerForm] = useForm({ 174 + const [registerForm, { resetFields, getFieldsValue, setFieldsValue, validate }] = useForm({
165 labelWidth: 120, 175 labelWidth: 120,
166 schemas, 176 schemas,
  177 + async submitFunc() {
  178 + // 表单验证
  179 + await validate();
  180 + let { endTs, interval, agg } = getFieldsValue();
  181 + if (!endTs) return;
  182 + // 数据收集
  183 + const dataArray: any[] = [];
  184 + const startTs = Date.now() - endTs;
  185 + endTs = Date.now();
  186 + // 发送请求
  187 + const res = await getDeviceHistoryInfo({
  188 + entityId: entityId.value,
  189 + keys: keys.join(),
  190 + startTs,
  191 + endTs,
  192 + interval,
  193 + agg,
  194 + });
  195 + // 处理数据
  196 + for (const key in res) {
  197 + for (const item1 of res[key]) {
  198 + let { ts, value } = item1;
  199 + const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
  200 + value = Number(value).toFixed(2);
  201 + dataArray.push([time, value, key]);
  202 + }
  203 + }
  204 +
  205 + const series: any = keys.map((item) => {
  206 + return {
  207 + name: item,
  208 + type: 'line',
  209 + stack: 'Total',
  210 + data: dataArray.filter((item1) => item1[2] === item),
  211 + };
  212 + });
  213 + // 设置数据
  214 + setOptions({
  215 + tooltip: {
  216 + trigger: 'axis',
  217 + },
  218 + legend: {
  219 + data: keys,
  220 + },
  221 + grid: {
  222 + left: '3%',
  223 + right: '4%',
  224 + bottom: '3%',
  225 + containLabel: true,
  226 + },
  227 + dataZoom: [
  228 + {
  229 + type: 'inside',
  230 + start: 0,
  231 + end: 50,
  232 + },
  233 + {
  234 + start: 20,
  235 + end: 40,
  236 + },
  237 + ],
  238 + xAxis: {
  239 + type: 'time',
  240 + boundaryGap: false,
  241 + },
  242 + yAxis: {
  243 + type: 'value',
  244 + boundaryGap: [0, '100%'],
  245 + },
  246 + series,
  247 + });
  248 + },
167 }); 249 });
168 - const handleClick = () => { 250 +
  251 + const chartRef = ref<HTMLDivElement | null>(null);
  252 + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  253 +
  254 + const openHistoryModal = async () => {
169 openModal(true); 255 openModal(true);
170 - };  
171 256
  257 + // 收集参数
  258 + const dataArray: any[] = [];
  259 + const startTs = Date.now() - 86400000; //最近一天
  260 + const endTs = Date.now();
  261 + // 发送请求
  262 + keys = await getDeviceDataKeys(entityId.value);
  263 + const res = await getDeviceHistoryInfo({
  264 + entityId: entityId.value,
  265 + keys: keys.join(),
  266 + startTs,
  267 + endTs,
  268 + interval: 7200000, //间隔两小时
  269 + agg: 'AVG',
  270 + });
  271 + // 处理数据
  272 + for (const key in res) {
  273 + for (const item1 of res[key]) {
  274 + let { ts, value } = item1;
  275 + const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
  276 + value = Number(value).toFixed(2);
  277 + dataArray.push([time, value, key]);
  278 + }
  279 + }
  280 + const series: any = keys.map((item) => {
  281 + return {
  282 + name: item,
  283 + type: 'line',
  284 + stack: 'Total',
  285 + data: dataArray.filter((item1) => item1[2] === item),
  286 + };
  287 + });
  288 + // 设置数据;
  289 + setOptions({
  290 + tooltip: {
  291 + trigger: 'axis',
  292 + },
  293 + legend: {
  294 + data: keys,
  295 + },
  296 + grid: {
  297 + left: '3%',
  298 + right: '4%',
  299 + bottom: '3%',
  300 + containLabel: true,
  301 + },
  302 + dataZoom: [
  303 + {
  304 + type: 'inside',
  305 + start: 0,
  306 + end: 50,
  307 + },
  308 + {
  309 + start: 0,
  310 + end: 20,
  311 + },
  312 + ],
  313 + xAxis: {
  314 + type: 'time',
  315 + boundaryGap: false,
  316 + },
  317 + yAxis: {
  318 + type: 'value',
  319 + boundaryGap: [0, '100%'],
  320 + },
  321 + series,
  322 + });
  323 +
  324 + setFieldsValue({
  325 + endTs: 86400000,
  326 + interval: 7200000,
  327 + agg: 'AVG',
  328 + });
  329 + };
  330 + const cancelHistoryModal = () => {
  331 + resetFields();
  332 + setOptions({});
  333 + };
  334 + onMounted(() => {
  335 + initMap();
  336 + (window as any).openHistoryModal = openHistoryModal;
  337 + });
172 return { 338 return {
173 wrapRef, 339 wrapRef,
174 registerTable, 340 registerTable,
175 deviceRowClick, 341 deviceRowClick,
176 DeviceState, 342 DeviceState,
177 - handleClick,  
178 registerModal, 343 registerModal,
179 registerForm, 344 registerForm,
  345 + chartRef,
  346 + cancelHistoryModal,
180 }; 347 };
181 }, 348 },
182 }); 349 });
183 </script> 350 </script>
184 -  
185 <style scoped> 351 <style scoped>
186 .wrapper { 352 .wrapper {
187 position: relative; 353 position: relative;
188 } 354 }
189 - .active {  
190 - background-color: #fff;  
191 - }  
192 .right-wrap { 355 .right-wrap {
193 padding-top: 10px; 356 padding-top: 10px;
194 width: 22%; 357 width: 22%;
@@ -13,32 +13,35 @@ @@ -13,32 +13,35 @@
13 light:border-#F2F2F5 13 light:border-#F2F2F5
14 " 14 "
15 > 15 >
16 - <img :src="item.imgUrl" style="width: 90px; height: 90px" /> 16 + <img :src="item.imgUrl" style="width: 5rem; height: 5rem" />
17 <div class="growCardItem-right"> 17 <div class="growCardItem-right">
18 <div class="flex justify-between ml-3"> 18 <div class="flex justify-between ml-3">
19 - <div style="font-size: 26px; color: #333">{{ item.value }}</div>  
20 - <img src="../../../../assets/images/tip.png" style="width: 20px; height: 20px" /> 19 + <div style="font-size: 1.625rem; color: #333">{{ item.value }}</div>
  20 + <img src="../../../../assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
21 </div> 21 </div>
22 <div class="ml-3">{{ item.title }}</div> 22 <div class="ml-3">{{ item.title }}</div>
23 - <div class="ml-1.5 mt-3 flex flex-nowrap" style="width: 240px" v-if="item.offLine"> 23 + <div class="ml-1.5 mt-3 flex flex-nowrap" style="width: 15rem" v-if="item.offLine">
24 <div class="count"> 24 <div class="count">
25 <img 25 <img
26 src="../../../../assets/images/online.png" 26 src="../../../../assets/images/online.png"
27 - style="width: 10px; height: 10px; margin-right: 4px" 27 + style="width: 0.6rem; height: 0.6rem"
  28 + class="mr-1"
28 /> 29 />
29 在线 {{ item.onLine }} 30 在线 {{ item.onLine }}
30 </div> 31 </div>
31 <div class="count"> 32 <div class="count">
32 <img 33 <img
33 src="../../../../assets/images/offline.png" 34 src="../../../../assets/images/offline.png"
34 - style="width: 10px; height: 10px; margin-right: 4px" 35 + style="width: 0.6rem; height: 0.6rem"
  36 + class="mr-1"
35 /> 37 />
36 离线 {{ item.offLine }} 38 离线 {{ item.offLine }}
37 </div> 39 </div>
38 <div class="count"> 40 <div class="count">
39 <img 41 <img
40 src="../../../../assets/images/inactive.png" 42 src="../../../../assets/images/inactive.png"
41 - style="width: 10px; height: 10px; margin-right: 4px" 43 + style="width: 0.6rem; height: 0.6rem"
  44 + class="mr-1"
42 /> 45 />
43 未激活 {{ item.inactive }} 46 未激活 {{ item.inactive }}
44 </div> 47 </div>
@@ -56,24 +59,24 @@ @@ -56,24 +59,24 @@
56 59
57 <style scoped lang="less"> 60 <style scoped lang="less">
58 .growCardItem { 61 .growCardItem {
59 - height: 179px; 62 + height: 11.187rem;
60 color: #666; 63 color: #666;
61 .growCardItem-top { 64 .growCardItem-top {
62 display: flex; 65 display: flex;
63 - margin: 20px;  
64 - padding-bottom: 10px; 66 + margin: 1.25rem;
  67 + padding-bottom: 0.625rem;
65 .growCardItem-right { 68 .growCardItem-right {
66 - width: 300px; 69 + width: 18.75rem;
67 .count { 70 .count {
68 display: flex; 71 display: flex;
69 - font-size: 12px; 72 + font-size: 0.75rem;
70 align-items: center; 73 align-items: center;
71 - margin-left: 8px; 74 + margin-left: 0.5rem;
72 } 75 }
73 } 76 }
74 } 77 }
75 .growCardItem-bottom { 78 .growCardItem-bottom {
76 - margin-left: 20px; 79 + margin-left: 1.25rem;
77 } 80 }
78 } 81 }
79 </style> 82 </style>
@@ -8,8 +8,8 @@ @@ -8,8 +8,8 @@
8 import { useWebSocket } from '@vueuse/core'; 8 import { useWebSocket } from '@vueuse/core';
9 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; 9 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
10 import { getAuthCache } from '/@/utils/auth'; 10 import { getAuthCache } from '/@/utils/auth';
11 - import type { socketDataType } from '../../types';  
12 import { useMessage } from '/@/hooks/web/useMessage'; 11 import { useMessage } from '/@/hooks/web/useMessage';
  12 + import type { socketDataType } from '../../types';
13 export default defineComponent({ 13 export default defineComponent({
14 name: 'RealTimeData', 14 name: 'RealTimeData',
15 components: { 15 components: {
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 }, 22 },
23 }, 23 },
24 setup(props) { 24 setup(props) {
25 - const token = getAuthCache(JWT_TOKEN_KEY); 25 + const token: string = getAuthCache(JWT_TOKEN_KEY);
26 const state = reactive({ 26 const state = reactive({
27 server: `ws://101.133.234.90:8080/api/ws/plugins/telemetry?token=${token}`, 27 server: `ws://101.133.234.90:8080/api/ws/plugins/telemetry?token=${token}`,
28 sendValue: JSON.stringify({ 28 sendValue: JSON.stringify({
@@ -54,25 +54,24 @@ @@ -54,25 +54,24 @@
54 dataSource: state.recordList, 54 dataSource: state.recordList,
55 }); 55 });
56 56
57 - const { send } = useWebSocket(state.server, { 57 + const { send, close } = useWebSocket(state.server, {
58 onConnected() { 58 onConnected() {
59 send(state.sendValue); 59 send(state.sendValue);
60 console.log('建立连接了'); 60 console.log('建立连接了');
61 - console.log(props.deviceDetail.tbDeviceId);  
62 }, 61 },
63 onMessage(_, e) { 62 onMessage(_, e) {
64 const { data } = JSON.parse(e.data); 63 const { data } = JSON.parse(e.data);
65 - console.log('来新消息了', 'data---', data);  
66 - const newArray: any = []; 64 + console.log('来新消息了', '---data---', data);
  65 + const newArray: socketDataType[] = [];
67 for (const key in data) { 66 for (const key in data) {
68 - const newData = data[key].flat(1); 67 + const [time, value] = data[key].flat(1);
69 let obj = { 68 let obj = {
70 key, 69 key,
71 - time: newData[0],  
72 - value: newData[1], 70 + time,
  71 + value,
73 }; 72 };
74 if (state.recordList.length === 0) { 73 if (state.recordList.length === 0) {
75 - state.recordList.push(obj); 74 + state.recordList.unshift(obj);
76 } else { 75 } else {
77 newArray.push(obj); 76 newArray.push(obj);
78 } 77 }
@@ -80,9 +79,9 @@ @@ -80,9 +79,9 @@
80 newArray.forEach((item) => { 79 newArray.forEach((item) => {
81 let flag = false; 80 let flag = false;
82 state.recordList.forEach((item1) => { 81 state.recordList.forEach((item1) => {
83 - if (item.key === item1.key) {  
84 - item1.time = item.time; 82 + if (item1.key === item.key) {
85 item1.value = item.value; 83 item1.value = item.value;
  84 + item1.time = item.time;
86 flag = true; 85 flag = true;
87 } 86 }
88 }); 87 });
@@ -91,7 +90,10 @@ @@ -91,7 +90,10 @@
91 } 90 }
92 }); 91 });
93 }, 92 },
94 - onDisconnected() {}, 93 + onDisconnected() {
  94 + console.log('断开连接了');
  95 + close();
  96 + },
95 onError() { 97 onError() {
96 createMessage.error('webSocket连接超时,请联系管理员'); 98 createMessage.error('webSocket连接超时,请联系管理员');
97 }, 99 },