Commit cdcf00db4f093080c8be79ab5dc70e61f3a25ffd

Authored by ww
1 parent f78ff140

feat: 新增流量计组件

... ... @@ -272,8 +272,12 @@
272 272 /**
273 273 * @description 图片组件
274 274 */
275   - IMAGE: 'image'
276   -
  275 + IMAGE: 'image',
  276 +
  277 + /**
  278 + * @description 流量计
  279 + */
  280 + FLOWMETER: 'flowmeter'
277 281 }
278 282
279 283 Sidebar.prototype.enumComponentTypeValue = {
... ... @@ -364,7 +368,12 @@
364 368 /**
365 369 * @description 运行于停止
366 370 */
367   - RUNNING_AND_STOP: 'runningAndStop'
  371 + RUNNING_AND_STOP: 'runningAndStop',
  372 +
  373 + /**
  374 + * @description 流量计
  375 + */
  376 + FLOWMETER_PANEL: 'flowmeter'
368 377 }
369 378
370 379 /**
... ... @@ -721,8 +730,8 @@
721 730 //更多图形,显示出来的的标题跟id,同时包括图片
722 731
723 732 // 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
  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
726 735 this.setComponentPermission(LINE, [RUNNING_AND_STOP, DYNAMIC_EFFECT])
727 736 this.setComponentPermission(DEFAULT, [DYNAMIC_EFFECT])
728 737 this.setComponentPermission(REAL_TIME, [DYNAMIC_EFFECT])
... ... @@ -736,6 +745,7 @@
736 745 this.setComponentPermission(SWITCH, [DATA_SOURCE, SWITCH_STATE_SETTING])
737 746 this.setComponentPermission(PARAMS_SETTING_BUTTON, [DATA_SOURCE, ONLY_SINGLE_EVENT])
738 747 this.setComponentPermission(IMAGE, [DATA_SOURCE])
  748 + this.setComponentPermission(FLOWMETER, [DATA_SOURCE, FLOWMETER_PANEL])
739 749
740 750 var thingskitEntries = [
741 751 { title: mxResources.get('general'), id: 'general', image: IMAGE_PATH + '/sidebar-general.png' },
... ...
... ... @@ -42,10 +42,29 @@
42 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 63 // Adds Atlassian shapes
46 64 // 图表
47 65 Sidebar.prototype.addChartsPalette = function () {
48 66 this.chartsComponentInit()
  67 +
49 68 const self = this
50 69 const componentType = this.enumComponentType
51 70 const { DATA_SOURCE, DYNAMIC_EFFECT, DISPLAY_TYPE } = this.enumPermissionPanel
... ... @@ -74,7 +93,19 @@
74 93 const id = self.generatorChartsId()
75 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 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 111 this.addPaletteFunctions('charts', '图表', false, fns);
... ... @@ -210,7 +241,8 @@
210 241 Sidebar.prototype.addClickHandler = function (elt, ds, cells) {
211 242 const cell = cells[0]
212 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 248 * @description 拓展Sidebar鼠标按下
... ... @@ -218,7 +250,7 @@
218 250 */
219 251 const mouseDown = ds.mouseDown
220 252 ds.mouseDown = function (evt) {
221   - if (validate) {
  253 + if (validateChart) {
222 254 const id = self.generatorChartsId()
223 255 const geo = Object.assign(graph.model.getGeometry(cell), { width: 400, height: 400 })
224 256 cell.setGeometry(geo)
... ... @@ -227,6 +259,14 @@
227 259 self.graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, enumConst.CHART_CELL_DEFAULT_HEIGHT);
228 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 270 mouseDown.apply(this, arguments)
231 271 };
232 272
... ... @@ -239,7 +279,7 @@
239 279 try {
240 280 mouseUp.apply(this, arguments)
241 281 } finally {
242   - if (validate) {
  282 + if (validateChart) {
243 283 const id = self.getCellId(cell)
244 284 const chartType = cell.getAttribute(basicAttr.COMPONENT_TYPE)
245 285 self.generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)
... ... @@ -332,6 +372,7 @@
332 372 const { width, height, chartType } = domIdMapping.get(id)
333 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 398 refresh.apply(this, arguments)
358 399 if (!arguments.length) {
359 400 Sidebar.prototype.initChartInstance(this)
  401 + Sidebar.prototype.resetFlowmeter(this)
360 402 }
361 403 }
362 404
... ... @@ -367,6 +409,7 @@
367 409 EditorUi.prototype.setFileData = function () {
368 410 setFileData.apply(this, arguments)
369 411 Sidebar.prototype.initChartInstance(this.editor.graph)
  412 + Sidebar.prototype.resetFlowmeter(this.editor.graph)
370 413 }
371 414
372 415 // const selectPage = EditorUi.prototype.selectPage
... ... @@ -496,5 +539,419 @@
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 + UUID: flowmeterAttr.UUID,
  937 + COMPONENT_TYPE: basicAttr.COMPONENT_TYPE,
  938 + }
  939 +}
  940 +
  941 +/**
  942 + * @description 获取流量计类型
  943 + * @returns {{RECT: 'rect', CIRCLE: 'circle'}}
  944 + */
  945 +function getFlowmeterType() {
  946 + const type = Sidebar.prototype.enumFlowmeterType
  947 + return {
  948 + RECT: type.RECT,
  949 + CIRCLE: type.CIRCLE
  950 + }
  951 +}
  952 +
  953 +
  954 +function getComponentType() {
  955 + const componentType = Sidebar.prototype.enumComponentType
  956 + return
  957 +}
... ...
... ... @@ -5132,7 +5132,8 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5132 5132 [permissionKey.VIDEO]: createVideoBindPanel,
5133 5133 [permissionKey.SWITCH_STATE_SETTING]: createSwitchStateSettingPanel,
5134 5134 [permissionKey.ONLY_SINGLE_EVENT]: createParamsSettingButtonPanel,
5135   - [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel
  5135 + [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel,
  5136 + [permissionKey.FLOWMETER_PANEL]: createFlowmeterPanel
5136 5137 }
5137 5138
5138 5139
... ... @@ -5241,6 +5242,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5241 5242 * @type {Function}
5242 5243 */
5243 5244 const refreshFn = echoRefreshFn
  5245 +
5244 5246 echoRefreshFn = function () {
5245 5247 refreshFn.apply(this)
5246 5248 const { dataSources: [dataSource] = [] } = currentNodeData || {}
... ... @@ -5428,6 +5430,70 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5428 5430 }
5429 5431
5430 5432 /**
  5433 + * @description 创建流量计面板
  5434 + */
  5435 + function createFlowmeterPanel() {
  5436 +
  5437 + const enumConst = {
  5438 + MAX_VALUE: 'maxValue',
  5439 + MIN_VALUE: 'minValue',
  5440 + BG_COLOR: 'bgColor',
  5441 + WAVE_FIRST: 'waveFirst',
  5442 + WAVE_SECOND: 'waveSecond',
  5443 + WAVE_THIRD: 'waveThird',
  5444 + COMPONENT_TYPE: 'componentType'
  5445 + }
  5446 +
  5447 + const enumEl = {
  5448 + BG_COLOR_PICKER_EL: 'bgColorPickerEl',
  5449 + WAVE_FIRST: 'waveFirstEl',
  5450 + WAVE_SECOND: 'waveSecondEl',
  5451 + WAVE_THIRD: 'waveThirdEl',
  5452 + }
  5453 +
  5454 + const componentType = Sidebar.prototype.enumComponentType
  5455 + const flowmeterType = Sidebar.prototype.enumFlowmeterType
  5456 + const { TYPE } = getFlowmeterAttrKey()
  5457 + const cell = vertices[0]
  5458 + const isThemometerComponent = cell.getAttribute(TYPE) === flowmeterType.THERMOMETER
  5459 +
  5460 + if (isThemometerComponent) return
  5461 +
  5462 + const fragment = document.createDocumentFragment()
  5463 + const title = createTitle('流量计配置')
  5464 + $(title).addClass('override__title--default')
  5465 +
  5466 + const defaultPanel = createPanel()
  5467 + $(defaultPanel).addClass('override__panel--default')
  5468 + $(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>`)
  5469 + // $(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>`)
  5470 + // $(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>`)
  5471 + $(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>`)
  5472 + $(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>`)
  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.WAVE_SECOND}" type="color" name="${enumConst.WAVE_SECOND}" value="#3461c1" /></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_THIRD}" type="color" name="${enumConst.WAVE_THIRD}" value="#2d55aa" /></div></div>`)
  5475 +
  5476 +
  5477 + fragment.append(title)
  5478 + fragment.append(defaultPanel)
  5479 + $(container).append(fragment)
  5480 +
  5481 + function init() {
  5482 +
  5483 + const refreshFn = echoRefreshFn
  5484 + echoRefreshFn = function () {
  5485 + refreshFn.apply(this)
  5486 + const { dataSources: [dataSource] = [] } = currentNodeData || {}
  5487 + form.val(CONTAINER_FILTER, dataSource?.additional || {})
  5488 + }
  5489 +
  5490 + form.render()
  5491 + }
  5492 +
  5493 + init()
  5494 + }
  5495 +
  5496 + /**
5431 5497 * @description 是否是折线图
5432 5498 * @param {boolean} isLineChart
5433 5499 */
... ... @@ -5900,6 +5966,7 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5900 5966 }
5901 5967 const { field } = data
5902 5968 const value = getValueOnSubmit(field)
  5969 + if (!value) return
5903 5970 await to(autoSaveGraphInfo())
5904 5971 const [err, res] = await to(ConfigurationNodeApi.updateNodeInfo(value))
5905 5972 if (err) return
... ... @@ -5927,12 +5994,13 @@ DataFormatPanel.prototype.addDataFont = function (container) {
5927 5994 [componentType.VIDEO]: getVideoSubmitValue,
5928 5995 [componentType.SWITCH]: getSwitchSubmitValue,
5929 5996 [componentType.PARAMS_SETTING_BUTTON]: getSwitchSubmitValue,
5930   - [componentType.IMAGE]: getSubmitValue
  5997 + [componentType.IMAGE]: getSubmitValue,
  5998 + [componentType.FLOWMETER]: getFlowmeterSubmitValue,
5931 5999 }
5932 6000
5933 6001 const cell = vertices[0]
5934 6002 const type = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
5935   - return renderMapping[type]?.(field) || {}
  6003 + return renderMapping[type]?.(field) || false
5936 6004
5937 6005 function getSubmitValue(field) {
5938 6006 const ENABLED_FLAG = 'on'
... ... @@ -6062,6 +6130,47 @@ DataFormatPanel.prototype.addDataFont = function (container) {
6062 6130 }
6063 6131 return value
6064 6132 }
  6133 +
  6134 + function getFlowmeterSubmitValue(field = {}) {
  6135 + 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 +
  6149 + const value = {
  6150 + configurationId,
  6151 + contentId: currentPageId.id,
  6152 + nodeId: graphId,
  6153 + [enumCategory.ACT]: [],
  6154 + [enumCategory.EVENT]: [],
  6155 + [enumCategory.DATA_SOURCE]: {
  6156 + [enumDataSourceConst.ORG_ID]: field[enumDataSourceConst.ORG_ID],
  6157 + [enumDataSourceConst.DEVICE_ID]: field[enumDataSourceConst.DEVICE_ID],
  6158 + [enumDataSourceConst.DEVICE_TYPE]: field[enumDataSourceConst.DEVICE_TYPE],
  6159 + [enumDataSourceConst.DEVICE_PROFILE_ID]: field[enumDataSourceConst.DEVICE_PROFILE_ID],
  6160 + [enumDataSourceConst.ATTR]: field[enumDataSourceConst.ATTR],
  6161 + [enumDataSourceConst.ADDITIONAL]: {
  6162 + [additionalKey.COMPONENT_TYPE]: field[additionalKey.COMPONENT_TYPE],
  6163 + [additionalKey.MIN_VALUE]: field[additionalKey.MIN_VALUE],
  6164 + [additionalKey.MAX_VALUE]: field[additionalKey.MAX_VALUE],
  6165 + [additionalKey.BG_COLOR]: field[additionalKey.BG_COLOR],
  6166 + [additionalKey.WAVE_FIRST_COLOR]: field[additionalKey.WAVE_FIRST_COLOR],
  6167 + [additionalKey.WAVE_SECOND_COLOR]: field[additionalKey.WAVE_SECOND_COLOR],
  6168 + [additionalKey.WAVE_THIRD_COLOR]: field[additionalKey.WAVE_THIRD_COLOR],
  6169 + }
  6170 + },
  6171 + }
  6172 + return value
  6173 + }
6065 6174 }
6066 6175
6067 6176 /**
... ... @@ -13660,7 +13769,7 @@ class DispatchCenter {
13660 13769 subList.forEach(item => {
13661 13770 const { dataOrigin, additional } = item
13662 13771 if (dataOrigin === 'dataSources') {
13663   - if (additional) {
  13772 + if (additional && (additional || {})?.dataType ) {
13664 13773 const { dataType } = additional || {}
13665 13774 if (dataType === HandleDataSource.enumDataBindType.REAL) {
13666 13775 this.dataSourceHandlerInstance.updateRealTimeDataSource(message, item)
... ... @@ -13678,10 +13787,6 @@ class DispatchCenter {
13678 13787 handleFunction(message, item)
13679 13788 }
13680 13789 })
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 13790 }
13686 13791
13687 13792 /**
... ... @@ -13712,6 +13817,40 @@ class DispatchCenter {
13712 13817 if (!id) return
13713 13818 const [err, res] = await to(ConfigurationNodeApi.getConfigurationInfo('CONTENT', id))
13714 13819 this.contentData = res
  13820 + this.afterGetContentDataNode()
  13821 + }
  13822 +
  13823 + afterGetContentDataNode() {
  13824 + setTimeout(() => {
  13825 + const componentType = Sidebar.prototype.enumComponentType
  13826 + const flowmeterComponent = this.contentData.dataSources.filter(item => item.additional && (item.additional || {}).componentType === componentType.FLOWMETER)
  13827 + const { MIN_VALUE, MAX_VALUE, BG_COLOR, WAVE_FIRST_COLOR, WAVE_SECOND_COLOR, WAVE_THIRD_COLOR } = HandleDataSource.enumConst
  13828 + flowmeterComponent.forEach(item => {
  13829 + const { nodeId, additional } = item
  13830 + const { bgColor, maxValue, minValue, waveFirst, waveSecond, waveThird } = additional || {}
  13831 + const cell = this.graph?.getCellsById([nodeId])?.[0]
  13832 + if (cell) {
  13833 + cell.setAttribute(BG_COLOR, bgColor)
  13834 + cell.setAttribute(MIN_VALUE, minValue)
  13835 + cell.setAttribute(MAX_VALUE, maxValue)
  13836 + cell.setAttribute(WAVE_FIRST_COLOR, waveFirst)
  13837 + cell.setAttribute(WAVE_SECOND_COLOR, waveSecond)
  13838 + cell.setAttribute(WAVE_THIRD_COLOR, waveThird)
  13839 +
  13840 + const { UUID } = getFlowmeterAttrKey()
  13841 + const id = cell.getAttribute(UUID)
  13842 +
  13843 + const element = document.getElementById(id)
  13844 +
  13845 + if (element) {
  13846 + element.querySelectorAll(`.${BG_COLOR}`).forEach(item => item.style.fill = bgColor)
  13847 + element.querySelectorAll(`.${WAVE_FIRST_COLOR}`).forEach(item => item.style.fill = waveFirst)
  13848 + element.querySelectorAll(`.${WAVE_SECOND_COLOR}`).forEach(item => item.style.fill = waveSecond)
  13849 + element.querySelectorAll(`.${WAVE_THIRD_COLOR}`).forEach(item => item.style.fill = waveThird)
  13850 + }
  13851 + }
  13852 + })
  13853 + }, 10);
13715 13854 }
13716 13855
13717 13856 sendSubscribeMessage() {
... ... @@ -14043,7 +14182,42 @@ class HandleDataSource {
14043 14182 /**
14044 14183 * @description 属性名称
14045 14184 */
14046   - ATTR_NAME: 'attrName'
  14185 + ATTR_NAME: 'attrName',
  14186 +
  14187 + /**
  14188 + * @description 组件类型
  14189 + */
  14190 + COMPONENT_TYPE: 'componentType',
  14191 +
  14192 + /**
  14193 + * @description 流量计最大值
  14194 + */
  14195 + MAX_VALUE: 'maxValue',
  14196 +
  14197 + /**
  14198 + * @description 流量计最小值
  14199 + */
  14200 + MIN_VALUE: 'minValue',
  14201 +
  14202 + /**
  14203 + * @description 流量计背景颜色
  14204 + */
  14205 + BG_COLOR: 'bgColor',
  14206 +
  14207 + /**
  14208 + * @description 流量计颜色一
  14209 + */
  14210 + WAVE_FIRST_COLOR: 'waveFirst',
  14211 +
  14212 + /**
  14213 + * @description 流量计颜色二
  14214 + */
  14215 + WAVE_SECOND_COLOR: 'waveSecond',
  14216 +
  14217 + /**
  14218 + * @description 流量计颜色三
  14219 + */
  14220 + WAVE_THIRD_COLOR: 'waveThird',
14047 14221 }
14048 14222
14049 14223
... ... @@ -14074,8 +14248,6 @@ class HandleDataSource {
14074 14248
14075 14249 constructor(DispatchInstance) {
14076 14250 this.DispatchInstance = DispatchInstance
14077   - // this.generatorCommonDataSourceMapping()
14078   - // this.generatorChartDataSourceMapping()
14079 14251 }
14080 14252
14081 14253 get graph() {
... ... @@ -14114,76 +14286,6 @@ class HandleDataSource {
14114 14286 }
14115 14287
14116 14288 /**
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 14289 * @description 订阅事件 绑定回调
14188 14290 * @param eventName
14189 14291 * @param callback
... ... @@ -14197,12 +14299,20 @@ class HandleDataSource {
14197 14299 * @param {} message
14198 14300 */
14199 14301 updateCommonDataSource(message, record) {
  14302 + console.log('enter')
14200 14303 const { nodeId, attr } = record
14201   - const node = this.getNodeByCmdId(nodeId)
  14304 + const node = this.getNodeByNodeId(nodeId)
  14305 + const { data } = message
  14306 + const type = this.getComponentType(node)
  14307 +
  14308 + if (node && type === this.componentType.FLOWMETER) {
  14309 + this.handleFlowmeterComponent(message, record)
  14310 + return
  14311 + }
14202 14312
  14313 + // 需要刷新页面
14203 14314 node && this.updatePage(() => {
14204   - const { data } = message
14205   - const type = this.getComponentType(node)
  14315 +
14206 14316 if (type === this.componentType.SWITCH) {
14207 14317 this.handleSwitchComponent(message, record)
14208 14318 return
... ... @@ -14232,7 +14342,7 @@ class HandleDataSource {
14232 14342 handleSwitchComponent(message, record) {
14233 14343 const { data = {} } = message
14234 14344 const { nodeId, attr } = record
14235   - const node = this.getNodeByCmdId(nodeId)
  14345 + const node = this.getNodeByNodeId(nodeId)
14236 14346 const [[_timespan, receiveValue] = []] = data[attr] || []
14237 14347 const switchConfig = this.DispatchInstance.contentData.act.find(item => item.id === nodeId && item.type === 'SWITCH')
14238 14348 const { condition = [] } = switchConfig || {}
... ... @@ -14273,11 +14383,46 @@ class HandleDataSource {
14273 14383
14274 14384 }
14275 14385
  14386 + handleFlowmeterComponent(message, record) {
  14387 + const { data = {} } = message
  14388 + if (!data) return
  14389 + const { nodeId, attr } = record
  14390 + const node = this.getNodeByNodeId(nodeId)
  14391 +
  14392 + let [[_timespan, receiveValue] = []] = data[attr] || []
  14393 + const { UUID, TYPE } = getFlowmeterAttrKey()
  14394 + const type = node.getAttribute(TYPE)
  14395 + const flowmeterType = Sidebar.prototype.enumFlowmeterType
  14396 + const id = node.getAttribute(UUID)
  14397 + const element = document.getElementById(id)
  14398 + const bindData = this.getBindData(nodeId)
  14399 + const { additional } = bindData || {}
  14400 + const { maxValue, minValue } = additional || {}
  14401 +
  14402 + receiveValue = isNaN(receiveValue) ? 0 : Number(receiveValue)
  14403 + console.log('enter')
  14404 + if (element) {
  14405 + if (type === flowmeterType.CIRCLE || type === flowmeterType.RECT) {
  14406 + const element = document.getElementById(id).querySelector('svg')
  14407 + element.style.setProperty('--value', receiveValue)
  14408 + }
  14409 + if (type === flowmeterType.THERMOMETER) {
  14410 + const element = document.getElementById(id).querySelector('svg')
  14411 + const range = 140
  14412 + const ratio = (190 - 15) / range
  14413 + receiveValue = receiveValue >= 0 ? receiveValue + 20 : 20 - Math.abs(receiveValue)
  14414 + receiveValue = 190 - receiveValue * ratio
  14415 + receiveValue = receiveValue < 15 ? 15 : receiveValue
  14416 + element.style.setProperty('--value', receiveValue)
  14417 + }
  14418 + }
  14419 + }
  14420 +
14276 14421 handleParamSettingButton(message, record) {
14277 14422 const { data = {} } = message
14278 14423 if (!data) return
14279 14424 const { nodeId, attr } = record
14280   - const node = this.getNodeByCmdId(nodeId)
  14425 + const node = this.getNodeByNodeId(nodeId)
14281 14426 const [[_timespan, receiveValue] = []] = data[attr] || []
14282 14427 this.updatePage(() => {
14283 14428 node.setAttribute('label', `<button class="param-setting-button">${receiveValue}</button>`)
... ... @@ -14287,7 +14432,7 @@ class HandleDataSource {
14287 14432 handleImageComponent(message, record) {
14288 14433 const { data = {} } = message
14289 14434 const { nodeId, attr } = record
14290   - const node = this.getNodeByCmdId(nodeId)
  14435 + const node = this.getNodeByNodeId(nodeId)
14291 14436 const [[_timespan, receiveValue] = []] = data[attr] || []
14292 14437 this.updatePage(() => {
14293 14438 node.setAttribute('label', `<img class="basic-component__image" alt="图片" src="${receiveValue}" />`)
... ... @@ -14303,7 +14448,7 @@ class HandleDataSource {
14303 14448 const { data = {} } = message
14304 14449 const { nodeId, attr, additional = {} } = record
14305 14450 const { agg } = additional
14306   - const node = this.getNodeByCmdId(nodeId)
  14451 + const node = this.getNodeByNodeId(nodeId)
14307 14452 if (!node) return
14308 14453 const enumConst = Sidebar.prototype.enumCellBasicAttribute
14309 14454 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
... ... @@ -14334,7 +14479,7 @@ class HandleDataSource {
14334 14479 const { data = {} } = message
14335 14480 const { nodeId, attr, additional = {} } = record
14336 14481 const { agg } = additional
14337   - const node = this.getNodeByCmdId(nodeId)
  14482 + const node = this.getNodeByNodeId(nodeId)
14338 14483 if (!node) return
14339 14484 const enumConst = Sidebar.prototype.enumCellBasicAttribute
14340 14485 const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
... ... @@ -14703,7 +14848,7 @@ class HandleDataSource {
14703 14848 * @param subscriptionId
14704 14849 * @return {*}
14705 14850 */
14706   - getNodeByCmdId(nodeId) {
  14851 + getNodeByNodeId(nodeId) {
14707 14852 // const nodeId = this.getNodeIdByCmdId(subscriptionId)
14708 14853 return this.contentAllCell.find(item => item.id === nodeId)
14709 14854 }
... ...
... ... @@ -1682,8 +1682,10 @@ Graph.sanitizeHtml = function(value, editing)
1682 1682 return null;
1683 1683 };
1684 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 /**
... ...
  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>
... ...