Commit 55f983a650ef14c16aa6108af2c622a9a1df0600

Authored by ww
2 parents 83f3cf6d 983cac4e

merge: 合并代码解决冲突

@@ -102,7 +102,7 @@ function copyFileUsageOssServer(cb) { @@ -102,7 +102,7 @@ function copyFileUsageOssServer(cb) {
102 const outPath = './build/oss' 102 const outPath = './build/oss'
103 103
104 for (const filePath of copyFileList) { 104 for (const filePath of copyFileList) {
105 - src(path.resolve(__dirname, filePath), {allowEmpty:true}) 105 + src(path.resolve(__dirname, filePath), { allowEmpty: true })
106 .pipe((dest(outPath))) 106 .pipe((dest(outPath)))
107 } 107 }
108 108
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 5
6 <head> 6 <head>
7 <!-- <title>Flowchart Maker &amp; Online Diagram Software</title>--> 7 <!-- <title>Flowchart Maker &amp; Online Diagram Software</title>-->
8 - <title>thingskit 云组态</title> 8 + <title>云组态</title>
9 <meta charset="utf-8"> 9 <meta charset="utf-8">
10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
11 <meta name="Description" 11 <meta name="Description"
@@ -22,26 +22,26 @@ @@ -22,26 +22,26 @@
22 <meta name="mobile-web-app-capable" content="yes"> 22 <meta name="mobile-web-app-capable" content="yes">
23 <meta name="theme-color" content="#d89000"> 23 <meta name="theme-color" content="#d89000">
24 24
25 - <link rel="stylesheet" href="./js/plugin/layui/css/layui.css?v=1681890411146"> 25 + <link rel="stylesheet" href="./js/plugin/layui/css/layui.css?v=1687228273654">
26 26
27 <!-- load configure file --> 27 <!-- load configure file -->
28 - <script src="./js/config/config.js?v=1681890411146"></script> 28 + <script src="./js/config/config.js?v=1687228273654"></script>
29 29
30 <!-- crypto-js --> 30 <!-- crypto-js -->
31 - <script src="./js/plugin/crypto-js/crypto-js.js?v=1681890411146"></script> 31 + <script src="./js/plugin/crypto-js/crypto-js.js?v=1687228273654"></script>
32 32
33 <!-- storage persistent --> 33 <!-- storage persistent -->
34 - <script src="./js/const/persistentStorage.js?v=1681890411146"></script> 34 + <script src="./js/const/persistentStorage.js?v=1687228273654"></script>
35 <!-- Global const --> 35 <!-- Global const -->
36 - <script src="./js/const/const.js?v=1681890411146"></script> 36 + <script src="./js/const/const.js?v=1687228273654"></script>
37 37
38 <!-- Axios --> 38 <!-- Axios -->
39 - <script src="./js/plugin/axios/axios.min.js?v=1681890411146"></script>  
40 - <script src="./js/plugin/axios/DefHttp.js?v=1681890411146"></script>  
41 - <script src="./js/api/index.js?v=1681890411146"></script> 39 + <script src="./js/plugin/axios/axios.min.js?v=1687228273654"></script>
  40 + <script src="./js/plugin/axios/DefHttp.js?v=1687228273654"></script>
  41 + <script src="./js/api/index.js?v=1687228273654"></script>
42 42
43 <!-- load script --> 43 <!-- load script -->
44 - <script src="./js/config/loadScript.js?v=1681890411146"></script> 44 + <script src="./js/config/loadScript.js?v=1687228273654"></script>
45 45
46 <!-- act editor --> 46 <!-- act editor -->
47 <!-- <script src="https://oss.yuntengcloud.com/iotdocs/thingskit-scada/ace.js"></script> --> 47 <!-- <script src="https://oss.yuntengcloud.com/iotdocs/thingskit-scada/ace.js"></script> -->
@@ -54,7 +54,7 @@ @@ -54,7 +54,7 @@
54 <!-- <script src="https://vjs.zencdn.net/7.10.2/video.min.js"></script> --> 54 <!-- <script src="https://vjs.zencdn.net/7.10.2/video.min.js"></script> -->
55 <!-- <script src="https://oss.yuntengcloud.com/iotdocs/thingskit-scada/video.min.js"></script> --> 55 <!-- <script src="https://oss.yuntengcloud.com/iotdocs/thingskit-scada/video.min.js"></script> -->
56 56
57 - <script src="./js/plugin/layui/layui.js?v=1681890411146"></script> 57 + <script src="./js/plugin/layui/layui.js?v=1687228273654"></script>
58 <!-- <link rel="stylesheet" href="https://cdnjs.loli.net/ajax/libs/layui/2.6.8/css/layui.min.css" 58 <!-- <link rel="stylesheet" href="https://cdnjs.loli.net/ajax/libs/layui/2.6.8/css/layui.min.css"
59 integrity="sha512-iQBJbsNHXUcgEIgWThd2dr8tOdKPvICwqjPEZYY81z3eMya44A5MiAqfWSCh+Ee1YzNYkdrI982Qhwgr8LEYOQ==" 59 integrity="sha512-iQBJbsNHXUcgEIgWThd2dr8tOdKPvICwqjPEZYY81z3eMya44A5MiAqfWSCh+Ee1YzNYkdrI982Qhwgr8LEYOQ=="
60 crossorigin="anonymous" referrerpolicy="no-referrer" /> 60 crossorigin="anonymous" referrerpolicy="no-referrer" />
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 crossorigin="anonymous" referrerpolicy="no-referrer"></script> --> 63 crossorigin="anonymous" referrerpolicy="no-referrer"></script> -->
64 64
65 <!-- 引入修改样式 --> 65 <!-- 引入修改样式 -->
66 - <link rel="stylesheet" href="./styles/formatChange.css?v=1681890411146"> 66 + <link rel="stylesheet" href="./styles/formatChange.css?v=1687228273654">
67 67
68 <script type="text/javascript"> 68 <script type="text/javascript">
69 /** 69 /**
@@ -306,7 +306,7 @@ @@ -306,7 +306,7 @@
306 var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') || 306 var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') ||
307 (hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net'); 307 (hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net');
308 308
309 - const releaseVersion = '1681890411146' 309 + const releaseVersion = '1687228273654'
310 const appMinSrc = Enable_OSS ? `${OSS_Prefix}app.min.js?v=${releaseVersion}` : `js/app.min.js?v=${releaseVersion}` 310 const appMinSrc = Enable_OSS ? `${OSS_Prefix}app.min.js?v=${releaseVersion}` : `js/app.min.js?v=${releaseVersion}`
311 function loadAppJS() { 311 function loadAppJS() {
312 mxscript(appMinSrc, function () { 312 mxscript(appMinSrc, function () {
@@ -349,17 +349,17 @@ @@ -349,17 +349,17 @@
349 } 349 }
350 }; 350 };
351 </script> 351 </script>
352 - <link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm">  
353 - <link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png"> 352 + <!-- <link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm"> -->
  353 + <!-- <link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png"> -->
354 <!-- <link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">--> 354 <!-- <link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">-->
355 <link rel="icon" type="image/png" sizes="32x32" href="images/logo-32x32.png"> 355 <link rel="icon" type="image/png" sizes="32x32" href="images/logo-32x32.png">
356 - <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png"> 356 + <!-- <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png"> -->
357 <!-- <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">--> 357 <!-- <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">-->
358 <link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#d89000"> 358 <link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#d89000">
359 <link rel="stylesheet" type="text/css" href="styles/grapheditor.css"> 359 <link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
360 - <link rel="preconnect" href="https://storage.googleapis.com"> 360 + <!-- <link rel="preconnect" href="https://storage.googleapis.com">
361 <link rel="canonical" href="https://app.diagrams.net"> 361 <link rel="canonical" href="https://app.diagrams.net">
362 - <link rel="manifest" href="images/manifest.json"> 362 + <link rel="manifest" href="images/manifest.json"> -->
363 <!-- <link rel="shortcut icon" href="favicon.ico">--> 363 <!-- <link rel="shortcut icon" href="favicon.ico">-->
364 <link rel="shortcut icon" href="images/logo-16x16.ico"> 364 <link rel="shortcut icon" href="images/logo-16x16.ico">
365 <style type="text/css"> 365 <style type="text/css">
@@ -591,7 +591,6 @@ @@ -591,7 +591,6 @@
591 <script> 591 <script>
592 592
593 function isMobile() { 593 function isMobile() {
594 - // http://192.168.10.106:8083/thingskit-drawio/?configurationId=5c683cb0-48fb-4062-aabf-a5093eea0446&userId=1262f76c-ec5d-4091-85a1-52c552915d4e&lightbox=1  
595 var urlParams = (function () { 594 var urlParams = (function () {
596 var result = new Object(); 595 var result = new Object();
597 var params = window.location.search.slice(1).split('&'); 596 var params = window.location.search.slice(1).split('&');
@@ -649,7 +648,7 @@ @@ -649,7 +648,7 @@
649 left: 'center', 648 left: 'center',
650 top: 'center', 649 top: 'center',
651 style: { 650 style: {
652 - text: platfromInfo.name || 'ThingsKit Scada', 651 + text: platfromInfo.name || 'Scada',
653 fontSize: isMobile() ? 20 : 70, 652 fontSize: isMobile() ? 20 : 70,
654 fontWeight: 'bold', 653 fontWeight: 'bold',
655 lineDash: [0, 200], 654 lineDash: [0, 200],
@@ -207,7 +207,7 @@ class ConfigurationNodeApi { @@ -207,7 +207,7 @@ class ConfigurationNodeApi {
207 * @returns 207 * @returns
208 */ 208 */
209 static getDictionaryValue(dictCode) { 209 static getDictionaryValue(dictCode) {
210 - return defHttp.post('/yt/dict_item/find', {dictCode}) 210 + return defHttp.post('/yt/dict_item/find', { dictCode })
211 } 211 }
212 212
213 /** 213 /**
@@ -235,4 +235,24 @@ class ConfigurationNodeApi { @@ -235,4 +235,24 @@ class ConfigurationNodeApi {
235 static closeFlvPlay(url, browserId) { 235 static closeFlvPlay(url, browserId) {
236 return defHttp.get(`/yt/rtsp/closeFlv?url=${encodeURIComponent(url)}&browserId=${browserId}`) 236 return defHttp.get(`/yt/rtsp/closeFlv?url=${encodeURIComponent(url)}&browserId=${browserId}`)
237 } 237 }
  238 +
  239 + /**
  240 + *
  241 + * @typedef {object} AlarmListRequestParamsType
  242 + * @property { number } page
  243 + * @property { number } pageSize
  244 + * @property { string } status
  245 + * @property { string } alarmType
  246 + * @property { string } severity
  247 + * @property { string[] } deviceIds
  248 + * @property { string } organizationId
  249 + * @property { string } deviceName
  250 + * @property { string } startTime
  251 + * @property { string } endTime
  252 + * @param {AlarmListRequestParamsType} params
  253 + * @returns
  254 + */
  255 + static getAlarmList(params) {
  256 + return defHttp.post('/yt/alarm/configuration/page', params)
  257 + }
238 } 258 }
@@ -4094,6 +4094,7 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) { @@ -4094,6 +4094,7 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) {
4094 return pageSizeControl.apply(this, arguments) 4094 return pageSizeControl.apply(this, arguments)
4095 } 4095 }
4096 Editor.configurationName = response.configurationName + ".drawio"; 4096 Editor.configurationName = response.configurationName + ".drawio";
  4097 + document.title = response.configurationName
4097 if (response.configurationContentList.length > 0) { 4098 if (response.configurationContentList.length > 0) {
4098 response.configurationContentList.forEach((item) => { 4099 response.configurationContentList.forEach((item) => {
4099 Editor.configurationContentId = item.id; 4100 Editor.configurationContentId = item.id;
@@ -1703,6 +1703,8 @@ EditorUi.prototype.createPageInsertTab = function() @@ -1703,6 +1703,8 @@ EditorUi.prototype.createPageInsertTab = function()
1703 mxEvent.addListener(tab, 'click', mxUtils.bind(this, function(evt) 1703 mxEvent.addListener(tab, 'click', mxUtils.bind(this, function(evt)
1704 { 1704 {
1705 this.insertPage(); 1705 this.insertPage();
  1706 + var formats = PageSetupDialog.getFormats()
  1707 + this.editor.graph.pageFormat = formats[0].format
1706 mxEvent.consume(evt); 1708 mxEvent.consume(evt);
1707 })); 1709 }));
1708 1710
@@ -272,8 +272,17 @@ @@ -272,8 +272,17 @@
272 /** 272 /**
273 * @description 图片组件 273 * @description 图片组件
274 */ 274 */
275 - IMAGE: 'image'  
276 - 275 + IMAGE: 'image',
  276 +
  277 + /**
  278 + * @description 流量计
  279 + */
  280 + FLOWMETER: 'flowmeter',
  281 +
  282 + /**
  283 + * @description 告警列表
  284 + */
  285 + ALARM_LIST: 'alarmList'
277 } 286 }
278 287
279 Sidebar.prototype.enumComponentTypeValue = { 288 Sidebar.prototype.enumComponentTypeValue = {
@@ -364,7 +373,17 @@ @@ -364,7 +373,17 @@
364 /** 373 /**
365 * @description 运行于停止 374 * @description 运行于停止
366 */ 375 */
367 - RUNNING_AND_STOP: 'runningAndStop' 376 + RUNNING_AND_STOP: 'runningAndStop',
  377 +
  378 + /**
  379 + * @description 流量计
  380 + */
  381 + FLOWMETER_PANEL: 'flowmeter',
  382 +
  383 + /**
  384 + * @description 告警列表
  385 + */
  386 + ALARM_LIST_PANEL: 'alarmListPanel'
368 } 387 }
369 388
370 /** 389 /**
@@ -721,8 +740,8 @@ @@ -721,8 +740,8 @@
721 //更多图形,显示出来的的标题跟id,同时包括图片 740 //更多图形,显示出来的的标题跟id,同时包括图片
722 741
723 // TODO thingsKit 设置数据绑定展示面板 742 // TODO thingsKit 设置数据绑定展示面板
724 - const { LINE_CHART_EXPAND, BAR_CHART_EXPAND, DASHBOARD_CHART_EXPAND, DYNAMIC_EFFECT, DATA_SOURCE, VAR_IMAGE, INTERACTION, VIDEO: VIDEO_PANEL, SWITCH_STATE_SETTING, ONLY_SINGLE_EVENT, RUNNING_AND_STOP } = this.enumPermissionPanel  
725 - const { LINE, LINE_CHART, REAL_TIME, TITLE, VARIABLE, DEFAULT, BAR_CHART, VIDEO, SWITCH, PARAMS_SETTING_BUTTON, DASHBOARD_CHART, IMAGE } = this.enumComponentType 743 + const { LINE_CHART_EXPAND, BAR_CHART_EXPAND, DASHBOARD_CHART_EXPAND, DYNAMIC_EFFECT, DATA_SOURCE, VAR_IMAGE, INTERACTION, VIDEO: VIDEO_PANEL, SWITCH_STATE_SETTING, ONLY_SINGLE_EVENT, RUNNING_AND_STOP, FLOWMETER_PANEL, ALARM_LIST_PANEL } = this.enumPermissionPanel
  744 + const { LINE, LINE_CHART, REAL_TIME, TITLE, VARIABLE, DEFAULT, BAR_CHART, VIDEO, SWITCH, PARAMS_SETTING_BUTTON, DASHBOARD_CHART, IMAGE, FLOWMETER, ALARM_LIST } = this.enumComponentType
726 this.setComponentPermission(LINE, [RUNNING_AND_STOP, DYNAMIC_EFFECT]) 745 this.setComponentPermission(LINE, [RUNNING_AND_STOP, DYNAMIC_EFFECT])
727 this.setComponentPermission(DEFAULT, [DYNAMIC_EFFECT]) 746 this.setComponentPermission(DEFAULT, [DYNAMIC_EFFECT])
728 this.setComponentPermission(REAL_TIME, [DYNAMIC_EFFECT]) 747 this.setComponentPermission(REAL_TIME, [DYNAMIC_EFFECT])
@@ -736,6 +755,8 @@ @@ -736,6 +755,8 @@
736 this.setComponentPermission(SWITCH, [DATA_SOURCE, SWITCH_STATE_SETTING]) 755 this.setComponentPermission(SWITCH, [DATA_SOURCE, SWITCH_STATE_SETTING])
737 this.setComponentPermission(PARAMS_SETTING_BUTTON, [DATA_SOURCE, ONLY_SINGLE_EVENT]) 756 this.setComponentPermission(PARAMS_SETTING_BUTTON, [DATA_SOURCE, ONLY_SINGLE_EVENT])
738 this.setComponentPermission(IMAGE, [DATA_SOURCE]) 757 this.setComponentPermission(IMAGE, [DATA_SOURCE])
  758 + this.setComponentPermission(FLOWMETER, [DATA_SOURCE, FLOWMETER_PANEL])
  759 + this.setComponentPermission(ALARM_LIST, [ALARM_LIST_PANEL])
739 760
740 var thingskitEntries = [ 761 var thingskitEntries = [
741 { title: mxResources.get('general'), id: 'general', image: IMAGE_PATH + '/sidebar-general.png' }, 762 { title: mxResources.get('general'), id: 'general', image: IMAGE_PATH + '/sidebar-general.png' },
@@ -1268,24 +1289,24 @@ @@ -1268,24 +1289,24 @@
1268 this.addBasicComponentsPalette(); 1289 this.addBasicComponentsPalette();
1269 // 控制元件 1290 // 控制元件
1270 this.addControlComponentsPalette(); 1291 this.addControlComponentsPalette();
  1292 + // 图表
  1293 + this.addChartsPalette();
  1294 + // 按钮
  1295 + this.addButtonPalette();
  1296 + // 灯
  1297 + this.addLightPalette();
1271 // 发动机 1298 // 发动机
1272 this.addEnginePalette(); 1299 this.addEnginePalette();
1273 // 阀门 1300 // 阀门
1274 this.addValvePalette(); 1301 this.addValvePalette();
1275 - // 图表  
1276 - this.addChartsPalette();  
1277 // 风机 1302 // 风机
1278 this.addFanPalette(); 1303 this.addFanPalette();
1279 // 污水处理 1304 // 污水处理
1280 this.addSewagePalette(); 1305 this.addSewagePalette();
1281 // 管道 1306 // 管道
1282 this.addConduitPalette(); 1307 this.addConduitPalette();
1283 - // 按钮  
1284 - this.addButtonPalette();  
1285 // 仪表 1308 // 仪表
1286 this.addInstrumentPalette(); 1309 this.addInstrumentPalette();
1287 - // 灯  
1288 - this.addLightPalette();  
1289 // 空气 1310 // 空气
1290 this.addAirPalette(false); 1311 this.addAirPalette(false);
1291 // 过滤器 1312 // 过滤器
@@ -65,6 +65,17 @@ @@ -65,6 +65,17 @@
65 this.setCellAttributes(cell, { [basicAttr.COMPONENT_TYPE]: componentType.IMAGE }) 65 this.setCellAttributes(cell, { [basicAttr.COMPONENT_TYPE]: componentType.IMAGE })
66 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '图片'); 66 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '图片');
67 })), 67 })),
  68 +
  69 + this.addEntry(this.getTagsForStencil(gn, '告警列表', 'basic').join(' '), mxUtils.bind(this, function () {
  70 + const id = AlarmListComponent.genId()
  71 + const template = AlarmListComponent.createAlarmList(null, 280, 200, id)
  72 + const cell = new mxCell(template, new mxGeometry(0, 0, 280, 200), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;');
  73 + cell.setVertex(true)
  74 + const { UUID } = AlarmListComponent.getAttributeKeys()
  75 + this.setCellAttributes(cell, { [basicAttr.COMPONENT_TYPE]: componentType.ALARM_LIST, [UUID]: id })
  76 + console.log(cell)
  77 + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '告警列表');
  78 + })),
68 ]; 79 ];
69 80
70 this.addPaletteFunctions(dt, '基础元件', true, fns); 81 this.addPaletteFunctions(dt, '基础元件', true, fns);
@@ -77,6 +88,13 @@ @@ -77,6 +88,13 @@
77 return cell.getAttribute(basicAttr.COMPONENT_TYPE) === componentType.VIDEO 88 return cell.getAttribute(basicAttr.COMPONENT_TYPE) === componentType.VIDEO
78 } 89 }
79 90
  91 +
  92 + Sidebar.prototype.isAlarmList = function (cell) {
  93 + const basicAttr = Sidebar.prototype.enumCellBasicAttribute
  94 + const componentType = Sidebar.prototype.enumComponentType
  95 + return cell.getAttribute(basicAttr.COMPONENT_TYPE) === componentType.ALARM_LIST
  96 + }
  97 +
80 /** 98 /**
81 * @description charts cell发生resize时改变charts size 99 * @description charts cell发生resize时改变charts size
82 * @type {Function} 100 * @type {Function}
@@ -87,6 +105,12 @@ @@ -87,6 +105,12 @@
87 const { width, height } = rect 105 const { width, height } = rect
88 cell.setAttribute('label', createVideoTemplate(width, height)) 106 cell.setAttribute('label', createVideoTemplate(width, height))
89 } 107 }
  108 +
  109 + if (AlarmListComponent.isAlarmList(cell)) {
  110 + const { width, height } = rect
  111 +
  112 + cell.setAttribute('label', AlarmListComponent.createAlarmList(null, width, height))
  113 + }
90 cellResized.apply(this, arguments) 114 cellResized.apply(this, arguments)
91 } 115 }
92 116
@@ -128,7 +152,7 @@ @@ -128,7 +152,7 @@
128 const allDateNode = document.querySelectorAll('.thingKit-component__real-time .real-time__date') 152 const allDateNode = document.querySelectorAll('.thingKit-component__real-time .real-time__date')
129 for (const time of allTimeNode) { 153 for (const time of allTimeNode) {
130 const date = new Date() 154 const date = new Date()
131 - time.innerHTML = `${date.getHours() < 10 ? '0' : ''}${date.getHours()}:${date.getMinutes() < 10 ? '0' : ''}${date.getMinutes()}:${date.getSeconds() < 10 ? '0' : ''}${date.getSeconds()}` 155 + time.innerHTML = [date.getHours(), date.getMinutes(), date.getSeconds()].map(item => item.toString().padStart(2, 0)).join(':')
132 } 156 }
133 157
134 for (const date of allDateNode) { 158 for (const date of allDateNode) {
@@ -138,4 +162,123 @@ @@ -138,4 +162,123 @@
138 } 162 }
139 163
140 initRealTimeComponent() 164 initRealTimeComponent()
  165 +
141 })(); 166 })();
  167 +
  168 +
  169 +function AlarmListComponent() {
  170 +
  171 +}
  172 +
  173 +/**
  174 + *
  175 + * @param {{deviceName: string, status: string, startTs: string}[]} list
  176 + * @param {number} width
  177 + * @param {number} height
  178 + * @param {string} id
  179 + * @returns
  180 + */
  181 +AlarmListComponent.createAlarmList = function (list, width = 280, height = 200, id) {
  182 +
  183 + list = list || Array.from({ length: 10 }, ((_, index) => ({
  184 + deviceName: `示例设备${index + 1}`, startTs: Date.now(), status: [
  185 + "CLEARED_UNACK",
  186 + "ACTIVE_UNACK",
  187 + "CLEARED_ACK",
  188 + "ACTIVE_ACK"
  189 + ][index % 4]
  190 + })))
  191 +
  192 + id = id || AlarmListComponent.genId()
  193 +
  194 + var template = `
  195 + <div id="${id}" class="alarm-list" style="overflow-y: auto; overflow-x: hidden; width: ${width}px; height: ${height}px;">
  196 + <div class="list-wrapper" style="font-size: 12px;" data-auto-scroll>
  197 + ${AlarmListComponent.createAlarmItem(list)}
  198 + </div>
  199 + </div>
  200 + `
  201 + return template.replace(/\n/g, '')
  202 +}
  203 +
  204 +/**
  205 + *
  206 + * @param {{deviceName: string, status: string, startTs: string}[] | {deviceName: string, status: string, startTs: string}} record
  207 + * @returns
  208 + */
  209 +AlarmListComponent.createAlarmItem = function (record) {
  210 + if (record && !Array.isArray(record)) {
  211 + record = [record]
  212 + }
  213 +
  214 + var itemHeight = 50
  215 +
  216 + function formatTime(time) {
  217 + try {
  218 + var date = new Date(time)
  219 + var year = date.getFullYear()
  220 + var month = date.getMonth() + 1
  221 + var day = date.getDate()
  222 + var hour = date.getHours()
  223 + var minute = date.getMinutes()
  224 + var second = date.getSeconds()
  225 + return `${year}-${month.toString().padStart(2, 0)}-${day.toString().padStart(2, 0)} ${hour.toString().padStart(2, 0)}:${minute.toString().padStart(2, 0)}:${second.toString().padStart(2, 0)}`
  226 + } catch (error) {
  227 + return '暂无时间 '
  228 + }
  229 + }
  230 +
  231 + return (record || []).map(item => {
  232 + var { deviceName, status, startTs } = item || {}
  233 +
  234 + var stateStyle = {
  235 + CLEARED_UNACK: 'color: #cf1322;background: #fff1f0;border-color: #ffa39e;',
  236 + ACTIVE_UNACK: 'color: #d46b08;background: #fff7e6;border-color: #ffd591;',
  237 + CLEARED_ACK: 'color: #08979c;background: #e6fffb;border-color: #87e8de;',
  238 + ACTIVE_ACK: 'color: #389e0d;background: #f6ffed;border-color: #b7eb8f;',
  239 + }
  240 +
  241 + var stateName = {
  242 + CLEARED_UNACK: '清除未确认',
  243 + ACTIVE_UNACK: '激活未确认',
  244 + CLEARED_ACK: '清除已确认',
  245 + ACTIVE_ACK: '激活已确认',
  246 + }
  247 +
  248 + var template = `
  249 + <div class="alarm-list-item" style="height: ${itemHeight}px; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; border-bottom: 1px solid black; text-align: left; min-width: 280px; width: 100%;">
  250 + <div style="padding: 0 10px;">
  251 + <span style="margin-right: 5px;">设备:</span>
  252 + <span>${deviceName}</span>
  253 + </div>
  254 + <div style="padding: 5px 10px;">
  255 + <span style="margin-right: 5px;">时间:</span>
  256 + <span>${formatTime(startTs)}</span>
  257 + <span style="padding: 5px; border-radius: 5px; margin-left: 5px; border: 1px solid; font-size: 12px; ${stateStyle[status]}">${stateName[status]}</span>
  258 + </div>
  259 + </div>`
  260 + return template
  261 + }).join('').replace(/\n/g, '')
  262 +}
  263 +
  264 +AlarmListComponent.genId = function () {
  265 + return `alarm-list-${Number(Math.random().toString().substring(2)).toString(16)}`
  266 +}
  267 +
  268 +AlarmListComponent.isAlarmList = function (cell) {
  269 + const basicAttr = Sidebar.prototype.enumCellBasicAttribute
  270 + const componentType = Sidebar.prototype.enumComponentType
  271 + return cell.getAttribute(basicAttr.COMPONENT_TYPE) === componentType.ALARM_LIST
  272 +}
  273 +
  274 +AlarmListComponent.getAttributeKeys = function () {
  275 + return {
  276 + UUID: 'uuid',
  277 + }
  278 +}
  279 +
  280 +AlarmListComponent.setAutoScroll = function (cell) {
  281 + // RAFSetInterval(() => {
  282 + // console.log(cell)
  283 + // }, 2000)
  284 +}
@@ -89,6 +89,11 @@ @@ -89,6 +89,11 @@
89 })); 89 }));
90 }) 90 })
91 91
  92 + this.setVariableImageLib('switch', '开关', [
  93 + { name: 'switch-on', path: '/images/thingskit/switch-on.svg', staticPath: `${Proxy_Prefix}/images/thingskit/switch-on.svg` },
  94 + { name: 'switch-off', path: '/images/thingskit/switch-on.svg', staticPath: `${Proxy_Prefix}/images/thingskit/switch-off.svg` },
  95 + ])
  96 +
92 this.setVariableImageLib(dt, label, lib) 97 this.setVariableImageLib(dt, label, lib)
93 98
94 this.addPaletteFunctions(dt, label, false, fns); 99 this.addPaletteFunctions(dt, label, false, fns);
@@ -42,10 +42,29 @@ @@ -42,10 +42,29 @@
42 CHART_IMG_PLACEHOLDER_SIZE: 30, 42 CHART_IMG_PLACEHOLDER_SIZE: 30,
43 } 43 }
44 44
  45 + Sidebar.prototype.enumFlowmeterAttr = {
  46 + WIDTH: 'width',
  47 + HEIGHT: 'height',
  48 + VALUE: 'value',
  49 + TYPE: 'type',
  50 + UUID: 'uuid',
  51 + BG_COLOR: 'bgColor',
  52 + WAVE_FIRST_COLOR: 'waveFirstColor',
  53 + WAVE_SECOND_COLOR: 'waveSecondColor',
  54 + WAVE_THIRD_COLOR: 'waveThirdColor',
  55 + }
  56 +
  57 + Sidebar.prototype.enumFlowmeterType = {
  58 + RECT: 'rect',
  59 + CIRCLE: 'circle',
  60 + THERMOMETER: 'thermometer'
  61 + }
  62 +
45 // Adds Atlassian shapes 63 // Adds Atlassian shapes
46 // 图表 64 // 图表
47 Sidebar.prototype.addChartsPalette = function () { 65 Sidebar.prototype.addChartsPalette = function () {
48 this.chartsComponentInit() 66 this.chartsComponentInit()
  67 +
49 const self = this 68 const self = this
50 const componentType = this.enumComponentType 69 const componentType = this.enumComponentType
51 const { DATA_SOURCE, DYNAMIC_EFFECT, DISPLAY_TYPE } = this.enumPermissionPanel 70 const { DATA_SOURCE, DYNAMIC_EFFECT, DISPLAY_TYPE } = this.enumPermissionPanel
@@ -74,7 +93,19 @@ @@ -74,7 +93,19 @@
74 const id = self.generatorChartsId() 93 const id = self.generatorChartsId()
75 const cell = self.generatorCell(id, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumConst.CHART_IMG_PLACEHOLDER_SIZE, componentType.DASHBOARD_CHART, `${Proxy_Prefix}/images/thingskit/dashboard-chart.png`) 94 const cell = self.generatorCell(id, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumConst.CHART_IMG_PLACEHOLDER_SIZE, componentType.DASHBOARD_CHART, `${Proxy_Prefix}/images/thingskit/dashboard-chart.png`)
76 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '仪表盘'); 95 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '仪表盘');
77 - })) 96 + })),
  97 + this.addEntry('flowmeter-circle', mxUtils.bind(this, function () {
  98 + const cell = self.generateFlowmeterCell(100, 100, Sidebar.prototype.enumFlowmeterType.CIRCLE)
  99 + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '流量计');
  100 + })),
  101 + this.addEntry('flowmeter-rect', mxUtils.bind(this, function () {
  102 + const cell = self.generateFlowmeterCell(100, 100, Sidebar.prototype.enumFlowmeterType.RECT)
  103 + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '流量计');
  104 + })),
  105 + this.addEntry('flowmeter-thermometer', mxUtils.bind(this, function () {
  106 + const cell = self.generateFlowmeterCell(100, 100, Sidebar.prototype.enumFlowmeterType.THERMOMETER)
  107 + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '流量计');
  108 + })),
78 ]; 109 ];
79 110
80 this.addPaletteFunctions('charts', '图表', false, fns); 111 this.addPaletteFunctions('charts', '图表', false, fns);
@@ -210,7 +241,8 @@ @@ -210,7 +241,8 @@
210 Sidebar.prototype.addClickHandler = function (elt, ds, cells) { 241 Sidebar.prototype.addClickHandler = function (elt, ds, cells) {
211 const cell = cells[0] 242 const cell = cells[0]
212 const cellValue = cell.value 243 const cellValue = cell.value
213 - const validate = cellValue && cellValue.nodeName === 'UserObject' && this.isChartCell(cell) 244 + const validateChart = cellValue && cellValue.nodeName === 'UserObject' && this.isChartCell(cell)
  245 + const validateFlowmeter = cellValue && cellValue.nodeName === 'UserObject' && this.isFlowmeter(cell)
214 246
215 /** 247 /**
216 * @description 拓展Sidebar鼠标按下 248 * @description 拓展Sidebar鼠标按下
@@ -218,7 +250,7 @@ @@ -218,7 +250,7 @@
218 */ 250 */
219 const mouseDown = ds.mouseDown 251 const mouseDown = ds.mouseDown
220 ds.mouseDown = function (evt) { 252 ds.mouseDown = function (evt) {
221 - if (validate) { 253 + if (validateChart) {
222 const id = self.generatorChartsId() 254 const id = self.generatorChartsId()
223 const geo = Object.assign(graph.model.getGeometry(cell), { width: 400, height: 400 }) 255 const geo = Object.assign(graph.model.getGeometry(cell), { width: 400, height: 400 })
224 cell.setGeometry(geo) 256 cell.setGeometry(geo)
@@ -227,6 +259,14 @@ @@ -227,6 +259,14 @@
227 self.graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, enumConst.CHART_CELL_DEFAULT_HEIGHT); 259 self.graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, enumConst.CHART_CELL_DEFAULT_HEIGHT);
228 self.graph.setAttributeForCell(cell, 'label', self.createChartsNode(id)) 260 self.graph.setAttributeForCell(cell, 'label', self.createChartsNode(id))
229 } 261 }
  262 +
  263 + if (validateFlowmeter) {
  264 + const { UUID, TYPE } = getFlowmeterAttrKey()
  265 + const id = uuid()
  266 + const type = self.graph.getAttributeForCell(cell, TYPE)
  267 + self.graph.setAttributeForCell(cell, UUID, id);
  268 + self.graph.setAttributeForCell(cell, 'label', Sidebar.prototype.generateFlowmeterTemplate(id, type));
  269 + }
230 mouseDown.apply(this, arguments) 270 mouseDown.apply(this, arguments)
231 }; 271 };
232 272
@@ -239,7 +279,7 @@ @@ -239,7 +279,7 @@
239 try { 279 try {
240 mouseUp.apply(this, arguments) 280 mouseUp.apply(this, arguments)
241 } finally { 281 } finally {
242 - if (validate) { 282 + if (validateChart) {
243 const id = self.getCellId(cell) 283 const id = self.getCellId(cell)
244 const chartType = cell.getAttribute(basicAttr.COMPONENT_TYPE) 284 const chartType = cell.getAttribute(basicAttr.COMPONENT_TYPE)
245 self.generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType) 285 self.generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)
@@ -332,6 +372,7 @@ @@ -332,6 +372,7 @@
332 const { width, height, chartType } = domIdMapping.get(id) 372 const { width, height, chartType } = domIdMapping.get(id)
333 Sidebar.prototype.generatorEChartInstance(id, width, height, chartType) 373 Sidebar.prototype.generatorEChartInstance(id, width, height, chartType)
334 } 374 }
  375 + // Sidebar.prototype.initFlowmeter(graph)
335 } 376 }
336 377
337 /** 378 /**
@@ -357,6 +398,7 @@ @@ -357,6 +398,7 @@
357 refresh.apply(this, arguments) 398 refresh.apply(this, arguments)
358 if (!arguments.length) { 399 if (!arguments.length) {
359 Sidebar.prototype.initChartInstance(this) 400 Sidebar.prototype.initChartInstance(this)
  401 + Sidebar.prototype.resetFlowmeter(this)
360 } 402 }
361 } 403 }
362 404
@@ -367,6 +409,7 @@ @@ -367,6 +409,7 @@
367 EditorUi.prototype.setFileData = function () { 409 EditorUi.prototype.setFileData = function () {
368 setFileData.apply(this, arguments) 410 setFileData.apply(this, arguments)
369 Sidebar.prototype.initChartInstance(this.editor.graph) 411 Sidebar.prototype.initChartInstance(this.editor.graph)
  412 + Sidebar.prototype.resetFlowmeter(this.editor.graph)
370 } 413 }
371 414
372 // const selectPage = EditorUi.prototype.selectPage 415 // const selectPage = EditorUi.prototype.selectPage
@@ -496,5 +539,418 @@ @@ -496,5 +539,418 @@
496 } 539 }
497 } 540 }
498 541
  542 + function uuid() {
  543 + return Number(Math.random().toString().substring(2)).toString(32)
  544 + }
  545 +
  546 + /**
  547 + * @description 生成流量计模版
  548 + */
  549 + Sidebar.prototype.generateFlowmeterTemplate = function (id = uuid(), type) {
  550 + const flowmeterType = Sidebar.prototype.enumFlowmeterType
  551 + const typeGenFn = {
  552 + [flowmeterType.CIRCLE]: Sidebar.prototype.generateFlowmeterCircle,
  553 + [flowmeterType.RECT]: Sidebar.prototype.generateFlowmeterRect,
  554 + [flowmeterType.THERMOMETER]: Sidebar.prototype.generateFlowmeterThermometer,
  555 + }
  556 + const template = `<div class="flowmeter" style="transform: scale(0.9);font-size: 0;" id="${id}">${typeGenFn[type] && typeGenFn[type]()}</div>`
  557 + return template
  558 + }
  559 +
  560 + Sidebar.prototype.generateFlowmeterCell = function (width = 100, height = 100, type) {
  561 + const id = uuid()
  562 + const template = Sidebar.prototype.generateFlowmeterTemplate(id, type)
  563 + const cell = new mxCell(template, new mxGeometry(0, 0, width, height), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;');
  564 + cell.setVertex(true)
  565 + const componentType = Sidebar.prototype.enumComponentType
  566 + const { WIDTH, HEIGHT, VALUE, TYPE, COMPONENT_TYPE, UUID } = getFlowmeterAttrKey()
  567 + this.graph.setAttributeForCell(cell, WIDTH, width);
  568 + this.graph.setAttributeForCell(cell, HEIGHT, height);
  569 + this.graph.setAttributeForCell(cell, VALUE, 20);
  570 + this.graph.setAttributeForCell(cell, TYPE, type);
  571 + this.graph.setAttributeForCell(cell, UUID, id);
  572 + this.graph.setAttributeForCell(cell, COMPONENT_TYPE, componentType.FLOWMETER);
  573 + return cell
  574 + }
  575 +
  576 + Sidebar.prototype.generateFlowmeterRect = function () {
  577 + return `
  578 + <svg class="waves-rect" viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"
  579 + xmlns:xlink="http://www.w3.org/1999/xlink"
  580 + style="--width: 100; --height: 100; --value: 20; --play-state: running; --full-flag: clamp(0, calc(100 - var(--value)), 1); --over-min-flag: clamp(0, calc(calc(var(--value) - 1) * -1), 1);">
  581 + <defs>
  582 + <style>
  583 + .waves-rect {
  584 + width: calc(var(--width) * 1px);
  585 + height: calc(var(--height) * 1px);
  586 + }
  587 +
  588 + @keyframes move {
  589 + from {
  590 + transform: translate(-90px, 0%);
  591 + }
  592 +
  593 + to {
  594 + transform: translate(85px, 0%);
  595 + }
  596 + }
  597 +
  598 + .wave {
  599 + animation: move 3s linear infinite;
  600 + animation-play-state: running;
  601 + }
  602 +
  603 + .wave:nth-child(1) {
  604 + animation-delay: -2s;
  605 + animation-duration: 9s;
  606 + }
  607 +
  608 + .wave:nth-child(2) {
  609 + animation-delay: -4s;
  610 + animation-duration: 6s;
  611 + }
  612 +
  613 + .wave:nth-child(3) {
  614 + animation-delay: -6s;
  615 + animation-duration: 3s;
  616 + }
  617 +
  618 + .waves-rect>g+rect {
  619 + transform: translateY(calc(calc(100 - var(--value)) * var(--full-flag) * 1% + var(--full-flag) * 15%));
  620 + transition: transform linear 1s;
  621 + }
  622 +
  623 + .height {
  624 + transform: translateY(calc(var(--value) * -1% - 10% + var(--over-min-flag) * 10%));
  625 + transition: transform linear 1s;
  626 + }
  627 +
  628 + .waves-rect .text {
  629 + display: flex;
  630 + justify-content: center;
  631 + align-items: center;
  632 + width: 100%;
  633 + height: 100%;
  634 + color: #fff;
  635 + font-size: 18px;
  636 + font-weight: 700;
  637 + }
  638 +
  639 + .waves-rect .text::after {
  640 + counter-reset: value var(--value);
  641 + content: counter(value) ' %';
  642 + }
  643 + </style>
  644 + <path id="wave" d="M-160 118c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v100h-352z" />
  645 + </defs>
  646 + <rect class="bgColor" x="0" y="0" width="100" height="100" fill="#8badcb"></rect>
  647 + <g class="height">
  648 + <use class="wave waveFirst" xlink:href="#wave" fill="#4579e2" x="0" y="0"></use>
  649 + <use class="wave waveSecond" xlink:href="#wave" fill="#3461c1" x="0" y="2"></use>
  650 + <use class="wave waveThird" xlink:href="#wave" fill="#2d55aa" x="0" y="4"></use>
  651 + </g>
  652 + <rect class="waveThird" x="0" y="0" width="100" height="100" fill="#2d55aa"></rect>
  653 + <foreignObject x="0" y="0" width="100" height="100" text-anchor="middle" dominant-baseline="middle">
  654 + <div xmlns="http://www.w3.org/1999/xhtml" class="text"></div>
  655 + </foreignObject>
  656 +</svg>
  657 + `.replace(/\n/g, '')
  658 + }
  659 +
  660 + Sidebar.prototype.generateFlowmeterCircle = function () {
  661 + return `
  662 + <svg class="waves-circle" viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"
  663 + xmlns:xlink="http://www.w3.org/1999/xlink"
  664 + style="--width: 100; --height: 100; --value: 20; --play-state: running; --full-flag: clamp(0, calc(100 - var(--value)), 1); --over-min-flag: clamp(0, calc(calc(var(--value) - 1) * -1), 1)">
  665 + <style>
  666 + .waves-circle {
  667 + width: calc(min(var(--width), var(--height)) * 1px);
  668 + height: calc(min(var(--width), var(--height)) * 1px);
  669 + clip-path: circle(calc(min(var(--width), var(--height)) / 2 * 1px));
  670 + }
  671 +
  672 + @keyframes move {
  673 + from {
  674 + transform: translate(-90px, 0%);
  675 + }
  676 +
  677 + to {
  678 + transform: translate(85px, 0%);
  679 + }
  680 + }
  681 +
  682 + .wave {
  683 + animation: move 3s linear infinite;
  684 + animation-play-state: var(--play-state);
  685 + }
  686 +
  687 + .wave:nth-child(1) {
  688 + animation-delay: -2s;
  689 + animation-duration: 9s;
  690 + }
  691 +
  692 + .wave:nth-child(2) {
  693 + animation-delay: -4s;
  694 + animation-duration: 6s;
  695 + }
  696 +
  697 + .wave:nth-child(3) {
  698 + animation-delay: -6s;
  699 + animation-duration: 3s;
  700 + }
  701 +
  702 + .height {
  703 + transform: translateY(calc(var(--value) * -1% - 10% + var(--over-min-flag) * 10%));
  704 + transition: transform linear 1s;
  705 + }
  706 +
  707 + .waves-circle>g+rect {
  708 + transform: translateY(calc(calc(100 - var(--value)) * var(--full-flag) * 1% + var(--full-flag) * 15%));
  709 + transition: transform linear 1s;
  710 + }
  711 +
  712 + .waves-circle .text {
  713 + display: flex;
  714 + justify-content: center;
  715 + align-items: center;
  716 + width: 100%;
  717 + height: 100%;
  718 + color: #fff;
  719 + font-size: 18px;
  720 + font-weight: 700;
  721 + }
  722 +
  723 + .waves-circle .text::after {
  724 + counter-reset: value var(--value);
  725 + content: counter(value) ' %';
  726 + }
  727 + </style>
  728 + <defs>
  729 + <path id="wave" d="M-160 118c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v100h-352z" />
  730 + </defs>
  731 + <circle class="bgColor" cx="50" cy="50" r="50" fill="#8badcb" />
  732 + <g class="height">
  733 + <use class="wave waveFirst" xlink:href="#wave" fill="#4579e2" x="0" y="0"></use>
  734 + <use class="wave waveSecond" xlink:href="#wave" fill="#3461c1" x="0" y="2"></use>
  735 + <use class="wave waveThird" xlink:href="#wave" fill="#2d55aa" x="0" y="4"></use>
  736 + </g>
  737 + <rect class="waveThird" x="0" y="0" width="100" height="100" fill="#2d55aa"></rect>
  738 + <foreignObject x="0" y="0" width="100" height="100" text-anchor="middle" dominant-baseline="middle">
  739 + <div xmlns="http://www.w3.org/1999/xhtml" class="text"></div>
  740 + </foreignObject>
  741 + </svg>
  742 + `.replace(/\n/g, '')
  743 + }
  744 +
  745 + Sidebar.prototype.generateFlowmeterThermometer = function () {
  746 + return `
  747 + <svg class="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg"
  748 + style="--range: 4; --min: 50; --max: 70; --width: 100; --height: 100; --value: 50;">
  749 + <style>
  750 + .flowmeter-thermometer {
  751 + width: calc(min(var(--width), var(--height)) * 1px);
  752 + height: calc(min(var(--width), var(--height)) * 1px);
  753 + }
  754 +
  755 + .thermometer-mercury-column {
  756 + y: var(--value);
  757 + }
  758 +
  759 + .tick-label {
  760 + font-size: 12px;
  761 + text-align: right;
  762 + overflow: hidden;
  763 + text-overflow: ellipsis;
  764 + color: #5b6b73;
  765 + }
  766 +
  767 + .thermometer-mercury-column {
  768 + transition: y .5s cubic-bezier(0.52, 0.05, 0.47, 0.99);
  769 + }
  770 + </style>
  771 + <defs>
  772 + <radialGradient id="thermometerdiv_meter_2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
  773 + <stop offset="0%" style="stop-color: rgb(230, 200, 200);"></stop>
  774 + <stop offset="90%" style="stop-color: rgb(230, 0, 0);"></stop>
  775 + </radialGradient>
  776 + <clipPath id="over">
  777 + <rect width="100" height="190" x="100" y="10" />
  778 + </clipPath>
  779 + </defs>
  780 + <circle r="9.25" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px;">
  781 + </circle>
  782 + <rect x="99.75" y="14.25" height="192.75" width="18.5"
  783 + style="shape-rendering: crispedges; fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px;">
  784 + </rect>
  785 + <circle r="8.75" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: none;"></circle>
  786 + <circle r="18" cx="109" cy="207" style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136);"></circle>
  787 + <rect x="100.25" y="14.25" height="192.75" width="17.5"
  788 + style="shape-rendering: crispedges; fill: rgb(255, 255, 255); stroke: none;"></rect>
  789 + <line class="thermometer-min-line" x1="99.75" x2="140.25" y1="165" y2="165"
  790 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispedges;"></line>
  791 + <text class="thermometer-min-label" x="120.25" y="168.46428571428572" dy="0.72em"
  792 + style="fill: rgb(0, 0, 230); font-size: 10px;">min</text>
  793 + <line class="thermometer-max-line" x1="99.75" x2="140.25" y1="40" y2="40"
  794 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispedges;"></line>
  795 + <text class="thermometer-max-label" x="120.25" y="35.285714285714306"
  796 + style="fill: rgb(230, 0, 0); font-size: 10px;">max</text>
  797 + <rect class="thermometer-mercury-column" x="104" y="15" width="10.5" height="190"
  798 + style="shape-rendering: crispedges; fill: rgb(230, 0, 0);" clip-path="url(#over)"></rect>
  799 + <circle r="13" cx="109" cy="207"
  800 + style="fill: url(&quot;#thermometerdiv_meter_2&quot;); stroke: rgb(230, 0, 0); stroke-width: 2px;"></circle>
  801 + <g class="thermometer-temperature-axis" transform="translate(99.75,0)" fill="none" font-size="10"
  802 + font-family="sans-serif" text-anchor="end">
  803 + <g class="tick" opacity="1" transform="translate(0,190)">
  804 + <line stroke="currentColor" x2="-7"
  805 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  806 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  807 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">-20</div>
  808 + </foreignObject>
  809 + </g>
  810 + <g class="tick" opacity="1" transform="translate(0,165)">
  811 + <line stroke="currentColor" x2="-7"
  812 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  813 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  814 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">0</div>
  815 + </foreignObject>
  816 + </g>
  817 + <g class="tick" opacity="1" transform="translate(0,140)">
  818 + <line stroke="currentColor" x2="-7"
  819 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  820 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  821 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">20</div>
  822 + </foreignObject>
  823 + </g>
  824 + <g class="tick" opacity="1" transform="translate(0,115)">
  825 + <line stroke="currentColor" x2="-7"
  826 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  827 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  828 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">40</div>
  829 + </foreignObject>
  830 + </g>
  831 + <g class="tick" opacity="1" transform="translate(0,90)">
  832 + <line stroke="currentColor" x2="-7"
  833 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  834 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  835 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">60</div>
  836 + </foreignObject>
  837 + </g>
  838 + <g class="tick" opacity="1" transform="translate(0,65)">
  839 + <line stroke="currentColor" x2="-7"
  840 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  841 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  842 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">80</div>
  843 + </foreignObject>
  844 + </g>
  845 + <g class="tick" opacity="1" transform="translate(0,40)">
  846 + <line stroke="currentColor" x2="-7"
  847 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  848 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  849 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">100</div>
  850 + </foreignObject>
  851 + </g>
  852 + <g class="tick" opacity="1" transform="translate(0,15)">
  853 + <line stroke="currentColor" x2="-7"
  854 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  855 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  856 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">120</div>
  857 + </foreignObject>
  858 + </g>
  859 + </g>
  860 + </svg>
  861 + `.replace(/\n/g, '')
  862 + }
  863 +
  864 + Sidebar.prototype.resetFlowmeter = function (graph) {
  865 + const { COMPONENT_TYPE, UUID, WIDTH, HEIGHT } = getFlowmeterAttrKey()
  866 + const componentType = Sidebar.prototype.enumComponentType
  867 + const cells = Object.entries(graph.getModel().cells || {}).map(([_, item]) => item) || []
  868 + const needReset = cells.filter(item => item.value && item.getAttribute(COMPONENT_TYPE) === componentType.FLOWMETER)
  869 +
  870 + needReset.forEach(item => {
  871 + const id = item.getAttribute(UUID)
  872 + const element = document.getElementById(id).querySelector('svg')
  873 + if (element) {
  874 + const width = item.getAttribute(WIDTH)
  875 + const height = item.getAttribute(HEIGHT)
  876 +
  877 + element.style.setProperty('--width', width)
  878 + element.style.setProperty('--height', height)
  879 + }
  880 + })
  881 + }
  882 +
  883 + Sidebar.prototype.isFlowmeter = function (cell) {
  884 + const basicAttr = Sidebar.prototype.enumCellBasicAttribute
  885 + const componentType = Sidebar.prototype.enumComponentType
  886 + return cell.getAttribute(basicAttr.COMPONENT_TYPE) === componentType.FLOWMETER
  887 + }
  888 +
  889 + Sidebar.prototype.updateFlowmeterCell = function (cell, { width, height, value, type }) {
  890 + const { WIDTH, HEIGHT, VALUE, TYPE, UUID } = getFlowmeterAttrKey()
  891 + const id = cell.getAttribute(UUID)
  892 + const element = document.getElementById(id).querySelector('svg')
  893 + if (element) {
  894 + width && element.style.setProperty('--width', width)
  895 + height && element.style.setProperty('--height', height)
  896 + value && element.style.setProperty('--value', value)
  897 + width && cell.setAttribute(WIDTH, width)
  898 + height && cell.setAttribute(HEIGHT, height)
  899 + value && cell.setAttribute(VALUE, value)
  900 + type && cell.setAttribute(TYPE, type)
  901 + }
  902 + }
  903 +
  904 + /**
  905 + * @description charts cell发生resize时改变charts size
  906 + * @type {Function}
  907 + */
  908 + const cellResized = mxGraph.prototype.cellResized
  909 + mxGraph.prototype.cellResized = function (cell, rect) {
  910 +
  911 + if (Sidebar.prototype.isFlowmeter(cell)) {
  912 + const { width, height } = rect
  913 + Sidebar.prototype.updateFlowmeterCell(cell, { width, height })
  914 + }
  915 + cellResized.apply(this, arguments)
  916 + }
499 })(); 917 })();
500 918
  919 +/**
  920 + * @description 获取流量计属性key
  921 + * @returns {{WIDTH: 'width', HEIGHT: 'height', VALUE: 'value', TYPE: 'type', UUID: 'uuid', COMPONENT_TYPE: 'componentType', BG_COLOR: 'bgColor', WAVE_FIRST_COLOR: 'waveFirstColor', WAVE_SECOND_COLOR: 'waveSecondColor', WAVE_THIRD_COLOR: 'waveThirdColor'}}
  922 + */
  923 +function getFlowmeterAttrKey() {
  924 + const basicAttr = Sidebar.prototype.enumCellBasicAttribute
  925 + const flowmeterAttr = Sidebar.prototype.enumFlowmeterAttr
  926 + return {
  927 + WIDTH: flowmeterAttr.WIDTH,
  928 + HEIGHT: flowmeterAttr.HEIGHT,
  929 + VALUE: flowmeterAttr.VALUE,
  930 + TYPE: flowmeterAttr.TYPE,
  931 + UUID: flowmeterAttr.UUID,
  932 + BG_COLOR: flowmeterAttr.BG_COLOR,
  933 + WAVE_FIRST_COLOR: flowmeterAttr.WAVE_FIRST_COLOR,
  934 + WAVE_SECOND_COLOR: flowmeterAttr.WAVE_SECOND_COLOR,
  935 + WAVE_THIRD_COLOR: flowmeterAttr.WAVE_THIRD_COLOR,
  936 + COMPONENT_TYPE: basicAttr.COMPONENT_TYPE,
  937 + }
  938 +}
  939 +
  940 +/**
  941 + * @description 获取流量计类型
  942 + * @returns {{RECT: 'rect', CIRCLE: 'circle'}}
  943 + */
  944 +function getFlowmeterType() {
  945 + const type = Sidebar.prototype.enumFlowmeterType
  946 + return {
  947 + RECT: type.RECT,
  948 + CIRCLE: type.CIRCLE
  949 + }
  950 +}
  951 +
  952 +
  953 +function getComponentType() {
  954 + const componentType = Sidebar.prototype.enumComponentType
  955 + return
  956 +}
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '开关'); 30 return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '开关');
31 })), 31 })),
32 this.addEntry(this.getTagsForStencil(gn, 'Params Setting', dt).join(' '), mxUtils.bind(this, function () { 32 this.addEntry(this.getTagsForStencil(gn, 'Params Setting', dt).join(' '), mxUtils.bind(this, function () {
33 - const cell = new mxCell('<button class="param-setting-button">参数设置</button>', new mxGeometry(0, 0, 100, 60), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;'); 33 + const cell = new mxCell('参数设置', new mxGeometry(0, 0, 60, 32), 'text;html=1;strokeColor=#1890ff;fillColor=#1890ff;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;fontColor=#FFFFFF;rounded=1;shadow=0;');
34 // 自定义属性 34 // 自定义属性
35 const cellAttribute = { 35 const cellAttribute = {
36 [COMPONENT_TYPE]: PARAMS_SETTING_BUTTON 36 [COMPONENT_TYPE]: PARAMS_SETTING_BUTTON
@@ -55,11 +55,6 @@ @@ -55,11 +55,6 @@
55 })); 55 }));
56 }) 56 })
57 57
58 - this.setVariableImageLib('switch', '开关', [  
59 - { name: 'switch-on', path: '/images/thingskit/switch-on.svg', staticPath: `${Proxy_Prefix}/images/thingskit/switch-on.svg` },  
60 - { name: 'switch-off', path: '/images/thingskit/switch-on.svg', staticPath: `${Proxy_Prefix}/images/thingskit/switch-off.svg` },  
61 - ])  
62 -  
63 this.setVariableImageLib(dt, label, lib) 58 this.setVariableImageLib(dt, label, lib)
64 59
65 this.addPaletteFunctions(dt, label, false, fns); 60 this.addPaletteFunctions(dt, label, false, fns);
@@ -2294,10 +2294,15 @@ EditorUi.prototype.initCanvas = function() @@ -2294,10 +2294,15 @@ EditorUi.prototype.initCanvas = function()
2294 var layout = this.graph.getPageLayout(); 2294 var layout = this.graph.getPageLayout();
2295 var page = this.graph.getPageSize(); 2295 var page = this.graph.getPageSize();
2296 2296
2297 - return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width),  
2298 - this.scale * (this.translate.y + layout.y * page.height),  
2299 - this.scale * layout.width * page.width,  
2300 - this.scale * layout.height * page.height); 2297 + // TODO thingsKit 固定页面尺寸 超出边界后隐藏
  2298 + return new mxRectangle(this.scale * (this.translate.x),
  2299 + this.scale * (this.translate.y),
  2300 + this.scale * page.width,
  2301 + this.scale * page.height)
  2302 + // return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width),
  2303 + // this.scale * (this.translate.y + layout.y * page.height),
  2304 + // this.scale * layout.width * page.width,
  2305 + // this.scale * layout.height * page.height);
2301 }; 2306 };
2302 2307
2303 graph.getPreferredPageSize = function(bounds, width, height) 2308 graph.getPreferredPageSize = function(bounds, width, height)
@@ -3381,11 +3386,28 @@ EditorUi.prototype.lightboxFit = function(maxHeight) @@ -3381,11 +3386,28 @@ EditorUi.prototype.lightboxFit = function(maxHeight)
3381 } 3386 }
3382 3387
3383 // LATER: Use initial graph bounds to avoid rounding errors 3388 // LATER: Use initial graph bounds to avoid rounding errors
3384 - this.editor.graph.maxFitScale = this.lightboxMaxFitScale;  
3385 - this.editor.graph.fit(border, null, null, null, null, null, maxHeight);  
3386 - this.editor.graph.maxFitScale = null;  
3387 - // TODO thingsKit lightbox 默认缩放为1  
3388 - this.editor.graph.view.setScale(1); 3389 + // this.editor.graph.maxFitScale = this.lightboxMaxFitScale;
  3390 + // this.editor.graph.fit(border, null, null, null, null, null, maxHeight);
  3391 + // this.editor.graph.maxFitScale = null;
  3392 + // // TODO thingsKit lightbox 默认缩放为1
  3393 + // console.log(this.editor.graph.view)
  3394 + // this.editor.graph.view.setScale(1);
  3395 + /// TODO thingsKit 设置内容最大化
  3396 + var margin = 2;
  3397 + var max = 3;
  3398 + var graph = this.editor.graph
  3399 + var pageFormat = graph.pageFormat
  3400 + var bounds = graph.getGraphBounds();
  3401 + var cw = graph.container.clientWidth - margin;
  3402 + var ch = graph.container.clientHeight - margin;
  3403 + var w = pageFormat.width
  3404 + // var w = pageFormat.width / graph.view.scale;
  3405 + var h = pageFormat.height;
  3406 + // var h = pageFormat.height / graph.view.scale;
  3407 + var s = Math.min(max, Math.min(cw / w, ch / h));
  3408 + graph.view.scaleAndTranslate(s,
  3409 + (margin + cw - w * s) / (2 * s) - bounds.x / graph.view.scale,
  3410 + (margin + ch - h * s) / (2 * s) - bounds.y / graph.view.scale);
3389 } 3411 }
3390 }; 3412 };
3391 3413
@@ -4903,8 +4903,8 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -4903,8 +4903,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4903 const ss = ui.getSelectionState(); 4903 const ss = ui.getSelectionState();
4904 const vertices = ss.vertices || [] 4904 const vertices = ss.vertices || []
4905 const sidebarInstance = ui.sidebar 4905 const sidebarInstance = ui.sidebar
4906 - console.log(vertices)  
4907 - console.log(ui) 4906 + // console.log(vertices)
  4907 + // console.log(ui)
4908 4908
4909 const hasModifyNotSave = editor.status 4909 const hasModifyNotSave = editor.status
4910 4910
@@ -4922,7 +4922,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -4922,7 +4922,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4922 const graphId = vertices[0].id; 4922 const graphId = vertices[0].id;
4923 4923
4924 // 解构全局属性layui要用到的模块 4924 // 解构全局属性layui要用到的模块
4925 - const { layer, form, jquery: $, colorpicker, upload, element } = layui; 4925 + const { layer, form, jquery: $, colorpicker, upload, element, laydate } = layui;
4926 4926
4927 const CONTAINER_FILTER = 'containerFilter' 4927 const CONTAINER_FILTER = 'containerFilter'
4928 $(container).addClass('layui-form').attr('lay-filter', CONTAINER_FILTER) 4928 $(container).addClass('layui-form').attr('lay-filter', CONTAINER_FILTER)
@@ -4959,8 +4959,8 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -4959,8 +4959,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4959 4959
4960 /** 4960 /**
4961 * @description 覆盖当前节点数据 4961 * @description 覆盖当前节点数据
4962 - * @param {'act' | 'dataSources' | 'event'} key  
4963 - * @param {string} 4962 + * @param {'act' | 'dataSources' | 'event'} key
  4963 + * @param {string}
4964 */ 4964 */
4965 function overrideCurrentData(key, uuid = 'id', value = {}) { 4965 function overrideCurrentData(key, uuid = 'id', value = {}) {
4966 if (!currentNodeData[key]) currentNodeData[key] = [] 4966 if (!currentNodeData[key]) currentNodeData[key] = []
@@ -5063,7 +5063,8 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5063,7 +5063,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5063 GATEWAY: 'GATEWAY', 5063 GATEWAY: 'GATEWAY',
5064 ADDITIONAL: 'additional', 5064 ADDITIONAL: 'additional',
5065 DEVICE_PROFILE_ID: 'deviceProfileId', 5065 DEVICE_PROFILE_ID: 'deviceProfileId',
5066 - DEVICE_TYPE: 'deviceType' 5066 + DEVICE_TYPE: 'deviceType',
  5067 + SOURCE_OPTION: 'sourceOption'
5067 } 5068 }
5068 5069
5069 /** 5070 /**
@@ -5132,7 +5133,9 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5132,7 +5133,9 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5132 [permissionKey.VIDEO]: createVideoBindPanel, 5133 [permissionKey.VIDEO]: createVideoBindPanel,
5133 [permissionKey.SWITCH_STATE_SETTING]: createSwitchStateSettingPanel, 5134 [permissionKey.SWITCH_STATE_SETTING]: createSwitchStateSettingPanel,
5134 [permissionKey.ONLY_SINGLE_EVENT]: createParamsSettingButtonPanel, 5135 [permissionKey.ONLY_SINGLE_EVENT]: createParamsSettingButtonPanel,
5135 - [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel 5136 + [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel,
  5137 + [permissionKey.FLOWMETER_PANEL]: createFlowmeterPanel,
  5138 + [permissionKey.ALARM_LIST_PANEL]: createAlarmListPanel
5136 } 5139 }
5137 5140
5138 5141
@@ -5142,7 +5145,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5142,7 +5145,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5142 const permission = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE) 5145 const permission = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
5143 const needDisplayPanel = sidebarInstance.getComponentPermission(permission) 5146 const needDisplayPanel = sidebarInstance.getComponentPermission(permission)
5144 for (const key of needDisplayPanel) { 5147 for (const key of needDisplayPanel) {
5145 - renderMapping[key]() 5148 + renderMapping[key]?.()
5146 } 5149 }
5147 if (needDisplayPanel.length) createSubmitPanel() 5150 if (needDisplayPanel.length) createSubmitPanel()
5148 UseLayUi.nextTick(() => form.render()) 5151 UseLayUi.nextTick(() => form.render())
@@ -5177,7 +5180,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5177,7 +5180,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5177 const event = currentNodeData.event ?? [] 5180 const event = currentNodeData.event ?? []
5178 const actionType = {} 5181 const actionType = {}
5179 5182
5180 - const hasExistEl = $(`.layui-form[lay-filter="${CONTAINER_FILTER}"]`).find('input[type="checkbox"]') 5183 + const hasExistEl = $(`.interaction__container`).find('input[type="checkbox"]')
5181 $(hasExistEl).each((i) => { 5184 $(hasExistEl).each((i) => {
5182 $(hasExistEl[i]).attr('disabled', true) 5185 $(hasExistEl[i]).attr('disabled', true)
5183 }) 5186 })
@@ -5241,6 +5244,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5241,6 +5244,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5241 * @type {Function} 5244 * @type {Function}
5242 */ 5245 */
5243 const refreshFn = echoRefreshFn 5246 const refreshFn = echoRefreshFn
  5247 +
5244 echoRefreshFn = function () { 5248 echoRefreshFn = function () {
5245 refreshFn.apply(this) 5249 refreshFn.apply(this)
5246 const { dataSources: [dataSource] = [] } = currentNodeData || {} 5250 const { dataSources: [dataSource] = [] } = currentNodeData || {}
@@ -5428,8 +5432,554 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5428,8 +5432,554 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5428 } 5432 }
5429 5433
5430 /** 5434 /**
  5435 + * @description 创建流量计面板
  5436 + */
  5437 + function createFlowmeterPanel() {
  5438 +
  5439 + const enumConst = {
  5440 + MAX_VALUE: 'maxValue',
  5441 + MIN_VALUE: 'minValue',
  5442 + BG_COLOR: 'bgColor',
  5443 + WAVE_FIRST: 'waveFirst',
  5444 + WAVE_SECOND: 'waveSecond',
  5445 + WAVE_THIRD: 'waveThird',
  5446 + COMPONENT_TYPE: 'componentType'
  5447 + }
  5448 +
  5449 + const enumEl = {
  5450 + BG_COLOR_PICKER_EL: 'bgColorPickerEl',
  5451 + WAVE_FIRST: 'waveFirstEl',
  5452 + WAVE_SECOND: 'waveSecondEl',
  5453 + WAVE_THIRD: 'waveThirdEl',
  5454 + }
  5455 +
  5456 + const componentType = Sidebar.prototype.enumComponentType
  5457 + const flowmeterType = Sidebar.prototype.enumFlowmeterType
  5458 + const { TYPE } = getFlowmeterAttrKey()
  5459 + const cell = vertices[0]
  5460 + const isThemometerComponent = cell.getAttribute(TYPE) === flowmeterType.THERMOMETER
  5461 +
  5462 + if (isThemometerComponent) return
  5463 +
  5464 + const fragment = document.createDocumentFragment()
  5465 + const title = createTitle('流量计配置')
  5466 + $(title).addClass('override__title--default')
  5467 +
  5468 + const defaultPanel = createPanel()
  5469 + $(defaultPanel).addClass('override__panel--default')
  5470 + $(defaultPanel).append(`<div style="display: none;" class="layui-form-item data-source__component-select"><label class="layui-form-label">最小值</label><div class="layui-input-block"><input class="layui-input" name="${enumConst.COMPONENT_TYPE}" value="${componentType.FLOWMETER}" placeholder="组件类型"></div></div>`)
  5471 + // $(defaultPanel).append(`<div class="layui-form-item data-source__component-select"><label class="layui-form-label">最小值</label><div class="layui-input-block"><input class="layui-input" name="${enumConst.MIN_VALUE}" value="0" placeholder="请输入最小值"></div></div>`)
  5472 + // $(defaultPanel).append(`<div class="layui-form-item data-source__component-select"><label class="layui-form-label">最大值</label><div class="layui-input-block"><input class="layui-input" name="${enumConst.MAX_VALUE}" value="100" placeholder="请输入最大值"></div></div>`)
  5473 + $(defaultPanel).append(`<div style="display: ${isThemometerComponent ? 'none' : 'block'}" class="layui-form-item data-source__component-select"><label class="layui-form-label">背景色</label><div class="layui-input-block" style="display: flex; align-items: center;"><input style="border: none;" id="${enumEl.BG_COLOR_PICKER_EL}" type="color" name="${enumConst.BG_COLOR}" value="#8badcb" /></div></div>`)
  5474 + $(defaultPanel).append(`<div style="display: ${isThemometerComponent ? 'none' : 'block'}" class="layui-form-item data-source__component-select"><label class="layui-form-label">颜色一</label><div class="layui-input-block" style="display: flex; align-items: center;"><input style="border: none;" id="${enumEl.WAVE_FIRST}" type="color" name="${enumConst.WAVE_FIRST}" value="#4579e2" /></div></div>`)
  5475 + $(defaultPanel).append(`<div style="display: ${isThemometerComponent ? 'none' : 'block'}" class="layui-form-item data-source__component-select"><label class="layui-form-label">颜色二</label><div class="layui-input-block" style="display: flex; align-items: center;"><input style="border: none;" id="${enumEl.WAVE_SECOND}" type="color" name="${enumConst.WAVE_SECOND}" value="#3461c1" /></div></div>`)
  5476 + $(defaultPanel).append(`<div style="display: ${isThemometerComponent ? 'none' : 'block'}" class="layui-form-item data-source__component-select"><label class="layui-form-label">颜色三</label><div class="layui-input-block" style="display: flex; align-items: center;"><input style="border: none;" id="${enumEl.WAVE_THIRD}" type="color" name="${enumConst.WAVE_THIRD}" value="#2d55aa" /></div></div>`)
  5477 +
  5478 +
  5479 + fragment.append(title)
  5480 + fragment.append(defaultPanel)
  5481 + $(container).append(fragment)
  5482 +
  5483 + function init() {
  5484 +
  5485 + const refreshFn = echoRefreshFn
  5486 + echoRefreshFn = function () {
  5487 + refreshFn.apply(this)
  5488 + const { dataSources: [dataSource] = [] } = currentNodeData || {}
  5489 + form.val(CONTAINER_FILTER, dataSource?.additional || {})
  5490 + }
  5491 +
  5492 + form.render()
  5493 + }
  5494 +
  5495 + init()
  5496 + }
  5497 +
  5498 + /**
  5499 + * @description 创建告警列表面板
  5500 + */
  5501 + function createAlarmListPanel() {
  5502 +
  5503 + const enumActionEl = {
  5504 + BIND_DEVICE_ICON: 'bindDevice',
  5505 + DATE_RANGE: 'dateRange',
  5506 + DEVICES_INPUT: 'devicesGroup',
  5507 + AUTO_PLAY: 'autoPlay',
  5508 + START_TIME: 'startTime',
  5509 + END_TIME: 'endTime',
  5510 + }
  5511 +
  5512 + const enumFields = {
  5513 + DEVICE_INFO: 'devicesInfo',
  5514 + AUTO_PLAY: 'autoPlay',
  5515 + INTERVAL: 'interval',
  5516 + START_TIME: 'startTime',
  5517 + END_TIME: 'endTime',
  5518 + }
  5519 +
  5520 + const fragment = document.createDocumentFragment()
  5521 + const title = createTitle('数据绑定')
  5522 + $(title).addClass('override__title--default')
  5523 +
  5524 +
  5525 + const deviceGroupPanel = createPanel()
  5526 + $(deviceGroupPanel).addClass('override__panel--default')
  5527 + $(deviceGroupPanel).append(`<div style="display: flex; justify-content: space-between;"><input style="display: none;" id="${enumActionEl.DEVICES_INPUT}" name="${enumFields.DEVICE_INFO}" /><div>设备绑定</div><i id="${enumActionEl.BIND_DEVICE_ICON}" class="layui-icon-edit layui-icon" style="cursor: pointer;"><i/></div>`)
  5528 +
  5529 + const queryTimeRangePanel = createPanel()
  5530 + $(queryTimeRangePanel).addClass('override__panel--default')
  5531 + $(queryTimeRangePanel).append(`<input style="display: none;" type="number" id="${enumActionEl.START_TIME}" name="${enumFields.START_TIME}"><input style="display: none;" type="number" id="${enumActionEl.END_TIME}" name="${enumFields.END_TIME}">`)
  5532 + $(queryTimeRangePanel).append(`<div class="layui-form-item" style="margin-bottom: 0;"><label class="layui-form-label" style="width: 80px; padding: 9px 0; text-align: left;">查询时间</label><div class="layui-input-block" style="margin-left: 80px;"><input style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap;" class="layui-input" id="${enumActionEl.DATE_RANGE}" autocomplete="off" placeholder="请选择查询日期"></div></div>`)
  5533 +
  5534 + const autoPlayPanel = createPanel()
  5535 + $(autoPlayPanel).addClass('override__panel--default')
  5536 + $(autoPlayPanel).append(`<div class="layui-form-item" style="margin-bottom: 0;" id="${enumActionEl.AUTO_PLAY}"><label class="layui-form-label" style="width: 80px; padding: 9px 0; text-align: left;">自动滚动</label><div class="layui-input-block" style="margin-left: 80px;"><input type="checkbox" name="${enumFields.AUTO_PLAY}" title="开启|关闭" lay-skin="switch"> </div></div>`)
  5537 + // $(autoPlayPanel).append(`<div class="layui-form-item" style="margin-bottom: 0;" id="${enumActionEl.AUTO_PLAY}"><label class="layui-form-label" style="width: 80px; padding: 9px 0; text-align: left;">自动滚动</label><div class="layui-input-block" style="margin-left: 80px;"><input type="radio" name="${enumActionEl.AUTO_PLAY}" value="true" title="开" checked><input type="radio" name="${enumActionEl.AUTO_PLAY}" value="false" title="关"></div></div>`)
  5538 +
  5539 + const playIntervalPanel = createPanel()
  5540 + $(playIntervalPanel).addClass('override__panel--default')
  5541 + $(playIntervalPanel).append(`<div class="layui-form-item" style="margin-bottom: 0;"><label class="layui-form-label" style="width: 80px; padding: 9px 0; text-align: left;">停留时间()</label><div class="layui-input-block" style="margin-left: 80px;"><input class="layui-input" name="${enumFields.INTERVAL}" placeholder="请输入报警停留时间" /></div></div>`)
  5542 +
  5543 + function openBindDeviceLayer() {
  5544 +
  5545 + const enumTableActionEl = {
  5546 + TABLE_ID: 'deviceGroupEl',
  5547 + TREE_CLASS: 'org-tree',
  5548 + ADD_ROW_BUTTON: 'addRow',
  5549 + DELETE_BUTTON: 'delete-btn',
  5550 + SUBMIT_BUTTON: 'deviceGroupsSubmitBtn'
  5551 + }
  5552 +
  5553 + const enumLayFilter = {
  5554 + ROW_FILTER_NAME: 'deviceBindRowFilter',
  5555 + SUBMIT: 'deviceGroupsSubmit'
  5556 + }
  5557 +
  5558 + const enumDeviceKeys = {
  5559 + DEVICE_TYPE: 'deviceType',
  5560 + ORGANIZATION_ID: 'organizationId',
  5561 + DEVICE_PROFILE_ID: 'deviceProfileId',
  5562 + DEVICE_ID: 'deviceId'
  5563 + }
  5564 +
  5565 + let rowCount = 0
  5566 +
  5567 + let deviceType = []
  5568 +
  5569 + let organizationList = []
  5570 +
  5571 + const getRowFilter = () => {
  5572 + rowCount++
  5573 + return `${enumLayFilter.ROW_FILTER_NAME}-${rowCount}`
  5574 + }
  5575 +
  5576 + async function getOrganization(organizationId, rowFilter) {
  5577 + if (!organizationList.length) {
  5578 + const [err, res] = await to(ConfigurationNodeApi.getOrgTree())
  5579 + if (!err) organizationList = res
  5580 + }
  5581 +
  5582 + $(`#${enumTableActionEl.TABLE_ID}`).find(`tbody tr .${enumTableActionEl.TREE_CLASS}`).each((_index, item) => {
  5583 +
  5584 + const rowFilter = $(item).parents(`tr`).attr('lay-filter')
  5585 +
  5586 + if ($(item).data('initialize')) return
  5587 + $(item).data('initialize', true)
  5588 +
  5589 + UseLayUi.createTreeSelect({
  5590 + elem: item,
  5591 + layFilter: enumDeviceKeys.ORGANIZATION_ID,
  5592 + singleUsage: false,
  5593 + hiddenLabel: true,
  5594 + layVerify: 'required',
  5595 + layVerType: 'tips',
  5596 + popupMountToBody: true,
  5597 + treeProps: {
  5598 + data: organizationList,
  5599 + onlyIconControl: true,
  5600 + click(node) {
  5601 + getDevice(rowFilter)
  5602 + },
  5603 + },
  5604 + })
  5605 + })
  5606 +
  5607 + form.render()
  5608 +
  5609 + if (organizationId) {
  5610 + UseLayUi.nextTick(() => {
  5611 + const node = UseLayUi.findTreeObjectByField(organizationList, organizationId)
  5612 + $(`#${enumTableActionEl.TABLE_ID} tr[lay-filter="${rowFilter}"] input[name="${enumDeviceKeys.ORGANIZATION_ID}"]`).val(organizationId).parent().find('span').html(node?.name)
  5613 + })
  5614 + }
  5615 + }
  5616 +
  5617 + async function getDeviceType(value, rowFilter) {
  5618 + if (!deviceType.length) {
  5619 + const [err, res] = await to(ConfigurationNodeApi.getDictionaryValue('device_type'))
  5620 + if (!err) deviceType = res
  5621 + }
  5622 + const template = UseLayUi.generateOptionTemplate({ dataSource: deviceType, labelField: 'itemText', valueField: 'itemValue' })
  5623 + $(`#${enumTableActionEl.TABLE_ID}`).find(`select[name="${enumDeviceKeys.DEVICE_TYPE}"]`).each((_index, target) => {
  5624 + // if ($(target).children()) return
  5625 + if ($(target).data('initialize')) return
  5626 + $(target).data('initialize', true)
  5627 + $(target).html(template)
  5628 + })
  5629 + form.render()
  5630 +
  5631 + if (value) {
  5632 + UseLayUi.nextTick(() => {
  5633 + form.val(rowFilter, { [enumDeviceKeys.DEVICE_TYPE]: value })
  5634 + })
  5635 + }
  5636 + }
  5637 +
  5638 + async function getDeviceProfile(currentRowFilter, record, value) {
  5639 + const { deviceType } = record || form.val(currentRowFilter) || {}
  5640 + if (!deviceType) return
  5641 + const [err, res] = await to(ConfigurationNodeApi.getProduct(deviceType))
  5642 + const deviceProfile = res
  5643 + const template = UseLayUi.generateOptionTemplate({ dataSource: deviceProfile, labelField: 'name', valueField: 'id' })
  5644 + $(`#${enumTableActionEl.TABLE_ID}`).find(`tr[lay-filter="${currentRowFilter}"] select[name="${enumDeviceKeys.DEVICE_PROFILE_ID}"]`).html(template)
  5645 + form.render()
  5646 +
  5647 + if (value) {
  5648 + UseLayUi.nextTick(() => {
  5649 + form.val(currentRowFilter, { [enumDeviceKeys.DEVICE_PROFILE_ID]: value })
  5650 + })
  5651 + }
  5652 + }
  5653 +
  5654 + async function getDevice(currentRowFilter, record, value) {
  5655 + const { deviceProfileId, organizationId, deviceType } = record || form.val(currentRowFilter) || {}
  5656 + if (![deviceProfileId, organizationId, deviceType].every(Boolean)) return
  5657 + const [err, res] = await to(ConfigurationNodeApi.getMeetConditionsDevice({ deviceProfileId, deviceType, organizationId }))
  5658 + if (err) return
  5659 + const template = UseLayUi.generateOptionTemplate({ dataSource: res, labelField: 'name', valueField: 'id', alias: 'alias' })
  5660 + $(`#${enumTableActionEl.TABLE_ID}`).find(`tr[lay-filter="${currentRowFilter}"] select[name="${enumDeviceKeys.DEVICE_ID}"]`).html(template)
  5661 + form.render()
  5662 + if (value) {
  5663 + UseLayUi.nextTick(() => {
  5664 + form.val(currentRowFilter, { [enumDeviceKeys.DEVICE_ID]: value })
  5665 + })
  5666 + }
  5667 + }
  5668 +
  5669 + function echoDeviceBindInfo() {
  5670 + const dataSource = currentNodeData?.dataSources?.find(item => item.nodeId === nodeInfo.id)
  5671 + if (!dataSource) return
  5672 + const record = dataSource.sourceOption
  5673 + const devicesInfo = record.devicesInfo
  5674 + const value = UseLayUi.parseStringToJSON(devicesInfo, [])
  5675 + if (value.length) {
  5676 + $(`#${enumTableActionEl.TABLE_ID}`).find('tbody').empty()
  5677 + }
  5678 + value.forEach((item) => {
  5679 + const template = createRow()
  5680 + const rowFilter = `${enumLayFilter.ROW_FILTER_NAME}-${rowCount}`
  5681 + $(`#${enumTableActionEl.TABLE_ID}`).find('tbody').append(template)
  5682 +
  5683 + // form.render(null, rowFilter)
  5684 + getDeviceType(item[enumDeviceKeys.DEVICE_TYPE], rowFilter)
  5685 + getOrganization(item[enumDeviceKeys.ORGANIZATION_ID], rowFilter)
  5686 + getDeviceProfile(rowFilter, item, item[enumDeviceKeys.DEVICE_PROFILE_ID])
  5687 + getDevice(rowFilter, item, item[enumDeviceKeys.DEVICE_ID])
  5688 + })
  5689 + }
  5690 +
  5691 + function createEventListener(index) {
  5692 +
  5693 + /**
  5694 + * @description 下拉选项定位
  5695 + */
  5696 + $(`#${enumTableActionEl.TABLE_ID}`).on('click', '.layui-form-select', event => {
  5697 + const width = event.currentTarget.offsetWidth || 200
  5698 + const height = event.currentTarget.offsetHeight || 38
  5699 + const offset = $(event.currentTarget).offset()
  5700 + $(event.currentTarget).find('dl').css({
  5701 + position: 'fixed',
  5702 + 'min-width': width + 'px',
  5703 + top: offset.top + height + 'px',
  5704 + left: offset.left + 'px'
  5705 + })
  5706 + })
  5707 +
  5708 + $(`#${enumTableActionEl.ADD_ROW_BUTTON}`).on('click', (event) => {
  5709 + const template = createRow()
  5710 + $(`#${enumTableActionEl.TABLE_ID}`).find('tbody').append(template)
  5711 + getDeviceType()
  5712 + getOrganization()
  5713 + form.render()
  5714 + })
  5715 +
  5716 + $(`#${enumTableActionEl.TABLE_ID}`).on('click', `tr .${enumTableActionEl.DELETE_BUTTON}`, (event) => {
  5717 + $(event.currentTarget).parents('tr').remove()
  5718 + })
  5719 +
  5720 + form.on('select', (data) => {
  5721 + const { elem, value } = data || {}
  5722 + if (!elem) return
  5723 +
  5724 + const key = $(elem).attr('name')
  5725 + const currentRow = $(elem).parents('tr')
  5726 + const currentRowFilter = $(currentRow).attr('lay-filter')
  5727 +
  5728 + if (key === enumDeviceKeys.DEVICE_TYPE) {
  5729 + getDeviceProfile(currentRowFilter)
  5730 + form.val(currentRowFilter, {
  5731 + deviceId: null,
  5732 + deviceProfileId: null
  5733 + })
  5734 + }
  5735 +
  5736 + if (key === enumDeviceKeys.DEVICE_PROFILE_ID) {
  5737 + getDevice(currentRowFilter)
  5738 + form.val(currentRowFilter, {
  5739 + deviceId: null
  5740 + })
  5741 + }
  5742 + })
  5743 +
  5744 + form.on(`submit`, () => {
  5745 + const deviceGroupValues = []
  5746 + $(`#${enumTableActionEl.TABLE_ID}`).find(`tbody tr`).each((_index, item) => {
  5747 + const rowFilter = $(item).attr('lay-filter')
  5748 + const value = form.val(rowFilter)
  5749 + deviceGroupValues.push(value)
  5750 + $(`#${enumActionEl.DEVICES_INPUT}`).val(JSON.stringify(deviceGroupValues))
  5751 + layer.close(index)
  5752 + })
  5753 + })
  5754 + }
  5755 +
  5756 + function createRow() {
  5757 + const template = `
  5758 + <tr lay-filter="${getRowFilter()}" class="layui-form">
  5759 + <td class="layui-form-item" style="margin: 0;">
  5760 + <div>
  5761 + <select lay-verify="required" lay-verType="tips" name="${enumDeviceKeys.DEVICE_TYPE}"></select>
  5762 + </div>
  5763 + </td>
  5764 + <td class="layui-form-item" style="margin: 0;">
  5765 + <div>
  5766 + <select lay-verify="required" lay-verType="tips" name="${enumDeviceKeys.DEVICE_PROFILE_ID}"></select>
  5767 + </div>
  5768 + </td>
  5769 + <td class="layui-form-item" style="margin: 0;">
  5770 + <div class="${enumTableActionEl.TREE_CLASS}" style="width: 100%;">
  5771 + <select lay-verify="required" lay-verType="tips" name="${enumDeviceKeys.ORGANIZATION_ID}"></select>
  5772 + </div>
  5773 + </td>
  5774 + <td class="layui-form-item" style="margin: 0;">
  5775 + <div>
  5776 + <select lay-verify="required" lay-verType="tips" name="${enumDeviceKeys.DEVICE_ID}"></select>
  5777 + </div>
  5778 + </td>
  5779 + <td>
  5780 + <div class="${enumTableActionEl.DELETE_BUTTON}" style="cursor: pointer; color: #1E9FFF;">删除</div>
  5781 + </td>
  5782 + </tr>
  5783 + `
  5784 + return template
  5785 + }
  5786 +
  5787 + layer.open({
  5788 + title: '设备绑定',
  5789 + area: ['800px', '540px'],
  5790 + content: `
  5791 + <div class="override__table layui-form">
  5792 + <table id="${enumTableActionEl.TABLE_ID}" class="layui-table">
  5793 + <colgroup>
  5794 + <col width="150">
  5795 + <col width="150">
  5796 + <col width="150">
  5797 + <col width="150">
  5798 + <col width="100">
  5799 + </colgroup>
  5800 + <thead>
  5801 + <tr>
  5802 + <th>设备类型</th>
  5803 + <th>产品</th>
  5804 + <th>组织</th>
  5805 + <th>设备</th>
  5806 + <th>操作</th>
  5807 + </tr>
  5808 + </thead>
  5809 + <tbody>
  5810 + ${createRow()}
  5811 + </tbody>
  5812 + </table>
  5813 + <div style="display: flex; justify-content: center;">
  5814 + <button id="${enumTableActionEl.ADD_ROW_BUTTON}" type="button" class="layui-btn layui-btn-fluid layui-btn-normal">增加</button>
  5815 + </div>
  5816 + </div>
  5817 + `,
  5818 + async success(el, index) {
  5819 + form.render()
  5820 + await getDeviceType()
  5821 + await getOrganization()
  5822 + createEventListener(index)
  5823 + $(el).addClass('layui-form').find('.layui-layer-btn0').attr('lay-submit', true).attr('lay-filter', enumTableActionEl.SUBMIT_BUTTON)
  5824 + echoDeviceBindInfo()
  5825 + },
  5826 + yes() {
  5827 + return false
  5828 + }
  5829 + })
  5830 + }
  5831 +
  5832 + function createDateRange() {
  5833 + laydate.render({
  5834 + elem: `#${enumActionEl.DATE_RANGE}`,
  5835 + type: 'datetime',
  5836 + range: true,
  5837 + shortcuts: [
  5838 + {
  5839 + text: '过去一小时',
  5840 + value: (() => {
  5841 + const now = Date.now()
  5842 + const date1 = new Date(now - 1000 * 60 * 60)
  5843 + return [date1, new Date(now)]
  5844 + })()
  5845 + },
  5846 + {
  5847 + text: '今天',
  5848 + value: (() => {
  5849 + const date1 = new Date()
  5850 + const date2 = new Date()
  5851 + date1.setHours(0)
  5852 + date1.setMinutes(0)
  5853 + date1.setSeconds(0)
  5854 + date2.setHours(23)
  5855 + date2.setMinutes(59)
  5856 + date2.setSeconds(59)
  5857 + return [date1, date2]
  5858 + })()
  5859 + },
  5860 + {
  5861 + text: '过去七天',
  5862 + value: (() => {
  5863 + const now = Date.now()
  5864 + const date1 = new Date(now - 1000 * 60 * 60 * 24 * 7)
  5865 + return [date1, new Date(now)]
  5866 + })()
  5867 + },
  5868 + {
  5869 + text: '过去三十天',
  5870 + value: (() => {
  5871 + const now = Date.now()
  5872 + const date1 = new Date(now - 1000 * 60 * 60 * 24 * 30)
  5873 + return [date1, new Date(now)]
  5874 + })()
  5875 + },
  5876 + {
  5877 + text: '本月',
  5878 + value: (() => {
  5879 + const date1 = new Date()
  5880 + let date2 = new Date()
  5881 + date1.setDate(1)
  5882 + date1.setHours(0)
  5883 + date1.setMinutes(0)
  5884 + date1.setSeconds(0)
  5885 +
  5886 + date2.setMonth(date2.getMonth() + 1)
  5887 + date2 = new Date(date2.valueOf() - 1000 * 60 * 60 * ((date2.getDate()) * 24))
  5888 + date2.setHours(23)
  5889 + date2.setMinutes(59)
  5890 + date2.setSeconds(59)
  5891 + return [date1, date2]
  5892 + })()
  5893 + },
  5894 + {
  5895 + text: '近半年',
  5896 + value: (() => {
  5897 + const date1 = new Date()
  5898 + const date2 = new Date()
  5899 +
  5900 + date1.getMonth() < 6
  5901 + ? date1.setFullYear(date1.getFullYear() - 1) && date1.setMonth(12 - (6 % (date1.getMonth())))
  5902 + : date1.setMonth(date1.getMonth() - 6)
  5903 +
  5904 + return [date1, date2]
  5905 + })()
  5906 + },
  5907 + {
  5908 + text: '近一年',
  5909 + value: (() => {
  5910 + const date1 = new Date()
  5911 + const date2 = new Date()
  5912 + date1.setFullYear(date1.getFullYear() - 1)
  5913 + return [date1, date2]
  5914 + })()
  5915 + }
  5916 + ],
  5917 + done(value, date, endDate) {
  5918 + const [startTime, endTime] = value.split(' - ')
  5919 + const startTs = new Date(startTime).valueOf()
  5920 + const endTs = new Date(endTime).valueOf()
  5921 +
  5922 + $(`#${enumActionEl.START_TIME}`).val(startTs)
  5923 + $(`#${enumActionEl.END_TIME}`).val(endTs)
  5924 + }
  5925 + })
  5926 + }
  5927 +
  5928 + function formatTimespanToDateString(timespan) {
  5929 + if (isNaN(timespan)) return
  5930 + const date = new Date(Number(timespan))
  5931 + const year = date.getFullYear()
  5932 + const month = (date.getMonth() + 1).toString().padStart(2, 0)
  5933 + const day = date.getDate().toString().padStart(2, 0)
  5934 + const hour = date.getHours().toString().padStart(2, 0)
  5935 + const minute = date.getMinutes().toString().padStart(2, 0)
  5936 + const second = date.getSeconds().toString().padStart(2, 0)
  5937 +
  5938 + return `${year}-${month}-${day} ${hour}:${minute}:${second}`
  5939 + }
  5940 +
  5941 + async function echoData() {
  5942 + const getBindValue = currentNodeData?.dataSources?.find(item => item.nodeId === nodeInfo.id)
  5943 + if (!getBindValue) return
  5944 + const record = getBindValue.sourceOption
  5945 + form.val(CONTAINER_FILTER, record)
  5946 +
  5947 + const startTime = formatTimespanToDateString(record.startTime)
  5948 + const endTime = formatTimespanToDateString(record.endTime)
  5949 + laydate.render({
  5950 + elem: `#${enumActionEl.DATE_RANGE}`,
  5951 + value: `${startTime} - ${endTime}`
  5952 + })
  5953 + }
  5954 +
  5955 + function init() {
  5956 + $(`#${enumActionEl.BIND_DEVICE_ICON}`).on('click', openBindDeviceLayer)
  5957 + form.render()
  5958 +
  5959 + createDateRange()
  5960 +
  5961 + const refreshFN = echoRefreshFn
  5962 + echoRefreshFn = async function () {
  5963 + refreshFN.apply(this, arguments)
  5964 + await echoData()
  5965 + }
  5966 +
  5967 + }
  5968 +
  5969 + fragment.append(title)
  5970 + fragment.append(deviceGroupPanel)
  5971 + fragment.append(queryTimeRangePanel)
  5972 + fragment.append(autoPlayPanel)
  5973 + fragment.append(playIntervalPanel)
  5974 + $(container).append(fragment)
  5975 +
  5976 + UseLayUi.nextTick(init)
  5977 +
  5978 + }
  5979 +
  5980 + /**
5431 * @description 是否是折线图 5981 * @description 是否是折线图
5432 - * @param {boolean} isLineChart 5982 + * @param {boolean} isLineChart
5433 */ 5983 */
5434 function createChartBindPanel(chartType) { 5984 function createChartBindPanel(chartType) {
5435 const fragment = document.createDocumentFragment() 5985 const fragment = document.createDocumentFragment()
@@ -5900,6 +6450,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5900,6 +6450,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5900 } 6450 }
5901 const { field } = data 6451 const { field } = data
5902 const value = getValueOnSubmit(field) 6452 const value = getValueOnSubmit(field)
  6453 + if (!value) return
5903 await to(autoSaveGraphInfo()) 6454 await to(autoSaveGraphInfo())
5904 const [err, res] = await to(ConfigurationNodeApi.updateNodeInfo(value)) 6455 const [err, res] = await to(ConfigurationNodeApi.updateNodeInfo(value))
5905 if (err) return 6456 if (err) return
@@ -5927,12 +6478,14 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -5927,12 +6478,14 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5927 [componentType.VIDEO]: getVideoSubmitValue, 6478 [componentType.VIDEO]: getVideoSubmitValue,
5928 [componentType.SWITCH]: getSwitchSubmitValue, 6479 [componentType.SWITCH]: getSwitchSubmitValue,
5929 [componentType.PARAMS_SETTING_BUTTON]: getSwitchSubmitValue, 6480 [componentType.PARAMS_SETTING_BUTTON]: getSwitchSubmitValue,
5930 - [componentType.IMAGE]: getSubmitValue 6481 + [componentType.IMAGE]: getSubmitValue,
  6482 + [componentType.FLOWMETER]: getFlowmeterSubmitValue,
  6483 + [componentType.ALARM_LIST]: getAlarmListSubmitValue,
5931 } 6484 }
5932 6485
5933 const cell = vertices[0] 6486 const cell = vertices[0]
5934 const type = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE) 6487 const type = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
5935 - return renderMapping[type]?.(field) || {} 6488 + return renderMapping[type]?.(field) || false
5936 6489
5937 function getSubmitValue(field) { 6490 function getSubmitValue(field) {
5938 const ENABLED_FLAG = 'on' 6491 const ENABLED_FLAG = 'on'
@@ -6027,8 +6580,8 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -6027,8 +6580,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6027 } 6580 }
6028 6581
6029 /** 6582 /**
6030 - * @description 处理开关组件的保存值  
6031 - * @returns 6583 + * @description 处理开关组件的保存值
  6584 + * @returns
6032 */ 6585 */
6033 function getSwitchSubmitValue(field) { 6586 function getSwitchSubmitValue(field) {
6034 const dataSources = getDataSourceBindValue() 6587 const dataSources = getDataSourceBindValue()
@@ -6062,6 +6615,56 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -6062,6 +6615,56 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6062 } 6615 }
6063 return value 6616 return value
6064 } 6617 }
  6618 +
  6619 + function getFlowmeterSubmitValue(field = {}) {
  6620 + const additionalKey = HandleDataSource.enumConst
  6621 +
  6622 + const value = {
  6623 + configurationId,
  6624 + contentId: currentPageId.id,
  6625 + nodeId: graphId,
  6626 + [enumCategory.ACT]: [],
  6627 + [enumCategory.EVENT]: [],
  6628 + [enumCategory.DATA_SOURCE]: {
  6629 + [enumDataSourceConst.ORG_ID]: field[enumDataSourceConst.ORG_ID],
  6630 + [enumDataSourceConst.DEVICE_ID]: field[enumDataSourceConst.DEVICE_ID],
  6631 + [enumDataSourceConst.DEVICE_TYPE]: field[enumDataSourceConst.DEVICE_TYPE],
  6632 + [enumDataSourceConst.DEVICE_PROFILE_ID]: field[enumDataSourceConst.DEVICE_PROFILE_ID],
  6633 + [enumDataSourceConst.ATTR]: field[enumDataSourceConst.ATTR],
  6634 + [enumDataSourceConst.ADDITIONAL]: {
  6635 + [additionalKey.COMPONENT_TYPE]: field[additionalKey.COMPONENT_TYPE],
  6636 + // [additionalKey.MIN_VALUE]: field[additionalKey.MIN_VALUE],
  6637 + // [additionalKey.MAX_VALUE]: field[additionalKey.MAX_VALUE],
  6638 + [additionalKey.BG_COLOR]: field[additionalKey.BG_COLOR],
  6639 + [additionalKey.WAVE_FIRST_COLOR]: field[additionalKey.WAVE_FIRST_COLOR],
  6640 + [additionalKey.WAVE_SECOND_COLOR]: field[additionalKey.WAVE_SECOND_COLOR],
  6641 + [additionalKey.WAVE_THIRD_COLOR]: field[additionalKey.WAVE_THIRD_COLOR],
  6642 + }
  6643 + },
  6644 + }
  6645 + return value
  6646 + }
  6647 +
  6648 + function getAlarmListSubmitValue(filed = {}) {
  6649 + console.log(field?.devicesInfo)
  6650 + if (!field?.devicesInfo) {
  6651 + UseLayUi.errorMsg('请先进行设备绑定!')
  6652 + return
  6653 + }
  6654 + const value = {
  6655 + configurationId,
  6656 + contentId: currentPageId.id,
  6657 + nodeId: graphId,
  6658 + [enumCategory.ACT]: [],
  6659 + [enumCategory.EVENT]: [],
  6660 + [enumCategory.DATA_SOURCE]: {
  6661 + [enumDataSourceConst.ADDITIONAL]: { componentType: 'alarmList' },
  6662 + [enumDataSourceConst.SOURCE_OPTION]: field
  6663 + }
  6664 + }
  6665 +
  6666 + return value
  6667 + }
6065 } 6668 }
6066 6669
6067 /** 6670 /**
@@ -7032,7 +7635,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7032,7 +7635,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7032 } 7635 }
7033 7636
7034 /** 7637 /**
7035 - * @description 7638 + * @description
7036 */ 7639 */
7037 const recordData = { 7640 const recordData = {
7038 enabled: false 7641 enabled: false
@@ -7049,7 +7652,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7049,7 +7652,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7049 7652
7050 // 参数设置 7653 // 参数设置
7051 [enumConst.JSON_COMMAND]: content[enumConst.JSON_COMMAND], 7654 [enumConst.JSON_COMMAND]: content[enumConst.JSON_COMMAND],
7052 - [enumConst.WAY]: content[enumConst.WAY], 7655 + [enumConst.WAY]: content[enumConst.WAY] || enumWayType.ONE_WAY,
7053 [enumConst.COMMAND_TYPE]: content[enumConst.COMMAND_TYPE], 7656 [enumConst.COMMAND_TYPE]: content[enumConst.COMMAND_TYPE],
7054 [enumConst.TCP_COMMAND]: content[enumConst.TCP_COMMAND], 7657 [enumConst.TCP_COMMAND]: content[enumConst.TCP_COMMAND],
7055 [enumConst.TRANSPORTTYPE]: content[enumConst.TRANSPORTTYPE], 7658 [enumConst.TRANSPORTTYPE]: content[enumConst.TRANSPORTTYPE],
@@ -7073,7 +7676,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7073,7 +7676,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7073 7676
7074 /** 7677 /**
7075 * @description 控制form 7678 * @description 控制form
7076 - * @param {enumActionType} value 7679 + * @param {enumActionType} value
7077 */ 7680 */
7078 async function controlFormDisplay(value, isTCP, isCustom) { 7681 async function controlFormDisplay(value, isTCP, isCustom) {
7079 if (value === enumActionType.PAGE) { 7682 if (value === enumActionType.PAGE) {
@@ -7141,7 +7744,6 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7141,7 +7744,6 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7141 return false 7744 return false
7142 } 7745 }
7143 } else { 7746 } else {
7144 - console.log(formVal)  
7145 if (!isJson(formVal[enumConst.JSON_COMMAND])) { 7747 if (!isJson(formVal[enumConst.JSON_COMMAND])) {
7146 UseLayUi.topErrorMsg('命令配置存在错误') 7748 UseLayUi.topErrorMsg('命令配置存在错误')
7147 return false 7749 return false
@@ -7214,7 +7816,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7214,7 +7816,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7214 7816
7215 /** 7817 /**
7216 * @description 生成命令类型选项 7818 * @description 生成命令类型选项
7217 - * @returns 7819 + * @returns
7218 */ 7820 */
7219 function generateCommandTypeOptions() { 7821 function generateCommandTypeOptions() {
7220 const options = [ 7822 const options = [
@@ -7404,7 +8006,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7404,7 +8006,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7404 <label class="layui-form-label" style="width: 120px;">单向/双向 ${createHelpMessage(`单向:服务器向网关设备、直连设备发送指令。发送指令后,设备不会返回任何信息。\n 8006 <label class="layui-form-label" style="width: 120px;">单向/双向 ${createHelpMessage(`单向:服务器向网关设备、直连设备发送指令。发送指令后,设备不会返回任何信息。\n
7405 双向:服务器向网关设备、直连设备发送指令。发送指令后,设备返回响应信息。`, 'way')}</label> 8007 双向:服务器向网关设备、直连设备发送指令。发送指令后,设备返回响应信息。`, 'way')}</label>
7406 <div class="layui-input-block" style="margin-left: 150px;"> 8008 <div class="layui-input-block" style="margin-left: 150px;">
7407 - <input type="radio" name="${enumConst.WAY}" value="${enumWayType.ONE_WAY}" title="单向" checked=""> 8009 + <input type="radio" name="${enumConst.WAY}" value="${enumWayType.ONE_WAY}" title="单向" checked>
7408 <input type="radio" name="${enumConst.WAY}" value="${enumWayType.TWO_WAY}" title="双向"> 8010 <input type="radio" name="${enumConst.WAY}" value="${enumWayType.TWO_WAY}" title="双向">
7409 </div> 8011 </div>
7410 </div> 8012 </div>
@@ -7527,6 +8129,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7527,6 +8129,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7527 SLAVE_DEVICE_ID: 'slaveDeviceId', 8129 SLAVE_DEVICE_ID: 'slaveDeviceId',
7528 ATTR: 'attr', 8130 ATTR: 'attr',
7529 GATEWAY: 'GATEWAY', 8131 GATEWAY: 'GATEWAY',
  8132 + TITLE: 'title'
7530 } 8133 }
7531 8134
7532 const enumDisplayType = HandleDynamicEffect.enumDisplayType 8135 const enumDisplayType = HandleDynamicEffect.enumDisplayType
@@ -7563,6 +8166,16 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7563,6 +8166,16 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7563 ` 8166 `
7564 } 8167 }
7565 8168
  8169 + function generateTitle() {
  8170 + return `
  8171 + <div class="layui-form-item" style="margin-bottom: 0px">
  8172 + <div class="layui-input-block" style="margin-left: 0px;">
  8173 + <input name="${enumConst.TITLE}" class="layui-input" lay-verType="tips" />
  8174 + </div>
  8175 + </div>
  8176 + `
  8177 + }
  8178 +
7566 /** 8179 /**
7567 * @description 添加一条记录 8180 * @description 添加一条记录
7568 */ 8181 */
@@ -7577,6 +8190,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7577,6 +8190,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7577 <td> 8190 <td>
7578 <input autocomplete="off" lay-verType="tips" lay-verify="required" type="number" name="${enumConst.MAX}" class="layui-input ${enumActionEl.MAX_FILTER}"> 8191 <input autocomplete="off" lay-verType="tips" lay-verify="required" type="number" name="${enumConst.MAX}" class="layui-input ${enumActionEl.MAX_FILTER}">
7579 </td> 8192 </td>
  8193 + ${IS_DISPLAY && `<td>${generateTitle()}</td>`}
7580 <td style="text-align: center;"> 8194 <td style="text-align: center;">
7581 <button type="button" class="layui-btn layui-btn-primary layui-border-red ${enumActionEl.DEL_BTN_EL}">删除</button> 8195 <button type="button" class="layui-btn layui-btn-primary layui-border-red ${enumActionEl.DEL_BTN_EL}">删除</button>
7582 </td> 8196 </td>
@@ -7745,6 +8359,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7745,6 +8359,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7745 ${IS_RUNNING ? '<th style="text-align:center">类型</th>' : ''} 8359 ${IS_RUNNING ? '<th style="text-align:center">类型</th>' : ''}
7746 <th style="text-align:center">最小值(>=)</th> 8360 <th style="text-align:center">最小值(>=)</th>
7747 <th style="text-align:center">最大值(<=)</th> 8361 <th style="text-align:center">最大值(<=)</th>
  8362 + ${IS_DISPLAY ? '<th style="text-align:center">标签</th>' : ''}
7748 <th style="text-align:center">操作</th> 8363 <th style="text-align:center">操作</th>
7749 </tr> 8364 </tr>
7750 </thead> 8365 </thead>
@@ -7810,7 +8425,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -7810,7 +8425,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7810 8425
7811 /** 8426 /**
7812 * @description 8427 * @description
7813 - * @param {} event 8428 + * @param {} event
7814 */ 8429 */
7815 function handleStateSetting(event) { 8430 function handleStateSetting(event) {
7816 8431
@@ -8296,7 +8911,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -8296,7 +8911,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8296 } 8911 }
8297 8912
8298 /** 8913 /**
8299 - * 8914 + *
8300 * @returns {{orgId: string, attr: string, deviceId: string, devi}} 8915 * @returns {{orgId: string, attr: string, deviceId: string, devi}}
8301 */ 8916 */
8302 function getValue() { 8917 function getValue() {
@@ -8628,7 +9243,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -8628,7 +9243,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8628 CONTAINER_FILTER: 'imgContainerFilter', 9243 CONTAINER_FILTER: 'imgContainerFilter',
8629 9244
8630 /** 9245 /**
8631 - * @description 9246 + * @description
8632 */ 9247 */
8633 SET_IMG_EL: 'variableImageTableSetImgEl', 9248 SET_IMG_EL: 'variableImageTableSetImgEl',
8634 9249
@@ -8662,7 +9277,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -8662,7 +9277,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8662 9277
8663 /** 9278 /**
8664 * @description 设置回显 9279 * @description 设置回显
8665 - * @param {} value 9280 + * @param {} value
8666 */ 9281 */
8667 function setValue(value = {}) { 9282 function setValue(value = {}) {
8668 form.val(getFormFilter, value) 9283 form.val(getFormFilter, value)
@@ -8670,7 +9285,7 @@ DataFormatPanel.prototype.addDataFont = function (container) { @@ -8670,7 +9285,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8670 9285
8671 /** 9286 /**
8672 * @description 获取值 9287 * @description 获取值
8673 - * @returns 9288 + * @returns
8674 */ 9289 */
8675 function getValue() { 9290 function getValue() {
8676 return form.val(getFormFilter) || {} 9291 return form.val(getFormFilter) || {}
@@ -12541,10 +13156,22 @@ class UseLayUi { @@ -12541,10 +13156,22 @@ class UseLayUi {
12541 ` 13156 `
12542 } 13157 }
12543 13158
  13159 + static parseStringToJSON(string, defaultValue = {}) {
  13160 + try {
  13161 + if (typeof string === 'string') {
  13162 + const value = JSON.parse(string)
  13163 + if (typeof value === 'object') return value
  13164 + }
  13165 + return defaultValue
  13166 + } catch (error) {
  13167 + return defaultValue
  13168 + }
  13169 + }
  13170 +
12544 /** 13171 /**
12545 * @description 生成输入框控件 13172 * @description 生成输入框控件
12546 - * @param {{label: string, value: string, labelWidth: number, numberInput: boolean}} params  
12547 - * @returns 13173 + * @param {{label: string, value: string, labelWidth: number, numberInput: boolean}} params
  13174 + * @returns
12548 */ 13175 */
12549 static createInputTemplate({ label, value, labelWidth = 80, required = false, type = 'TEXT' }) { 13176 static createInputTemplate({ label, value, labelWidth = 80, required = false, type = 'TEXT' }) {
12550 return ` 13177 return `
@@ -12562,19 +13189,19 @@ class UseLayUi { @@ -12562,19 +13189,19 @@ class UseLayUi {
12562 } 13189 }
12563 13190
12564 /** 13191 /**
12565 - * 13192 + *
12566 * @param {{ 13193 * @param {{
12567 - * accessMode: 'r' | 'w', 13194 + * accessMode: 'r' | 'w',
12568 * functionName: string, 13195 * functionName: string,
12569 - * id: string,  
12570 - * identifier: string, 13196 + * id: string,
  13197 + * identifier: string,
12571 * dataType: { 13198 * dataType: {
12572 - * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',  
12573 - * specs: { unit: {value: string, label: string},  
12574 - * unitName: string,  
12575 - * valueRange: {min: number, max: number}, 13199 + * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
  13200 + * specs: { unit: {value: string, label: string},
  13201 + * unitName: string,
  13202 + * valueRange: {min: number, max: number},
12576 * length: number 13203 * length: number
12577 - * }}}[]} inputData 13204 + * }}}[]} inputData
12578 * @param {number} labelWidth = 80 13205 * @param {number} labelWidth = 80
12579 * @param {} 13206 * @param {}
12580 */ 13207 */
@@ -12592,20 +13219,20 @@ class UseLayUi { @@ -12592,20 +13219,20 @@ class UseLayUi {
12592 return template 13219 return template
12593 } 13220 }
12594 /** 13221 /**
12595 - * 13222 + *
12596 * @param {{ 13223 * @param {{
12597 - * accessMode: 'r' | 'w', 13224 + * accessMode: 'r' | 'w',
12598 * functionName: string, 13225 * functionName: string,
12599 - * id: string,  
12600 - * identifier: string, 13226 + * id: string,
  13227 + * identifier: string,
12601 * dataType: { 13228 * dataType: {
12602 - * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',  
12603 - * specs: { unit: {value: string, label: string},  
12604 - * unitName: string,  
12605 - * valueRange: {min: number, max: number}, 13229 + * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
  13230 + * specs: { unit: {value: string, label: string},
  13231 + * unitName: string,
  13232 + * valueRange: {min: number, max: number},
12606 * length: number 13233 * length: number
12607 - * }}}[]} inputData  
12608 - * @param {Record<string, any>} value = 80 13234 + * }}}[]} inputData
  13235 + * @param {Record<string, any>} value = 80
12609 */ 13236 */
12610 static validateThingsModelInputDataForm(inputData, value, needFormat = false) { 13237 static validateThingsModelInputDataForm(inputData, value, needFormat = false) {
12611 let flag = true 13238 let flag = true
@@ -12708,12 +13335,13 @@ class UseLayUi { @@ -12708,12 +13335,13 @@ class UseLayUi {
12708 13335
12709 /** 13336 /**
12710 * @description generator options template 生产下拉选项模板 13337 * @description generator options template 生产下拉选项模板
12711 - * @param options  
12712 - * @param {object[]} [dataSource] options.dataSource  
12713 - * @param {boolean} [addPlaceholderOption = true] options.addPlaceholderOption  
12714 - * @param {string} [labelField = 'name'] options.labelField  
12715 - * @param {string} [valueField = 'name'] options.valueField  
12716 - * @param {string} [alias] options.alias 13338 + * @typedef {Object} GenerateOptionTemplateParamsType
  13339 + * @property {any[]} [dataSource] options.dataSource
  13340 + * @property {boolean} [addPlaceholderOption = true] options.addPlaceholderOption
  13341 + * @property {string} [labelField = 'name'] options.labelField
  13342 + * @property {string} [valueField = 'name'] options.valueField
  13343 + * @property {string} [alias] options.alias
  13344 + * @param {GenerateOptionTemplateParamsType} options
12717 * @returns {*} 13345 * @returns {*}
12718 */ 13346 */
12719 static generateOptionTemplate(options) { 13347 static generateOptionTemplate(options) {
@@ -12858,17 +13486,21 @@ class UseLayUi { @@ -12858,17 +13486,21 @@ class UseLayUi {
12858 // TODO Tree Select 13486 // TODO Tree Select
12859 /** 13487 /**
12860 * @description create a tree select controls 13488 * @description create a tree select controls
12861 - * @param {string} [options.layFilter] options.layFilter  
12862 - * @param {string} [options.label] options.label  
12863 - * @param {object} [options.treeProps] options.treeProps  
12864 - * @param {HTMLDivElement} [options.elem] options.elem  
12865 - * @param {boolean} [options.singleUsage = true] options.singleUsage  
12866 - * @param {Function} [options.customSetTree = ((record) => ({ id: record.id, title: record.name }))] options.customSetTree  
12867 - * @param {boolean} [options.autoFormatDataSource = true] options.autoFormatDataSource  
12868 - * @param {string} [options.layVerify] options.layVerify  
12869 - * @param {string} [options.layVerType] options.layVerType  
12870 - * @param {boolean} [options.addPlaceholderOption] options.addPlaceholderOption  
12871 - * @param {Function} [options.treeProps.onReady] options.treeProps.onReady 13489 + * @typedef CreateTreeSelectParamsType
  13490 + * @property {string} [layFilter] options.layFilter
  13491 + * @property {string} [label] options.label
  13492 + * @property {object} [treeProps] options.treeProps
  13493 + * @property {HTMLDivElement} [elem] options.elem
  13494 + * @property {boolean} [singleUsage = true] options.singleUsage
  13495 + * @property {Function} [customSetTree = ((record) => ({ id: record.id, title: record.name }))] options.customSetTree
  13496 + * @property {boolean} [autoFormatDataSource = true] options.autoFormatDataSource
  13497 + * @property {string} [layVerify] options.layVerify
  13498 + * @property {string} [layVerType] options.layVerType
  13499 + * @property {boolean} [addPlaceholderOption] options.addPlaceholderOption
  13500 + * @property {boolean} [hiddenLabel] - hiddenLabel
  13501 + * @property {Function} [treeProps.onReady] options.treeProps.onReady
  13502 + * @property {boolean} [popupMountToBody] popupMountToBody
  13503 + * @param {CreateTreeSelectParamsType} options
12872 */ 13504 */
12873 static createTreeSelect(options) { 13505 static createTreeSelect(options) {
12874 const CLASS_NAME = 'things-kit-tree-select' 13506 const CLASS_NAME = 'things-kit-tree-select'
@@ -12889,27 +13521,29 @@ class UseLayUi { @@ -12889,27 +13521,29 @@ class UseLayUi {
12889 childrenField = 'children', 13521 childrenField = 'children',
12890 layVerify, 13522 layVerify,
12891 layVerType, 13523 layVerType,
12892 - addPlaceholderOption 13524 + addPlaceholderOption,
  13525 + hiddenLabel,
  13526 + popupMountToBody
12893 } = options 13527 } = options
12894 13528
12895 let { data = [], click, onReady } = treeProps 13529 let { data = [], click, onReady } = treeProps
12896 13530
12897 let template = ` 13531 let template = `
12898 - <div class="layui-form-item ${CLASS_NAME} ${className}">  
12899 - <label class="layui-form-label">${label}</label> 13532 + <div class="layui-form-item ${CLASS_NAME} ${className}" style="margin-bottom: ${hiddenLabel ? '0' : '15px'};">
  13533 + <label class="layui-form-label" style="display: ${hiddenLabel ? 'none' : 'block'};">${label}</label>
12900 <div class="layui-input-block"> 13534 <div class="layui-input-block">
12901 - <div class="layui-unselect layui-form-select ${SELECT_CLS}">  
12902 - <div class="layui-select-title">  
12903 - <span class="layui-input layui-unselect tree-select__label">请选择</span>  
12904 - <input ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)} type="text" style="visibility: hidden; position: absolute; top: 0" name="${layFilter}">  
12905 - <i class="layui-edge"></i> 13535 + <div class="layui-unselect layui-form-select ${SELECT_CLS}">
  13536 + <div class="layui-select-title">
  13537 + <span class="layui-input layui-unselect tree-select__label">请选择</span>
  13538 + <input ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)} type="text" style="visibility: hidden; position: absolute; top: 0" name="${layFilter}">
  13539 + <i class="layui-edge"></i>
  13540 + </div>
  13541 + <dl class="layui-anim layui-anim-upbit">
  13542 + <dd>
  13543 + <ul class="tree-select__tree-mount"></ul>
  13544 + </dd>
  13545 + </dl>
12906 </div> 13546 </div>
12907 - <dl class="layui-anim layui-anim-upbit">  
12908 - <dd>  
12909 - <ul class="tree-select__tree-mount"></ul>  
12910 - </dd>  
12911 - </dl>  
12912 - </div>  
12913 </div> 13547 </div>
12914 </div>` 13548 </div>`
12915 13549
@@ -12933,7 +13567,7 @@ class UseLayUi { @@ -12933,7 +13567,7 @@ class UseLayUi {
12933 $(elem).html(template) 13567 $(elem).html(template)
12934 const treeData = UseLayUi.formatTreeDataSource(data, customSetTree, valueField, labelField, childrenField) 13568 const treeData = UseLayUi.formatTreeDataSource(data, customSetTree, valueField, labelField, childrenField)
12935 if (addPlaceholderOption) treeData.unshift({ title: '请选择', id: undefined }) 13569 if (addPlaceholderOption) treeData.unshift({ title: '请选择', id: undefined })
12936 - // mount tree 13570 + // mount tree
12937 tree.render({ 13571 tree.render({
12938 ...treeProps, 13572 ...treeProps,
12939 ...(autoFormatDataSource ? { data: treeData } : {}), 13573 ...(autoFormatDataSource ? { data: treeData } : {}),
@@ -12950,6 +13584,21 @@ class UseLayUi { @@ -12950,6 +13584,21 @@ class UseLayUi {
12950 $(document).find('.layui-form-select').removeClass('layui-form-selected') 13584 $(document).find('.layui-form-select').removeClass('layui-form-selected')
12951 $(this).parents(`.${SELECT_CLS}`).toggleClass("layui-form-selected"); 13585 $(this).parents(`.${SELECT_CLS}`).toggleClass("layui-form-selected");
12952 layui.stope(e); 13586 layui.stope(e);
  13587 + if (popupMountToBody) {
  13588 + const popup = $(this).parents(`.${SELECT_CLS}`).find('.layui-anim')
  13589 + const titleEl = $(this).parents(`.${SELECT_CLS}`).find('.layui-select-title')
  13590 + const offset = $(titleEl).offset()
  13591 + const height = $(titleEl).height()
  13592 + const width = $(titleEl).width()
  13593 +
  13594 + $(popup).css({
  13595 + position: 'fixed',
  13596 + top: `${offset.top + height}px`,
  13597 + 'min-width': width,
  13598 + left: `${offset.left}px`,
  13599 + width
  13600 + })
  13601 + }
12953 }) 13602 })
12954 .on('click', '.layui-anim', (e) => { 13603 .on('click', '.layui-anim', (e) => {
12955 layui.stope(e) 13604 layui.stope(e)
@@ -13104,7 +13753,7 @@ class UseLayUi { @@ -13104,7 +13753,7 @@ class UseLayUi {
13104 class Utils { 13753 class Utils {
13105 /** 13754 /**
13106 * @description 字符串是否能转换为对象 13755 * @description 字符串是否能转换为对象
13107 - * @param {string} value 13756 + * @param {string} value
13108 */ 13757 */
13109 static stringIsJSON(value) { 13758 static stringIsJSON(value) {
13110 try { 13759 try {
@@ -13118,8 +13767,8 @@ class Utils { @@ -13118,8 +13767,8 @@ class Utils {
13118 13767
13119 /** 13768 /**
13120 * @description 字符串转对象 13769 * @description 字符串转对象
13121 - * @param {string} value  
13122 - * @returns 13770 + * @param {string} value
  13771 + * @returns
13123 */ 13772 */
13124 static stringToJSON(value, defaultValue = {}) { 13773 static stringToJSON(value, defaultValue = {}) {
13125 try { 13774 try {
@@ -13660,7 +14309,7 @@ class DispatchCenter { @@ -13660,7 +14309,7 @@ class DispatchCenter {
13660 subList.forEach(item => { 14309 subList.forEach(item => {
13661 const { dataOrigin, additional } = item 14310 const { dataOrigin, additional } = item
13662 if (dataOrigin === 'dataSources') { 14311 if (dataOrigin === 'dataSources') {
13663 - if (additional) { 14312 + if (additional && (additional || {})?.dataType) {
13664 const { dataType } = additional || {} 14313 const { dataType } = additional || {}
13665 if (dataType === HandleDataSource.enumDataBindType.REAL) { 14314 if (dataType === HandleDataSource.enumDataBindType.REAL) {
13666 this.dataSourceHandlerInstance.updateRealTimeDataSource(message, item) 14315 this.dataSourceHandlerInstance.updateRealTimeDataSource(message, item)
@@ -13678,10 +14327,6 @@ class DispatchCenter { @@ -13678,10 +14327,6 @@ class DispatchCenter {
13678 handleFunction(message, item) 14327 handleFunction(message, item)
13679 } 14328 }
13680 }) 14329 })
13681 - // this.subscribeEvent(cmdId, this.updateCommonDataSource.bind(this))  
13682 - return  
13683 - // const { subscriptionId, data } = message  
13684 - // DispatchCenter.instance.publishEvent(subscriptionId, data, message, event, ws)  
13685 } 14330 }
13686 14331
13687 /** 14332 /**
@@ -13712,6 +14357,92 @@ class DispatchCenter { @@ -13712,6 +14357,92 @@ class DispatchCenter {
13712 if (!id) return 14357 if (!id) return
13713 const [err, res] = await to(ConfigurationNodeApi.getConfigurationInfo('CONTENT', id)) 14358 const [err, res] = await to(ConfigurationNodeApi.getConfigurationInfo('CONTENT', id))
13714 this.contentData = res 14359 this.contentData = res
  14360 + this.afterGetContentDataNode()
  14361 + }
  14362 +
  14363 + afterGetContentDataNode() {
  14364 + this.handleFlowmeterComponent()
  14365 + this.handleAlarmList()
  14366 + }
  14367 +
  14368 + handleFlowmeterComponent() {
  14369 + setTimeout(() => {
  14370 + const componentType = Sidebar.prototype.enumComponentType
  14371 + if (!this.contentData?.dataSources) return
  14372 + const flowmeterComponent = this.contentData?.dataSources?.filter(item => item.additional && (item.additional || {}).componentType === componentType.FLOWMETER)
  14373 + const { MIN_VALUE, MAX_VALUE, BG_COLOR, WAVE_FIRST_COLOR, WAVE_SECOND_COLOR, WAVE_THIRD_COLOR } = HandleDataSource.enumConst
  14374 + flowmeterComponent.forEach(item => {
  14375 + const { nodeId, additional } = item
  14376 + const { bgColor, maxValue, minValue, waveFirst, waveSecond, waveThird } = additional || {}
  14377 + const cell = this.graph?.getCellsById([nodeId])?.[0]
  14378 + if (cell) {
  14379 + cell.setAttribute(BG_COLOR, bgColor)
  14380 + cell.setAttribute(MIN_VALUE, minValue)
  14381 + cell.setAttribute(MAX_VALUE, maxValue)
  14382 + cell.setAttribute(WAVE_FIRST_COLOR, waveFirst)
  14383 + cell.setAttribute(WAVE_SECOND_COLOR, waveSecond)
  14384 + cell.setAttribute(WAVE_THIRD_COLOR, waveThird)
  14385 +
  14386 + const { UUID } = getFlowmeterAttrKey()
  14387 + const id = cell.getAttribute(UUID)
  14388 +
  14389 + const element = document.getElementById(id)
  14390 +
  14391 + if (element) {
  14392 + element.querySelectorAll(`.${BG_COLOR}`).forEach(item => item.style.fill = bgColor)
  14393 + element.querySelectorAll(`.${WAVE_FIRST_COLOR}`).forEach(item => item.style.fill = waveFirst)
  14394 + element.querySelectorAll(`.${WAVE_SECOND_COLOR}`).forEach(item => item.style.fill = waveSecond)
  14395 + element.querySelectorAll(`.${WAVE_THIRD_COLOR}`).forEach(item => item.style.fill = waveThird)
  14396 + }
  14397 + }
  14398 + })
  14399 + }, 10);
  14400 + }
  14401 +
  14402 +
  14403 + // 处理告警列表
  14404 + async handleAlarmList() {
  14405 + const componentType = Sidebar.prototype.enumComponentType
  14406 + if (!this.contentData?.dataSources) return
  14407 + const alarmList = this.contentData?.dataSources?.filter(item => item.additional && (item.additional || {}).componentType === componentType.ALARM_LIST)
  14408 + const allCell = Object.entries(this.graph?.getModel()?.cells || {}).map(([_, item]) => item) || []
  14409 + const { UUID } = AlarmListComponent.getAttributeKeys()
  14410 + const { jquery: $ } = layui
  14411 + for (const item of alarmList || []) {
  14412 + const { nodeId, sourceOption } = item || {}
  14413 + const { devicesInfo, autoPlay, interval, startTime, endTime } = sourceOption || {}
  14414 + const node = allCell.find(item => item.id === nodeId)
  14415 + const deviceIds = UseLayUi.parseStringToJSON(devicesInfo, []).map(item => item.deviceId)
  14416 + const id = node.getAttribute(UUID)
  14417 + const element = document.getElementById(id)
  14418 + if (element) {
  14419 + const [err, data] = await to(ConfigurationNodeApi.getAlarmList({ page: 1, pageSize: 30, startTime: Number(startTime), endTime: Number(endTime), deviceIds }))
  14420 + if (err) return
  14421 + if (data?.items && data?.items?.length) {
  14422 + const template = AlarmListComponent.createAlarmItem(data.items)
  14423 + $(`#${id}`).find('.list-wrapper').html(template)
  14424 + } else {
  14425 + $(`#${id}`).find('.list-wrapper').html(`<div>暂无数据</div>`)
  14426 + }
  14427 + if (autoPlay) {
  14428 + const wrapperHeight = $(`#${id}`).height()
  14429 + const listWrapper = $(`#${id}`).find('.list-wrapper')
  14430 + const allHeight = listWrapper.height()
  14431 + const itemHeight = allHeight / listWrapper.children().length
  14432 + let scrollDistance = 0
  14433 + const cancel = RAFSetInterval(() => {
  14434 + try {
  14435 + scrollDistance = itemHeight + scrollDistance
  14436 + if (scrollDistance + wrapperHeight > allHeight) scrollDistance = 0
  14437 + document.getElementById(id).scrollTo({ top: scrollDistance, behavior: 'smooth' })
  14438 + } catch (error) {
  14439 + cancel?.()
  14440 + }
  14441 +
  14442 + }, Number(interval) * 1000)
  14443 + }
  14444 + }
  14445 + }
13715 } 14446 }
13716 14447
13717 sendSubscribeMessage() { 14448 sendSubscribeMessage() {
@@ -13751,7 +14482,7 @@ class DispatchCenter { @@ -13751,7 +14482,7 @@ class DispatchCenter {
13751 * orgName?: string, 14482 * orgName?: string,
13752 * slaveDeviceName?: string 14483 * slaveDeviceName?: string
13753 * } 14484 * }
13754 - * }} record 14485 + * }} record
13755 */ 14486 */
13756 const setDeviceMapping = (record) => { 14487 const setDeviceMapping = (record) => {
13757 const { deviceId, slaveDeviceId } = record 14488 const { deviceId, slaveDeviceId } = record
@@ -13767,12 +14498,12 @@ class DispatchCenter { @@ -13767,12 +14498,12 @@ class DispatchCenter {
13767 /** 14498 /**
13768 * @type {{ 14499 * @type {{
13769 * id: string, 14500 * id: string,
13770 - * nodeId: string, 14501 + * nodeId: string,
13771 * deviceId: string, 14502 * deviceId: string,
13772 - * slaveDeviceId: string,  
13773 - * attr: string,  
13774 - * enabled: boolean,  
13775 - * additional: object, 14503 + * slaveDeviceId: string,
  14504 + * attr: string,
  14505 + * enabled: boolean,
  14506 + * additional: object,
13776 * condition: object 14507 * condition: object
13777 * }[]} 14508 * }[]}
13778 */ 14509 */
@@ -13792,10 +14523,10 @@ class DispatchCenter { @@ -13792,10 +14523,10 @@ class DispatchCenter {
13792 }) 14523 })
13793 14524
13794 /** 14525 /**
13795 - * @param {{  
13796 - * entityId: string,  
13797 - * cmdId: number,  
13798 - * keys: string, 14526 + * @param {{
  14527 + * entityId: string,
  14528 + * cmdId: number,
  14529 + * keys: string,
13799 * agg?: string, 14530 * agg?: string,
13800 * interval?: number, 14531 * interval?: number,
13801 * startTs?: number, 14532 * startTs?: number,
@@ -13866,7 +14597,7 @@ class DispatchCenter { @@ -13866,7 +14597,7 @@ class DispatchCenter {
13866 entityId: key, 14597 entityId: key,
13867 cmdId, 14598 cmdId,
13868 agg, 14599 agg,
13869 - keys: getKeys(moreFilterRecord.map(item => item.attr)), 14600 + keys: item.attr,
13870 interval: toNumber(interval, 1000), 14601 interval: toNumber(interval, 1000),
13871 startTs: Date.now() - toNumber(effectScope), 14602 startTs: Date.now() - toNumber(effectScope),
13872 ...(dataType === HandleDataSource.enumDataBindType.REAL ? {} : { endTs: Date.now() }) 14603 ...(dataType === HandleDataSource.enumDataBindType.REAL ? {} : { endTs: Date.now() })
@@ -14043,7 +14774,42 @@ class HandleDataSource { @@ -14043,7 +14774,42 @@ class HandleDataSource {
14043 /** 14774 /**
14044 * @description 属性名称 14775 * @description 属性名称
14045 */ 14776 */
14046 - ATTR_NAME: 'attrName' 14777 + ATTR_NAME: 'attrName',
  14778 +
  14779 + /**
  14780 + * @description 组件类型
  14781 + */
  14782 + COMPONENT_TYPE: 'componentType',
  14783 +
  14784 + /**
  14785 + * @description 流量计最大值
  14786 + */
  14787 + MAX_VALUE: 'maxValue',
  14788 +
  14789 + /**
  14790 + * @description 流量计最小值
  14791 + */
  14792 + MIN_VALUE: 'minValue',
  14793 +
  14794 + /**
  14795 + * @description 流量计背景颜色
  14796 + */
  14797 + BG_COLOR: 'bgColor',
  14798 +
  14799 + /**
  14800 + * @description 流量计颜色一
  14801 + */
  14802 + WAVE_FIRST_COLOR: 'waveFirst',
  14803 +
  14804 + /**
  14805 + * @description 流量计颜色二
  14806 + */
  14807 + WAVE_SECOND_COLOR: 'waveSecond',
  14808 +
  14809 + /**
  14810 + * @description 流量计颜色三
  14811 + */
  14812 + WAVE_THIRD_COLOR: 'waveThird',
14047 } 14813 }
14048 14814
14049 14815
@@ -14074,8 +14840,6 @@ class HandleDataSource { @@ -14074,8 +14840,6 @@ class HandleDataSource {
14074 14840
14075 constructor(DispatchInstance) { 14841 constructor(DispatchInstance) {
14076 this.DispatchInstance = DispatchInstance 14842 this.DispatchInstance = DispatchInstance
14077 - // this.generatorCommonDataSourceMapping()  
14078 - // this.generatorChartDataSourceMapping()  
14079 } 14843 }
14080 14844
14081 get graph() { 14845 get graph() {
@@ -14114,76 +14878,6 @@ class HandleDataSource { @@ -14114,76 +14878,6 @@ class HandleDataSource {
14114 } 14878 }
14115 14879
14116 /** 14880 /**
14117 - * @description 生成普通数据源绑定映射关系  
14118 - * @param dataSources  
14119 - * @return {{cmdId: number, entityType: string, keys: *, scope: string, entityId: *}[]}  
14120 - */  
14121 - generatorCommonDataSourceMapping() {  
14122 - const msg = this.commonDataSourceBindList.map((datum) => {  
14123 - const { deviceId, attr, nodeId, slaveDeviceId } = datum  
14124 - const cmdId = this.getCmdId(nodeId)  
14125 - const sendMsgTemplate = {  
14126 - entityType: "DEVICE",  
14127 - entityId: slaveDeviceId ? slaveDeviceId : deviceId,  
14128 - scope: "LATEST_TELEMETRY",  
14129 - cmdId,  
14130 - keys: attr,  
14131 - }  
14132 - this.dataSourceNodeMapping.set(nodeId, datum)  
14133 - this.subscribeEvent(cmdId, this.updateCommonDataSource.bind(this))  
14134 - return sendMsgTemplate  
14135 - })  
14136 - const { REAL } = HandleDataSource.enumDataBindType  
14137 - if (msg.length) this.sendMsg({ [REAL]: msg })  
14138 - }  
14139 -  
14140 -  
14141 - /**  
14142 - * @description 图表数据源绑定关系  
14143 - * @param {any[]} dataSource  
14144 - */  
14145 - generatorChartDataSourceMapping() {  
14146 - const realList = []  
14147 - const historyList = []  
14148 - const { HISTORY, REAL } = HandleDataSource.enumDataBindType  
14149 - const { STARTTs, ENDTs } = HandleDataSource.enumConst  
14150 - for (const item of this.chartDataSourceBindList) {  
14151 - const { additional = {}, deviceId, attr, nodeId, slaveDeviceId } = item  
14152 - if (!attr) continue  
14153 - const { agg, interval = 1000, dataType, effectScope = 0 } = additional  
14154 - const cmdId = this.getCmdId(nodeId)  
14155 - const template = {  
14156 - entityType: "DEVICE",  
14157 - entityId: slaveDeviceId ? slaveDeviceId : deviceId,  
14158 - cmdId,  
14159 - interval: Number(interval),  
14160 - agg,  
14161 - keys: attr,  
14162 - }  
14163 - let scope = isNaN(effectScope) ? 0 : Number(effectScope)  
14164 - if (dataType === HISTORY) {  
14165 - template[STARTTs] = Date.now() - scope  
14166 - template[ENDTs] = Date.now()  
14167 - historyList.push(template)  
14168 - this.subscribeEvent(cmdId, (message) => {  
14169 - this.updateHistoryDataSource(message, agg)  
14170 - })  
14171 - }  
14172 - else if (dataType === REAL) {  
14173 - template[STARTTs] = Date.now() - scope  
14174 - // template['timeWindow'] = interval  
14175 - realList.push(template)  
14176 - this.subscribeEvent(cmdId, (message) => {  
14177 - this.updateRealTimeDataSource(message, agg)  
14178 - })  
14179 - }  
14180 - this.dataSourceNodeMapping.set(nodeId, item)  
14181 - }  
14182 -  
14183 - if (historyList.length || realList.length) this.sendMsg({ [HISTORY]: historyList, [REAL]: realList })  
14184 - }  
14185 -  
14186 - /**  
14187 * @description 订阅事件 绑定回调 14881 * @description 订阅事件 绑定回调
14188 * @param eventName 14882 * @param eventName
14189 * @param callback 14883 * @param callback
@@ -14194,15 +14888,22 @@ class HandleDataSource { @@ -14194,15 +14888,22 @@ class HandleDataSource {
14194 14888
14195 /** 14889 /**
14196 * @description 更新变量值 14890 * @description 更新变量值
14197 - * @param {} message 14891 + * @param {} message
14198 */ 14892 */
14199 updateCommonDataSource(message, record) { 14893 updateCommonDataSource(message, record) {
14200 const { nodeId, attr } = record 14894 const { nodeId, attr } = record
14201 - const node = this.getNodeByCmdId(nodeId) 14895 + const node = this.getNodeByNodeId(nodeId)
  14896 + const { data } = message
  14897 + const type = this.getComponentType(node)
14202 14898
  14899 + if (node && type === this.componentType.FLOWMETER) {
  14900 + this.handleFlowmeterComponent(message, record)
  14901 + return
  14902 + }
  14903 +
  14904 + // 需要刷新页面
14203 node && this.updatePage(() => { 14905 node && this.updatePage(() => {
14204 - const { data } = message  
14205 - const type = this.getComponentType(node) 14906 +
14206 if (type === this.componentType.SWITCH) { 14907 if (type === this.componentType.SWITCH) {
14207 this.handleSwitchComponent(message, record) 14908 this.handleSwitchComponent(message, record)
14208 return 14909 return
@@ -14227,13 +14928,14 @@ class HandleDataSource { @@ -14227,13 +14928,14 @@ class HandleDataSource {
14227 14928
14228 /** 14929 /**
14229 * @description 处理switch 组件 14930 * @description 处理switch 组件
14230 - * @param {} message 14931 + * @param {} message
14231 */ 14932 */
14232 handleSwitchComponent(message, record) { 14933 handleSwitchComponent(message, record) {
14233 const { data = {} } = message 14934 const { data = {} } = message
14234 const { nodeId, attr } = record 14935 const { nodeId, attr } = record
14235 - const node = this.getNodeByCmdId(nodeId) 14936 + const node = this.getNodeByNodeId(nodeId)
14236 const [[_timespan, receiveValue] = []] = data[attr] || [] 14937 const [[_timespan, receiveValue] = []] = data[attr] || []
  14938 + if (receiveValue === null || receiveValue === undefined) return
14237 const switchConfig = this.DispatchInstance.contentData.act.find(item => item.id === nodeId && item.type === 'SWITCH') 14939 const switchConfig = this.DispatchInstance.contentData.act.find(item => item.id === nodeId && item.type === 'SWITCH')
14238 const { condition = [] } = switchConfig || {} 14940 const { condition = [] } = switchConfig || {}
14239 let reg = /image=[^;]+/g 14941 let reg = /image=[^;]+/g
@@ -14253,7 +14955,7 @@ class HandleDataSource { @@ -14253,7 +14955,7 @@ class HandleDataSource {
14253 if ((style || '').includes(imagePath)) return 14955 if ((style || '').includes(imagePath)) return
14254 const sendValue = getSendValue(type) 14956 const sendValue = getSendValue(type)
14255 node.setStyle(style.replace(reg, `image=${imagePath}`)) 14957 node.setStyle(style.replace(reg, `image=${imagePath}`))
14256 - node.setAttribute('label', '') 14958 + // node.setAttribute('label', '')
14257 node.setAttribute(SWITCH_VALUE, receiveValue) 14959 node.setAttribute(SWITCH_VALUE, receiveValue)
14258 node.setAttribute(SWITCH_SEND_VALUE, sendValue) 14960 node.setAttribute(SWITCH_SEND_VALUE, sendValue)
14259 node.setAttribute(SWITCH_STATE, type) 14961 node.setAttribute(SWITCH_STATE, type)
@@ -14273,21 +14975,56 @@ class HandleDataSource { @@ -14273,21 +14975,56 @@ class HandleDataSource {
14273 14975
14274 } 14976 }
14275 14977
  14978 + handleFlowmeterComponent(message, record) {
  14979 + const { data = {} } = message
  14980 + if (!data) return
  14981 + const { nodeId, attr } = record
  14982 + const node = this.getNodeByNodeId(nodeId)
  14983 +
  14984 + let [[_timespan, receiveValue] = []] = data[attr] || []
  14985 + const { UUID, TYPE } = getFlowmeterAttrKey()
  14986 + const type = node.getAttribute(TYPE)
  14987 + const flowmeterType = Sidebar.prototype.enumFlowmeterType
  14988 + const id = node.getAttribute(UUID)
  14989 + const element = document.getElementById(id)
  14990 + const bindData = this.getBindData(nodeId)
  14991 + const { additional } = bindData || {}
  14992 + const { maxValue, minValue } = additional || {}
  14993 +
  14994 + receiveValue = isNaN(receiveValue) ? 0 : Number(receiveValue)
  14995 + if (element) {
  14996 + if (type === flowmeterType.CIRCLE || type === flowmeterType.RECT) {
  14997 + const element = document.getElementById(id).querySelector('svg')
  14998 + element.style.setProperty('--value', receiveValue)
  14999 + }
  15000 + if (type === flowmeterType.THERMOMETER) {
  15001 + const element = document.getElementById(id).querySelector('svg')
  15002 + const range = 140
  15003 + const ratio = (190 - 15) / range
  15004 + receiveValue = receiveValue >= 0 ? receiveValue + 20 : 20 - Math.abs(receiveValue)
  15005 + receiveValue = 190 - receiveValue * ratio
  15006 + receiveValue = receiveValue < 15 ? 15 : receiveValue
  15007 + element.style.setProperty('--value', receiveValue)
  15008 + }
  15009 + }
  15010 + }
  15011 +
14276 handleParamSettingButton(message, record) { 15012 handleParamSettingButton(message, record) {
14277 const { data = {} } = message 15013 const { data = {} } = message
14278 if (!data) return 15014 if (!data) return
14279 const { nodeId, attr } = record 15015 const { nodeId, attr } = record
14280 - const node = this.getNodeByCmdId(nodeId) 15016 + const node = this.getNodeByNodeId(nodeId)
14281 const [[_timespan, receiveValue] = []] = data[attr] || [] 15017 const [[_timespan, receiveValue] = []] = data[attr] || []
  15018 + if (receiveValue === null || receiveValue === undefined) return
14282 this.updatePage(() => { 15019 this.updatePage(() => {
14283 - node.setAttribute('label', `<button class="param-setting-button">${receiveValue}</button>`) 15020 + node.setAttribute('label', receiveValue)
14284 }, node) 15021 }, node)
14285 } 15022 }
14286 15023
14287 handleImageComponent(message, record) { 15024 handleImageComponent(message, record) {
14288 const { data = {} } = message 15025 const { data = {} } = message
14289 const { nodeId, attr } = record 15026 const { nodeId, attr } = record
14290 - const node = this.getNodeByCmdId(nodeId) 15027 + const node = this.getNodeByNodeId(nodeId)
14291 const [[_timespan, receiveValue] = []] = data[attr] || [] 15028 const [[_timespan, receiveValue] = []] = data[attr] || []
14292 this.updatePage(() => { 15029 this.updatePage(() => {
14293 node.setAttribute('label', `<img class="basic-component__image" alt="图片" src="${receiveValue}" />`) 15030 node.setAttribute('label', `<img class="basic-component__image" alt="图片" src="${receiveValue}" />`)
@@ -14296,14 +15033,14 @@ class HandleDataSource { @@ -14296,14 +15033,14 @@ class HandleDataSource {
14296 15033
14297 /** 15034 /**
14298 * @description 更新实时数据 15035 * @description 更新实时数据
14299 - * @param {} message 15036 + * @param {} message
14300 * @param {} record 聚合方式 15037 * @param {} record 聚合方式
14301 */ 15038 */
14302 updateRealTimeDataSource(message, record) { 15039 updateRealTimeDataSource(message, record) {
14303 const { data = {} } = message 15040 const { data = {} } = message
14304 const { nodeId, attr, additional = {} } = record 15041 const { nodeId, attr, additional = {} } = record
14305 const { agg } = additional 15042 const { agg } = additional
14306 - const node = this.getNodeByCmdId(nodeId) 15043 + const node = this.getNodeByNodeId(nodeId)
14307 if (!node) return 15044 if (!node) return
14308 const enumConst = Sidebar.prototype.enumCellBasicAttribute 15045 const enumConst = Sidebar.prototype.enumCellBasicAttribute
14309 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping 15046 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
@@ -14334,7 +15071,7 @@ class HandleDataSource { @@ -14334,7 +15071,7 @@ class HandleDataSource {
14334 const { data = {} } = message 15071 const { data = {} } = message
14335 const { nodeId, attr, additional = {} } = record 15072 const { nodeId, attr, additional = {} } = record
14336 const { agg } = additional 15073 const { agg } = additional
14337 - const node = this.getNodeByCmdId(nodeId) 15074 + const node = this.getNodeByNodeId(nodeId)
14338 if (!node) return 15075 if (!node) return
14339 const enumConst = Sidebar.prototype.enumCellBasicAttribute 15076 const enumConst = Sidebar.prototype.enumCellBasicAttribute
14340 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping 15077 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
@@ -14411,9 +15148,9 @@ class HandleDataSource { @@ -14411,9 +15148,9 @@ class HandleDataSource {
14411 } 15148 }
14412 15149
14413 /** 15150 /**
14414 - *  
14415 - * @param {@} params  
14416 - * @returns 15151 + *
  15152 + * @param {@} params
  15153 + * @returns
14417 */ 15154 */
14418 getBasicChartOption(params = { dataList: [], attr: '', chartType: 'bar', action, additional }) { 15155 getBasicChartOption(params = { dataList: [], attr: '', chartType: 'bar', action, additional }) {
14419 const { dataList = [], attr = '', chartType = 'bar', action, additional = {} } = params 15156 const { dataList = [], attr = '', chartType = 'bar', action, additional = {} } = params
@@ -14500,6 +15237,8 @@ class HandleDataSource { @@ -14500,6 +15237,8 @@ class HandleDataSource {
14500 const [timespan, value] = dataList[i] 15237 const [timespan, value] = dataList[i]
14501 xAxisData.push(new Date(Number(timespan)).toLocaleTimeString()) 15238 xAxisData.push(new Date(Number(timespan)).toLocaleTimeString())
14502 seriesValue.push(Number(value)) 15239 seriesValue.push(Number(value))
  15240 + xAxisData = xAxisData.slice(0, 30)
  15241 + seriesValue = seriesValue.slice(0, 30)
14503 } 15242 }
14504 15243
14505 if (Number(oldOptions.dataZoom[0].endValue) === seriesValue.length - 1) { 15244 if (Number(oldOptions.dataZoom[0].endValue) === seriesValue.length - 1) {
@@ -14681,8 +15420,8 @@ class HandleDataSource { @@ -14681,8 +15420,8 @@ class HandleDataSource {
14681 15420
14682 /** 15421 /**
14683 * @description 获取cmdId 15422 * @description 获取cmdId
14684 - * @param {string} nodeId  
14685 - * @returns 15423 + * @param {string} nodeId
  15424 + * @returns
14686 */ 15425 */
14687 getCmdId(nodeId) { 15426 getCmdId(nodeId) {
14688 return this.DispatchInstance.getCmdId(nodeId) 15427 return this.DispatchInstance.getCmdId(nodeId)
@@ -14703,7 +15442,7 @@ class HandleDataSource { @@ -14703,7 +15442,7 @@ class HandleDataSource {
14703 * @param subscriptionId 15442 * @param subscriptionId
14704 * @return {*} 15443 * @return {*}
14705 */ 15444 */
14706 - getNodeByCmdId(nodeId) { 15445 + getNodeByNodeId(nodeId) {
14707 // const nodeId = this.getNodeIdByCmdId(subscriptionId) 15446 // const nodeId = this.getNodeIdByCmdId(subscriptionId)
14708 return this.contentAllCell.find(item => item.id === nodeId) 15447 return this.contentAllCell.find(item => item.id === nodeId)
14709 } 15448 }
@@ -14714,8 +15453,8 @@ class HandleDataSource { @@ -14714,8 +15453,8 @@ class HandleDataSource {
14714 15453
14715 /** 15454 /**
14716 * @description 发送socket 消息 15455 * @description 发送socket 消息
14717 - * @param {any} msg  
14718 - * @returns 15456 + * @param {any} msg
  15457 + * @returns
14719 */ 15458 */
14720 sendMsg(msg) { 15459 sendMsg(msg) {
14721 return this.DispatchInstance.sendMessageToGetRealTimeData(msg) 15460 return this.DispatchInstance.sendMessageToGetRealTimeData(msg)
@@ -14985,7 +15724,6 @@ class HandleDataInteraction { @@ -14985,7 +15724,6 @@ class HandleDataInteraction {
14985 const { COMPONENT_TYPE } = Sidebar.prototype.enumCellBasicAttribute 15724 const { COMPONENT_TYPE } = Sidebar.prototype.enumCellBasicAttribute
14986 const contentData = this.contentData 15725 const contentData = this.contentData
14987 const currentNode = this.contentAllCell.find(item => item.id === nodeId) 15726 const currentNode = this.contentAllCell.find(item => item.id === nodeId)
14988 -  
14989 const enumConst = { 15727 const enumConst = {
14990 VALUE: 'value', 15728 VALUE: 'value',
14991 ISSUED_WAY: 'way', 15729 ISSUED_WAY: 'way',
@@ -15166,7 +15904,7 @@ class HandleDataInteraction { @@ -15166,7 +15904,7 @@ class HandleDataInteraction {
15166 } else { 15904 } else {
15167 const replaceValue = currentNode.getAttribute(SWITCH_SEND_VALUE) 15905 const replaceValue = currentNode.getAttribute(SWITCH_SEND_VALUE)
15168 value = jsonParse(content.jsonCommand) 15906 value = jsonParse(content.jsonCommand)
15169 - value = replaceAttrPlaceholder(value, attr, replaceValue) 15907 + value = replaceAttrPlaceholder(value, attr, isNaN(replaceValue) ? 0 : Number(replaceValue))
15170 if (value) flag = true 15908 if (value) flag = true
15171 } 15909 }
15172 } else { 15910 } else {
@@ -15340,7 +16078,7 @@ class HandleDataInteraction { @@ -15340,7 +16078,7 @@ class HandleDataInteraction {
15340 } 16078 }
15341 16079
15342 try { 16080 try {
15343 - handle[componentType]() 16081 + handle[componentType]?.()
15344 } catch (error) { 16082 } catch (error) {
15345 throw error 16083 throw error
15346 } 16084 }
@@ -15501,7 +16239,7 @@ class HandleDynamicEffect { @@ -15501,7 +16239,7 @@ class HandleDynamicEffect {
15501 } 16239 }
15502 16240
15503 get contentAllCell() { 16241 get contentAllCell() {
15504 - // return this.graph.getDefaultParent().children || [] 16242 + // return this.graph.getDefaultParent().children || []
15505 return Object.entries(this.graph?.getModel()?.cells || {}).map(([_, item]) => item) || [] 16243 return Object.entries(this.graph?.getModel()?.cells || {}).map(([_, item]) => item) || []
15506 } 16244 }
15507 16245
@@ -15658,6 +16396,7 @@ class HandleDynamicEffect { @@ -15658,6 +16396,7 @@ class HandleDynamicEffect {
15658 } else if (condition.type === HandleDynamicEffect.enumDisplayType.HIDDEN) { 16396 } else if (condition.type === HandleDynamicEffect.enumDisplayType.HIDDEN) {
15659 isShow = false 16397 isShow = false
15660 } 16398 }
  16399 + console.log(condition)
15661 const updateFn = () => { 16400 const updateFn = () => {
15662 if (!isShow) { 16401 if (!isShow) {
15663 Object.keys(HandleDynamicEffect.enumActType).forEach(key => { 16402 Object.keys(HandleDynamicEffect.enumActType).forEach(key => {
@@ -15670,7 +16409,10 @@ class HandleDynamicEffect { @@ -15670,7 +16409,10 @@ class HandleDynamicEffect {
15670 const temp = this.actNodeMapping.get(node.id) 16409 const temp = this.actNodeMapping.get(node.id)
15671 temp.display = true 16410 temp.display = true
15672 } 16411 }
15673 - 16412 + if (condition.title) {
  16413 + node.setAttribute('label', condition.title)
  16414 + console.log(node)
  16415 + }
15674 node.setVisible(isShow) 16416 node.setVisible(isShow)
15675 } 16417 }
15676 this.insertOnceUpdateFn(node, updateFn) 16418 this.insertOnceUpdateFn(node, updateFn)
@@ -15976,8 +16718,8 @@ class HandleDynamicEffect { @@ -15976,8 +16718,8 @@ class HandleDynamicEffect {
15976 16718
15977 /** 16719 /**
15978 * @description 验证数据动效优先级 显示隐藏优先级最高 16720 * @description 验证数据动效优先级 显示隐藏优先级最高
15979 - * @param {string} nodeId  
15980 - * @returns 16721 + * @param {string} nodeId
  16722 + * @returns
15981 */ 16723 */
15982 validatePriority(nodeId) { 16724 validatePriority(nodeId) {
15983 return this.actNodeMapping.get(nodeId).display 16725 return this.actNodeMapping.get(nodeId).display
@@ -16011,7 +16753,7 @@ class UpdateQueue { @@ -16011,7 +16753,7 @@ class UpdateQueue {
16011 16753
16012 /** 16754 /**
16013 * @description 创建更新队列 16755 * @description 创建更新队列
16014 - * @param {number} time 16756 + * @param {number} time
16015 */ 16757 */
16016 createUpdateQueue(time) { 16758 createUpdateQueue(time) {
16017 const callback = () => { 16759 const callback = () => {
@@ -16165,13 +16907,13 @@ function RAFSetInterval(callback, time) { @@ -16165,13 +16907,13 @@ function RAFSetInterval(callback, time) {
16165 class Validate { 16907 class Validate {
16166 /** 16908 /**
16167 * @description 16909 * @description
16168 - * @type {{value: any, message: string, required?: boolean, validator?: any}[]} list 16910 + * @type {{value: any, message: string, required?: boolean, validator?: any}[]} list
16169 */ 16911 */
16170 list = [] 16912 list = []
16171 16913
16172 /** 16914 /**
16173 * @description 16915 * @description
16174 - * @param {{value: any, message: string, required?: boolean, validator?: any}[]} ruleList 16916 + * @param {{value: any, message: string, required?: boolean, validator?: any}[]} ruleList
16175 */ 16917 */
16176 constructor(ruleList = []) { 16918 constructor(ruleList = []) {
16177 this.list = ruleList 16919 this.list = ruleList
@@ -16183,7 +16925,7 @@ class Validate { @@ -16183,7 +16925,7 @@ class Validate {
16183 16925
16184 /** 16926 /**
16185 * @description 设置规则 16927 * @description 设置规则
16186 - * @param {{value: any, message: string, required?: boolean, validator?: any}} rule 16928 + * @param {{value: any, message: string, required?: boolean, validator?: any}} rule
16187 */ 16929 */
16188 set(rule) { 16930 set(rule) {
16189 this.list.push(rule) 16931 this.list.push(rule)
@@ -1682,8 +1682,10 @@ Graph.sanitizeHtml = function(value, editing) @@ -1682,8 +1682,10 @@ Graph.sanitizeHtml = function(value, editing)
1682 return null; 1682 return null;
1683 }; 1683 };
1684 function idX(id) { return id }; 1684 function idX(id) { return id };
1685 -  
1686 - return html_sanitize(value, urlX, idX); 1685 + // console.log(html_sanitize(value, urlX, idX))
  1686 + // TODO THINGS_KIT 取消html标签限制
  1687 + return value
  1688 + // return html_sanitize(value, urlX, idX);
1687 }; 1689 };
1688 1690
1689 /** 1691 /**
@@ -167,6 +167,10 @@ @@ -167,6 +167,10 @@
167 table-layout: fixed; 167 table-layout: fixed;
168 } 168 }
169 169
  170 +.override__table .org-tree .layui-input-block {
  171 + margin-left: 0;
  172 +}
  173 +
170 /**/ 174 /**/
171 .override__radio-default { 175 .override__radio-default {
172 margin-right: 16px; 176 margin-right: 16px;
@@ -503,36 +507,6 @@ @@ -503,36 +507,6 @@
503 margin-left: 0; 507 margin-left: 0;
504 } 508 }
505 509
506 -/* 参数设置按钮 */  
507 -.param-setting-button {  
508 - color: #fff;  
509 - border-color: #1890ff;  
510 - background: #1890ff;  
511 - text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);  
512 - box-shadow: 0 2px #0000000b;  
513 -  
514 - line-height: 1.5715;  
515 - position: relative;  
516 - display: inline-block;  
517 - font-weight: 400;  
518 - white-space: nowrap;  
519 - text-align: center;  
520 - background-image: none;  
521 - border: 1px solid transparent;  
522 - box-shadow: 0 2px #00000004;  
523 - cursor: pointer;  
524 - transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);  
525 - -webkit-user-select: none;  
526 - -moz-user-select: none;  
527 - -ms-user-select: none;  
528 - user-select: none;  
529 - touch-action: manipulation;  
530 - height: 32px;  
531 - padding: 4px 15px;  
532 - font-size: 14px;  
533 - border-radius: 2px;  
534 -}  
535 -  
536 /* ========== help message ========== */ 510 /* ========== help message ========== */
537 .thingskit-help-message { 511 .thingskit-help-message {
538 cursor: pointer; 512 cursor: pointer;
  1 +<svg class="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg"
  2 + style="--range: 4; --min: 50; --max: 70; --width: 500; --height: 500; --value: 50;">
  3 + <style>
  4 + .flowmeter-thermometer {
  5 + width: calc(min(var(--width), var(--height)) * 1px);
  6 + height: calc(min(var(--width), var(--height)) * 1px);
  7 + }
  8 +
  9 + .thermometer-mercury-column {
  10 + y: var(--value);
  11 + }
  12 +
  13 + .tick-label {
  14 + font-size: 12px;
  15 + text-align: right;
  16 + overflow: hidden;
  17 + text-overflow: ellipsis;
  18 + color: #5b6b73;
  19 + }
  20 +
  21 + .thermometer-mercury-column {
  22 + transition: y .5s cubic-bezier(0.19, 1, 0.22, 1);
  23 + }
  24 + </style>
  25 + <defs>
  26 + <radialGradient id="thermometerdiv_meter_2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
  27 + <stop offset="0%" style="stop-color: rgb(230, 200, 200);"></stop>
  28 + <stop offset="90%" style="stop-color: rgb(230, 0, 0);"></stop>
  29 + </radialGradient>
  30 + <clipPath id="over">
  31 + <rect width="100" height="190" x="100" y="10" />
  32 + </clipPath>
  33 + </defs>
  34 + <circle r="9.25" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px;">
  35 + </circle>
  36 + <rect x="99.75" y="14.25" height="192.75" width="18.5"
  37 + style="shape-rendering: crispedges; fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px;">
  38 + </rect>
  39 + <circle r="8.75" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: none;"></circle>
  40 + <circle r="18" cx="109" cy="207" style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136);"></circle>
  41 + <rect x="100.25" y="14.25" height="192.75" width="17.5"
  42 + style="shape-rendering: crispedges; fill: rgb(255, 255, 255); stroke: none;"></rect>
  43 + <line class="thermometer-min-line" x1="99.75" x2="140.25" y1="165" y2="165"
  44 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispedges;"></line>
  45 + <text class="thermometer-min-label" x="120.25" y="168.46428571428572" dy="0.72em"
  46 + style="fill: rgb(0, 0, 230); font-size: 10px;">min</text>
  47 + <line class="thermometer-max-line" x1="99.75" x2="140.25" y1="40" y2="40"
  48 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispedges;"></line>
  49 + <text class="thermometer-max-label" x="120.25" y="35.285714285714306"
  50 + style="fill: rgb(230, 0, 0); font-size: 10px;">max</text>
  51 + <rect class="thermometer-mercury-column" x="104" y="15" width="10.5" height="190"
  52 + style="shape-rendering: crispedges; fill: rgb(230, 0, 0);" clip-path="url(#over)"></rect>
  53 + <circle r="13" cx="109" cy="207"
  54 + style="fill: url(&quot;#thermometerdiv_meter_2&quot;); stroke: rgb(230, 0, 0); stroke-width: 2px;"></circle>
  55 + <foreignObject>
  56 + <div></div>
  57 + </foreignObject>
  58 + <g class="thermometer-temperature-axis" transform="translate(99.75,0)" fill="none" font-size="10"
  59 + font-family="sans-serif" text-anchor="end">
  60 + <g class="tick" opacity="1" transform="translate(0,190)">
  61 + <line stroke="currentColor" x2="-7"
  62 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  63 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  64 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">-20</div>
  65 + </foreignObject>
  66 + </g>
  67 + <g class="tick" opacity="1" transform="translate(0,165)">
  68 + <line stroke="currentColor" x2="-7"
  69 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  70 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  71 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">0</div>
  72 + </foreignObject>
  73 + </g>
  74 + <g class="tick" opacity="1" transform="translate(0,140)">
  75 + <line stroke="currentColor" x2="-7"
  76 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  77 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  78 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">20</div>
  79 + </foreignObject>
  80 + </g>
  81 + <g class="tick" opacity="1" transform="translate(0,115)">
  82 + <line stroke="currentColor" x2="-7"
  83 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  84 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  85 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">40</div>
  86 + </foreignObject>
  87 + </g>
  88 + <g class="tick" opacity="1" transform="translate(0,90)">
  89 + <line stroke="currentColor" x2="-7"
  90 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  91 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  92 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">60</div>
  93 + </foreignObject>
  94 + </g>
  95 + <g class="tick" opacity="1" transform="translate(0,65)">
  96 + <line stroke="currentColor" x2="-7"
  97 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  98 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  99 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">80</div>
  100 + </foreignObject>
  101 + </g>
  102 + <g class="tick" opacity="1" transform="translate(0,40)">
  103 + <line stroke="currentColor" x2="-7"
  104 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  105 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  106 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">100</div>
  107 + </foreignObject>
  108 + </g>
  109 + <g class="tick" opacity="1" transform="translate(0,15)">
  110 + <line stroke="currentColor" x2="-7"
  111 + style="stroke: rgb(136, 136, 136); shape-rendering: crispedges; stroke-width: 1px;"></line>
  112 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  113 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">120</div>
  114 + </foreignObject>
  115 + </g>
  116 + </g>
  117 +</svg>