Commit 9f945cdfe99a7f908465575e4670768c1a21364b

Authored by xp.Huang
2 parents b22c9db1 5a6f1a86

Merge branch 'feat/new-component-alarm-table' into 'main_dev'

Feat/new component alarm table

See merge request yunteng/thingskit-scada!72
... ... @@ -5,7 +5,7 @@
5 5
6 6 <head>
7 7 <!-- <title>Flowchart Maker &amp; Online Diagram Software</title>-->
8   - <title>thingskit 云组态</title>
  8 + <title>云组态</title>
9 9 <meta charset="utf-8">
10 10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
11 11 <meta name="Description"
... ... @@ -22,26 +22,26 @@
22 22 <meta name="mobile-web-app-capable" content="yes">
23 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=1686710985762">
26 26
27 27 <!-- load configure file -->
28   - <script src="./js/config/config.js?v=1681890411146"></script>
  28 + <script src="./js/config/config.js?v=1686710985762"></script>
29 29
30 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=1686710985762"></script>
32 32
33 33 <!-- storage persistent -->
34   - <script src="./js/const/persistentStorage.js?v=1681890411146"></script>
  34 + <script src="./js/const/persistentStorage.js?v=1686710985762"></script>
35 35 <!-- Global const -->
36   - <script src="./js/const/const.js?v=1681890411146"></script>
  36 + <script src="./js/const/const.js?v=1686710985762"></script>
37 37
38 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=1686710985762"></script>
  40 + <script src="./js/plugin/axios/DefHttp.js?v=1686710985762"></script>
  41 + <script src="./js/api/index.js?v=1686710985762"></script>
42 42
43 43 <!-- load script -->
44   - <script src="./js/config/loadScript.js?v=1681890411146"></script>
  44 + <script src="./js/config/loadScript.js?v=1686710985762"></script>
45 45
46 46 <!-- act editor -->
47 47 <!-- <script src="https://oss.yuntengcloud.com/iotdocs/thingskit-scada/ace.js"></script> -->
... ... @@ -54,7 +54,7 @@
54 54 <!-- <script src="https://vjs.zencdn.net/7.10.2/video.min.js"></script> -->
55 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=1686710985762"></script>
58 58 <!-- <link rel="stylesheet" href="https://cdnjs.loli.net/ajax/libs/layui/2.6.8/css/layui.min.css"
59 59 integrity="sha512-iQBJbsNHXUcgEIgWThd2dr8tOdKPvICwqjPEZYY81z3eMya44A5MiAqfWSCh+Ee1YzNYkdrI982Qhwgr8LEYOQ=="
60 60 crossorigin="anonymous" referrerpolicy="no-referrer" />
... ... @@ -63,7 +63,7 @@
63 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=1686710985762">
67 67
68 68 <script type="text/javascript">
69 69 /**
... ... @@ -306,7 +306,7 @@
306 306 var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') ||
307 307 (hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net');
308 308
309   - const releaseVersion = '1681890411146'
  309 + const releaseVersion = '1686710985762'
310 310 const appMinSrc = Enable_OSS ? `${OSS_Prefix}app.min.js?v=${releaseVersion}` : `js/app.min.js?v=${releaseVersion}`
311 311 function loadAppJS() {
312 312 mxscript(appMinSrc, function () {
... ... @@ -591,7 +591,6 @@
591 591 <script>
592 592
593 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 594 var urlParams = (function () {
596 595 var result = new Object();
597 596 var params = window.location.search.slice(1).split('&');
... ... @@ -649,7 +648,7 @@
649 648 left: 'center',
650 649 top: 'center',
651 650 style: {
652   - text: platfromInfo.name || 'ThingsKit Scada',
  651 + text: platfromInfo.name || 'Scada',
653 652 fontSize: isMobile() ? 20 : 70,
654 653 fontWeight: 'bold',
655 654 lineDash: [0, 200],
... ...
... ... @@ -207,7 +207,7 @@ class ConfigurationNodeApi {
207 207 * @returns
208 208 */
209 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 /**
... ... @@ -227,4 +227,24 @@ class ConfigurationNodeApi {
227 227 static getThingsModelServiceByDeviceProfileId(id) {
228 228 return defHttp.get('/yt/things_model/get_services/' + id)
229 229 }
  230 +
  231 + /**
  232 + *
  233 + * @typedef {object} AlarmListRequestParamsType
  234 + * @property { number } page
  235 + * @property { number } pageSize
  236 + * @property { string } status
  237 + * @property { string } alarmType
  238 + * @property { string } severity
  239 + * @property { string[] } deviceIds
  240 + * @property { string } organizationId
  241 + * @property { string } deviceName
  242 + * @property { string } startTime
  243 + * @property { string } endTime
  244 + * @param {AlarmListRequestParamsType} params
  245 + * @returns
  246 + */
  247 + static getAlarmList(params) {
  248 + return defHttp.post('/yt/alarm/configuration/page', params)
  249 + }
230 250 }
... ...
... ... @@ -4094,6 +4094,7 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) {
4094 4094 return pageSizeControl.apply(this, arguments)
4095 4095 }
4096 4096 Editor.configurationName = response.configurationName + ".drawio";
  4097 + document.title = response.configurationName
4097 4098 if (response.configurationContentList.length > 0) {
4098 4099 response.configurationContentList.forEach((item) => {
4099 4100 Editor.configurationContentId = item.id;
... ...
... ... @@ -277,7 +277,12 @@
277 277 /**
278 278 * @description 流量计
279 279 */
280   - FLOWMETER: 'flowmeter'
  280 + FLOWMETER: 'flowmeter',
  281 +
  282 + /**
  283 + * @description 告警列表
  284 + */
  285 + ALARM_LIST: 'alarmList'
281 286 }
282 287
283 288 Sidebar.prototype.enumComponentTypeValue = {
... ... @@ -373,7 +378,12 @@
373 378 /**
374 379 * @description 流量计
375 380 */
376   - FLOWMETER_PANEL: 'flowmeter'
  381 + FLOWMETER_PANEL: 'flowmeter',
  382 +
  383 + /**
  384 + * @description 告警列表
  385 + */
  386 + ALARM_LIST_PANEL: 'alarmListPanel'
377 387 }
378 388
379 389 /**
... ... @@ -730,8 +740,8 @@
730 740 //更多图形,显示出来的的标题跟id,同时包括图片
731 741
732 742 // TODO thingsKit 设置数据绑定展示面板
733   - 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 } = this.enumPermissionPanel
734   - const { LINE, LINE_CHART, REAL_TIME, TITLE, VARIABLE, DEFAULT, BAR_CHART, VIDEO, SWITCH, PARAMS_SETTING_BUTTON, DASHBOARD_CHART, IMAGE, FLOWMETER } = 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
735 745 this.setComponentPermission(LINE, [RUNNING_AND_STOP, DYNAMIC_EFFECT])
736 746 this.setComponentPermission(DEFAULT, [DYNAMIC_EFFECT])
737 747 this.setComponentPermission(REAL_TIME, [DYNAMIC_EFFECT])
... ... @@ -746,6 +756,7 @@
746 756 this.setComponentPermission(PARAMS_SETTING_BUTTON, [DATA_SOURCE, ONLY_SINGLE_EVENT])
747 757 this.setComponentPermission(IMAGE, [DATA_SOURCE])
748 758 this.setComponentPermission(FLOWMETER, [DATA_SOURCE, FLOWMETER_PANEL])
  759 + this.setComponentPermission(ALARM_LIST, [ALARM_LIST_PANEL])
749 760
750 761 var thingskitEntries = [
751 762 { title: mxResources.get('general'), id: 'general', image: IMAGE_PATH + '/sidebar-general.png' },
... ...
... ... @@ -65,6 +65,17 @@
65 65 this.setCellAttributes(cell, { [basicAttr.COMPONENT_TYPE]: componentType.IMAGE })
66 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 81 this.addPaletteFunctions(dt, '基础元件', true, fns);
... ... @@ -77,6 +88,13 @@
77 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 99 * @description charts cell发生resize时改变charts size
82 100 * @type {Function}
... ... @@ -87,6 +105,12 @@
87 105 const { width, height } = rect
88 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 114 cellResized.apply(this, arguments)
91 115 }
92 116
... ... @@ -128,7 +152,7 @@
128 152 const allDateNode = document.querySelectorAll('.thingKit-component__real-time .real-time__date')
129 153 for (const time of allTimeNode) {
130 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 158 for (const date of allDateNode) {
... ... @@ -138,4 +162,123 @@
138 162 }
139 163
140 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 +}
... ...
... ... @@ -2294,10 +2294,15 @@ EditorUi.prototype.initCanvas = function()
2294 2294 var layout = this.graph.getPageLayout();
2295 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 2308 graph.getPreferredPageSize = function(bounds, width, height)
... ... @@ -3383,9 +3388,25 @@ EditorUi.prototype.lightboxFit = function(maxHeight)
3383 3388 // LATER: Use initial graph bounds to avoid rounding errors
3384 3389 this.editor.graph.maxFitScale = this.lightboxMaxFitScale;
3385 3390 this.editor.graph.fit(border, null, null, null, null, null, maxHeight);
3386   - this.editor.graph.maxFitScale = null;
  3391 + // this.editor.graph.maxFitScale = null;
3387 3392 // TODO thingsKit lightbox 默认缩放为1
3388   - this.editor.graph.view.setScale(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 +
  3400 + var bounds = graph.getGraphBounds();
  3401 + var cw = graph.container.clientWidth - margin;
  3402 + var ch = graph.container.clientHeight - margin;
  3403 + var w = bounds.width / graph.view.scale;
  3404 + var h = bounds.height / graph.view.scale;
  3405 + var s = Math.min(max, Math.min(cw / w, ch / h));
  3406 +
  3407 + graph.view.scaleAndTranslate(s,
  3408 + (margin + cw - w * s) / (2 * s) - bounds.x / graph.view.scale,
  3409 + (margin + ch - h * s) / (2 * s) - bounds.y / graph.view.scale);
3389 3410 }
3390 3411 };
3391 3412
... ...
... ... @@ -4903,8 +4903,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4903 4903 const ss = ui.getSelectionState();
4904 4904 const vertices = ss.vertices || []
4905 4905 const sidebarInstance = ui.sidebar
4906   - console.log(vertices)
4907   - console.log(ui)
  4906 + // console.log(vertices)
  4907 + // console.log(ui)
4908 4908
4909 4909 const hasModifyNotSave = editor.status
4910 4910
... ... @@ -4922,7 +4922,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4922 4922 const graphId = vertices[0].id;
4923 4923
4924 4924 // 解构全局属性layui要用到的模块
4925   - const { layer, form, jquery: $, colorpicker, upload, element } = layui;
  4925 + const { layer, form, jquery: $, colorpicker, upload, element, laydate } = layui;
4926 4926
4927 4927 const CONTAINER_FILTER = 'containerFilter'
4928 4928 $(container).addClass('layui-form').attr('lay-filter', CONTAINER_FILTER)
... ... @@ -4959,8 +4959,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
4959 4959
4960 4960 /**
4961 4961 * @description 覆盖当前节点数据
4962   - * @param {'act' | 'dataSources' | 'event'} key
4963   - * @param {string}
  4962 + * @param {'act' | 'dataSources' | 'event'} key
  4963 + * @param {string}
4964 4964 */
4965 4965 function overrideCurrentData(key, uuid = 'id', value = {}) {
4966 4966 if (!currentNodeData[key]) currentNodeData[key] = []
... ... @@ -5063,7 +5063,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5063 5063 GATEWAY: 'GATEWAY',
5064 5064 ADDITIONAL: 'additional',
5065 5065 DEVICE_PROFILE_ID: 'deviceProfileId',
5066   - DEVICE_TYPE: 'deviceType'
  5066 + DEVICE_TYPE: 'deviceType',
  5067 + SOURCE_OPTION: 'sourceOption'
5067 5068 }
5068 5069
5069 5070 /**
... ... @@ -5133,7 +5134,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5133 5134 [permissionKey.SWITCH_STATE_SETTING]: createSwitchStateSettingPanel,
5134 5135 [permissionKey.ONLY_SINGLE_EVENT]: createParamsSettingButtonPanel,
5135 5136 [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel,
5136   - [permissionKey.FLOWMETER_PANEL]: createFlowmeterPanel
  5137 + [permissionKey.FLOWMETER_PANEL]: createFlowmeterPanel,
  5138 + [permissionKey.ALARM_LIST_PANEL]: createAlarmListPanel
5137 5139 }
5138 5140
5139 5141
... ... @@ -5143,7 +5145,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5143 5145 const permission = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
5144 5146 const needDisplayPanel = sidebarInstance.getComponentPermission(permission)
5145 5147 for (const key of needDisplayPanel) {
5146   - renderMapping[key]()
  5148 + renderMapping[key]?.()
5147 5149 }
5148 5150 if (needDisplayPanel.length) createSubmitPanel()
5149 5151 UseLayUi.nextTick(() => form.render())
... ... @@ -5178,7 +5180,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5178 5180 const event = currentNodeData.event ?? []
5179 5181 const actionType = {}
5180 5182
5181   - const hasExistEl = $(`.layui-form[lay-filter="${CONTAINER_FILTER}"]`).find('input[type="checkbox"]')
  5183 + const hasExistEl = $(`.interaction__container`).find('input[type="checkbox"]')
5182 5184 $(hasExistEl).each((i) => {
5183 5185 $(hasExistEl[i]).attr('disabled', true)
5184 5186 })
... ... @@ -5494,8 +5496,490 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5494 5496 }
5495 5497
5496 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 + /**
5497 5981 * @description 是否是折线图
5498   - * @param {boolean} isLineChart
  5982 + * @param {boolean} isLineChart
5499 5983 */
5500 5984 function createChartBindPanel(chartType) {
5501 5985 const fragment = document.createDocumentFragment()
... ... @@ -5996,6 +6480,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5996 6480 [componentType.PARAMS_SETTING_BUTTON]: getSwitchSubmitValue,
5997 6481 [componentType.IMAGE]: getSubmitValue,
5998 6482 [componentType.FLOWMETER]: getFlowmeterSubmitValue,
  6483 + [componentType.ALARM_LIST]: getAlarmListSubmitValue,
5999 6484 }
6000 6485
6001 6486 const cell = vertices[0]
... ... @@ -6095,8 +6580,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6095 6580 }
6096 6581
6097 6582 /**
6098   - * @description 处理开关组件的保存值
6099   - * @returns
  6583 + * @description 处理开关组件的保存值
  6584 + * @returns
6100 6585 */
6101 6586 function getSwitchSubmitValue(field) {
6102 6587 const dataSources = getDataSourceBindValue()
... ... @@ -6133,18 +6618,6 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6133 6618
6134 6619 function getFlowmeterSubmitValue(field = {}) {
6135 6620 const additionalKey = HandleDataSource.enumConst
6136   - const minValue = field[additionalKey.MIN_VALUE]
6137   - const maxValue = field[additionalKey.MAX_VALUE]
6138   -
6139   - if ([minValue, maxValue].some(item => isNaN(item))) {
6140   - UseLayUi.topErrorMsg('最大值或最小值不是一个数字!')
6141   - return false
6142   - }
6143   -
6144   - if (Number(minValue) > Number(maxValue)) {
6145   - UseLayUi.topErrorMsg('最小值大于最大值!')
6146   - return false
6147   - }
6148 6621
6149 6622 const value = {
6150 6623 configurationId,
... ... @@ -6160,8 +6633,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6160 6633 [enumDataSourceConst.ATTR]: field[enumDataSourceConst.ATTR],
6161 6634 [enumDataSourceConst.ADDITIONAL]: {
6162 6635 [additionalKey.COMPONENT_TYPE]: field[additionalKey.COMPONENT_TYPE],
6163   - [additionalKey.MIN_VALUE]: field[additionalKey.MIN_VALUE],
6164   - [additionalKey.MAX_VALUE]: field[additionalKey.MAX_VALUE],
  6636 + // [additionalKey.MIN_VALUE]: field[additionalKey.MIN_VALUE],
  6637 + // [additionalKey.MAX_VALUE]: field[additionalKey.MAX_VALUE],
6165 6638 [additionalKey.BG_COLOR]: field[additionalKey.BG_COLOR],
6166 6639 [additionalKey.WAVE_FIRST_COLOR]: field[additionalKey.WAVE_FIRST_COLOR],
6167 6640 [additionalKey.WAVE_SECOND_COLOR]: field[additionalKey.WAVE_SECOND_COLOR],
... ... @@ -6171,6 +6644,27 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6171 6644 }
6172 6645 return value
6173 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 + }
6174 6668 }
6175 6669
6176 6670 /**
... ... @@ -7141,7 +7635,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7141 7635 }
7142 7636
7143 7637 /**
7144   - * @description
  7638 + * @description
7145 7639 */
7146 7640 const recordData = {
7147 7641 enabled: false
... ... @@ -7158,7 +7652,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7158 7652
7159 7653 // 参数设置
7160 7654 [enumConst.JSON_COMMAND]: content[enumConst.JSON_COMMAND],
7161   - [enumConst.WAY]: content[enumConst.WAY],
  7655 + [enumConst.WAY]: content[enumConst.WAY] || enumWayType.ONE_WAY,
7162 7656 [enumConst.COMMAND_TYPE]: content[enumConst.COMMAND_TYPE],
7163 7657 [enumConst.TCP_COMMAND]: content[enumConst.TCP_COMMAND],
7164 7658 [enumConst.TRANSPORTTYPE]: content[enumConst.TRANSPORTTYPE],
... ... @@ -7182,7 +7676,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7182 7676
7183 7677 /**
7184 7678 * @description 控制form
7185   - * @param {enumActionType} value
  7679 + * @param {enumActionType} value
7186 7680 */
7187 7681 async function controlFormDisplay(value, isTCP, isCustom) {
7188 7682 if (value === enumActionType.PAGE) {
... ... @@ -7250,7 +7744,6 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7250 7744 return false
7251 7745 }
7252 7746 } else {
7253   - console.log(formVal)
7254 7747 if (!isJson(formVal[enumConst.JSON_COMMAND])) {
7255 7748 UseLayUi.topErrorMsg('命令配置存在错误')
7256 7749 return false
... ... @@ -7323,7 +7816,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7323 7816
7324 7817 /**
7325 7818 * @description 生成命令类型选项
7326   - * @returns
  7819 + * @returns
7327 7820 */
7328 7821 function generateCommandTypeOptions() {
7329 7822 const options = [
... ... @@ -7513,7 +8006,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7513 8006 <label class="layui-form-label" style="width: 120px;">单向/双向 ${createHelpMessage(`单向:服务器向网关设备、直连设备发送指令。发送指令后,设备不会返回任何信息。\n
7514 8007 双向:服务器向网关设备、直连设备发送指令。发送指令后,设备返回响应信息。`, 'way')}</label>
7515 8008 <div class="layui-input-block" style="margin-left: 150px;">
7516   - <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>
7517 8010 <input type="radio" name="${enumConst.WAY}" value="${enumWayType.TWO_WAY}" title="双向">
7518 8011 </div>
7519 8012 </div>
... ... @@ -7919,7 +8412,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
7919 8412
7920 8413 /**
7921 8414 * @description
7922   - * @param {} event
  8415 + * @param {} event
7923 8416 */
7924 8417 function handleStateSetting(event) {
7925 8418
... ... @@ -8405,7 +8898,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8405 8898 }
8406 8899
8407 8900 /**
8408   - *
  8901 + *
8409 8902 * @returns {{orgId: string, attr: string, deviceId: string, devi}}
8410 8903 */
8411 8904 function getValue() {
... ... @@ -8737,7 +9230,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8737 9230 CONTAINER_FILTER: 'imgContainerFilter',
8738 9231
8739 9232 /**
8740   - * @description
  9233 + * @description
8741 9234 */
8742 9235 SET_IMG_EL: 'variableImageTableSetImgEl',
8743 9236
... ... @@ -8771,7 +9264,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8771 9264
8772 9265 /**
8773 9266 * @description 设置回显
8774   - * @param {} value
  9267 + * @param {} value
8775 9268 */
8776 9269 function setValue(value = {}) {
8777 9270 form.val(getFormFilter, value)
... ... @@ -8779,7 +9272,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
8779 9272
8780 9273 /**
8781 9274 * @description 获取值
8782   - * @returns
  9275 + * @returns
8783 9276 */
8784 9277 function getValue() {
8785 9278 return form.val(getFormFilter) || {}
... ... @@ -12650,10 +13143,22 @@ class UseLayUi {
12650 13143 `
12651 13144 }
12652 13145
  13146 + static parseStringToJSON(string, defaultValue = {}) {
  13147 + try {
  13148 + if (typeof string === 'string') {
  13149 + const value = JSON.parse(string)
  13150 + if (typeof value === 'object') return value
  13151 + }
  13152 + return defaultValue
  13153 + } catch (error) {
  13154 + return defaultValue
  13155 + }
  13156 + }
  13157 +
12653 13158 /**
12654 13159 * @description 生成输入框控件
12655   - * @param {{label: string, value: string, labelWidth: number, numberInput: boolean}} params
12656   - * @returns
  13160 + * @param {{label: string, value: string, labelWidth: number, numberInput: boolean}} params
  13161 + * @returns
12657 13162 */
12658 13163 static createInputTemplate({ label, value, labelWidth = 80, required = false, type = 'TEXT' }) {
12659 13164 return `
... ... @@ -12671,19 +13176,19 @@ class UseLayUi {
12671 13176 }
12672 13177
12673 13178 /**
12674   - *
  13179 + *
12675 13180 * @param {{
12676   - * accessMode: 'r' | 'w',
  13181 + * accessMode: 'r' | 'w',
12677 13182 * functionName: string,
12678   - * id: string,
12679   - * identifier: string,
  13183 + * id: string,
  13184 + * identifier: string,
12680 13185 * dataType: {
12681   - * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
12682   - * specs: { unit: {value: string, label: string},
12683   - * unitName: string,
12684   - * valueRange: {min: number, max: number},
  13186 + * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
  13187 + * specs: { unit: {value: string, label: string},
  13188 + * unitName: string,
  13189 + * valueRange: {min: number, max: number},
12685 13190 * length: number
12686   - * }}}[]} inputData
  13191 + * }}}[]} inputData
12687 13192 * @param {number} labelWidth = 80
12688 13193 * @param {}
12689 13194 */
... ... @@ -12701,20 +13206,20 @@ class UseLayUi {
12701 13206 return template
12702 13207 }
12703 13208 /**
12704   - *
  13209 + *
12705 13210 * @param {{
12706   - * accessMode: 'r' | 'w',
  13211 + * accessMode: 'r' | 'w',
12707 13212 * functionName: string,
12708   - * id: string,
12709   - * identifier: string,
  13213 + * id: string,
  13214 + * identifier: string,
12710 13215 * dataType: {
12711   - * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
12712   - * specs: { unit: {value: string, label: string},
12713   - * unitName: string,
12714   - * valueRange: {min: number, max: number},
  13216 + * type: 'TEXT' | 'INT' | 'DOUBLE' | 'STRUCT',
  13217 + * specs: { unit: {value: string, label: string},
  13218 + * unitName: string,
  13219 + * valueRange: {min: number, max: number},
12715 13220 * length: number
12716   - * }}}[]} inputData
12717   - * @param {Record<string, any>} value = 80
  13221 + * }}}[]} inputData
  13222 + * @param {Record<string, any>} value = 80
12718 13223 */
12719 13224 static validateThingsModelInputDataForm(inputData, value, needFormat = false) {
12720 13225 let flag = true
... ... @@ -12817,12 +13322,13 @@ class UseLayUi {
12817 13322
12818 13323 /**
12819 13324 * @description generator options template 生产下拉选项模板
12820   - * @param options
12821   - * @param {object[]} [dataSource] options.dataSource
12822   - * @param {boolean} [addPlaceholderOption = true] options.addPlaceholderOption
12823   - * @param {string} [labelField = 'name'] options.labelField
12824   - * @param {string} [valueField = 'name'] options.valueField
12825   - * @param {string} [alias] options.alias
  13325 + * @typedef {Object} GenerateOptionTemplateParamsType
  13326 + * @property {any[]} [dataSource] options.dataSource
  13327 + * @property {boolean} [addPlaceholderOption = true] options.addPlaceholderOption
  13328 + * @property {string} [labelField = 'name'] options.labelField
  13329 + * @property {string} [valueField = 'name'] options.valueField
  13330 + * @property {string} [alias] options.alias
  13331 + * @param {GenerateOptionTemplateParamsType} options
12826 13332 * @returns {*}
12827 13333 */
12828 13334 static generateOptionTemplate(options) {
... ... @@ -12967,17 +13473,21 @@ class UseLayUi {
12967 13473 // TODO Tree Select
12968 13474 /**
12969 13475 * @description create a tree select controls
12970   - * @param {string} [options.layFilter] options.layFilter
12971   - * @param {string} [options.label] options.label
12972   - * @param {object} [options.treeProps] options.treeProps
12973   - * @param {HTMLDivElement} [options.elem] options.elem
12974   - * @param {boolean} [options.singleUsage = true] options.singleUsage
12975   - * @param {Function} [options.customSetTree = ((record) => ({ id: record.id, title: record.name }))] options.customSetTree
12976   - * @param {boolean} [options.autoFormatDataSource = true] options.autoFormatDataSource
12977   - * @param {string} [options.layVerify] options.layVerify
12978   - * @param {string} [options.layVerType] options.layVerType
12979   - * @param {boolean} [options.addPlaceholderOption] options.addPlaceholderOption
12980   - * @param {Function} [options.treeProps.onReady] options.treeProps.onReady
  13476 + * @typedef CreateTreeSelectParamsType
  13477 + * @property {string} [layFilter] options.layFilter
  13478 + * @property {string} [label] options.label
  13479 + * @property {object} [treeProps] options.treeProps
  13480 + * @property {HTMLDivElement} [elem] options.elem
  13481 + * @property {boolean} [singleUsage = true] options.singleUsage
  13482 + * @property {Function} [customSetTree = ((record) => ({ id: record.id, title: record.name }))] options.customSetTree
  13483 + * @property {boolean} [autoFormatDataSource = true] options.autoFormatDataSource
  13484 + * @property {string} [layVerify] options.layVerify
  13485 + * @property {string} [layVerType] options.layVerType
  13486 + * @property {boolean} [addPlaceholderOption] options.addPlaceholderOption
  13487 + * @property {boolean} [hiddenLabel] - hiddenLabel
  13488 + * @property {Function} [treeProps.onReady] options.treeProps.onReady
  13489 + * @property {boolean} [popupMountToBody] popupMountToBody
  13490 + * @param {CreateTreeSelectParamsType} options
12981 13491 */
12982 13492 static createTreeSelect(options) {
12983 13493 const CLASS_NAME = 'things-kit-tree-select'
... ... @@ -12998,27 +13508,29 @@ class UseLayUi {
12998 13508 childrenField = 'children',
12999 13509 layVerify,
13000 13510 layVerType,
13001   - addPlaceholderOption
  13511 + addPlaceholderOption,
  13512 + hiddenLabel,
  13513 + popupMountToBody
13002 13514 } = options
13003 13515
13004 13516 let { data = [], click, onReady } = treeProps
13005 13517
13006 13518 let template = `
13007   - <div class="layui-form-item ${CLASS_NAME} ${className}">
13008   - <label class="layui-form-label">${label}</label>
  13519 + <div class="layui-form-item ${CLASS_NAME} ${className}" style="margin-bottom: ${hiddenLabel ? '0' : '15px'};">
  13520 + <label class="layui-form-label" style="display: ${hiddenLabel ? 'none' : 'block'};">${label}</label>
13009 13521 <div class="layui-input-block">
13010   - <div class="layui-unselect layui-form-select ${SELECT_CLS}">
13011   - <div class="layui-select-title">
13012   - <span class="layui-input layui-unselect tree-select__label">请选择</span>
13013   - <input ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)} type="text" style="visibility: hidden; position: absolute; top: 0" name="${layFilter}">
13014   - <i class="layui-edge"></i>
  13522 + <div class="layui-unselect layui-form-select ${SELECT_CLS}">
  13523 + <div class="layui-select-title">
  13524 + <span class="layui-input layui-unselect tree-select__label">请选择</span>
  13525 + <input ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)} type="text" style="visibility: hidden; position: absolute; top: 0" name="${layFilter}">
  13526 + <i class="layui-edge"></i>
  13527 + </div>
  13528 + <dl class="layui-anim layui-anim-upbit">
  13529 + <dd>
  13530 + <ul class="tree-select__tree-mount"></ul>
  13531 + </dd>
  13532 + </dl>
13015 13533 </div>
13016   - <dl class="layui-anim layui-anim-upbit">
13017   - <dd>
13018   - <ul class="tree-select__tree-mount"></ul>
13019   - </dd>
13020   - </dl>
13021   - </div>
13022 13534 </div>
13023 13535 </div>`
13024 13536
... ... @@ -13042,7 +13554,7 @@ class UseLayUi {
13042 13554 $(elem).html(template)
13043 13555 const treeData = UseLayUi.formatTreeDataSource(data, customSetTree, valueField, labelField, childrenField)
13044 13556 if (addPlaceholderOption) treeData.unshift({ title: '请选择', id: undefined })
13045   - // mount tree
  13557 + // mount tree
13046 13558 tree.render({
13047 13559 ...treeProps,
13048 13560 ...(autoFormatDataSource ? { data: treeData } : {}),
... ... @@ -13059,6 +13571,21 @@ class UseLayUi {
13059 13571 $(document).find('.layui-form-select').removeClass('layui-form-selected')
13060 13572 $(this).parents(`.${SELECT_CLS}`).toggleClass("layui-form-selected");
13061 13573 layui.stope(e);
  13574 + if (popupMountToBody) {
  13575 + const popup = $(this).parents(`.${SELECT_CLS}`).find('.layui-anim')
  13576 + const titleEl = $(this).parents(`.${SELECT_CLS}`).find('.layui-select-title')
  13577 + const offset = $(titleEl).offset()
  13578 + const height = $(titleEl).height()
  13579 + const width = $(titleEl).width()
  13580 +
  13581 + $(popup).css({
  13582 + position: 'fixed',
  13583 + top: `${offset.top + height}px`,
  13584 + 'min-width': width,
  13585 + left: `${offset.left}px`,
  13586 + width
  13587 + })
  13588 + }
13062 13589 })
13063 13590 .on('click', '.layui-anim', (e) => {
13064 13591 layui.stope(e)
... ... @@ -13213,7 +13740,7 @@ class UseLayUi {
13213 13740 class Utils {
13214 13741 /**
13215 13742 * @description 字符串是否能转换为对象
13216   - * @param {string} value
  13743 + * @param {string} value
13217 13744 */
13218 13745 static stringIsJSON(value) {
13219 13746 try {
... ... @@ -13227,8 +13754,8 @@ class Utils {
13227 13754
13228 13755 /**
13229 13756 * @description 字符串转对象
13230   - * @param {string} value
13231   - * @returns
  13757 + * @param {string} value
  13758 + * @returns
13232 13759 */
13233 13760 static stringToJSON(value, defaultValue = {}) {
13234 13761 try {
... ... @@ -13769,7 +14296,7 @@ class DispatchCenter {
13769 14296 subList.forEach(item => {
13770 14297 const { dataOrigin, additional } = item
13771 14298 if (dataOrigin === 'dataSources') {
13772   - if (additional && (additional || {})?.dataType ) {
  14299 + if (additional && (additional || {})?.dataType) {
13773 14300 const { dataType } = additional || {}
13774 14301 if (dataType === HandleDataSource.enumDataBindType.REAL) {
13775 14302 this.dataSourceHandlerInstance.updateRealTimeDataSource(message, item)
... ... @@ -13821,9 +14348,15 @@ class DispatchCenter {
13821 14348 }
13822 14349
13823 14350 afterGetContentDataNode() {
  14351 + this.handleFlowmeterComponent()
  14352 + this.handleAlarmList()
  14353 + }
  14354 +
  14355 + handleFlowmeterComponent() {
13824 14356 setTimeout(() => {
13825 14357 const componentType = Sidebar.prototype.enumComponentType
13826   - const flowmeterComponent = this.contentData.dataSources.filter(item => item.additional && (item.additional || {}).componentType === componentType.FLOWMETER)
  14358 + if (!this.contentData?.dataSources) return
  14359 + const flowmeterComponent = this.contentData?.dataSources?.filter(item => item.additional && (item.additional || {}).componentType === componentType.FLOWMETER)
13827 14360 const { MIN_VALUE, MAX_VALUE, BG_COLOR, WAVE_FIRST_COLOR, WAVE_SECOND_COLOR, WAVE_THIRD_COLOR } = HandleDataSource.enumConst
13828 14361 flowmeterComponent.forEach(item => {
13829 14362 const { nodeId, additional } = item
... ... @@ -13853,6 +14386,52 @@ class DispatchCenter {
13853 14386 }, 10);
13854 14387 }
13855 14388
  14389 +
  14390 + // 处理告警列表
  14391 + async handleAlarmList() {
  14392 + const componentType = Sidebar.prototype.enumComponentType
  14393 + if (!this.contentData?.dataSources) return
  14394 + const alarmList = this.contentData?.dataSources?.filter(item => item.additional && (item.additional || {}).componentType === componentType.ALARM_LIST)
  14395 + const allCell = Object.entries(this.graph?.getModel()?.cells || {}).map(([_, item]) => item) || []
  14396 + const { UUID } = AlarmListComponent.getAttributeKeys()
  14397 + const { jquery: $ } = layui
  14398 + for (const item of alarmList || []) {
  14399 + const { nodeId, sourceOption } = item || {}
  14400 + const { devicesInfo, autoPlay, interval, startTime, endTime } = sourceOption || {}
  14401 + const node = allCell.find(item => item.id === nodeId)
  14402 + const deviceIds = UseLayUi.parseStringToJSON(devicesInfo, []).map(item => item.deviceId)
  14403 + const id = node.getAttribute(UUID)
  14404 + const element = document.getElementById(id)
  14405 + if (element) {
  14406 + const [err, data] = await to(ConfigurationNodeApi.getAlarmList({ page: 1, pageSize: 30, startTime: Number(startTime), endTime: Number(endTime), deviceIds }))
  14407 + if (err) return
  14408 + if (data?.items && data?.items?.length) {
  14409 + const template = AlarmListComponent.createAlarmItem(data.items)
  14410 + $(`#${id}`).find('.list-wrapper').html(template)
  14411 + } else {
  14412 + $(`#${id}`).find('.list-wrapper').html(`<div>暂无数据</div>`)
  14413 + }
  14414 + if (autoPlay) {
  14415 + const wrapperHeight = $(`#${id}`).height()
  14416 + const listWrapper = $(`#${id}`).find('.list-wrapper')
  14417 + const allHeight = listWrapper.height()
  14418 + const itemHeight = allHeight / listWrapper.children().length
  14419 + let scrollDistance = 0
  14420 + const cancel = RAFSetInterval(() => {
  14421 + try {
  14422 + scrollDistance = itemHeight + scrollDistance
  14423 + if (scrollDistance + wrapperHeight > allHeight) scrollDistance = 0
  14424 + document.getElementById(id).scrollTo({ top: scrollDistance, behavior: 'smooth' })
  14425 + } catch (error) {
  14426 + cancel?.()
  14427 + }
  14428 +
  14429 + }, Number(interval) * 1000)
  14430 + }
  14431 + }
  14432 + }
  14433 + }
  14434 +
13856 14435 sendSubscribeMessage() {
13857 14436 const message = this.generateSubscribeMessage()
13858 14437 this.socket.send(JSON.stringify(message))
... ... @@ -13890,7 +14469,7 @@ class DispatchCenter {
13890 14469 * orgName?: string,
13891 14470 * slaveDeviceName?: string
13892 14471 * }
13893   - * }} record
  14472 + * }} record
13894 14473 */
13895 14474 const setDeviceMapping = (record) => {
13896 14475 const { deviceId, slaveDeviceId } = record
... ... @@ -13906,12 +14485,12 @@ class DispatchCenter {
13906 14485 /**
13907 14486 * @type {{
13908 14487 * id: string,
13909   - * nodeId: string,
  14488 + * nodeId: string,
13910 14489 * deviceId: string,
13911   - * slaveDeviceId: string,
13912   - * attr: string,
13913   - * enabled: boolean,
13914   - * additional: object,
  14490 + * slaveDeviceId: string,
  14491 + * attr: string,
  14492 + * enabled: boolean,
  14493 + * additional: object,
13915 14494 * condition: object
13916 14495 * }[]}
13917 14496 */
... ... @@ -13931,10 +14510,10 @@ class DispatchCenter {
13931 14510 })
13932 14511
13933 14512 /**
13934   - * @param {{
13935   - * entityId: string,
13936   - * cmdId: number,
13937   - * keys: string,
  14513 + * @param {{
  14514 + * entityId: string,
  14515 + * cmdId: number,
  14516 + * keys: string,
13938 14517 * agg?: string,
13939 14518 * interval?: number,
13940 14519 * startTs?: number,
... ... @@ -14296,10 +14875,9 @@ class HandleDataSource {
14296 14875
14297 14876 /**
14298 14877 * @description 更新变量值
14299   - * @param {} message
  14878 + * @param {} message
14300 14879 */
14301 14880 updateCommonDataSource(message, record) {
14302   - console.log('enter')
14303 14881 const { nodeId, attr } = record
14304 14882 const node = this.getNodeByNodeId(nodeId)
14305 14883 const { data } = message
... ... @@ -14337,13 +14915,14 @@ class HandleDataSource {
14337 14915
14338 14916 /**
14339 14917 * @description 处理switch 组件
14340   - * @param {} message
  14918 + * @param {} message
14341 14919 */
14342 14920 handleSwitchComponent(message, record) {
14343 14921 const { data = {} } = message
14344 14922 const { nodeId, attr } = record
14345 14923 const node = this.getNodeByNodeId(nodeId)
14346 14924 const [[_timespan, receiveValue] = []] = data[attr] || []
  14925 + if (receiveValue === null || receiveValue === undefined) return
14347 14926 const switchConfig = this.DispatchInstance.contentData.act.find(item => item.id === nodeId && item.type === 'SWITCH')
14348 14927 const { condition = [] } = switchConfig || {}
14349 14928 let reg = /image=[^;]+/g
... ... @@ -14363,7 +14942,7 @@ class HandleDataSource {
14363 14942 if ((style || '').includes(imagePath)) return
14364 14943 const sendValue = getSendValue(type)
14365 14944 node.setStyle(style.replace(reg, `image=${imagePath}`))
14366   - node.setAttribute('label', '')
  14945 + // node.setAttribute('label', '')
14367 14946 node.setAttribute(SWITCH_VALUE, receiveValue)
14368 14947 node.setAttribute(SWITCH_SEND_VALUE, sendValue)
14369 14948 node.setAttribute(SWITCH_STATE, type)
... ... @@ -14400,7 +14979,6 @@ class HandleDataSource {
14400 14979 const { maxValue, minValue } = additional || {}
14401 14980
14402 14981 receiveValue = isNaN(receiveValue) ? 0 : Number(receiveValue)
14403   - console.log('enter')
14404 14982 if (element) {
14405 14983 if (type === flowmeterType.CIRCLE || type === flowmeterType.RECT) {
14406 14984 const element = document.getElementById(id).querySelector('svg')
... ... @@ -14424,8 +15002,9 @@ class HandleDataSource {
14424 15002 const { nodeId, attr } = record
14425 15003 const node = this.getNodeByNodeId(nodeId)
14426 15004 const [[_timespan, receiveValue] = []] = data[attr] || []
  15005 + if (receiveValue === null || receiveValue === undefined) return
14427 15006 this.updatePage(() => {
14428   - node.setAttribute('label', `<button class="param-setting-button">${receiveValue}</button>`)
  15007 + node.setAttribute('label', receiveValue)
14429 15008 }, node)
14430 15009 }
14431 15010
... ... @@ -14441,7 +15020,7 @@ class HandleDataSource {
14441 15020
14442 15021 /**
14443 15022 * @description 更新实时数据
14444   - * @param {} message
  15023 + * @param {} message
14445 15024 * @param {} record 聚合方式
14446 15025 */
14447 15026 updateRealTimeDataSource(message, record) {
... ... @@ -14556,9 +15135,9 @@ class HandleDataSource {
14556 15135 }
14557 15136
14558 15137 /**
14559   - *
14560   - * @param {@} params
14561   - * @returns
  15138 + *
  15139 + * @param {@} params
  15140 + * @returns
14562 15141 */
14563 15142 getBasicChartOption(params = { dataList: [], attr: '', chartType: 'bar', action, additional }) {
14564 15143 const { dataList = [], attr = '', chartType = 'bar', action, additional = {} } = params
... ... @@ -14826,8 +15405,8 @@ class HandleDataSource {
14826 15405
14827 15406 /**
14828 15407 * @description 获取cmdId
14829   - * @param {string} nodeId
14830   - * @returns
  15408 + * @param {string} nodeId
  15409 + * @returns
14831 15410 */
14832 15411 getCmdId(nodeId) {
14833 15412 return this.DispatchInstance.getCmdId(nodeId)
... ... @@ -14859,8 +15438,8 @@ class HandleDataSource {
14859 15438
14860 15439 /**
14861 15440 * @description 发送socket 消息
14862   - * @param {any} msg
14863   - * @returns
  15441 + * @param {any} msg
  15442 + * @returns
14864 15443 */
14865 15444 sendMsg(msg) {
14866 15445 return this.DispatchInstance.sendMessageToGetRealTimeData(msg)
... ... @@ -15130,7 +15709,6 @@ class HandleDataInteraction {
15130 15709 const { COMPONENT_TYPE } = Sidebar.prototype.enumCellBasicAttribute
15131 15710 const contentData = this.contentData
15132 15711 const currentNode = this.contentAllCell.find(item => item.id === nodeId)
15133   -
15134 15712 const enumConst = {
15135 15713 VALUE: 'value',
15136 15714 ISSUED_WAY: 'way',
... ... @@ -15311,7 +15889,7 @@ class HandleDataInteraction {
15311 15889 } else {
15312 15890 const replaceValue = currentNode.getAttribute(SWITCH_SEND_VALUE)
15313 15891 value = jsonParse(content.jsonCommand)
15314   - value = replaceAttrPlaceholder(value, attr, replaceValue)
  15892 + value = replaceAttrPlaceholder(value, attr, isNaN(replaceValue) ? 0 : Number(replaceValue))
15315 15893 if (value) flag = true
15316 15894 }
15317 15895 } else {
... ... @@ -15485,7 +16063,7 @@ class HandleDataInteraction {
15485 16063 }
15486 16064
15487 16065 try {
15488   - handle[componentType]()
  16066 + handle[componentType]?.()
15489 16067 } catch (error) {
15490 16068 throw error
15491 16069 }
... ... @@ -15646,7 +16224,7 @@ class HandleDynamicEffect {
15646 16224 }
15647 16225
15648 16226 get contentAllCell() {
15649   - // return this.graph.getDefaultParent().children || []
  16227 + // return this.graph.getDefaultParent().children || []
15650 16228 return Object.entries(this.graph?.getModel()?.cells || {}).map(([_, item]) => item) || []
15651 16229 }
15652 16230
... ... @@ -16048,8 +16626,8 @@ class HandleDynamicEffect {
16048 16626
16049 16627 /**
16050 16628 * @description 验证数据动效优先级 显示隐藏优先级最高
16051   - * @param {string} nodeId
16052   - * @returns
  16629 + * @param {string} nodeId
  16630 + * @returns
16053 16631 */
16054 16632 validatePriority(nodeId) {
16055 16633 return this.actNodeMapping.get(nodeId).display
... ... @@ -16083,7 +16661,7 @@ class UpdateQueue {
16083 16661
16084 16662 /**
16085 16663 * @description 创建更新队列
16086   - * @param {number} time
  16664 + * @param {number} time
16087 16665 */
16088 16666 createUpdateQueue(time) {
16089 16667 const callback = () => {
... ... @@ -16237,13 +16815,13 @@ function RAFSetInterval(callback, time) {
16237 16815 class Validate {
16238 16816 /**
16239 16817 * @description
16240   - * @type {{value: any, message: string, required?: boolean, validator?: any}[]} list
  16818 + * @type {{value: any, message: string, required?: boolean, validator?: any}[]} list
16241 16819 */
16242 16820 list = []
16243 16821
16244 16822 /**
16245 16823 * @description
16246   - * @param {{value: any, message: string, required?: boolean, validator?: any}[]} ruleList
  16824 + * @param {{value: any, message: string, required?: boolean, validator?: any}[]} ruleList
16247 16825 */
16248 16826 constructor(ruleList = []) {
16249 16827 this.list = ruleList
... ... @@ -16255,7 +16833,7 @@ class Validate {
16255 16833
16256 16834 /**
16257 16835 * @description 设置规则
16258   - * @param {{value: any, message: string, required?: boolean, validator?: any}} rule
  16836 + * @param {{value: any, message: string, required?: boolean, validator?: any}} rule
16259 16837 */
16260 16838 set(rule) {
16261 16839 this.list.push(rule)
... ...
... ... @@ -167,6 +167,10 @@
167 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 175 .override__radio-default {
172 176 margin-right: 16px;
... ... @@ -503,36 +507,6 @@
503 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 510 /* ========== help message ========== */
537 511 .thingskit-help-message {
538 512 cursor: pointer;
... ...