Browse Source

场景预警管理、预警监测

hongrunxia 1 year ago
parent
commit
83cac5b9bb
100 changed files with 6878 additions and 668 deletions
  1. 3 3
      index.html
  2. 3514 0
      public/js/adapter.min.js
  3. 3 0
      public/js/config.js
  4. 305 0
      public/js/webrtcstreamer.js
  5. BIN
      public/model/glft/gas/gasPump_2023-07-25.glb
  6. BIN
      public/model/glft/gas/gasPump_2023-08-18.glb
  7. BIN
      src/assets/images/vent/alarm-icons/ccq.png
  8. BIN
      src/assets/images/vent/alarm-icons/cf.png
  9. BIN
      src/assets/images/vent/alarm-icons/fc.png
  10. BIN
      src/assets/images/vent/alarm-icons/fm.png
  11. BIN
      src/assets/images/vent/alarm-icons/js.png
  12. BIN
      src/assets/images/vent/alarm-icons/penfen.png
  13. BIN
      src/assets/images/vent/alarm-icons/penlin.png
  14. BIN
      src/assets/images/vent/alarm-icons/pw.png
  15. BIN
      src/assets/images/vent/alarm-icons/wasibeng.png
  16. BIN
      src/assets/images/vent/alarm-icons/wasichoucaig.png
  17. BIN
      src/assets/images/vent/alarm-icons/yafeng.png
  18. BIN
      src/assets/images/vent/alarm-icons/zhudan.png
  19. BIN
      src/assets/images/vent/alarm-icons/zhujiang.png
  20. BIN
      src/assets/images/vent/alarm-icons/zhushan.png
  21. BIN
      src/assets/images/vent/alarm/bottom.png
  22. 11 0
      src/assets/images/vent/alarm/bottom.svg
  23. BIN
      src/assets/images/vent/alarm/bottom1.png
  24. BIN
      src/assets/images/vent/alarm/center-bg.png
  25. BIN
      src/assets/images/vent/alarm/data-bg.png
  26. BIN
      src/assets/images/vent/alarm/icon-animation.png
  27. 11 0
      src/assets/images/vent/alarm/icon-animation.svg
  28. BIN
      src/assets/images/vent/alarm/icon-bg.png
  29. 20 0
      src/assets/images/vent/alarm/icon-device.svg
  30. 28 0
      src/assets/images/vent/alarm/icon-dust.svg
  31. 20 0
      src/assets/images/vent/alarm/icon-fire.svg
  32. 24 0
      src/assets/images/vent/alarm/icon-gas.svg
  33. 20 0
      src/assets/images/vent/alarm/icon-vent.svg
  34. 5 0
      src/assets/images/vent/alarm/top-animation.svg
  35. 5 0
      src/assets/images/vent/alarm/top-animation1.svg
  36. 5 0
      src/assets/images/vent/alarm/top-animation2.svg
  37. BIN
      src/assets/images/vent/alarm/warning-bg.png
  38. BIN
      src/assets/images/vent/alarm/warning-icon-bg-a1.png
  39. BIN
      src/assets/images/vent/alarm/warning-icon-bg-a2.png
  40. BIN
      src/assets/images/vent/alarm/warning-icon-bg-a3.png
  41. BIN
      src/assets/images/vent/alarm/warning-icon-bg-a4.png
  42. BIN
      src/assets/images/vent/alarm/warning-icon-bg-a5.png
  43. BIN
      src/assets/images/vent/alarm/warning-icon-bg1.png
  44. BIN
      src/assets/images/vent/alarm/warning-icon-bg2.png
  45. BIN
      src/assets/images/vent/alarm/warning-icon-bg3.png
  46. BIN
      src/assets/images/vent/alarm/warning-icon-bg4.png
  47. BIN
      src/assets/images/vent/alarm/warning-icon-bg5.png
  48. BIN
      src/assets/images/vent/box-bottom-bg.png
  49. BIN
      src/assets/images/vent/box-top-bg.png
  50. BIN
      src/assets/images/vent/control-switch-bg1.png
  51. BIN
      src/assets/images/vent/device-detail-card1.png
  52. BIN
      src/assets/images/vent/fire-bg-top.png
  53. BIN
      src/assets/images/vent/icon-bottom-bg.png
  54. 9 0
      src/assets/images/vent/inner-icon.svg
  55. 9 0
      src/assets/images/vent/outer-icon.svg
  56. BIN
      src/assets/images/vent/plane.png
  57. BIN
      src/assets/images/vent/plane1.png
  58. BIN
      src/assets/images/vent/pump1.png
  59. BIN
      src/assets/images/vent/small-bg1.png
  60. BIN
      src/assets/images/vent/value-bg.png
  61. BIN
      src/assets/images/vent/vent-param-bg.png
  62. 0 2
      src/components/Container/src/Adaptive.vue
  63. 1 0
      src/components/Table/src/hooks/useTableContext.ts
  64. 2 1
      src/components/chart/BarAndLine.vue
  65. 308 83
      src/components/chart/BarSingle.vue
  66. 100 0
      src/components/chart/BarSingle1.vue
  67. 389 0
      src/components/chart/BarSingle2.vue
  68. 1 0
      src/components/chart/Line.vue
  69. 317 0
      src/components/vent/numberDraw.vue
  70. 29 5
      src/hooks/setting/index.ts
  71. 1 1
      src/hooks/system/useListPage.ts
  72. 15 11
      src/layouts/default/sider/bottomSideder.vue
  73. 3 1
      src/main.ts
  74. 7 23
      src/qiankun/apps.ts
  75. 4 0
      src/router/helper/routeHelper.ts
  76. 3 2
      src/store/modules/permission.ts
  77. 11 4
      src/utils/echartsUtil.ts
  78. 1 0
      src/utils/http/axios/index.ts
  79. 2 1
      src/utils/lib/echarts.ts
  80. 1 1
      src/utils/threejs/main.worker.ts
  81. 4 3
      src/utils/threejs/useThree.ts
  82. 81 0
      src/utils/ui.js
  83. 0 1
      src/views/system/notice/index.vue
  84. 16 10
      src/views/vent/deviceManager/comment/DeviceModal.vue
  85. 7 27
      src/views/vent/deviceManager/comment/NormalTable.vue
  86. 117 0
      src/views/vent/deviceManager/comment/warningTabel/BaseModal.vue
  87. 5 5
      src/views/vent/deviceManager/comment/warningTabel/DevicePointTable.vue
  88. 138 130
      src/views/vent/deviceManager/comment/warningTabel/index1.vue
  89. 62 195
      src/views/vent/deviceManager/comment/warningTabel/index2.vue
  90. 216 109
      src/views/vent/deviceManager/comment/warningTabel/index3.vue
  91. 12 1
      src/views/vent/deviceManager/comment/warningTabel/warning.api.ts
  92. 396 9
      src/views/vent/deviceManager/comment/warningTabel/warning.data.ts
  93. 1 1
      src/views/vent/deviceManager/deviceTable/index.vue
  94. 34 13
      src/views/vent/deviceManager/pointTabel/point.data.ts
  95. 29 2
      src/views/vent/deviceManager/substationTabel/index.vue
  96. 4 1
      src/views/vent/deviceManager/substationTabel/substation.api.ts
  97. 101 23
      src/views/vent/deviceManager/substationTabel/substation.data.ts
  98. 55 0
      src/views/vent/deviceManager/workingFace/workingFace.data.ts
  99. 203 0
      src/views/vent/monitorManager/alarmMonitor/AlarmHistoryTable.vue
  100. 210 0
      src/views/vent/monitorManager/alarmMonitor/DetailModal.vue

+ 3 - 3
index.html

@@ -9,10 +9,10 @@
     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
     <!-- <link rel="icon" href="/logo.png" /> -->
     <!-- 全局配置 -->
-    <script>
-      window._CONFIG = {};
-    </script>
+    <script src="/js/config.js"></script> 
     <script src="/js/liveplayer-lib.min.js"></script>
+    <script src="/js/webrtcstreamer.js"></script>
+    <script src="/js/adapter.min.js"></script>
     <script type="text/javascript" src="http://182.92.126.35:9050/web-apps/apps/api/documents/api.js"></script>
   </head>
   <body style="background-color: #09172C">

+ 3514 - 0
public/js/adapter.min.js

@@ -0,0 +1,3514 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+
+'use strict';
+
+var _adapter_factory = require('./adapter_factory.js');
+
+var adapter = (0, _adapter_factory.adapterFactory)({ window: typeof window === 'undefined' ? undefined : window });
+module.exports = adapter; // this is the difference from adapter_core.
+
+},{"./adapter_factory.js":2}],2:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.adapterFactory = adapterFactory;
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+var _chrome_shim = require('./chrome/chrome_shim');
+
+var chromeShim = _interopRequireWildcard(_chrome_shim);
+
+var _firefox_shim = require('./firefox/firefox_shim');
+
+var firefoxShim = _interopRequireWildcard(_firefox_shim);
+
+var _safari_shim = require('./safari/safari_shim');
+
+var safariShim = _interopRequireWildcard(_safari_shim);
+
+var _common_shim = require('./common_shim');
+
+var commonShim = _interopRequireWildcard(_common_shim);
+
+var _sdp = require('sdp');
+
+var sdp = _interopRequireWildcard(_sdp);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+// Shimming starts here.
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+function adapterFactory() {
+  var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+      window = _ref.window;
+
+  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+    shimChrome: true,
+    shimFirefox: true,
+    shimSafari: true
+  };
+
+  // Utils.
+  var logging = utils.log;
+  var browserDetails = utils.detectBrowser(window);
+
+  var adapter = {
+    browserDetails: browserDetails,
+    commonShim: commonShim,
+    extractVersion: utils.extractVersion,
+    disableLog: utils.disableLog,
+    disableWarnings: utils.disableWarnings,
+    // Expose sdp as a convenience. For production apps include directly.
+    sdp: sdp
+  };
+
+  // Shim browser if found.
+  switch (browserDetails.browser) {
+    case 'chrome':
+      if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) {
+        logging('Chrome shim is not included in this adapter release.');
+        return adapter;
+      }
+      if (browserDetails.version === null) {
+        logging('Chrome shim can not determine version, not shimming.');
+        return adapter;
+      }
+      logging('adapter.js shimming chrome.');
+      // Export to the adapter global object visible in the browser.
+      adapter.browserShim = chromeShim;
+
+      // Must be called before shimPeerConnection.
+      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
+      commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
+
+      chromeShim.shimGetUserMedia(window, browserDetails);
+      chromeShim.shimMediaStream(window, browserDetails);
+      chromeShim.shimPeerConnection(window, browserDetails);
+      chromeShim.shimOnTrack(window, browserDetails);
+      chromeShim.shimAddTrackRemoveTrack(window, browserDetails);
+      chromeShim.shimGetSendersWithDtmf(window, browserDetails);
+      chromeShim.shimGetStats(window, browserDetails);
+      chromeShim.shimSenderReceiverGetStats(window, browserDetails);
+      chromeShim.fixNegotiationNeeded(window, browserDetails);
+
+      commonShim.shimRTCIceCandidate(window, browserDetails);
+      commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);
+      commonShim.shimConnectionState(window, browserDetails);
+      commonShim.shimMaxMessageSize(window, browserDetails);
+      commonShim.shimSendThrowTypeError(window, browserDetails);
+      commonShim.removeExtmapAllowMixed(window, browserDetails);
+      break;
+    case 'firefox':
+      if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) {
+        logging('Firefox shim is not included in this adapter release.');
+        return adapter;
+      }
+      logging('adapter.js shimming firefox.');
+      // Export to the adapter global object visible in the browser.
+      adapter.browserShim = firefoxShim;
+
+      // Must be called before shimPeerConnection.
+      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
+      commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
+
+      firefoxShim.shimGetUserMedia(window, browserDetails);
+      firefoxShim.shimPeerConnection(window, browserDetails);
+      firefoxShim.shimOnTrack(window, browserDetails);
+      firefoxShim.shimRemoveStream(window, browserDetails);
+      firefoxShim.shimSenderGetStats(window, browserDetails);
+      firefoxShim.shimReceiverGetStats(window, browserDetails);
+      firefoxShim.shimRTCDataChannel(window, browserDetails);
+      firefoxShim.shimAddTransceiver(window, browserDetails);
+      firefoxShim.shimGetParameters(window, browserDetails);
+      firefoxShim.shimCreateOffer(window, browserDetails);
+      firefoxShim.shimCreateAnswer(window, browserDetails);
+
+      commonShim.shimRTCIceCandidate(window, browserDetails);
+      commonShim.shimConnectionState(window, browserDetails);
+      commonShim.shimMaxMessageSize(window, browserDetails);
+      commonShim.shimSendThrowTypeError(window, browserDetails);
+      break;
+    case 'safari':
+      if (!safariShim || !options.shimSafari) {
+        logging('Safari shim is not included in this adapter release.');
+        return adapter;
+      }
+      logging('adapter.js shimming safari.');
+      // Export to the adapter global object visible in the browser.
+      adapter.browserShim = safariShim;
+
+      // Must be called before shimCallbackAPI.
+      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
+      commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
+
+      safariShim.shimRTCIceServerUrls(window, browserDetails);
+      safariShim.shimCreateOfferLegacy(window, browserDetails);
+      safariShim.shimCallbacksAPI(window, browserDetails);
+      safariShim.shimLocalStreamsAPI(window, browserDetails);
+      safariShim.shimRemoteStreamsAPI(window, browserDetails);
+      safariShim.shimTrackEventTransceiver(window, browserDetails);
+      safariShim.shimGetUserMedia(window, browserDetails);
+      safariShim.shimAudioContext(window, browserDetails);
+
+      commonShim.shimRTCIceCandidate(window, browserDetails);
+      commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);
+      commonShim.shimMaxMessageSize(window, browserDetails);
+      commonShim.shimSendThrowTypeError(window, browserDetails);
+      commonShim.removeExtmapAllowMixed(window, browserDetails);
+      break;
+    default:
+      logging('Unsupported browser!');
+      break;
+  }
+
+  return adapter;
+}
+
+// Browser shims.
+
+},{"./chrome/chrome_shim":3,"./common_shim":6,"./firefox/firefox_shim":7,"./safari/safari_shim":10,"./utils":11,"sdp":12}],3:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+var _getusermedia = require('./getusermedia');
+
+Object.defineProperty(exports, 'shimGetUserMedia', {
+  enumerable: true,
+  get: function get() {
+    return _getusermedia.shimGetUserMedia;
+  }
+});
+
+var _getdisplaymedia = require('./getdisplaymedia');
+
+Object.defineProperty(exports, 'shimGetDisplayMedia', {
+  enumerable: true,
+  get: function get() {
+    return _getdisplaymedia.shimGetDisplayMedia;
+  }
+});
+exports.shimMediaStream = shimMediaStream;
+exports.shimOnTrack = shimOnTrack;
+exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
+exports.shimGetStats = shimGetStats;
+exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
+exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
+exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
+exports.shimPeerConnection = shimPeerConnection;
+exports.fixNegotiationNeeded = fixNegotiationNeeded;
+
+var _utils = require('../utils.js');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function shimMediaStream(window) {
+  window.MediaStream = window.MediaStream || window.webkitMediaStream;
+}
+
+function shimOnTrack(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
+    Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
+      get: function get() {
+        return this._ontrack;
+      },
+      set: function set(f) {
+        if (this._ontrack) {
+          this.removeEventListener('track', this._ontrack);
+        }
+        this.addEventListener('track', this._ontrack = f);
+      },
+
+      enumerable: true,
+      configurable: true
+    });
+    var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
+    window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
+      var _this = this;
+
+      if (!this._ontrackpoly) {
+        this._ontrackpoly = function (e) {
+          // onaddstream does not fire when a track is added to an existing
+          // stream. But stream.onaddtrack is implemented so we use that.
+          e.stream.addEventListener('addtrack', function (te) {
+            var receiver = void 0;
+            if (window.RTCPeerConnection.prototype.getReceivers) {
+              receiver = _this.getReceivers().find(function (r) {
+                return r.track && r.track.id === te.track.id;
+              });
+            } else {
+              receiver = { track: te.track };
+            }
+
+            var event = new Event('track');
+            event.track = te.track;
+            event.receiver = receiver;
+            event.transceiver = { receiver: receiver };
+            event.streams = [e.stream];
+            _this.dispatchEvent(event);
+          });
+          e.stream.getTracks().forEach(function (track) {
+            var receiver = void 0;
+            if (window.RTCPeerConnection.prototype.getReceivers) {
+              receiver = _this.getReceivers().find(function (r) {
+                return r.track && r.track.id === track.id;
+              });
+            } else {
+              receiver = { track: track };
+            }
+            var event = new Event('track');
+            event.track = track;
+            event.receiver = receiver;
+            event.transceiver = { receiver: receiver };
+            event.streams = [e.stream];
+            _this.dispatchEvent(event);
+          });
+        };
+        this.addEventListener('addstream', this._ontrackpoly);
+      }
+      return origSetRemoteDescription.apply(this, arguments);
+    };
+  } else {
+    // even if RTCRtpTransceiver is in window, it is only used and
+    // emitted in unified-plan. Unfortunately this means we need
+    // to unconditionally wrap the event.
+    utils.wrapPeerConnectionEvent(window, 'track', function (e) {
+      if (!e.transceiver) {
+        Object.defineProperty(e, 'transceiver', { value: { receiver: e.receiver } });
+      }
+      return e;
+    });
+  }
+}
+
+function shimGetSendersWithDtmf(window) {
+  // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) {
+    var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) {
+      return {
+        track: track,
+        get dtmf() {
+          if (this._dtmf === undefined) {
+            if (track.kind === 'audio') {
+              this._dtmf = pc.createDTMFSender(track);
+            } else {
+              this._dtmf = null;
+            }
+          }
+          return this._dtmf;
+        },
+        _pc: pc
+      };
+    };
+
+    // augment addTrack when getSenders is not available.
+    if (!window.RTCPeerConnection.prototype.getSenders) {
+      window.RTCPeerConnection.prototype.getSenders = function getSenders() {
+        this._senders = this._senders || [];
+        return this._senders.slice(); // return a copy of the internal state.
+      };
+      var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
+      window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
+        var sender = origAddTrack.apply(this, arguments);
+        if (!sender) {
+          sender = shimSenderWithDtmf(this, track);
+          this._senders.push(sender);
+        }
+        return sender;
+      };
+
+      var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
+      window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
+        origRemoveTrack.apply(this, arguments);
+        var idx = this._senders.indexOf(sender);
+        if (idx !== -1) {
+          this._senders.splice(idx, 1);
+        }
+      };
+    }
+    var origAddStream = window.RTCPeerConnection.prototype.addStream;
+    window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
+      var _this2 = this;
+
+      this._senders = this._senders || [];
+      origAddStream.apply(this, [stream]);
+      stream.getTracks().forEach(function (track) {
+        _this2._senders.push(shimSenderWithDtmf(_this2, track));
+      });
+    };
+
+    var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
+    window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
+      var _this3 = this;
+
+      this._senders = this._senders || [];
+      origRemoveStream.apply(this, [stream]);
+
+      stream.getTracks().forEach(function (track) {
+        var sender = _this3._senders.find(function (s) {
+          return s.track === track;
+        });
+        if (sender) {
+          // remove sender
+          _this3._senders.splice(_this3._senders.indexOf(sender), 1);
+        }
+      });
+    };
+  } else if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
+    var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
+    window.RTCPeerConnection.prototype.getSenders = function getSenders() {
+      var _this4 = this;
+
+      var senders = origGetSenders.apply(this, []);
+      senders.forEach(function (sender) {
+        return sender._pc = _this4;
+      });
+      return senders;
+    };
+
+    Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
+      get: function get() {
+        if (this._dtmf === undefined) {
+          if (this.track.kind === 'audio') {
+            this._dtmf = this._pc.createDTMFSender(this.track);
+          } else {
+            this._dtmf = null;
+          }
+        }
+        return this._dtmf;
+      }
+    });
+  }
+}
+
+function shimGetStats(window) {
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+
+  var origGetStats = window.RTCPeerConnection.prototype.getStats;
+  window.RTCPeerConnection.prototype.getStats = function getStats() {
+    var _this5 = this;
+
+    var _arguments = Array.prototype.slice.call(arguments),
+        selector = _arguments[0],
+        onSucc = _arguments[1],
+        onErr = _arguments[2];
+
+    // If selector is a function then we are in the old style stats so just
+    // pass back the original getStats format to avoid breaking old users.
+
+
+    if (arguments.length > 0 && typeof selector === 'function') {
+      return origGetStats.apply(this, arguments);
+    }
+
+    // When spec-style getStats is supported, return those when called with
+    // either no arguments or the selector argument is null.
+    if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) {
+      return origGetStats.apply(this, []);
+    }
+
+    var fixChromeStats_ = function fixChromeStats_(response) {
+      var standardReport = {};
+      var reports = response.result();
+      reports.forEach(function (report) {
+        var standardStats = {
+          id: report.id,
+          timestamp: report.timestamp,
+          type: {
+            localcandidate: 'local-candidate',
+            remotecandidate: 'remote-candidate'
+          }[report.type] || report.type
+        };
+        report.names().forEach(function (name) {
+          standardStats[name] = report.stat(name);
+        });
+        standardReport[standardStats.id] = standardStats;
+      });
+
+      return standardReport;
+    };
+
+    // shim getStats with maplike support
+    var makeMapStats = function makeMapStats(stats) {
+      return new Map(Object.keys(stats).map(function (key) {
+        return [key, stats[key]];
+      }));
+    };
+
+    if (arguments.length >= 2) {
+      var successCallbackWrapper_ = function successCallbackWrapper_(response) {
+        onSucc(makeMapStats(fixChromeStats_(response)));
+      };
+
+      return origGetStats.apply(this, [successCallbackWrapper_, selector]);
+    }
+
+    // promise-support
+    return new Promise(function (resolve, reject) {
+      origGetStats.apply(_this5, [function (response) {
+        resolve(makeMapStats(fixChromeStats_(response)));
+      }, reject]);
+    }).then(onSucc, onErr);
+  };
+}
+
+function shimSenderReceiverGetStats(window) {
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
+    return;
+  }
+
+  // shim sender stats.
+  if (!('getStats' in window.RTCRtpSender.prototype)) {
+    var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
+    if (origGetSenders) {
+      window.RTCPeerConnection.prototype.getSenders = function getSenders() {
+        var _this6 = this;
+
+        var senders = origGetSenders.apply(this, []);
+        senders.forEach(function (sender) {
+          return sender._pc = _this6;
+        });
+        return senders;
+      };
+    }
+
+    var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
+    if (origAddTrack) {
+      window.RTCPeerConnection.prototype.addTrack = function addTrack() {
+        var sender = origAddTrack.apply(this, arguments);
+        sender._pc = this;
+        return sender;
+      };
+    }
+    window.RTCRtpSender.prototype.getStats = function getStats() {
+      var sender = this;
+      return this._pc.getStats().then(function (result) {
+        return (
+          /* Note: this will include stats of all senders that
+           *   send a track with the same id as sender.track as
+           *   it is not possible to identify the RTCRtpSender.
+           */
+          utils.filterStats(result, sender.track, true)
+        );
+      });
+    };
+  }
+
+  // shim receiver stats.
+  if (!('getStats' in window.RTCRtpReceiver.prototype)) {
+    var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
+    if (origGetReceivers) {
+      window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
+        var _this7 = this;
+
+        var receivers = origGetReceivers.apply(this, []);
+        receivers.forEach(function (receiver) {
+          return receiver._pc = _this7;
+        });
+        return receivers;
+      };
+    }
+    utils.wrapPeerConnectionEvent(window, 'track', function (e) {
+      e.receiver._pc = e.srcElement;
+      return e;
+    });
+    window.RTCRtpReceiver.prototype.getStats = function getStats() {
+      var receiver = this;
+      return this._pc.getStats().then(function (result) {
+        return utils.filterStats(result, receiver.track, false);
+      });
+    };
+  }
+
+  if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) {
+    return;
+  }
+
+  // shim RTCPeerConnection.getStats(track).
+  var origGetStats = window.RTCPeerConnection.prototype.getStats;
+  window.RTCPeerConnection.prototype.getStats = function getStats() {
+    if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) {
+      var track = arguments[0];
+      var sender = void 0;
+      var receiver = void 0;
+      var err = void 0;
+      this.getSenders().forEach(function (s) {
+        if (s.track === track) {
+          if (sender) {
+            err = true;
+          } else {
+            sender = s;
+          }
+        }
+      });
+      this.getReceivers().forEach(function (r) {
+        if (r.track === track) {
+          if (receiver) {
+            err = true;
+          } else {
+            receiver = r;
+          }
+        }
+        return r.track === track;
+      });
+      if (err || sender && receiver) {
+        return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError'));
+      } else if (sender) {
+        return sender.getStats();
+      } else if (receiver) {
+        return receiver.getStats();
+      }
+      return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError'));
+    }
+    return origGetStats.apply(this, arguments);
+  };
+}
+
+function shimAddTrackRemoveTrackWithNative(window) {
+  // shim addTrack/removeTrack with native variants in order to make
+  // the interactions with legacy getLocalStreams behave as in other browsers.
+  // Keeps a mapping stream.id => [stream, rtpsenders...]
+  window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
+    var _this8 = this;
+
+    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
+    return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
+      return _this8._shimmedLocalStreams[streamId][0];
+    });
+  };
+
+  var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
+  window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
+    if (!stream) {
+      return origAddTrack.apply(this, arguments);
+    }
+    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
+
+    var sender = origAddTrack.apply(this, arguments);
+    if (!this._shimmedLocalStreams[stream.id]) {
+      this._shimmedLocalStreams[stream.id] = [stream, sender];
+    } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
+      this._shimmedLocalStreams[stream.id].push(sender);
+    }
+    return sender;
+  };
+
+  var origAddStream = window.RTCPeerConnection.prototype.addStream;
+  window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
+    var _this9 = this;
+
+    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
+
+    stream.getTracks().forEach(function (track) {
+      var alreadyExists = _this9.getSenders().find(function (s) {
+        return s.track === track;
+      });
+      if (alreadyExists) {
+        throw new DOMException('Track already exists.', 'InvalidAccessError');
+      }
+    });
+    var existingSenders = this.getSenders();
+    origAddStream.apply(this, arguments);
+    var newSenders = this.getSenders().filter(function (newSender) {
+      return existingSenders.indexOf(newSender) === -1;
+    });
+    this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
+  };
+
+  var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
+  window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
+    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
+    delete this._shimmedLocalStreams[stream.id];
+    return origRemoveStream.apply(this, arguments);
+  };
+
+  var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
+  window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
+    var _this10 = this;
+
+    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
+    if (sender) {
+      Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
+        var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
+        if (idx !== -1) {
+          _this10._shimmedLocalStreams[streamId].splice(idx, 1);
+        }
+        if (_this10._shimmedLocalStreams[streamId].length === 1) {
+          delete _this10._shimmedLocalStreams[streamId];
+        }
+      });
+    }
+    return origRemoveTrack.apply(this, arguments);
+  };
+}
+
+function shimAddTrackRemoveTrack(window, browserDetails) {
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+  // shim addTrack and removeTrack.
+  if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) {
+    return shimAddTrackRemoveTrackWithNative(window);
+  }
+
+  // also shim pc.getLocalStreams when addTrack is shimmed
+  // to return the original streams.
+  var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams;
+  window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
+    var _this11 = this;
+
+    var nativeStreams = origGetLocalStreams.apply(this);
+    this._reverseStreams = this._reverseStreams || {};
+    return nativeStreams.map(function (stream) {
+      return _this11._reverseStreams[stream.id];
+    });
+  };
+
+  var origAddStream = window.RTCPeerConnection.prototype.addStream;
+  window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
+    var _this12 = this;
+
+    this._streams = this._streams || {};
+    this._reverseStreams = this._reverseStreams || {};
+
+    stream.getTracks().forEach(function (track) {
+      var alreadyExists = _this12.getSenders().find(function (s) {
+        return s.track === track;
+      });
+      if (alreadyExists) {
+        throw new DOMException('Track already exists.', 'InvalidAccessError');
+      }
+    });
+    // Add identity mapping for consistency with addTrack.
+    // Unless this is being used with a stream from addTrack.
+    if (!this._reverseStreams[stream.id]) {
+      var newStream = new window.MediaStream(stream.getTracks());
+      this._streams[stream.id] = newStream;
+      this._reverseStreams[newStream.id] = stream;
+      stream = newStream;
+    }
+    origAddStream.apply(this, [stream]);
+  };
+
+  var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
+  window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
+    this._streams = this._streams || {};
+    this._reverseStreams = this._reverseStreams || {};
+
+    origRemoveStream.apply(this, [this._streams[stream.id] || stream]);
+    delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id];
+    delete this._streams[stream.id];
+  };
+
+  window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
+    var _this13 = this;
+
+    if (this.signalingState === 'closed') {
+      throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
+    }
+    var streams = [].slice.call(arguments, 1);
+    if (streams.length !== 1 || !streams[0].getTracks().find(function (t) {
+      return t === track;
+    })) {
+      // this is not fully correct but all we can manage without
+      // [[associated MediaStreams]] internal slot.
+      throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError');
+    }
+
+    var alreadyExists = this.getSenders().find(function (s) {
+      return s.track === track;
+    });
+    if (alreadyExists) {
+      throw new DOMException('Track already exists.', 'InvalidAccessError');
+    }
+
+    this._streams = this._streams || {};
+    this._reverseStreams = this._reverseStreams || {};
+    var oldStream = this._streams[stream.id];
+    if (oldStream) {
+      // this is using odd Chrome behaviour, use with caution:
+      // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
+      // Note: we rely on the high-level addTrack/dtmf shim to
+      // create the sender with a dtmf sender.
+      oldStream.addTrack(track);
+
+      // Trigger ONN async.
+      Promise.resolve().then(function () {
+        _this13.dispatchEvent(new Event('negotiationneeded'));
+      });
+    } else {
+      var newStream = new window.MediaStream([track]);
+      this._streams[stream.id] = newStream;
+      this._reverseStreams[newStream.id] = stream;
+      this.addStream(newStream);
+    }
+    return this.getSenders().find(function (s) {
+      return s.track === track;
+    });
+  };
+
+  // replace the internal stream id with the external one and
+  // vice versa.
+  function replaceInternalStreamId(pc, description) {
+    var sdp = description.sdp;
+    Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
+      var externalStream = pc._reverseStreams[internalId];
+      var internalStream = pc._streams[externalStream.id];
+      sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id);
+    });
+    return new RTCSessionDescription({
+      type: description.type,
+      sdp: sdp
+    });
+  }
+  function replaceExternalStreamId(pc, description) {
+    var sdp = description.sdp;
+    Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
+      var externalStream = pc._reverseStreams[internalId];
+      var internalStream = pc._streams[externalStream.id];
+      sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id);
+    });
+    return new RTCSessionDescription({
+      type: description.type,
+      sdp: sdp
+    });
+  }
+  ['createOffer', 'createAnswer'].forEach(function (method) {
+    var nativeMethod = window.RTCPeerConnection.prototype[method];
+    var methodObj = _defineProperty({}, method, function () {
+      var _this14 = this;
+
+      var args = arguments;
+      var isLegacyCall = arguments.length && typeof arguments[0] === 'function';
+      if (isLegacyCall) {
+        return nativeMethod.apply(this, [function (description) {
+          var desc = replaceInternalStreamId(_this14, description);
+          args[0].apply(null, [desc]);
+        }, function (err) {
+          if (args[1]) {
+            args[1].apply(null, err);
+          }
+        }, arguments[2]]);
+      }
+      return nativeMethod.apply(this, arguments).then(function (description) {
+        return replaceInternalStreamId(_this14, description);
+      });
+    });
+    window.RTCPeerConnection.prototype[method] = methodObj[method];
+  });
+
+  var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
+  window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
+    if (!arguments.length || !arguments[0].type) {
+      return origSetLocalDescription.apply(this, arguments);
+    }
+    arguments[0] = replaceExternalStreamId(this, arguments[0]);
+    return origSetLocalDescription.apply(this, arguments);
+  };
+
+  // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
+
+  var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription');
+  Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', {
+    get: function get() {
+      var description = origLocalDescription.get.apply(this);
+      if (description.type === '') {
+        return description;
+      }
+      return replaceInternalStreamId(this, description);
+    }
+  });
+
+  window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
+    var _this15 = this;
+
+    if (this.signalingState === 'closed') {
+      throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
+    }
+    // We can not yet check for sender instanceof RTCRtpSender
+    // since we shim RTPSender. So we check if sender._pc is set.
+    if (!sender._pc) {
+      throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError');
+    }
+    var isLocal = sender._pc === this;
+    if (!isLocal) {
+      throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError');
+    }
+
+    // Search for the native stream the senders track belongs to.
+    this._streams = this._streams || {};
+    var stream = void 0;
+    Object.keys(this._streams).forEach(function (streamid) {
+      var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
+        return sender.track === track;
+      });
+      if (hasTrack) {
+        stream = _this15._streams[streamid];
+      }
+    });
+
+    if (stream) {
+      if (stream.getTracks().length === 1) {
+        // if this is the last track of the stream, remove the stream. This
+        // takes care of any shimmed _senders.
+        this.removeStream(this._reverseStreams[stream.id]);
+      } else {
+        // relying on the same odd chrome behaviour as above.
+        stream.removeTrack(sender.track);
+      }
+      this.dispatchEvent(new Event('negotiationneeded'));
+    }
+  };
+}
+
+function shimPeerConnection(window, browserDetails) {
+  if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
+    // very basic support for old versions.
+    window.RTCPeerConnection = window.webkitRTCPeerConnection;
+  }
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+
+  // shim implicit creation of RTCSessionDescription/RTCIceCandidate
+  if (browserDetails.version < 53) {
+    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
+      var nativeMethod = window.RTCPeerConnection.prototype[method];
+      var methodObj = _defineProperty({}, method, function () {
+        arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
+        return nativeMethod.apply(this, arguments);
+      });
+      window.RTCPeerConnection.prototype[method] = methodObj[method];
+    });
+  }
+}
+
+// Attempt to fix ONN in plan-b mode.
+function fixNegotiationNeeded(window, browserDetails) {
+  utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
+    var pc = e.target;
+    if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
+      if (pc.signalingState !== 'stable') {
+        return;
+      }
+    }
+    return e;
+  });
+}
+
+},{"../utils.js":11,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){
+/*
+ *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.shimGetDisplayMedia = shimGetDisplayMedia;
+function shimGetDisplayMedia(window, getSourceId) {
+  if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
+    return;
+  }
+  if (!window.navigator.mediaDevices) {
+    return;
+  }
+  // getSourceId is a function that returns a promise resolving with
+  // the sourceId of the screen/window/tab to be shared.
+  if (typeof getSourceId !== 'function') {
+    console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function');
+    return;
+  }
+  window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
+    return getSourceId(constraints).then(function (sourceId) {
+      var widthSpecified = constraints.video && constraints.video.width;
+      var heightSpecified = constraints.video && constraints.video.height;
+      var frameRateSpecified = constraints.video && constraints.video.frameRate;
+      constraints.video = {
+        mandatory: {
+          chromeMediaSource: 'desktop',
+          chromeMediaSourceId: sourceId,
+          maxFrameRate: frameRateSpecified || 3
+        }
+      };
+      if (widthSpecified) {
+        constraints.video.mandatory.maxWidth = widthSpecified;
+      }
+      if (heightSpecified) {
+        constraints.video.mandatory.maxHeight = heightSpecified;
+      }
+      return window.navigator.mediaDevices.getUserMedia(constraints);
+    });
+  };
+}
+
+},{}],5:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+exports.shimGetUserMedia = shimGetUserMedia;
+
+var _utils = require('../utils.js');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+var logging = utils.log;
+
+function shimGetUserMedia(window, browserDetails) {
+  var navigator = window && window.navigator;
+
+  if (!navigator.mediaDevices) {
+    return;
+  }
+
+  var constraintsToChrome_ = function constraintsToChrome_(c) {
+    if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) {
+      return c;
+    }
+    var cc = {};
+    Object.keys(c).forEach(function (key) {
+      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
+        return;
+      }
+      var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] };
+      if (r.exact !== undefined && typeof r.exact === 'number') {
+        r.min = r.max = r.exact;
+      }
+      var oldname_ = function oldname_(prefix, name) {
+        if (prefix) {
+          return prefix + name.charAt(0).toUpperCase() + name.slice(1);
+        }
+        return name === 'deviceId' ? 'sourceId' : name;
+      };
+      if (r.ideal !== undefined) {
+        cc.optional = cc.optional || [];
+        var oc = {};
+        if (typeof r.ideal === 'number') {
+          oc[oldname_('min', key)] = r.ideal;
+          cc.optional.push(oc);
+          oc = {};
+          oc[oldname_('max', key)] = r.ideal;
+          cc.optional.push(oc);
+        } else {
+          oc[oldname_('', key)] = r.ideal;
+          cc.optional.push(oc);
+        }
+      }
+      if (r.exact !== undefined && typeof r.exact !== 'number') {
+        cc.mandatory = cc.mandatory || {};
+        cc.mandatory[oldname_('', key)] = r.exact;
+      } else {
+        ['min', 'max'].forEach(function (mix) {
+          if (r[mix] !== undefined) {
+            cc.mandatory = cc.mandatory || {};
+            cc.mandatory[oldname_(mix, key)] = r[mix];
+          }
+        });
+      }
+    });
+    if (c.advanced) {
+      cc.optional = (cc.optional || []).concat(c.advanced);
+    }
+    return cc;
+  };
+
+  var shimConstraints_ = function shimConstraints_(constraints, func) {
+    if (browserDetails.version >= 61) {
+      return func(constraints);
+    }
+    constraints = JSON.parse(JSON.stringify(constraints));
+    if (constraints && _typeof(constraints.audio) === 'object') {
+      var remap = function remap(obj, a, b) {
+        if (a in obj && !(b in obj)) {
+          obj[b] = obj[a];
+          delete obj[a];
+        }
+      };
+      constraints = JSON.parse(JSON.stringify(constraints));
+      remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
+      remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
+      constraints.audio = constraintsToChrome_(constraints.audio);
+    }
+    if (constraints && _typeof(constraints.video) === 'object') {
+      // Shim facingMode for mobile & surface pro.
+      var face = constraints.video.facingMode;
+      face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : { ideal: face });
+      var getSupportedFacingModeLies = browserDetails.version < 66;
+
+      if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) {
+        delete constraints.video.facingMode;
+        var matches = void 0;
+        if (face.exact === 'environment' || face.ideal === 'environment') {
+          matches = ['back', 'rear'];
+        } else if (face.exact === 'user' || face.ideal === 'user') {
+          matches = ['front'];
+        }
+        if (matches) {
+          // Look for matches in label, or use last cam for back (typical).
+          return navigator.mediaDevices.enumerateDevices().then(function (devices) {
+            devices = devices.filter(function (d) {
+              return d.kind === 'videoinput';
+            });
+            var dev = devices.find(function (d) {
+              return matches.some(function (match) {
+                return d.label.toLowerCase().includes(match);
+              });
+            });
+            if (!dev && devices.length && matches.includes('back')) {
+              dev = devices[devices.length - 1]; // more likely the back cam
+            }
+            if (dev) {
+              constraints.video.deviceId = face.exact ? { exact: dev.deviceId } : { ideal: dev.deviceId };
+            }
+            constraints.video = constraintsToChrome_(constraints.video);
+            logging('chrome: ' + JSON.stringify(constraints));
+            return func(constraints);
+          });
+        }
+      }
+      constraints.video = constraintsToChrome_(constraints.video);
+    }
+    logging('chrome: ' + JSON.stringify(constraints));
+    return func(constraints);
+  };
+
+  var shimError_ = function shimError_(e) {
+    if (browserDetails.version >= 64) {
+      return e;
+    }
+    return {
+      name: {
+        PermissionDeniedError: 'NotAllowedError',
+        PermissionDismissedError: 'NotAllowedError',
+        InvalidStateError: 'NotAllowedError',
+        DevicesNotFoundError: 'NotFoundError',
+        ConstraintNotSatisfiedError: 'OverconstrainedError',
+        TrackStartError: 'NotReadableError',
+        MediaDeviceFailedDueToShutdown: 'NotAllowedError',
+        MediaDeviceKillSwitchOn: 'NotAllowedError',
+        TabCaptureError: 'AbortError',
+        ScreenCaptureError: 'AbortError',
+        DeviceCaptureError: 'AbortError'
+      }[e.name] || e.name,
+      message: e.message,
+      constraint: e.constraint || e.constraintName,
+      toString: function toString() {
+        return this.name + (this.message && ': ') + this.message;
+      }
+    };
+  };
+
+  var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
+    shimConstraints_(constraints, function (c) {
+      navigator.webkitGetUserMedia(c, onSuccess, function (e) {
+        if (onError) {
+          onError(shimError_(e));
+        }
+      });
+    });
+  };
+  navigator.getUserMedia = getUserMedia_.bind(navigator);
+
+  // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
+  // function which returns a Promise, it does not accept spec-style
+  // constraints.
+  if (navigator.mediaDevices.getUserMedia) {
+    var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
+    navigator.mediaDevices.getUserMedia = function (cs) {
+      return shimConstraints_(cs, function (c) {
+        return origGetUserMedia(c).then(function (stream) {
+          if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
+            stream.getTracks().forEach(function (track) {
+              track.stop();
+            });
+            throw new DOMException('', 'NotFoundError');
+          }
+          return stream;
+        }, function (e) {
+          return Promise.reject(shimError_(e));
+        });
+      });
+    };
+  }
+}
+
+},{"../utils.js":11}],6:[function(require,module,exports){
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+exports.shimRTCIceCandidate = shimRTCIceCandidate;
+exports.shimRTCIceCandidateRelayProtocol = shimRTCIceCandidateRelayProtocol;
+exports.shimMaxMessageSize = shimMaxMessageSize;
+exports.shimSendThrowTypeError = shimSendThrowTypeError;
+exports.shimConnectionState = shimConnectionState;
+exports.removeExtmapAllowMixed = removeExtmapAllowMixed;
+exports.shimAddIceCandidateNullOrEmpty = shimAddIceCandidateNullOrEmpty;
+exports.shimParameterlessSetLocalDescription = shimParameterlessSetLocalDescription;
+
+var _sdp = require('sdp');
+
+var _sdp2 = _interopRequireDefault(_sdp);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function shimRTCIceCandidate(window) {
+  // foundation is arbitrarily chosen as an indicator for full support for
+  // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
+  if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) {
+    return;
+  }
+
+  var NativeRTCIceCandidate = window.RTCIceCandidate;
+  window.RTCIceCandidate = function RTCIceCandidate(args) {
+    // Remove the a= which shouldn't be part of the candidate string.
+    if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) {
+      args = JSON.parse(JSON.stringify(args));
+      args.candidate = args.candidate.substr(2);
+    }
+
+    if (args.candidate && args.candidate.length) {
+      // Augment the native candidate with the parsed fields.
+      var nativeCandidate = new NativeRTCIceCandidate(args);
+      var parsedCandidate = _sdp2.default.parseCandidate(args.candidate);
+      var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate);
+
+      // Add a serializer that does not serialize the extra attributes.
+      augmentedCandidate.toJSON = function toJSON() {
+        return {
+          candidate: augmentedCandidate.candidate,
+          sdpMid: augmentedCandidate.sdpMid,
+          sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
+          usernameFragment: augmentedCandidate.usernameFragment
+        };
+      };
+      return augmentedCandidate;
+    }
+    return new NativeRTCIceCandidate(args);
+  };
+  window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
+
+  // Hook up the augmented candidate in onicecandidate and
+  // addEventListener('icecandidate', ...)
+  utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
+    if (e.candidate) {
+      Object.defineProperty(e, 'candidate', {
+        value: new window.RTCIceCandidate(e.candidate),
+        writable: 'false'
+      });
+    }
+    return e;
+  });
+}
+
+function shimRTCIceCandidateRelayProtocol(window) {
+  if (!window.RTCIceCandidate || window.RTCIceCandidate && 'relayProtocol' in window.RTCIceCandidate.prototype) {
+    return;
+  }
+
+  // Hook up the augmented candidate in onicecandidate and
+  // addEventListener('icecandidate', ...)
+  utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
+    if (e.candidate) {
+      var parsedCandidate = _sdp2.default.parseCandidate(e.candidate.candidate);
+      if (parsedCandidate.type === 'relay') {
+        // This is a libwebrtc-specific mapping of local type preference
+        // to relayProtocol.
+        e.candidate.relayProtocol = {
+          0: 'tls',
+          1: 'tcp',
+          2: 'udp'
+        }[parsedCandidate.priority >> 24];
+      }
+    }
+    return e;
+  });
+}
+
+function shimMaxMessageSize(window, browserDetails) {
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+
+  if (!('sctp' in window.RTCPeerConnection.prototype)) {
+    Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
+      get: function get() {
+        return typeof this._sctp === 'undefined' ? null : this._sctp;
+      }
+    });
+  }
+
+  var sctpInDescription = function sctpInDescription(description) {
+    if (!description || !description.sdp) {
+      return false;
+    }
+    var sections = _sdp2.default.splitSections(description.sdp);
+    sections.shift();
+    return sections.some(function (mediaSection) {
+      var mLine = _sdp2.default.parseMLine(mediaSection);
+      return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1;
+    });
+  };
+
+  var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) {
+    // TODO: Is there a better solution for detecting Firefox?
+    var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
+    if (match === null || match.length < 2) {
+      return -1;
+    }
+    var version = parseInt(match[1], 10);
+    // Test for NaN (yes, this is ugly)
+    return version !== version ? -1 : version;
+  };
+
+  var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) {
+    // Every implementation we know can send at least 64 KiB.
+    // Note: Although Chrome is technically able to send up to 256 KiB, the
+    //       data does not reach the other peer reliably.
+    //       See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
+    var canSendMaxMessageSize = 65536;
+    if (browserDetails.browser === 'firefox') {
+      if (browserDetails.version < 57) {
+        if (remoteIsFirefox === -1) {
+          // FF < 57 will send in 16 KiB chunks using the deprecated PPID
+          // fragmentation.
+          canSendMaxMessageSize = 16384;
+        } else {
+          // However, other FF (and RAWRTC) can reassemble PPID-fragmented
+          // messages. Thus, supporting ~2 GiB when sending.
+          canSendMaxMessageSize = 2147483637;
+        }
+      } else if (browserDetails.version < 60) {
+        // Currently, all FF >= 57 will reset the remote maximum message size
+        // to the default value when a data channel is created at a later
+        // stage. :(
+        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
+        canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536;
+      } else {
+        // FF >= 60 supports sending ~2 GiB
+        canSendMaxMessageSize = 2147483637;
+      }
+    }
+    return canSendMaxMessageSize;
+  };
+
+  var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) {
+    // Note: 65536 bytes is the default value from the SDP spec. Also,
+    //       every implementation we know supports receiving 65536 bytes.
+    var maxMessageSize = 65536;
+
+    // FF 57 has a slightly incorrect default remote max message size, so
+    // we need to adjust it here to avoid a failure when sending.
+    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
+    if (browserDetails.browser === 'firefox' && browserDetails.version === 57) {
+      maxMessageSize = 65535;
+    }
+
+    var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:');
+    if (match.length > 0) {
+      maxMessageSize = parseInt(match[0].substr(19), 10);
+    } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) {
+      // If the maximum message size is not present in the remote SDP and
+      // both local and remote are Firefox, the remote peer can receive
+      // ~2 GiB.
+      maxMessageSize = 2147483637;
+    }
+    return maxMessageSize;
+  };
+
+  var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
+  window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
+    this._sctp = null;
+    // Chrome decided to not expose .sctp in plan-b mode.
+    // As usual, adapter.js has to do an 'ugly worakaround'
+    // to cover up the mess.
+    if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
+      var _getConfiguration = this.getConfiguration(),
+          sdpSemantics = _getConfiguration.sdpSemantics;
+
+      if (sdpSemantics === 'plan-b') {
+        Object.defineProperty(this, 'sctp', {
+          get: function get() {
+            return typeof this._sctp === 'undefined' ? null : this._sctp;
+          },
+
+          enumerable: true,
+          configurable: true
+        });
+      }
+    }
+
+    if (sctpInDescription(arguments[0])) {
+      // Check if the remote is FF.
+      var isFirefox = getRemoteFirefoxVersion(arguments[0]);
+
+      // Get the maximum message size the local peer is capable of sending
+      var canSendMMS = getCanSendMaxMessageSize(isFirefox);
+
+      // Get the maximum message size of the remote peer.
+      var remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
+
+      // Determine final maximum message size
+      var maxMessageSize = void 0;
+      if (canSendMMS === 0 && remoteMMS === 0) {
+        maxMessageSize = Number.POSITIVE_INFINITY;
+      } else if (canSendMMS === 0 || remoteMMS === 0) {
+        maxMessageSize = Math.max(canSendMMS, remoteMMS);
+      } else {
+        maxMessageSize = Math.min(canSendMMS, remoteMMS);
+      }
+
+      // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
+      // attribute.
+      var sctp = {};
+      Object.defineProperty(sctp, 'maxMessageSize', {
+        get: function get() {
+          return maxMessageSize;
+        }
+      });
+      this._sctp = sctp;
+    }
+
+    return origSetRemoteDescription.apply(this, arguments);
+  };
+}
+
+function shimSendThrowTypeError(window) {
+  if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
+    return;
+  }
+
+  // Note: Although Firefox >= 57 has a native implementation, the maximum
+  //       message size can be reset for all data channels at a later stage.
+  //       See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
+
+  function wrapDcSend(dc, pc) {
+    var origDataChannelSend = dc.send;
+    dc.send = function send() {
+      var data = arguments[0];
+      var length = data.length || data.size || data.byteLength;
+      if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) {
+        throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)');
+      }
+      return origDataChannelSend.apply(dc, arguments);
+    };
+  }
+  var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel;
+  window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() {
+    var dataChannel = origCreateDataChannel.apply(this, arguments);
+    wrapDcSend(dataChannel, this);
+    return dataChannel;
+  };
+  utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) {
+    wrapDcSend(e.channel, e.target);
+    return e;
+  });
+}
+
+/* shims RTCConnectionState by pretending it is the same as iceConnectionState.
+ * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
+ * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
+ * since DTLS failures would be hidden. See
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
+ * for the Firefox tracking bug.
+ */
+function shimConnectionState(window) {
+  if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) {
+    return;
+  }
+  var proto = window.RTCPeerConnection.prototype;
+  Object.defineProperty(proto, 'connectionState', {
+    get: function get() {
+      return {
+        completed: 'connected',
+        checking: 'connecting'
+      }[this.iceConnectionState] || this.iceConnectionState;
+    },
+
+    enumerable: true,
+    configurable: true
+  });
+  Object.defineProperty(proto, 'onconnectionstatechange', {
+    get: function get() {
+      return this._onconnectionstatechange || null;
+    },
+    set: function set(cb) {
+      if (this._onconnectionstatechange) {
+        this.removeEventListener('connectionstatechange', this._onconnectionstatechange);
+        delete this._onconnectionstatechange;
+      }
+      if (cb) {
+        this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb);
+      }
+    },
+
+    enumerable: true,
+    configurable: true
+  });
+
+  ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) {
+    var origMethod = proto[method];
+    proto[method] = function () {
+      if (!this._connectionstatechangepoly) {
+        this._connectionstatechangepoly = function (e) {
+          var pc = e.target;
+          if (pc._lastConnectionState !== pc.connectionState) {
+            pc._lastConnectionState = pc.connectionState;
+            var newEvent = new Event('connectionstatechange', e);
+            pc.dispatchEvent(newEvent);
+          }
+          return e;
+        };
+        this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly);
+      }
+      return origMethod.apply(this, arguments);
+    };
+  });
+}
+
+function removeExtmapAllowMixed(window, browserDetails) {
+  /* remove a=extmap-allow-mixed for webrtc.org < M71 */
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+  if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
+    return;
+  }
+  if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
+    return;
+  }
+  var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
+  window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) {
+    if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
+      var sdp = desc.sdp.split('\n').filter(function (line) {
+        return line.trim() !== 'a=extmap-allow-mixed';
+      }).join('\n');
+      // Safari enforces read-only-ness of RTCSessionDescription fields.
+      if (window.RTCSessionDescription && desc instanceof window.RTCSessionDescription) {
+        arguments[0] = new window.RTCSessionDescription({
+          type: desc.type,
+          sdp: sdp
+        });
+      } else {
+        desc.sdp = sdp;
+      }
+    }
+    return nativeSRD.apply(this, arguments);
+  };
+}
+
+function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
+  // Support for addIceCandidate(null or undefined)
+  // as well as addIceCandidate({candidate: "", ...})
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=978582
+  // Note: must be called before other polyfills which change the signature.
+  if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
+    return;
+  }
+  var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
+  if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
+    return;
+  }
+  window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
+    if (!arguments[0]) {
+      if (arguments[1]) {
+        arguments[1].apply(null);
+      }
+      return Promise.resolve();
+    }
+    // Firefox 68+ emits and processes {candidate: "", ...}, ignore
+    // in older versions.
+    // Native support for ignoring exists for Chrome M77+.
+    // Safari ignores as well, exact version unknown but works in the same
+    // version that also ignores addIceCandidate(null).
+    if ((browserDetails.browser === 'chrome' && browserDetails.version < 78 || browserDetails.browser === 'firefox' && browserDetails.version < 68 || browserDetails.browser === 'safari') && arguments[0] && arguments[0].candidate === '') {
+      return Promise.resolve();
+    }
+    return nativeAddIceCandidate.apply(this, arguments);
+  };
+}
+
+// Note: Make sure to call this ahead of APIs that modify
+// setLocalDescription.length
+function shimParameterlessSetLocalDescription(window, browserDetails) {
+  if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
+    return;
+  }
+  var nativeSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
+  if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {
+    return;
+  }
+  window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
+    var _this = this;
+
+    var desc = arguments[0] || {};
+    if ((typeof desc === 'undefined' ? 'undefined' : _typeof(desc)) !== 'object' || desc.type && desc.sdp) {
+      return nativeSetLocalDescription.apply(this, arguments);
+    }
+    // The remaining steps should technically happen when SLD comes off the
+    // RTCPeerConnection's operations chain (not ahead of going on it), but
+    // this is too difficult to shim. Instead, this shim only covers the
+    // common case where the operations chain is empty. This is imperfect, but
+    // should cover many cases. Rationale: Even if we can't reduce the glare
+    // window to zero on imperfect implementations, there's value in tapping
+    // into the perfect negotiation pattern that several browsers support.
+    desc = { type: desc.type, sdp: desc.sdp };
+    if (!desc.type) {
+      switch (this.signalingState) {
+        case 'stable':
+        case 'have-local-offer':
+        case 'have-remote-pranswer':
+          desc.type = 'offer';
+          break;
+        default:
+          desc.type = 'answer';
+          break;
+      }
+    }
+    if (desc.sdp || desc.type !== 'offer' && desc.type !== 'answer') {
+      return nativeSetLocalDescription.apply(this, [desc]);
+    }
+    var func = desc.type === 'offer' ? this.createOffer : this.createAnswer;
+    return func.apply(this).then(function (d) {
+      return nativeSetLocalDescription.apply(_this, [d]);
+    });
+  };
+}
+
+},{"./utils":11,"sdp":12}],7:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+var _getusermedia = require('./getusermedia');
+
+Object.defineProperty(exports, 'shimGetUserMedia', {
+  enumerable: true,
+  get: function get() {
+    return _getusermedia.shimGetUserMedia;
+  }
+});
+
+var _getdisplaymedia = require('./getdisplaymedia');
+
+Object.defineProperty(exports, 'shimGetDisplayMedia', {
+  enumerable: true,
+  get: function get() {
+    return _getdisplaymedia.shimGetDisplayMedia;
+  }
+});
+exports.shimOnTrack = shimOnTrack;
+exports.shimPeerConnection = shimPeerConnection;
+exports.shimSenderGetStats = shimSenderGetStats;
+exports.shimReceiverGetStats = shimReceiverGetStats;
+exports.shimRemoveStream = shimRemoveStream;
+exports.shimRTCDataChannel = shimRTCDataChannel;
+exports.shimAddTransceiver = shimAddTransceiver;
+exports.shimGetParameters = shimGetParameters;
+exports.shimCreateOffer = shimCreateOffer;
+exports.shimCreateAnswer = shimCreateAnswer;
+
+var _utils = require('../utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function shimOnTrack(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
+    Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
+      get: function get() {
+        return { receiver: this.receiver };
+      }
+    });
+  }
+}
+
+function shimPeerConnection(window, browserDetails) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
+    return; // probably media.peerconnection.enabled=false in about:config
+  }
+  if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
+    // very basic support for old versions.
+    window.RTCPeerConnection = window.mozRTCPeerConnection;
+  }
+
+  if (browserDetails.version < 53) {
+    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
+    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
+      var nativeMethod = window.RTCPeerConnection.prototype[method];
+      var methodObj = _defineProperty({}, method, function () {
+        arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
+        return nativeMethod.apply(this, arguments);
+      });
+      window.RTCPeerConnection.prototype[method] = methodObj[method];
+    });
+  }
+
+  var modernStatsTypes = {
+    inboundrtp: 'inbound-rtp',
+    outboundrtp: 'outbound-rtp',
+    candidatepair: 'candidate-pair',
+    localcandidate: 'local-candidate',
+    remotecandidate: 'remote-candidate'
+  };
+
+  var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
+  window.RTCPeerConnection.prototype.getStats = function getStats() {
+    var _arguments = Array.prototype.slice.call(arguments),
+        selector = _arguments[0],
+        onSucc = _arguments[1],
+        onErr = _arguments[2];
+
+    return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
+      if (browserDetails.version < 53 && !onSucc) {
+        // Shim only promise getStats with spec-hyphens in type names
+        // Leave callback version alone; misc old uses of forEach before Map
+        try {
+          stats.forEach(function (stat) {
+            stat.type = modernStatsTypes[stat.type] || stat.type;
+          });
+        } catch (e) {
+          if (e.name !== 'TypeError') {
+            throw e;
+          }
+          // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
+          stats.forEach(function (stat, i) {
+            stats.set(i, Object.assign({}, stat, {
+              type: modernStatsTypes[stat.type] || stat.type
+            }));
+          });
+        }
+      }
+      return stats;
+    }).then(onSucc, onErr);
+  };
+}
+
+function shimSenderGetStats(window) {
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
+    return;
+  }
+  if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
+    return;
+  }
+  var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
+  if (origGetSenders) {
+    window.RTCPeerConnection.prototype.getSenders = function getSenders() {
+      var _this = this;
+
+      var senders = origGetSenders.apply(this, []);
+      senders.forEach(function (sender) {
+        return sender._pc = _this;
+      });
+      return senders;
+    };
+  }
+
+  var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
+  if (origAddTrack) {
+    window.RTCPeerConnection.prototype.addTrack = function addTrack() {
+      var sender = origAddTrack.apply(this, arguments);
+      sender._pc = this;
+      return sender;
+    };
+  }
+  window.RTCRtpSender.prototype.getStats = function getStats() {
+    return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map());
+  };
+}
+
+function shimReceiverGetStats(window) {
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
+    return;
+  }
+  if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
+    return;
+  }
+  var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
+  if (origGetReceivers) {
+    window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
+      var _this2 = this;
+
+      var receivers = origGetReceivers.apply(this, []);
+      receivers.forEach(function (receiver) {
+        return receiver._pc = _this2;
+      });
+      return receivers;
+    };
+  }
+  utils.wrapPeerConnectionEvent(window, 'track', function (e) {
+    e.receiver._pc = e.srcElement;
+    return e;
+  });
+  window.RTCRtpReceiver.prototype.getStats = function getStats() {
+    return this._pc.getStats(this.track);
+  };
+}
+
+function shimRemoveStream(window) {
+  if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) {
+    return;
+  }
+  window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
+    var _this3 = this;
+
+    utils.deprecated('removeStream', 'removeTrack');
+    this.getSenders().forEach(function (sender) {
+      if (sender.track && stream.getTracks().includes(sender.track)) {
+        _this3.removeTrack(sender);
+      }
+    });
+  };
+}
+
+function shimRTCDataChannel(window) {
+  // rename DataChannel to RTCDataChannel (native fix in FF60):
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
+  if (window.DataChannel && !window.RTCDataChannel) {
+    window.RTCDataChannel = window.DataChannel;
+  }
+}
+
+function shimAddTransceiver(window) {
+  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
+  // Firefox ignores the init sendEncodings options passed to addTransceiver
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
+    return;
+  }
+  var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
+  if (origAddTransceiver) {
+    window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() {
+      this.setParametersPromises = [];
+      // WebIDL input coercion and validation
+      var sendEncodings = arguments[1] && arguments[1].sendEncodings;
+      if (sendEncodings === undefined) {
+        sendEncodings = [];
+      }
+      sendEncodings = [].concat(_toConsumableArray(sendEncodings));
+      var shouldPerformCheck = sendEncodings.length > 0;
+      if (shouldPerformCheck) {
+        // If sendEncodings params are provided, validate grammar
+        sendEncodings.forEach(function (encodingParam) {
+          if ('rid' in encodingParam) {
+            var ridRegex = /^[a-z0-9]{0,16}$/i;
+            if (!ridRegex.test(encodingParam.rid)) {
+              throw new TypeError('Invalid RID value provided.');
+            }
+          }
+          if ('scaleResolutionDownBy' in encodingParam) {
+            if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
+              throw new RangeError('scale_resolution_down_by must be >= 1.0');
+            }
+          }
+          if ('maxFramerate' in encodingParam) {
+            if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
+              throw new RangeError('max_framerate must be >= 0.0');
+            }
+          }
+        });
+      }
+      var transceiver = origAddTransceiver.apply(this, arguments);
+      if (shouldPerformCheck) {
+        // Check if the init options were applied. If not we do this in an
+        // asynchronous way and save the promise reference in a global object.
+        // This is an ugly hack, but at the same time is way more robust than
+        // checking the sender parameters before and after the createOffer
+        // Also note that after the createoffer we are not 100% sure that
+        // the params were asynchronously applied so we might miss the
+        // opportunity to recreate offer.
+        var sender = transceiver.sender;
+
+        var params = sender.getParameters();
+        if (!('encodings' in params) ||
+        // Avoid being fooled by patched getParameters() below.
+        params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) {
+          params.encodings = sendEncodings;
+          sender.sendEncodings = sendEncodings;
+          this.setParametersPromises.push(sender.setParameters(params).then(function () {
+            delete sender.sendEncodings;
+          }).catch(function () {
+            delete sender.sendEncodings;
+          }));
+        }
+      }
+      return transceiver;
+    };
+  }
+}
+
+function shimGetParameters(window) {
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCRtpSender)) {
+    return;
+  }
+  var origGetParameters = window.RTCRtpSender.prototype.getParameters;
+  if (origGetParameters) {
+    window.RTCRtpSender.prototype.getParameters = function getParameters() {
+      var params = origGetParameters.apply(this, arguments);
+      if (!('encodings' in params)) {
+        params.encodings = [].concat(this.sendEncodings || [{}]);
+      }
+      return params;
+    };
+  }
+}
+
+function shimCreateOffer(window) {
+  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
+  // Firefox ignores the init sendEncodings options passed to addTransceiver
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
+    return;
+  }
+  var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
+  window.RTCPeerConnection.prototype.createOffer = function createOffer() {
+    var _this4 = this,
+        _arguments2 = arguments;
+
+    if (this.setParametersPromises && this.setParametersPromises.length) {
+      return Promise.all(this.setParametersPromises).then(function () {
+        return origCreateOffer.apply(_this4, _arguments2);
+      }).finally(function () {
+        _this4.setParametersPromises = [];
+      });
+    }
+    return origCreateOffer.apply(this, arguments);
+  };
+}
+
+function shimCreateAnswer(window) {
+  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
+  // Firefox ignores the init sendEncodings options passed to addTransceiver
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
+  if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
+    return;
+  }
+  var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
+  window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
+    var _this5 = this,
+        _arguments3 = arguments;
+
+    if (this.setParametersPromises && this.setParametersPromises.length) {
+      return Promise.all(this.setParametersPromises).then(function () {
+        return origCreateAnswer.apply(_this5, _arguments3);
+      }).finally(function () {
+        _this5.setParametersPromises = [];
+      });
+    }
+    return origCreateAnswer.apply(this, arguments);
+  };
+}
+
+},{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[function(require,module,exports){
+/*
+ *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.shimGetDisplayMedia = shimGetDisplayMedia;
+function shimGetDisplayMedia(window, preferredMediaSource) {
+  if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
+    return;
+  }
+  if (!window.navigator.mediaDevices) {
+    return;
+  }
+  window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
+    if (!(constraints && constraints.video)) {
+      var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined');
+      err.name = 'NotFoundError';
+      // from https://heycam.github.io/webidl/#idl-DOMException-error-names
+      err.code = 8;
+      return Promise.reject(err);
+    }
+    if (constraints.video === true) {
+      constraints.video = { mediaSource: preferredMediaSource };
+    } else {
+      constraints.video.mediaSource = preferredMediaSource;
+    }
+    return window.navigator.mediaDevices.getUserMedia(constraints);
+  };
+}
+
+},{}],9:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+exports.shimGetUserMedia = shimGetUserMedia;
+
+var _utils = require('../utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function shimGetUserMedia(window, browserDetails) {
+  var navigator = window && window.navigator;
+  var MediaStreamTrack = window && window.MediaStreamTrack;
+
+  navigator.getUserMedia = function (constraints, onSuccess, onError) {
+    // Replace Firefox 44+'s deprecation warning with unprefixed version.
+    utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia');
+    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
+  };
+
+  if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
+    var remap = function remap(obj, a, b) {
+      if (a in obj && !(b in obj)) {
+        obj[b] = obj[a];
+        delete obj[a];
+      }
+    };
+
+    var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
+    navigator.mediaDevices.getUserMedia = function (c) {
+      if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') {
+        c = JSON.parse(JSON.stringify(c));
+        remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
+        remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
+      }
+      return nativeGetUserMedia(c);
+    };
+
+    if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
+      var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
+      MediaStreamTrack.prototype.getSettings = function () {
+        var obj = nativeGetSettings.apply(this, arguments);
+        remap(obj, 'mozAutoGainControl', 'autoGainControl');
+        remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
+        return obj;
+      };
+    }
+
+    if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
+      var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
+      MediaStreamTrack.prototype.applyConstraints = function (c) {
+        if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') {
+          c = JSON.parse(JSON.stringify(c));
+          remap(c, 'autoGainControl', 'mozAutoGainControl');
+          remap(c, 'noiseSuppression', 'mozNoiseSuppression');
+        }
+        return nativeApplyConstraints.apply(this, [c]);
+      };
+    }
+  }
+}
+
+},{"../utils":11}],10:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+exports.shimLocalStreamsAPI = shimLocalStreamsAPI;
+exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI;
+exports.shimCallbacksAPI = shimCallbacksAPI;
+exports.shimGetUserMedia = shimGetUserMedia;
+exports.shimConstraints = shimConstraints;
+exports.shimRTCIceServerUrls = shimRTCIceServerUrls;
+exports.shimTrackEventTransceiver = shimTrackEventTransceiver;
+exports.shimCreateOfferLegacy = shimCreateOfferLegacy;
+exports.shimAudioContext = shimAudioContext;
+
+var _utils = require('../utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function shimLocalStreamsAPI(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
+    return;
+  }
+  if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
+    window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
+      if (!this._localStreams) {
+        this._localStreams = [];
+      }
+      return this._localStreams;
+    };
+  }
+  if (!('addStream' in window.RTCPeerConnection.prototype)) {
+    var _addTrack = window.RTCPeerConnection.prototype.addTrack;
+    window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
+      var _this = this;
+
+      if (!this._localStreams) {
+        this._localStreams = [];
+      }
+      if (!this._localStreams.includes(stream)) {
+        this._localStreams.push(stream);
+      }
+      // Try to emulate Chrome's behaviour of adding in audio-video order.
+      // Safari orders by track id.
+      stream.getAudioTracks().forEach(function (track) {
+        return _addTrack.call(_this, track, stream);
+      });
+      stream.getVideoTracks().forEach(function (track) {
+        return _addTrack.call(_this, track, stream);
+      });
+    };
+
+    window.RTCPeerConnection.prototype.addTrack = function addTrack(track) {
+      var _this2 = this;
+
+      for (var _len = arguments.length, streams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+        streams[_key - 1] = arguments[_key];
+      }
+
+      if (streams) {
+        streams.forEach(function (stream) {
+          if (!_this2._localStreams) {
+            _this2._localStreams = [stream];
+          } else if (!_this2._localStreams.includes(stream)) {
+            _this2._localStreams.push(stream);
+          }
+        });
+      }
+      return _addTrack.apply(this, arguments);
+    };
+  }
+  if (!('removeStream' in window.RTCPeerConnection.prototype)) {
+    window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
+      var _this3 = this;
+
+      if (!this._localStreams) {
+        this._localStreams = [];
+      }
+      var index = this._localStreams.indexOf(stream);
+      if (index === -1) {
+        return;
+      }
+      this._localStreams.splice(index, 1);
+      var tracks = stream.getTracks();
+      this.getSenders().forEach(function (sender) {
+        if (tracks.includes(sender.track)) {
+          _this3.removeTrack(sender);
+        }
+      });
+    };
+  }
+}
+
+function shimRemoteStreamsAPI(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
+    return;
+  }
+  if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
+    window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() {
+      return this._remoteStreams ? this._remoteStreams : [];
+    };
+  }
+  if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
+    Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
+      get: function get() {
+        return this._onaddstream;
+      },
+      set: function set(f) {
+        var _this4 = this;
+
+        if (this._onaddstream) {
+          this.removeEventListener('addstream', this._onaddstream);
+          this.removeEventListener('track', this._onaddstreampoly);
+        }
+        this.addEventListener('addstream', this._onaddstream = f);
+        this.addEventListener('track', this._onaddstreampoly = function (e) {
+          e.streams.forEach(function (stream) {
+            if (!_this4._remoteStreams) {
+              _this4._remoteStreams = [];
+            }
+            if (_this4._remoteStreams.includes(stream)) {
+              return;
+            }
+            _this4._remoteStreams.push(stream);
+            var event = new Event('addstream');
+            event.stream = stream;
+            _this4.dispatchEvent(event);
+          });
+        });
+      }
+    });
+    var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
+    window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
+      var pc = this;
+      if (!this._onaddstreampoly) {
+        this.addEventListener('track', this._onaddstreampoly = function (e) {
+          e.streams.forEach(function (stream) {
+            if (!pc._remoteStreams) {
+              pc._remoteStreams = [];
+            }
+            if (pc._remoteStreams.indexOf(stream) >= 0) {
+              return;
+            }
+            pc._remoteStreams.push(stream);
+            var event = new Event('addstream');
+            event.stream = stream;
+            pc.dispatchEvent(event);
+          });
+        });
+      }
+      return origSetRemoteDescription.apply(pc, arguments);
+    };
+  }
+}
+
+function shimCallbacksAPI(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
+    return;
+  }
+  var prototype = window.RTCPeerConnection.prototype;
+  var origCreateOffer = prototype.createOffer;
+  var origCreateAnswer = prototype.createAnswer;
+  var setLocalDescription = prototype.setLocalDescription;
+  var setRemoteDescription = prototype.setRemoteDescription;
+  var addIceCandidate = prototype.addIceCandidate;
+
+  prototype.createOffer = function createOffer(successCallback, failureCallback) {
+    var options = arguments.length >= 2 ? arguments[2] : arguments[0];
+    var promise = origCreateOffer.apply(this, [options]);
+    if (!failureCallback) {
+      return promise;
+    }
+    promise.then(successCallback, failureCallback);
+    return Promise.resolve();
+  };
+
+  prototype.createAnswer = function createAnswer(successCallback, failureCallback) {
+    var options = arguments.length >= 2 ? arguments[2] : arguments[0];
+    var promise = origCreateAnswer.apply(this, [options]);
+    if (!failureCallback) {
+      return promise;
+    }
+    promise.then(successCallback, failureCallback);
+    return Promise.resolve();
+  };
+
+  var withCallback = function withCallback(description, successCallback, failureCallback) {
+    var promise = setLocalDescription.apply(this, [description]);
+    if (!failureCallback) {
+      return promise;
+    }
+    promise.then(successCallback, failureCallback);
+    return Promise.resolve();
+  };
+  prototype.setLocalDescription = withCallback;
+
+  withCallback = function withCallback(description, successCallback, failureCallback) {
+    var promise = setRemoteDescription.apply(this, [description]);
+    if (!failureCallback) {
+      return promise;
+    }
+    promise.then(successCallback, failureCallback);
+    return Promise.resolve();
+  };
+  prototype.setRemoteDescription = withCallback;
+
+  withCallback = function withCallback(candidate, successCallback, failureCallback) {
+    var promise = addIceCandidate.apply(this, [candidate]);
+    if (!failureCallback) {
+      return promise;
+    }
+    promise.then(successCallback, failureCallback);
+    return Promise.resolve();
+  };
+  prototype.addIceCandidate = withCallback;
+}
+
+function shimGetUserMedia(window) {
+  var navigator = window && window.navigator;
+
+  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+    // shim not needed in Safari 12.1
+    var mediaDevices = navigator.mediaDevices;
+    var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
+    navigator.mediaDevices.getUserMedia = function (constraints) {
+      return _getUserMedia(shimConstraints(constraints));
+    };
+  }
+
+  if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+    navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
+      navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb);
+    }.bind(navigator);
+  }
+}
+
+function shimConstraints(constraints) {
+  if (constraints && constraints.video !== undefined) {
+    return Object.assign({}, constraints, { video: utils.compactObject(constraints.video) });
+  }
+
+  return constraints;
+}
+
+function shimRTCIceServerUrls(window) {
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+  // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
+  var OrigPeerConnection = window.RTCPeerConnection;
+  window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) {
+    if (pcConfig && pcConfig.iceServers) {
+      var newIceServers = [];
+      for (var i = 0; i < pcConfig.iceServers.length; i++) {
+        var server = pcConfig.iceServers[i];
+        if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) {
+          utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
+          server = JSON.parse(JSON.stringify(server));
+          server.urls = server.url;
+          delete server.url;
+          newIceServers.push(server);
+        } else {
+          newIceServers.push(pcConfig.iceServers[i]);
+        }
+      }
+      pcConfig.iceServers = newIceServers;
+    }
+    return new OrigPeerConnection(pcConfig, pcConstraints);
+  };
+  window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
+  // wrap static methods. Currently just generateCertificate.
+  if ('generateCertificate' in OrigPeerConnection) {
+    Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
+      get: function get() {
+        return OrigPeerConnection.generateCertificate;
+      }
+    });
+  }
+}
+
+function shimTrackEventTransceiver(window) {
+  // Add event.transceiver member over deprecated event.receiver
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
+    Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
+      get: function get() {
+        return { receiver: this.receiver };
+      }
+    });
+  }
+}
+
+function shimCreateOfferLegacy(window) {
+  var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
+  window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) {
+    if (offerOptions) {
+      if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
+        // support bit values
+        offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio;
+      }
+      var audioTransceiver = this.getTransceivers().find(function (transceiver) {
+        return transceiver.receiver.track.kind === 'audio';
+      });
+      if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
+        if (audioTransceiver.direction === 'sendrecv') {
+          if (audioTransceiver.setDirection) {
+            audioTransceiver.setDirection('sendonly');
+          } else {
+            audioTransceiver.direction = 'sendonly';
+          }
+        } else if (audioTransceiver.direction === 'recvonly') {
+          if (audioTransceiver.setDirection) {
+            audioTransceiver.setDirection('inactive');
+          } else {
+            audioTransceiver.direction = 'inactive';
+          }
+        }
+      } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) {
+        this.addTransceiver('audio', { direction: 'recvonly' });
+      }
+
+      if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
+        // support bit values
+        offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo;
+      }
+      var videoTransceiver = this.getTransceivers().find(function (transceiver) {
+        return transceiver.receiver.track.kind === 'video';
+      });
+      if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
+        if (videoTransceiver.direction === 'sendrecv') {
+          if (videoTransceiver.setDirection) {
+            videoTransceiver.setDirection('sendonly');
+          } else {
+            videoTransceiver.direction = 'sendonly';
+          }
+        } else if (videoTransceiver.direction === 'recvonly') {
+          if (videoTransceiver.setDirection) {
+            videoTransceiver.setDirection('inactive');
+          } else {
+            videoTransceiver.direction = 'inactive';
+          }
+        }
+      } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) {
+        this.addTransceiver('video', { direction: 'recvonly' });
+      }
+    }
+    return origCreateOffer.apply(this, arguments);
+  };
+}
+
+function shimAudioContext(window) {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || window.AudioContext) {
+    return;
+  }
+  window.AudioContext = window.webkitAudioContext;
+}
+
+},{"../utils":11}],11:[function(require,module,exports){
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree.
+ */
+/* eslint-env node */
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+exports.extractVersion = extractVersion;
+exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent;
+exports.disableLog = disableLog;
+exports.disableWarnings = disableWarnings;
+exports.log = log;
+exports.deprecated = deprecated;
+exports.detectBrowser = detectBrowser;
+exports.compactObject = compactObject;
+exports.walkStats = walkStats;
+exports.filterStats = filterStats;
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var logDisabled_ = true;
+var deprecationWarnings_ = true;
+
+/**
+ * Extract browser version out of the provided user agent string.
+ *
+ * @param {!string} uastring userAgent string.
+ * @param {!string} expr Regular expression used as match criteria.
+ * @param {!number} pos position in the version string to be returned.
+ * @return {!number} browser version.
+ */
+function extractVersion(uastring, expr, pos) {
+  var match = uastring.match(expr);
+  return match && match.length >= pos && parseInt(match[pos], 10);
+}
+
+// Wraps the peerconnection event eventNameToWrap in a function
+// which returns the modified event object (or false to prevent
+// the event).
+function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
+  if (!window.RTCPeerConnection) {
+    return;
+  }
+  var proto = window.RTCPeerConnection.prototype;
+  var nativeAddEventListener = proto.addEventListener;
+  proto.addEventListener = function (nativeEventName, cb) {
+    if (nativeEventName !== eventNameToWrap) {
+      return nativeAddEventListener.apply(this, arguments);
+    }
+    var wrappedCallback = function wrappedCallback(e) {
+      var modifiedEvent = wrapper(e);
+      if (modifiedEvent) {
+        if (cb.handleEvent) {
+          cb.handleEvent(modifiedEvent);
+        } else {
+          cb(modifiedEvent);
+        }
+      }
+    };
+    this._eventMap = this._eventMap || {};
+    if (!this._eventMap[eventNameToWrap]) {
+      this._eventMap[eventNameToWrap] = new Map();
+    }
+    this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
+    return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
+  };
+
+  var nativeRemoveEventListener = proto.removeEventListener;
+  proto.removeEventListener = function (nativeEventName, cb) {
+    if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) {
+      return nativeRemoveEventListener.apply(this, arguments);
+    }
+    if (!this._eventMap[eventNameToWrap].has(cb)) {
+      return nativeRemoveEventListener.apply(this, arguments);
+    }
+    var unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
+    this._eventMap[eventNameToWrap].delete(cb);
+    if (this._eventMap[eventNameToWrap].size === 0) {
+      delete this._eventMap[eventNameToWrap];
+    }
+    if (Object.keys(this._eventMap).length === 0) {
+      delete this._eventMap;
+    }
+    return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]);
+  };
+
+  Object.defineProperty(proto, 'on' + eventNameToWrap, {
+    get: function get() {
+      return this['_on' + eventNameToWrap];
+    },
+    set: function set(cb) {
+      if (this['_on' + eventNameToWrap]) {
+        this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]);
+        delete this['_on' + eventNameToWrap];
+      }
+      if (cb) {
+        this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb);
+      }
+    },
+
+    enumerable: true,
+    configurable: true
+  });
+}
+
+function disableLog(bool) {
+  if (typeof bool !== 'boolean') {
+    return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
+  }
+  logDisabled_ = bool;
+  return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
+}
+
+/**
+ * Disable or enable deprecation warnings
+ * @param {!boolean} bool set to true to disable warnings.
+ */
+function disableWarnings(bool) {
+  if (typeof bool !== 'boolean') {
+    return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
+  }
+  deprecationWarnings_ = !bool;
+  return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
+}
+
+function log() {
+  if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') {
+    if (logDisabled_) {
+      return;
+    }
+    if (typeof console !== 'undefined' && typeof console.log === 'function') {
+      console.log.apply(console, arguments);
+    }
+  }
+}
+
+/**
+ * Shows a deprecation warning suggesting the modern and spec-compatible API.
+ */
+function deprecated(oldMethod, newMethod) {
+  if (!deprecationWarnings_) {
+    return;
+  }
+  console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.');
+}
+
+/**
+ * Browser detector.
+ *
+ * @return {object} result containing browser and version
+ *     properties.
+ */
+function detectBrowser(window) {
+  // Returned result object.
+  var result = { browser: null, version: null };
+
+  // Fail early if it's not a browser
+  if (typeof window === 'undefined' || !window.navigator) {
+    result.browser = 'Not a browser.';
+    return result;
+  }
+
+  var navigator = window.navigator;
+
+
+  if (navigator.mozGetUserMedia) {
+    // Firefox.
+    result.browser = 'firefox';
+    result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
+  } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection) {
+    // Chrome, Chromium, Webview, Opera.
+    // Version matches Chrome/WebRTC version.
+    // Chrome 74 removed webkitGetUserMedia on http as well so we need the
+    // more complicated fallback to webkitRTCPeerConnection.
+    result.browser = 'chrome';
+    result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
+  } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
+    // Safari.
+    result.browser = 'safari';
+    result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
+    result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
+  } else {
+    // Default fallthrough: not supported.
+    result.browser = 'Not a supported browser.';
+    return result;
+  }
+
+  return result;
+}
+
+/**
+ * Checks if something is an object.
+ *
+ * @param {*} val The something you want to check.
+ * @return true if val is an object, false otherwise.
+ */
+function isObject(val) {
+  return Object.prototype.toString.call(val) === '[object Object]';
+}
+
+/**
+ * Remove all empty objects and undefined values
+ * from a nested object -- an enhanced and vanilla version
+ * of Lodash's `compact`.
+ */
+function compactObject(data) {
+  if (!isObject(data)) {
+    return data;
+  }
+
+  return Object.keys(data).reduce(function (accumulator, key) {
+    var isObj = isObject(data[key]);
+    var value = isObj ? compactObject(data[key]) : data[key];
+    var isEmptyObject = isObj && !Object.keys(value).length;
+    if (value === undefined || isEmptyObject) {
+      return accumulator;
+    }
+    return Object.assign(accumulator, _defineProperty({}, key, value));
+  }, {});
+}
+
+/* iterates the stats graph recursively. */
+function walkStats(stats, base, resultSet) {
+  if (!base || resultSet.has(base.id)) {
+    return;
+  }
+  resultSet.set(base.id, base);
+  Object.keys(base).forEach(function (name) {
+    if (name.endsWith('Id')) {
+      walkStats(stats, stats.get(base[name]), resultSet);
+    } else if (name.endsWith('Ids')) {
+      base[name].forEach(function (id) {
+        walkStats(stats, stats.get(id), resultSet);
+      });
+    }
+  });
+}
+
+/* filter getStats for a sender/receiver track. */
+function filterStats(result, track, outbound) {
+  var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
+  var filteredResult = new Map();
+  if (track === null) {
+    return filteredResult;
+  }
+  var trackStats = [];
+  result.forEach(function (value) {
+    if (value.type === 'track' && value.trackIdentifier === track.id) {
+      trackStats.push(value);
+    }
+  });
+  trackStats.forEach(function (trackStat) {
+    result.forEach(function (stats) {
+      if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
+        walkStats(result, stats, filteredResult);
+      }
+    });
+  });
+  return filteredResult;
+}
+
+},{}],12:[function(require,module,exports){
+/* eslint-env node */
+'use strict';
+
+// SDP helpers.
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+var SDPUtils = {};
+
+// Generate an alphanumeric identifier for cname or mids.
+// TODO: use UUIDs instead? https://gist.github.com/jed/982883
+SDPUtils.generateIdentifier = function () {
+  return Math.random().toString(36).substr(2, 10);
+};
+
+// The RTCP CNAME used by all peerconnections from the same JS.
+SDPUtils.localCName = SDPUtils.generateIdentifier();
+
+// Splits SDP into lines, dealing with both CRLF and LF.
+SDPUtils.splitLines = function (blob) {
+  return blob.trim().split('\n').map(function (line) {
+    return line.trim();
+  });
+};
+// Splits SDP into sessionpart and mediasections. Ensures CRLF.
+SDPUtils.splitSections = function (blob) {
+  var parts = blob.split('\nm=');
+  return parts.map(function (part, index) {
+    return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
+  });
+};
+
+// Returns the session description.
+SDPUtils.getDescription = function (blob) {
+  var sections = SDPUtils.splitSections(blob);
+  return sections && sections[0];
+};
+
+// Returns the individual media sections.
+SDPUtils.getMediaSections = function (blob) {
+  var sections = SDPUtils.splitSections(blob);
+  sections.shift();
+  return sections;
+};
+
+// Returns lines that start with a certain prefix.
+SDPUtils.matchPrefix = function (blob, prefix) {
+  return SDPUtils.splitLines(blob).filter(function (line) {
+    return line.indexOf(prefix) === 0;
+  });
+};
+
+// Parses an ICE candidate line. Sample input:
+// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
+// rport 55996"
+// Input can be prefixed with a=.
+SDPUtils.parseCandidate = function (line) {
+  var parts = void 0;
+  // Parse both variants.
+  if (line.indexOf('a=candidate:') === 0) {
+    parts = line.substring(12).split(' ');
+  } else {
+    parts = line.substring(10).split(' ');
+  }
+
+  var candidate = {
+    foundation: parts[0],
+    component: { 1: 'rtp', 2: 'rtcp' }[parts[1]] || parts[1],
+    protocol: parts[2].toLowerCase(),
+    priority: parseInt(parts[3], 10),
+    ip: parts[4],
+    address: parts[4], // address is an alias for ip.
+    port: parseInt(parts[5], 10),
+    // skip parts[6] == 'typ'
+    type: parts[7]
+  };
+
+  for (var i = 8; i < parts.length; i += 2) {
+    switch (parts[i]) {
+      case 'raddr':
+        candidate.relatedAddress = parts[i + 1];
+        break;
+      case 'rport':
+        candidate.relatedPort = parseInt(parts[i + 1], 10);
+        break;
+      case 'tcptype':
+        candidate.tcpType = parts[i + 1];
+        break;
+      case 'ufrag':
+        candidate.ufrag = parts[i + 1]; // for backward compatibility.
+        candidate.usernameFragment = parts[i + 1];
+        break;
+      default:
+        // extension handling, in particular ufrag. Don't overwrite.
+        if (candidate[parts[i]] === undefined) {
+          candidate[parts[i]] = parts[i + 1];
+        }
+        break;
+    }
+  }
+  return candidate;
+};
+
+// Translates a candidate object into SDP candidate attribute.
+// This does not include the a= prefix!
+SDPUtils.writeCandidate = function (candidate) {
+  var sdp = [];
+  sdp.push(candidate.foundation);
+
+  var component = candidate.component;
+  if (component === 'rtp') {
+    sdp.push(1);
+  } else if (component === 'rtcp') {
+    sdp.push(2);
+  } else {
+    sdp.push(component);
+  }
+  sdp.push(candidate.protocol.toUpperCase());
+  sdp.push(candidate.priority);
+  sdp.push(candidate.address || candidate.ip);
+  sdp.push(candidate.port);
+
+  var type = candidate.type;
+  sdp.push('typ');
+  sdp.push(type);
+  if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
+    sdp.push('raddr');
+    sdp.push(candidate.relatedAddress);
+    sdp.push('rport');
+    sdp.push(candidate.relatedPort);
+  }
+  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
+    sdp.push('tcptype');
+    sdp.push(candidate.tcpType);
+  }
+  if (candidate.usernameFragment || candidate.ufrag) {
+    sdp.push('ufrag');
+    sdp.push(candidate.usernameFragment || candidate.ufrag);
+  }
+  return 'candidate:' + sdp.join(' ');
+};
+
+// Parses an ice-options line, returns an array of option tags.
+// Sample input:
+// a=ice-options:foo bar
+SDPUtils.parseIceOptions = function (line) {
+  return line.substr(14).split(' ');
+};
+
+// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:
+// a=rtpmap:111 opus/48000/2
+SDPUtils.parseRtpMap = function (line) {
+  var parts = line.substr(9).split(' ');
+  var parsed = {
+    payloadType: parseInt(parts.shift(), 10) // was: id
+  };
+
+  parts = parts[0].split('/');
+
+  parsed.name = parts[0];
+  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
+  parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
+  // legacy alias, got renamed back to channels in ORTC.
+  parsed.numChannels = parsed.channels;
+  return parsed;
+};
+
+// Generates a rtpmap line from RTCRtpCodecCapability or
+// RTCRtpCodecParameters.
+SDPUtils.writeRtpMap = function (codec) {
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  var channels = codec.channels || codec.numChannels || 1;
+  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n';
+};
+
+// Parses a extmap line (headerextension from RFC 5285). Sample input:
+// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
+// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
+SDPUtils.parseExtmap = function (line) {
+  var parts = line.substr(9).split(' ');
+  return {
+    id: parseInt(parts[0], 10),
+    direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
+    uri: parts[1]
+  };
+};
+
+// Generates an extmap line from RTCRtpHeaderExtensionParameters or
+// RTCRtpHeaderExtension.
+SDPUtils.writeExtmap = function (headerExtension) {
+  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + '\r\n';
+};
+
+// Parses a fmtp line, returns dictionary. Sample input:
+// a=fmtp:96 vbr=on;cng=on
+// Also deals with vbr=on; cng=on
+SDPUtils.parseFmtp = function (line) {
+  var parsed = {};
+  var kv = void 0;
+  var parts = line.substr(line.indexOf(' ') + 1).split(';');
+  for (var j = 0; j < parts.length; j++) {
+    kv = parts[j].trim().split('=');
+    parsed[kv[0].trim()] = kv[1];
+  }
+  return parsed;
+};
+
+// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
+SDPUtils.writeFmtp = function (codec) {
+  var line = '';
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  if (codec.parameters && Object.keys(codec.parameters).length) {
+    var params = [];
+    Object.keys(codec.parameters).forEach(function (param) {
+      if (codec.parameters[param] !== undefined) {
+        params.push(param + '=' + codec.parameters[param]);
+      } else {
+        params.push(param);
+      }
+    });
+    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
+  }
+  return line;
+};
+
+// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
+// a=rtcp-fb:98 nack rpsi
+SDPUtils.parseRtcpFb = function (line) {
+  var parts = line.substr(line.indexOf(' ') + 1).split(' ');
+  return {
+    type: parts.shift(),
+    parameter: parts.join(' ')
+  };
+};
+
+// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
+SDPUtils.writeRtcpFb = function (codec) {
+  var lines = '';
+  var pt = codec.payloadType;
+  if (codec.preferredPayloadType !== undefined) {
+    pt = codec.preferredPayloadType;
+  }
+  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
+    // FIXME: special handling for trr-int?
+    codec.rtcpFeedback.forEach(function (fb) {
+      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n';
+    });
+  }
+  return lines;
+};
+
+// Parses a RFC 5576 ssrc media attribute. Sample input:
+// a=ssrc:3735928559 cname:something
+SDPUtils.parseSsrcMedia = function (line) {
+  var sp = line.indexOf(' ');
+  var parts = {
+    ssrc: parseInt(line.substr(7, sp - 7), 10)
+  };
+  var colon = line.indexOf(':', sp);
+  if (colon > -1) {
+    parts.attribute = line.substr(sp + 1, colon - sp - 1);
+    parts.value = line.substr(colon + 1);
+  } else {
+    parts.attribute = line.substr(sp + 1);
+  }
+  return parts;
+};
+
+// Parse a ssrc-group line (see RFC 5576). Sample input:
+// a=ssrc-group:semantics 12 34
+SDPUtils.parseSsrcGroup = function (line) {
+  var parts = line.substr(13).split(' ');
+  return {
+    semantics: parts.shift(),
+    ssrcs: parts.map(function (ssrc) {
+      return parseInt(ssrc, 10);
+    })
+  };
+};
+
+// Extracts the MID (RFC 5888) from a media section.
+// Returns the MID or undefined if no mid line was found.
+SDPUtils.getMid = function (mediaSection) {
+  var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
+  if (mid) {
+    return mid.substr(6);
+  }
+};
+
+// Parses a fingerprint line for DTLS-SRTP.
+SDPUtils.parseFingerprint = function (line) {
+  var parts = line.substr(14).split(' ');
+  return {
+    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
+    value: parts[1].toUpperCase() // the definition is upper-case in RFC 4572.
+  };
+};
+
+// Extracts DTLS parameters from SDP media section or sessionpart.
+// FIXME: for consistency with other functions this should only
+//   get the fingerprint line as input. See also getIceParameters.
+SDPUtils.getDtlsParameters = function (mediaSection, sessionpart) {
+  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:');
+  // Note: a=setup line is ignored since we use the 'auto' role in Edge.
+  return {
+    role: 'auto',
+    fingerprints: lines.map(SDPUtils.parseFingerprint)
+  };
+};
+
+// Serializes DTLS parameters to SDP.
+SDPUtils.writeDtlsParameters = function (params, setupType) {
+  var sdp = 'a=setup:' + setupType + '\r\n';
+  params.fingerprints.forEach(function (fp) {
+    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
+  });
+  return sdp;
+};
+
+// Parses a=crypto lines into
+//   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
+SDPUtils.parseCryptoLine = function (line) {
+  var parts = line.substr(9).split(' ');
+  return {
+    tag: parseInt(parts[0], 10),
+    cryptoSuite: parts[1],
+    keyParams: parts[2],
+    sessionParams: parts.slice(3)
+  };
+};
+
+SDPUtils.writeCryptoLine = function (parameters) {
+  return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (_typeof(parameters.keyParams) === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n';
+};
+
+// Parses the crypto key parameters into
+//   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
+SDPUtils.parseCryptoKeyParams = function (keyParams) {
+  if (keyParams.indexOf('inline:') !== 0) {
+    return null;
+  }
+  var parts = keyParams.substr(7).split('|');
+  return {
+    keyMethod: 'inline',
+    keySalt: parts[0],
+    lifeTime: parts[1],
+    mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
+    mkiLength: parts[2] ? parts[2].split(':')[1] : undefined
+  };
+};
+
+SDPUtils.writeCryptoKeyParams = function (keyParams) {
+  return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : '');
+};
+
+// Extracts all SDES parameters.
+SDPUtils.getCryptoParameters = function (mediaSection, sessionpart) {
+  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:');
+  return lines.map(SDPUtils.parseCryptoLine);
+};
+
+// Parses ICE information from SDP media section or sessionpart.
+// FIXME: for consistency with other functions this should only
+//   get the ice-ufrag and ice-pwd lines as input.
+SDPUtils.getIceParameters = function (mediaSection, sessionpart) {
+  var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0];
+  var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0];
+  if (!(ufrag && pwd)) {
+    return null;
+  }
+  return {
+    usernameFragment: ufrag.substr(12),
+    password: pwd.substr(10)
+  };
+};
+
+// Serializes ICE parameters to SDP.
+SDPUtils.writeIceParameters = function (params) {
+  var sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n';
+  if (params.iceLite) {
+    sdp += 'a=ice-lite\r\n';
+  }
+  return sdp;
+};
+
+// Parses the SDP media section and returns RTCRtpParameters.
+SDPUtils.parseRtpParameters = function (mediaSection) {
+  var description = {
+    codecs: [],
+    headerExtensions: [],
+    fecMechanisms: [],
+    rtcp: []
+  };
+  var lines = SDPUtils.splitLines(mediaSection);
+  var mline = lines[0].split(' ');
+  for (var i = 3; i < mline.length; i++) {
+    // find all codecs from mline[3..]
+    var pt = mline[i];
+    var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
+    if (rtpmapline) {
+      var codec = SDPUtils.parseRtpMap(rtpmapline);
+      var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' ');
+      // Only the first a=fmtp:<pt> is considered.
+      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
+      codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
+      description.codecs.push(codec);
+      // parse FEC mechanisms from rtpmap lines.
+      switch (codec.name.toUpperCase()) {
+        case 'RED':
+        case 'ULPFEC':
+          description.fecMechanisms.push(codec.name.toUpperCase());
+          break;
+        default:
+          // only RED and ULPFEC are recognized as FEC mechanisms.
+          break;
+      }
+    }
+  }
+  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) {
+    description.headerExtensions.push(SDPUtils.parseExtmap(line));
+  });
+  // FIXME: parse rtcp.
+  return description;
+};
+
+// Generates parts of the SDP media section describing the capabilities /
+// parameters.
+SDPUtils.writeRtpDescription = function (kind, caps) {
+  var sdp = '';
+
+  // Build the mline.
+  sdp += 'm=' + kind + ' ';
+  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
+  sdp += ' UDP/TLS/RTP/SAVPF ';
+  sdp += caps.codecs.map(function (codec) {
+    if (codec.preferredPayloadType !== undefined) {
+      return codec.preferredPayloadType;
+    }
+    return codec.payloadType;
+  }).join(' ') + '\r\n';
+
+  sdp += 'c=IN IP4 0.0.0.0\r\n';
+  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
+
+  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
+  caps.codecs.forEach(function (codec) {
+    sdp += SDPUtils.writeRtpMap(codec);
+    sdp += SDPUtils.writeFmtp(codec);
+    sdp += SDPUtils.writeRtcpFb(codec);
+  });
+  var maxptime = 0;
+  caps.codecs.forEach(function (codec) {
+    if (codec.maxptime > maxptime) {
+      maxptime = codec.maxptime;
+    }
+  });
+  if (maxptime > 0) {
+    sdp += 'a=maxptime:' + maxptime + '\r\n';
+  }
+
+  if (caps.headerExtensions) {
+    caps.headerExtensions.forEach(function (extension) {
+      sdp += SDPUtils.writeExtmap(extension);
+    });
+  }
+  // FIXME: write fecMechanisms.
+  return sdp;
+};
+
+// Parses the SDP media section and returns an array of
+// RTCRtpEncodingParameters.
+SDPUtils.parseRtpEncodingParameters = function (mediaSection) {
+  var encodingParameters = [];
+  var description = SDPUtils.parseRtpParameters(mediaSection);
+  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
+  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
+
+  // filter a=ssrc:... cname:, ignore PlanB-msid
+  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
+    return SDPUtils.parseSsrcMedia(line);
+  }).filter(function (parts) {
+    return parts.attribute === 'cname';
+  });
+  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
+  var secondarySsrc = void 0;
+
+  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) {
+    var parts = line.substr(17).split(' ');
+    return parts.map(function (part) {
+      return parseInt(part, 10);
+    });
+  });
+  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
+    secondarySsrc = flows[0][1];
+  }
+
+  description.codecs.forEach(function (codec) {
+    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
+      var encParam = {
+        ssrc: primarySsrc,
+        codecPayloadType: parseInt(codec.parameters.apt, 10)
+      };
+      if (primarySsrc && secondarySsrc) {
+        encParam.rtx = { ssrc: secondarySsrc };
+      }
+      encodingParameters.push(encParam);
+      if (hasRed) {
+        encParam = JSON.parse(JSON.stringify(encParam));
+        encParam.fec = {
+          ssrc: primarySsrc,
+          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
+        };
+        encodingParameters.push(encParam);
+      }
+    }
+  });
+  if (encodingParameters.length === 0 && primarySsrc) {
+    encodingParameters.push({
+      ssrc: primarySsrc
+    });
+  }
+
+  // we support both b=AS and b=TIAS but interpret AS as TIAS.
+  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
+  if (bandwidth.length) {
+    if (bandwidth[0].indexOf('b=TIAS:') === 0) {
+      bandwidth = parseInt(bandwidth[0].substr(7), 10);
+    } else if (bandwidth[0].indexOf('b=AS:') === 0) {
+      // use formula from JSEP to convert b=AS to TIAS value.
+      bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 - 50 * 40 * 8;
+    } else {
+      bandwidth = undefined;
+    }
+    encodingParameters.forEach(function (params) {
+      params.maxBitrate = bandwidth;
+    });
+  }
+  return encodingParameters;
+};
+
+// parses http://draft.ortc.org/#rtcrtcpparameters*
+SDPUtils.parseRtcpParameters = function (mediaSection) {
+  var rtcpParameters = {};
+
+  // Gets the first SSRC. Note that with RTX there might be multiple
+  // SSRCs.
+  var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
+    return SDPUtils.parseSsrcMedia(line);
+  }).filter(function (obj) {
+    return obj.attribute === 'cname';
+  })[0];
+  if (remoteSsrc) {
+    rtcpParameters.cname = remoteSsrc.value;
+    rtcpParameters.ssrc = remoteSsrc.ssrc;
+  }
+
+  // Edge uses the compound attribute instead of reducedSize
+  // compound is !reducedSize
+  var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
+  rtcpParameters.reducedSize = rsize.length > 0;
+  rtcpParameters.compound = rsize.length === 0;
+
+  // parses the rtcp-mux attrіbute.
+  // Note that Edge does not support unmuxed RTCP.
+  var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
+  rtcpParameters.mux = mux.length > 0;
+
+  return rtcpParameters;
+};
+
+SDPUtils.writeRtcpParameters = function (rtcpParameters) {
+  var sdp = '';
+  if (rtcpParameters.reducedSize) {
+    sdp += 'a=rtcp-rsize\r\n';
+  }
+  if (rtcpParameters.mux) {
+    sdp += 'a=rtcp-mux\r\n';
+  }
+  if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {
+    sdp += 'a=ssrc:' + rtcpParameters.ssrc + ' cname:' + rtcpParameters.cname + '\r\n';
+  }
+  return sdp;
+};
+
+// parses either a=msid: or a=ssrc:... msid lines and returns
+// the id of the MediaStream and MediaStreamTrack.
+SDPUtils.parseMsid = function (mediaSection) {
+  var parts = void 0;
+  var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
+  if (spec.length === 1) {
+    parts = spec[0].substr(7).split(' ');
+    return { stream: parts[0], track: parts[1] };
+  }
+  var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
+    return SDPUtils.parseSsrcMedia(line);
+  }).filter(function (msidParts) {
+    return msidParts.attribute === 'msid';
+  });
+  if (planB.length > 0) {
+    parts = planB[0].value.split(' ');
+    return { stream: parts[0], track: parts[1] };
+  }
+};
+
+// SCTP
+// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
+// to draft-ietf-mmusic-sctp-sdp-05
+SDPUtils.parseSctpDescription = function (mediaSection) {
+  var mline = SDPUtils.parseMLine(mediaSection);
+  var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
+  var maxMessageSize = void 0;
+  if (maxSizeLine.length > 0) {
+    maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
+  }
+  if (isNaN(maxMessageSize)) {
+    maxMessageSize = 65536;
+  }
+  var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
+  if (sctpPort.length > 0) {
+    return {
+      port: parseInt(sctpPort[0].substr(12), 10),
+      protocol: mline.fmt,
+      maxMessageSize: maxMessageSize
+    };
+  }
+  var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
+  if (sctpMapLines.length > 0) {
+    var parts = sctpMapLines[0].substr(10).split(' ');
+    return {
+      port: parseInt(parts[0], 10),
+      protocol: parts[1],
+      maxMessageSize: maxMessageSize
+    };
+  }
+};
+
+// SCTP
+// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
+// support by now receiving in this format, unless we originally parsed
+// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
+// protocol of DTLS/SCTP -- without UDP/ or TCP/)
+SDPUtils.writeSctpDescription = function (media, sctp) {
+  var output = [];
+  if (media.protocol !== 'DTLS/SCTP') {
+    output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctp-port:' + sctp.port + '\r\n'];
+  } else {
+    output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'];
+  }
+  if (sctp.maxMessageSize !== undefined) {
+    output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
+  }
+  return output.join('');
+};
+
+// Generate a session ID for SDP.
+// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
+// recommends using a cryptographically random +ve 64-bit value
+// but right now this should be acceptable and within the right range
+SDPUtils.generateSessionId = function () {
+  return Math.random().toString().substr(2, 21);
+};
+
+// Write boiler plate for start of SDP
+// sessId argument is optional - if not supplied it will
+// be generated randomly
+// sessVersion is optional and defaults to 2
+// sessUser is optional and defaults to 'thisisadapterortc'
+SDPUtils.writeSessionBoilerplate = function (sessId, sessVer, sessUser) {
+  var sessionId = void 0;
+  var version = sessVer !== undefined ? sessVer : 2;
+  if (sessId) {
+    sessionId = sessId;
+  } else {
+    sessionId = SDPUtils.generateSessionId();
+  }
+  var user = sessUser || 'thisisadapterortc';
+  // FIXME: sess-id should be an NTP timestamp.
+  return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n';
+};
+
+// Gets the direction from the mediaSection or the sessionpart.
+SDPUtils.getDirection = function (mediaSection, sessionpart) {
+  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
+  var lines = SDPUtils.splitLines(mediaSection);
+  for (var i = 0; i < lines.length; i++) {
+    switch (lines[i]) {
+      case 'a=sendrecv':
+      case 'a=sendonly':
+      case 'a=recvonly':
+      case 'a=inactive':
+        return lines[i].substr(2);
+      default:
+      // FIXME: What should happen here?
+    }
+  }
+  if (sessionpart) {
+    return SDPUtils.getDirection(sessionpart);
+  }
+  return 'sendrecv';
+};
+
+SDPUtils.getKind = function (mediaSection) {
+  var lines = SDPUtils.splitLines(mediaSection);
+  var mline = lines[0].split(' ');
+  return mline[0].substr(2);
+};
+
+SDPUtils.isRejected = function (mediaSection) {
+  return mediaSection.split(' ', 2)[1] === '0';
+};
+
+SDPUtils.parseMLine = function (mediaSection) {
+  var lines = SDPUtils.splitLines(mediaSection);
+  var parts = lines[0].substr(2).split(' ');
+  return {
+    kind: parts[0],
+    port: parseInt(parts[1], 10),
+    protocol: parts[2],
+    fmt: parts.slice(3).join(' ')
+  };
+};
+
+SDPUtils.parseOLine = function (mediaSection) {
+  var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
+  var parts = line.substr(2).split(' ');
+  return {
+    username: parts[0],
+    sessionId: parts[1],
+    sessionVersion: parseInt(parts[2], 10),
+    netType: parts[3],
+    addressType: parts[4],
+    address: parts[5]
+  };
+};
+
+// a very naive interpretation of a valid SDP.
+SDPUtils.isValidSDP = function (blob) {
+  if (typeof blob !== 'string' || blob.length === 0) {
+    return false;
+  }
+  var lines = SDPUtils.splitLines(blob);
+  for (var i = 0; i < lines.length; i++) {
+    if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
+      return false;
+    }
+    // TODO: check the modifier a bit more.
+  }
+  return true;
+};
+
+// Expose public methods.
+if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') {
+  module.exports = SDPUtils;
+}
+},{}]},{},[1])(1)
+});

+ 3 - 0
public/js/config.js

@@ -0,0 +1,3 @@
+const VUE_APP_URL = {
+  baseUrl: "http://182.92.126.35:9999"
+}

+ 305 - 0
public/js/webrtcstreamer.js

@@ -0,0 +1,305 @@
+var WebRtcStreamer = (function() {
+
+/** 
+ * Interface with WebRTC-streamer API
+ * @constructor
+ * @param {string} videoElement - id of the video element tag
+ * @param {string} srvurl -  url of webrtc-streamer (default is current location)
+*/
+var WebRtcStreamer = function WebRtcStreamer (videoElement, srvurl) {
+	if (typeof videoElement === "string") {
+		this.videoElement = document.getElementById(videoElement);
+	} else {
+		this.videoElement = videoElement;
+	}
+	this.srvurl           = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port;
+	this.pc               = null;    
+
+	this.mediaConstraints = { offerToReceiveAudio: true, offerToReceiveVideo: true };
+
+	this.iceServers = null;
+	this.earlyCandidates = [];
+}
+
+WebRtcStreamer.prototype._handleHttpErrors = function (response) {
+    if (!response.ok) {
+        throw Error(response.statusText);
+    }
+    return response;
+}
+
+/** 
+ * Connect a WebRTC Stream to videoElement 
+ * @param {string} videourl - id of WebRTC video stream
+ * @param {string} audiourl - id of WebRTC audio stream
+ * @param {string} options -  options of WebRTC call
+ * @param {string} stream  -  local stream to send
+*/
+WebRtcStreamer.prototype.connect = function(videourl, audiourl, options, localstream) {
+	this.disconnect();
+	
+	// getIceServers is not already received
+	if (!this.iceServers) {
+		console.log("Get IceServers");
+		
+		fetch(this.srvurl + "/api/getIceServers")
+			.then(this._handleHttpErrors)
+			.then( (response) => (response.json()) )
+			.then( (response) =>  this.onReceiveGetIceServers(response, videourl, audiourl, options, localstream))
+			.catch( (error) => this.onError("getIceServers " + error ))
+				
+	} else {
+		this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream);
+	}
+}
+
+/** 
+ * Disconnect a WebRTC Stream and clear videoElement source
+*/
+WebRtcStreamer.prototype.disconnect = function() {		
+	if (this.videoElement?.srcObject) {
+		this.videoElement.srcObject.getTracks().forEach(track => {
+			track.stop()
+			this.videoElement.srcObject.removeTrack(track);
+		});
+	}
+	if (this.pc) {
+		fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid)
+			.then(this._handleHttpErrors)
+			.catch( (error) => this.onError("hangup " + error ))
+
+		
+		try {
+			this.pc.close();
+		}
+		catch (e) {
+			console.log ("Failure close peer connection:" + e);
+		}
+		this.pc = null;
+	}
+}    
+
+/*
+* GetIceServers callback
+*/
+WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl, audiourl, options, stream) {
+	this.iceServers       = iceServers;
+	this.pcConfig         = iceServers || {"iceServers": [] };
+	try {            
+		this.createPeerConnection();
+
+		var callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl);
+		if (audiourl) {
+			callurl += "&audiourl="+encodeURIComponent(audiourl);
+		}
+		if (options) {
+			callurl += "&options="+encodeURIComponent(options);
+		}
+		
+		if (stream) {
+			this.pc.addStream(stream);
+		}
+
+                // clear early candidates
+		this.earlyCandidates.length = 0;
+		
+		// create Offer
+		this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => {
+			console.log("Create offer:" + JSON.stringify(sessionDescription));
+			
+			this.pc.setLocalDescription(sessionDescription)
+				.then(() => {
+					fetch(callurl, { method: "POST", body: JSON.stringify(sessionDescription) })
+						.then(this._handleHttpErrors)
+						.then( (response) => (response.json()) )
+						.catch( (error) => this.onError("call " + error ))
+						.then( (response) =>  this.onReceiveCall(response) )
+						.catch( (error) => this.onError("call " + error ))
+				
+				}, (error) => {
+					console.log ("setLocalDescription error:" + JSON.stringify(error)); 
+				});
+			
+		}, (error) => { 
+			alert("Create offer error:" + JSON.stringify(error));
+		});
+
+	} catch (e) {
+		this.disconnect();
+		alert("connect error: " + e);
+	}	    
+}
+
+
+WebRtcStreamer.prototype.getIceCandidate = function() {
+	fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid)
+		.then(this._handleHttpErrors)
+		.then( (response) => (response.json()) )
+		.then( (response) =>  this.onReceiveCandidate(response))
+		.catch( (error) => this.onError("getIceCandidate " + error ))
+}
+					
+/*
+* create RTCPeerConnection 
+*/
+WebRtcStreamer.prototype.createPeerConnection = function() {
+	console.log("createPeerConnection  config: " + JSON.stringify(this.pcConfig));
+	this.pc = new RTCPeerConnection(this.pcConfig);
+	var pc = this.pc;
+	pc.peerid = Math.random();		
+	
+	pc.onicecandidate = (evt) => this.onIceCandidate(evt);
+	pc.onaddstream    = (evt) => this.onAddStream(evt);
+	pc.oniceconnectionstatechange = (evt) => {  
+		console.log("oniceconnectionstatechange  state: " + pc.iceConnectionState);
+		if (this.videoElement) {
+			if (pc.iceConnectionState === "connected") {
+				this.videoElement.style.opacity = "1.0";
+			}			
+			else if (pc.iceConnectionState === "disconnected") {
+				this.videoElement.style.opacity = "0.25";
+			}			
+			else if ( (pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed") )  {
+				this.videoElement.style.opacity = "0.5";
+			} else if (pc.iceConnectionState === "new") {
+				this.getIceCandidate();
+			}
+		}
+	}
+	pc.ondatachannel = function(evt) {  
+		console.log("remote datachannel created:"+JSON.stringify(evt));
+		
+		evt.channel.onopen = function () {
+			console.log("remote datachannel open");
+			this.send("remote channel openned");
+		}
+		evt.channel.onmessage = function (event) {
+			console.log("remote datachannel recv:"+JSON.stringify(event.data));
+		}
+	}
+	pc.onicegatheringstatechange = function() {
+		if (pc.iceGatheringState === "complete") {
+			const recvs = pc.getReceivers();
+		
+			recvs.forEach((recv) => {
+			  if (recv.track && recv.track.kind === "video") {
+				console.log("codecs:" + JSON.stringify(recv.getParameters().codecs))
+			  }
+			});
+		  }
+	}
+
+	try {
+		var dataChannel = pc.createDataChannel("ClientDataChannel");
+		dataChannel.onopen = function() {
+			console.log("local datachannel open");
+			this.send("local channel openned");
+		}
+		dataChannel.onmessage = function(evt) {
+			console.log("local datachannel recv:"+JSON.stringify(evt.data));
+		}
+	} catch (e) {
+		console.log("Cannor create datachannel error: " + e);
+	}	
+	
+	console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig) );
+	return pc;
+}
+
+
+/*
+* RTCPeerConnection IceCandidate callback
+*/
+WebRtcStreamer.prototype.onIceCandidate = function (event) {
+	if (event.candidate) {
+		if (this.pc.currentRemoteDescription)  {
+			this.addIceCandidate(this.pc.peerid, event.candidate);					
+		} else {
+			this.earlyCandidates.push(event.candidate);
+		}
+	} 
+	else {
+		console.log("End of candidates.");
+	}
+}
+
+
+WebRtcStreamer.prototype.addIceCandidate = function(peerid, candidate) {
+	fetch(this.srvurl + "/api/addIceCandidate?peerid="+peerid, { method: "POST", body: JSON.stringify(candidate) })
+		.then(this._handleHttpErrors)
+		.then( (response) => (response.json()) )
+		.then( (response) =>  {console.log("addIceCandidate ok:" + response)})
+		.catch( (error) => this.onError("addIceCandidate " + error ))
+}
+				
+/*
+* RTCPeerConnection AddTrack callback
+*/
+WebRtcStreamer.prototype.onAddStream = function(event) {
+	console.log("Remote track added:" +  JSON.stringify(event));
+	
+	this.videoElement.srcObject = event.stream;
+	var promise = this.videoElement.play();
+	if (promise !== undefined) {
+	  promise.catch((error) => {
+		console.warn("error:"+error);
+		this.videoElement.setAttribute("controls", true);
+	  });
+	}
+}
+		
+/*
+* AJAX /call callback
+*/
+WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {
+
+	console.log("offer: " + JSON.stringify(dataJson));
+	var descr = new RTCSessionDescription(dataJson);
+	this.pc.setRemoteDescription(descr).then(() =>  { 
+			console.log ("setRemoteDescription ok");
+			while (this.earlyCandidates.length) {
+				var candidate = this.earlyCandidates.shift();
+				this.addIceCandidate(this.pc.peerid, candidate);				
+			}
+		
+			this.getIceCandidate()
+		}
+		, (error) => { 
+			console.log ("setRemoteDescription error:" + JSON.stringify(error)); 
+		});
+}	
+
+/*
+* AJAX /getIceCandidate callback
+*/
+WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) {
+	console.log("candidate: " + JSON.stringify(dataJson));
+	if (dataJson) {
+		for (var i=0; i<dataJson.length; i++) {
+			var candidate = new RTCIceCandidate(dataJson[i]);
+			
+			console.log("Adding ICE candidate :" + JSON.stringify(candidate) );
+			this.pc.addIceCandidate(candidate).then( () =>      { console.log ("addIceCandidate OK"); }
+				, (error) => { console.log ("addIceCandidate error:" + JSON.stringify(error)); } );
+		}
+		this.pc.addIceCandidate();
+	}
+}
+
+
+/*
+* AJAX callback for Error
+*/
+WebRtcStreamer.prototype.onError = function(status) {
+	console.log("onError:" + status);
+}
+
+return WebRtcStreamer;
+})();
+
+if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
+	window.WebRtcStreamer = WebRtcStreamer;
+}
+if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
+	module.exports = WebRtcStreamer;
+}

BIN
public/model/glft/gas/gasPump_2023-07-25.glb


BIN
public/model/glft/gas/gasPump_2023-08-18.glb


BIN
src/assets/images/vent/alarm-icons/ccq.png


BIN
src/assets/images/vent/alarm-icons/cf.png


BIN
src/assets/images/vent/alarm-icons/fc.png


BIN
src/assets/images/vent/alarm-icons/fm.png


BIN
src/assets/images/vent/alarm-icons/js.png


BIN
src/assets/images/vent/alarm-icons/penfen.png


BIN
src/assets/images/vent/alarm-icons/penlin.png


BIN
src/assets/images/vent/alarm-icons/pw.png


BIN
src/assets/images/vent/alarm-icons/wasibeng.png


BIN
src/assets/images/vent/alarm-icons/wasichoucaig.png


BIN
src/assets/images/vent/alarm-icons/yafeng.png


BIN
src/assets/images/vent/alarm-icons/zhudan.png


BIN
src/assets/images/vent/alarm-icons/zhujiang.png


BIN
src/assets/images/vent/alarm-icons/zhushan.png


BIN
src/assets/images/vent/alarm/bottom.png


+ 11 - 0
src/assets/images/vent/alarm/bottom.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="750" height="750" viewBox="0 0 750 750">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#28a6ff" stop-opacity="0"/>
+      <stop offset="0.516" stop-color="#28a6ff" stop-opacity="0.231"/>
+      <stop offset="0.932" stop-color="#28a6ff" stop-opacity="0.847"/>
+      <stop offset="1" stop-color="#3df6ff"/>
+    </linearGradient>
+  </defs>
+  <path id="椭圆_2601" data-name="椭圆 2601" d="M375,4a377,377,0,0,0-37.937,1.916A370.44,370.44,0,0,0,5.916,337.063a376.625,376.625,0,0,0,0,75.875A370.44,370.44,0,0,0,337.063,744.084a376.625,376.625,0,0,0,75.875,0A370.44,370.44,0,0,0,744.084,412.937a376.625,376.625,0,0,0,0-75.875A370.44,370.44,0,0,0,412.937,5.916,377,377,0,0,0,375,4m0-4C582.107,0,750,167.893,750,375S582.107,750,375,750,0,582.107,0,375,167.893,0,375,0Z" fill="url(#linear-gradient)"/>
+</svg>

BIN
src/assets/images/vent/alarm/bottom1.png


BIN
src/assets/images/vent/alarm/center-bg.png


BIN
src/assets/images/vent/alarm/data-bg.png


BIN
src/assets/images/vent/alarm/icon-animation.png


+ 11 - 0
src/assets/images/vent/alarm/icon-animation.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="71" height="71" viewBox="0 0 71 71">
+  <defs>
+    <radialGradient id="radial-gradient" cx="0.5" cy="0.5" r="0.5" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#3df6ff"/>
+      <stop offset="0.392" stop-color="#3dabff"/>
+      <stop offset="0.629" stop-color="#3dcaff" stop-opacity="0.11"/>
+      <stop offset="1" stop-color="#3df6ff" stop-opacity="0"/>
+    </radialGradient>
+  </defs>
+  <path id="椭圆_2600" data-name="椭圆 2600" d="M35,7A28,28,0,1,0,63,35,28.032,28.032,0,0,0,35,7m0-7A35,35,0,1,1,0,35,35,35,0,0,1,35,0Z" transform="translate(0.5 0.5)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#radial-gradient)"/>
+</svg>

BIN
src/assets/images/vent/alarm/icon-bg.png


+ 20 - 0
src/assets/images/vent/alarm/icon-device.svg

@@ -0,0 +1,20 @@
+<svg id="组_14116" data-name="组 14116" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55.336" height="55.336" viewBox="0 0 55.336 55.336">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.113" y1="0.088" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#36c7ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#36c7ff"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-2" x1="0.785" y1="0.935" x2="0.224" y2="0.083" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#d6f4ff"/>
+      <stop offset="0.259" stop-color="#d6f4ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#d6f4ff" stop-opacity="0"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-3" x1="0.295" x2="0.946" y2="0.843" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#83e4ff"/>
+      <stop offset="1" stop-color="#fff"/>
+    </linearGradient>
+  </defs>
+  <circle id="椭圆_2596" data-name="椭圆 2596" cx="27.668" cy="27.668" r="27.668" opacity="0.5" fill="url(#linear-gradient)"/>
+  <path id="路径_55647" data-name="路径 55647" d="M27.668,2.767A24.9,24.9,0,0,0,10.06,45.276,24.9,24.9,0,0,0,45.276,10.06,24.738,24.738,0,0,0,27.668,2.767m0-2.767A27.668,27.668,0,1,1,0,27.668,27.668,27.668,0,0,1,27.668,0Z" fill="url(#linear-gradient-2)"/>
+  <path id="路径_55648" data-name="路径 55648" d="M149.163,157.69v3.115H173.63v-2.82a6.408,6.408,0,0,0-.97-12.741H151.3a6.407,6.407,0,0,0-2.137,12.446Zm20.579-7.777a1.944,1.944,0,1,1-1.94,1.948v-.008A1.944,1.944,0,0,1,169.742,149.913Zm-7.762,0a1.944,1.944,0,1,1-1.94,1.948v-.008A1.939,1.939,0,0,1,161.981,149.913Zm-7.769,0a1.944,1.944,0,1,1-1.94,1.948v-.008A1.944,1.944,0,0,1,154.211,149.913Zm15.781-12.385L171.6,135.5a9.337,9.337,0,0,0-.811-.584,17.769,17.769,0,0,0-2.122-1.205c-5.586-2.714-11.642-2.645-17.282,2.1l1.637,1.993c4.768-4.01,9.778-4.07,14.53-1.751a15.524,15.524,0,0,1,1.827,1.038Q169.7,137.3,169.992,137.528Zm-3.828,1.433a9.823,9.823,0,0,0-11.256,1.364l1.637,1.993a7.316,7.316,0,0,1,8.5-1.023,10.088,10.088,0,0,1,1.076.614c.182.121.3.2.341.243l1.607-2.024A9.96,9.96,0,0,0,166.164,138.961Zm-5.033-9.725a19.786,19.786,0,0,1,8.83,2.1,21.506,21.506,0,0,1,2.539,1.448c.258.167.478.326.667.462l.2.159,1.607-2.024c-.2-.167-.561-.432-1.069-.773a23.851,23.851,0,0,0-2.842-1.614,22.276,22.276,0,0,0-9.975-2.357,20.389,20.389,0,0,0-13.105,5.154l1.637,1.993a17.9,17.9,0,0,1,11.506-4.548Z" transform="translate(-134.312 -116.052)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#linear-gradient-3)"/>
+</svg>

+ 28 - 0
src/assets/images/vent/alarm/icon-dust.svg

@@ -0,0 +1,28 @@
+<svg id="组_14115" data-name="组 14115" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55.336" height="55.336" viewBox="0 0 55.336 55.336">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.113" y1="0.088" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#36c7ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#36c7ff"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-2" x1="0.785" y1="0.935" x2="0.224" y2="0.083" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#d6f4ff"/>
+      <stop offset="0.259" stop-color="#d6f4ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#d6f4ff" stop-opacity="0"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-3" x1="0.295" x2="0.946" y2="0.843" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#83e4ff"/>
+      <stop offset="1" stop-color="#fff"/>
+    </linearGradient>
+  </defs>
+  <circle id="椭圆_2596" data-name="椭圆 2596" cx="27.668" cy="27.668" r="27.668" opacity="0.5" fill="url(#linear-gradient)"/>
+  <path id="路径_55647" data-name="路径 55647" d="M27.668,2.767A24.9,24.9,0,0,0,10.06,45.276,24.9,24.9,0,0,0,45.276,10.06,24.738,24.738,0,0,0,27.668,2.767m0-2.767A27.668,27.668,0,1,1,0,27.668,27.668,27.668,0,0,1,27.668,0Z" fill="url(#linear-gradient-2)"/>
+  <g id="组_14032" data-name="组 14032" transform="translate(11.036 12.959)">
+    <path id="路径_55460" data-name="路径 55460" d="M396.31,91.567m4.117,0a4.118,4.118,0,1,1-4.117-4.118A4.118,4.118,0,0,1,400.427,91.567Z" transform="translate(-379.979 -87.45)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55461" data-name="路径 55461" d="M638.078,587.7m4.12,0a4.119,4.119,0,1,1-4.12-4.119A4.12,4.12,0,0,1,642.2,587.7Z" transform="translate(-629.985 -566.678)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55462" data-name="路径 55462" d="M20.043,579.658m4.119,0a4.119,4.119,0,1,1-4.119-4.119A4.119,4.119,0,0,1,24.162,579.658Z" transform="translate(9.102 -558.914)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55463" data-name="路径 55463" d="M394.829,795.88m2.689,0a2.689,2.689,0,1,1-2.689-2.69A2.69,2.69,0,0,1,397.519,795.88Z" transform="translate(-377.069 -769.151)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55464" data-name="路径 55464" d="M837.3,340.608m2.689,0a2.689,2.689,0,1,1-2.689-2.689A2.69,2.69,0,0,1,839.99,340.608Z" transform="translate(-834.611 -329.388)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55465" data-name="路径 55465" d="M290.638,386.894m2.689,0a2.689,2.689,0,1,1-2.689-2.689A2.689,2.689,0,0,1,293.327,386.894Z" transform="translate(-269.328 -374.096)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55466" data-name="路径 55466" d="M30.952,219.982m2.689,0a2.689,2.689,0,1,1-2.689-2.689A2.689,2.689,0,0,1,33.641,219.982Z" transform="translate(-0.797 -212.87)" fill="url(#linear-gradient-3)"/>
+  </g>
+</svg>

+ 20 - 0
src/assets/images/vent/alarm/icon-fire.svg

@@ -0,0 +1,20 @@
+<svg id="组_14117" data-name="组 14117" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55.336" height="55.336" viewBox="0 0 55.336 55.336">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.113" y1="0.088" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#36c7ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#36c7ff"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-2" x1="0.785" y1="0.935" x2="0.224" y2="0.083" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#d6f4ff"/>
+      <stop offset="0.259" stop-color="#d6f4ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#d6f4ff" stop-opacity="0"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-3" x1="0.295" x2="0.946" y2="0.843" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#83e4ff"/>
+      <stop offset="1" stop-color="#fff"/>
+    </linearGradient>
+  </defs>
+  <circle id="椭圆_2596" data-name="椭圆 2596" cx="27.668" cy="27.668" r="27.668" opacity="0.5" fill="url(#linear-gradient)"/>
+  <path id="路径_55647" data-name="路径 55647" d="M27.668,2.767A24.9,24.9,0,0,0,10.06,45.276,24.9,24.9,0,0,0,45.276,10.06,24.738,24.738,0,0,0,27.668,2.767m0-2.767A27.668,27.668,0,1,1,0,27.668,27.668,27.668,0,0,1,27.668,0Z" fill="url(#linear-gradient-2)"/>
+  <path id="路径_55647-2" data-name="路径 55647" d="M178.554,111c1.722.574,2.3-1,1.579-1.579a6.272,6.272,0,0,1-2.583-7.032c.861-2.87,3.157-4.018,3.157-8.324,0,0,2.87,2.153,2.3,5.454,2.87-3.3,1.579-7.606,1-9.616,7.319,3.875,13.634,12.342,6.315,19.518-.861.718,0,2.009,1.292,1.579,19.949-11.338,4.88-28.273,2.3-30.282.861,2.009,1,5.167-.718,6.745-3.014-11.338-10.333-13.634-10.333-13.634.861,5.884-3.3,12.2-7.176,17.078a13.018,13.018,0,0,0-1.435-6.171c-.287,4.162-3.588,7.606-4.449,11.912-1.148,5.741.861,9.9,8.755,14.352h0Z" transform="translate(-157.979 -64.807)" fill="url(#linear-gradient-3)"/>
+</svg>

+ 24 - 0
src/assets/images/vent/alarm/icon-gas.svg

@@ -0,0 +1,24 @@
+<svg id="组_14118" data-name="组 14118" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55.336" height="55.336" viewBox="0 0 55.336 55.336">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.113" y1="0.088" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#36c7ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#36c7ff"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-2" x1="0.785" y1="0.935" x2="0.224" y2="0.083" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#d6f4ff"/>
+      <stop offset="0.259" stop-color="#d6f4ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#d6f4ff" stop-opacity="0"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-3" x1="0.295" x2="0.946" y2="0.843" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#83e4ff"/>
+      <stop offset="1" stop-color="#fff"/>
+    </linearGradient>
+  </defs>
+  <circle id="椭圆_2596" data-name="椭圆 2596" cx="27.668" cy="27.668" r="27.668" opacity="0.5" fill="url(#linear-gradient)"/>
+  <path id="路径_55647" data-name="路径 55647" d="M27.668,2.767A24.9,24.9,0,0,0,10.06,45.276,24.9,24.9,0,0,0,45.276,10.06,24.738,24.738,0,0,0,27.668,2.767m0-2.767A27.668,27.668,0,1,1,0,27.668,27.668,27.668,0,0,1,27.668,0Z" fill="url(#linear-gradient-2)"/>
+  <g id="图标_瓦斯预警" transform="translate(11.522 12.072)">
+    <path id="路径_55296" data-name="路径 55296" d="M21.939,57.661a3.969,3.969,0,0,0-.848.081A9.462,9.462,0,0,1,17.7,60.648a4.55,4.55,0,0,0-.263,1.494V64.2a7.1,7.1,0,0,1-14.209,0V52.675a1.615,1.615,0,0,0-3.229,0V64.219A10.354,10.354,0,1,0,20.708,64.2V62.16a1.263,1.263,0,1,1,2.524,0v5.43a1.615,1.615,0,0,0,3.229,0V62.16A4.548,4.548,0,0,0,21.939,57.661Z" transform="translate(0 -43.371)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55297" data-name="路径 55297" d="M51.407,61.833a4.521,4.521,0,0,0-3.391,2.906A9.237,9.237,0,0,0,51.407,61.833Zm-9.486,3.773c-.019.223-.019.425-.019.646v2.059a1.272,1.272,0,1,1-2.543,0V64.7a9.476,9.476,0,0,1-3.229-2.766v6.378a4.5,4.5,0,1,0,9-.019V66.253c0-.2,0-.383.019-.585a10.173,10.173,0,0,1-3.229-.061ZM53.164,55.94v.4a9.042,9.042,0,0,1-.425,2.785,7.169,7.169,0,0,1,6.62,7.124v5.43a1.636,1.636,0,0,0,.463,1.15,1.627,1.627,0,0,0,2.766-1.15v-5.43A10.376,10.376,0,0,0,53.163,55.94Z" transform="translate(-30.296 -47.463)" fill="url(#linear-gradient-3)"/>
+    <path id="路径_55298" data-name="路径 55298" d="M41.368,3.44a8.5,8.5,0,1,0,8.5,8.5A8.5,8.5,0,0,0,41.368,3.44Zm-.927,15.147.927-5.717h-3.7l4.629-7.623-.927,5.711h3.7Z" transform="translate(-27.563 -3.44)" fill="url(#linear-gradient-3)"/>
+  </g>
+</svg>

+ 20 - 0
src/assets/images/vent/alarm/icon-vent.svg

@@ -0,0 +1,20 @@
+<svg id="组_14114" data-name="组 14114" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55.336" height="55.336" viewBox="0 0 55.336 55.336">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.113" y1="0.088" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#36c7ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#36c7ff"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-2" x1="0.785" y1="0.935" x2="0.224" y2="0.083" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#d6f4ff"/>
+      <stop offset="0.259" stop-color="#d6f4ff" stop-opacity="0"/>
+      <stop offset="1" stop-color="#d6f4ff" stop-opacity="0"/>
+    </linearGradient>
+    <linearGradient id="linear-gradient-3" x1="0.295" x2="0.946" y2="0.843" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#83e4ff"/>
+      <stop offset="1" stop-color="#fff"/>
+    </linearGradient>
+  </defs>
+  <circle id="椭圆_2596" data-name="椭圆 2596" cx="27.668" cy="27.668" r="27.668" opacity="0.5" fill="url(#linear-gradient)"/>
+  <path id="路径_55647" data-name="路径 55647" d="M27.668,2.767A24.9,24.9,0,0,0,10.06,45.276,24.9,24.9,0,0,0,45.276,10.06,24.738,24.738,0,0,0,27.668,2.767m0-2.767A27.668,27.668,0,1,1,0,27.668,27.668,27.668,0,0,1,27.668,0Z" fill="url(#linear-gradient-2)"/>
+  <path id="路径_55649" data-name="路径 55649" d="M91.3,137.791a.834.834,0,0,1-.852-.816v-1.632a.834.834,0,0,1,.852-.816h22.993a1.633,1.633,0,1,0,0-3.264h-7.654a.834.834,0,0,1-.852-.816v-1.632a.834.834,0,0,1,.852-.816h7.654a4.9,4.9,0,1,1,0,9.791Zm-5.11,6.527a.834.834,0,0,1-.852-.816v-1.632a.834.834,0,0,1,.852-.816h20.439a.834.834,0,0,1,.852.816V143.5a.834.834,0,0,1-.852.816Zm8.516,6.527a.834.834,0,0,1-.852-.816V148.4a.834.834,0,0,1,.852-.816h5.11a.834.834,0,0,1,.852.816v1.632a.834.834,0,0,1-.852.816Zm8.516-3.264h11.071a4.9,4.9,0,1,1,0,9.791h-7.663a.834.834,0,0,1-.852-.816v-1.632a.834.834,0,0,1,.852-.816h7.663a1.633,1.633,0,1,0,0-3.264H103.218a.834.834,0,0,1-.852-.816V148.4a.834.834,0,0,1,.852-.816Z" transform="translate(-74.698 -115.018)" fill="url(#linear-gradient-3)"/>
+</svg>

+ 5 - 0
src/assets/images/vent/alarm/top-animation.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="414" height="414" viewBox="0 0 414 414">
+  <g id="组_14133" data-name="组 14133" transform="translate(-751 -214)">
+    <path id="椭圆_2601" data-name="椭圆 2601" d="M207,2a206.526,206.526,0,0,0-41.317,4.165,203.868,203.868,0,0,0-73.3,30.845,205.6,205.6,0,0,0-74.275,90.2A203.954,203.954,0,0,0,6.165,165.683a207.019,207.019,0,0,0,0,82.634,203.868,203.868,0,0,0,30.845,73.3,205.6,205.6,0,0,0,90.2,74.275,203.954,203.954,0,0,0,38.478,11.944,207.019,207.019,0,0,0,82.634,0,203.868,203.868,0,0,0,73.3-30.845,205.6,205.6,0,0,0,74.275-90.2,203.954,203.954,0,0,0,11.944-38.478,207.019,207.019,0,0,0,0-82.634,203.868,203.868,0,0,0-30.845-73.3,205.6,205.6,0,0,0-90.2-74.275A203.954,203.954,0,0,0,248.317,6.165,206.526,206.526,0,0,0,207,2m0-2C321.323,0,414,92.677,414,207S321.323,414,207,414,0,321.323,0,207,92.677,0,207,0Z" transform="translate(751 214)" fill="#3dd8ff" opacity="0.5"/>
+  </g>
+</svg>

+ 5 - 0
src/assets/images/vent/alarm/top-animation1.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="375.334" height="77.559" viewBox="0 0 375.334 77.559">
+  <g id="组_14135" data-name="组 14135" transform="translate(-755.058 -139.886)">
+    <path id="椭圆_2595" data-name="椭圆 2595" d="M186.917-.75c12.711,0,25.415.263,37.757.783,12.03.506,23.863,1.262,35.171,2.245,11.1.966,21.816,2.161,31.835,3.553,9.928,1.379,19.265,2.963,27.751,4.709s16.2,3.668,22.92,5.713A120.653,120.653,0,0,1,359.7,22.825a40.842,40.842,0,0,1,11.018,7.3c2.568,2.581,3.87,5.239,3.87,7.9s-1.3,5.322-3.87,7.9a40.842,40.842,0,0,1-11.018,7.3,120.653,120.653,0,0,1-17.345,6.572c-6.716,2.044-14.428,3.966-22.92,5.713s-17.823,3.329-27.751,4.709c-10.02,1.392-20.731,2.588-31.835,3.553-11.308.984-23.142,1.739-35.171,2.245-12.343.519-25.046.783-37.757.783s-25.415-.263-37.757-.783c-12.03-.506-23.863-1.262-35.171-2.245-11.1-.966-21.816-2.161-31.835-3.553-9.928-1.379-19.265-2.963-27.751-4.709s-16.2-3.668-22.92-5.713a120.652,120.652,0,0,1-17.345-6.572,40.841,40.841,0,0,1-11.018-7.3c-2.568-2.581-3.87-5.239-3.87-7.9s1.3-5.322,3.87-7.9a40.842,40.842,0,0,1,11.018-7.3,120.651,120.651,0,0,1,17.345-6.572C38.2,14.208,45.91,12.286,54.4,10.54S72.225,7.211,82.153,5.831c10.02-1.392,20.731-2.588,31.835-3.553C125.3,1.294,137.13.539,149.16.033,161.5-.487,174.206-.75,186.917-.75Zm0,76.98c49.941,0,96.885-4,132.186-11.257,17.134-3.523,30.578-7.623,39.96-12.184,9.642-4.688,14.53-9.653,14.53-14.759s-4.889-10.072-14.53-14.759c-9.382-4.561-22.826-8.66-39.96-12.184C283.8,3.827,236.858-.171,186.917-.171s-96.885,4-132.186,11.257C37.6,14.61,24.153,18.709,14.771,23.27,5.129,27.958.241,32.923.241,38.029S5.129,48.1,14.771,52.789c9.382,4.561,22.826,8.661,39.96,12.184C90.032,72.232,136.976,76.23,186.917,76.23Z" transform="translate(755.808 140.636)" fill="#28a6ff" opacity="0.7"/>
+  </g>
+</svg>

+ 5 - 0
src/assets/images/vent/alarm/top-animation2.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="414.878" height="91.7" viewBox="0 0 414.878 91.7">
+  <g id="组_14136" data-name="组 14136" transform="translate(-721.822 -158.273)">
+    <path id="椭圆_2595" data-name="椭圆 2595" d="M206.689-.75c14.05,0,28.092.311,41.735.925,13.3.6,26.377,1.492,38.877,2.655,12.275,1.142,24.114,2.555,35.189,4.2,10.974,1.631,21.294,3.5,30.674,5.567s17.91,4.337,25.335,6.754a128.134,128.134,0,0,1,19.172,7.771,45.082,45.082,0,0,1,12.178,8.633c2.838,3.051,4.278,6.195,4.278,9.344s-1.439,6.293-4.278,9.344a45.082,45.082,0,0,1-12.178,8.633A128.134,128.134,0,0,1,378.5,70.847c-7.424,2.417-15.948,4.69-25.335,6.754s-19.7,3.936-30.674,5.567c-11.075,1.646-22.915,3.059-35.189,4.2-12.5,1.163-25.58,2.056-38.877,2.655-13.643.614-27.685.925-41.735.925s-28.092-.311-41.735-.925c-13.3-.6-26.377-1.492-38.877-2.655-12.275-1.142-24.114-2.555-35.189-4.2-10.974-1.631-21.294-3.5-30.674-5.567S42.3,73.265,34.879,70.847a128.133,128.133,0,0,1-19.173-7.771A45.082,45.082,0,0,1,3.528,54.444C.689,51.393-.75,48.249-.75,45.1s1.439-6.293,4.278-9.344a45.083,45.083,0,0,1,12.178-8.633,128.133,128.133,0,0,1,19.173-7.771c7.424-2.417,15.948-4.69,25.335-6.754s19.7-3.936,30.674-5.567c11.075-1.646,22.915-3.059,35.189-4.2,12.5-1.163,25.58-2.056,38.877-2.655C178.6-.439,192.639-.75,206.689-.75Zm0,91.015c55.2,0,107.093-4.727,146.113-13.31,18.939-4.166,33.8-9.012,44.17-14.405,10.657-5.542,16.061-11.413,16.061-17.45s-5.4-11.908-16.061-17.45c-10.37-5.393-25.231-10.24-44.17-14.405C313.782,4.662,261.892-.065,206.689-.065S99.6,4.662,60.576,13.244c-18.939,4.166-33.8,9.012-44.17,14.405C5.749,33.192.345,39.063.345,45.1s5.4,11.908,16.061,17.45c10.37,5.393,25.231,10.24,44.17,14.405C99.6,85.538,151.487,90.265,206.689,90.265Z" transform="translate(722.572 159.023)" fill="#3dd8ff" opacity="0.5"/>
+  </g>
+</svg>

BIN
src/assets/images/vent/alarm/warning-bg.png


BIN
src/assets/images/vent/alarm/warning-icon-bg-a1.png


BIN
src/assets/images/vent/alarm/warning-icon-bg-a2.png


BIN
src/assets/images/vent/alarm/warning-icon-bg-a3.png


BIN
src/assets/images/vent/alarm/warning-icon-bg-a4.png


BIN
src/assets/images/vent/alarm/warning-icon-bg-a5.png


BIN
src/assets/images/vent/alarm/warning-icon-bg1.png


BIN
src/assets/images/vent/alarm/warning-icon-bg2.png


BIN
src/assets/images/vent/alarm/warning-icon-bg3.png


BIN
src/assets/images/vent/alarm/warning-icon-bg4.png


BIN
src/assets/images/vent/alarm/warning-icon-bg5.png


BIN
src/assets/images/vent/box-bottom-bg.png


BIN
src/assets/images/vent/box-top-bg.png


BIN
src/assets/images/vent/control-switch-bg1.png


BIN
src/assets/images/vent/device-detail-card1.png


BIN
src/assets/images/vent/fire-bg-top.png


BIN
src/assets/images/vent/icon-bottom-bg.png


+ 9 - 0
src/assets/images/vent/inner-icon.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="38.633" height="31.635" viewBox="0 0 38.633 31.635">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#f0faff"/>
+      <stop offset="1" stop-color="#3dd8ff"/>
+    </linearGradient>
+  </defs>
+  <path id="路径_55620" data-name="路径 55620" d="M538.239,501.147H532.21V473.516h16.1v-4h6.2v4h16.324v23.54a3.788,3.788,0,0,1-1.166,2.815,3.9,3.9,0,0,1-2.839,1.145h-7.347l1.32-3.609h3.08a1,1,0,0,0,.7-.286.95.95,0,0,0,.308-.725l.044-18.789H554.519v.924l-1.1,2.2h4.8l5.059,14.3h-6.2l-4.445-12.672-6.292,12.672h-6.2l8.183-16.5v-.924H538.239Z" transform="translate(-532.21 -469.512)" fill="url(#linear-gradient)"/>
+</svg>

+ 9 - 0
src/assets/images/vent/outer-icon.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="37.972" height="31.724" viewBox="0 0 37.972 31.724">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#f0faff"/>
+      <stop offset="1" stop-color="#3dd8ff"/>
+    </linearGradient>
+  </defs>
+  <path id="路径_55621" data-name="路径 55621" d="M384.985,495.214l1.672-3.784h7.172l.792-3.741h-3.432l-4-13.244h5.984l2.508,8.273,2.552-12.056H385.821l3.168-7h6.468l-1.232,2.64h11.748l-6.6,28.908H384.985Zm27.28.088h-5.456V463.578h5.456Zm7.744-28.292,2.948,25.564H417.5l-2.948-25.564Z" transform="translate(-384.985 -463.578)" fill="url(#linear-gradient)"/>
+</svg>

BIN
src/assets/images/vent/plane.png


BIN
src/assets/images/vent/plane1.png


BIN
src/assets/images/vent/pump1.png


BIN
src/assets/images/vent/small-bg1.png


BIN
src/assets/images/vent/value-bg.png


BIN
src/assets/images/vent/vent-param-bg.png


+ 0 - 2
src/components/Container/src/Adaptive.vue

@@ -49,12 +49,10 @@
               //传入宽高
               width.value = ctx.options.width;
               height.value = ctx.options.height;
-              console.log('[ 屏幕可见宽高 ] >', [width.value, height.value]);
             } else {
               //可见宽高
               width.value = dom.clientWidth;
               height.value = dom.clientHeight;
-              console.log('[ 屏幕可见宽高 ] >', [width.value, height.value]);
             }
             // 获取画布尺寸
             if (!originalWidth.value || !originalHeight.value) {

+ 1 - 0
src/components/Table/src/hooks/useTableContext.ts

@@ -4,6 +4,7 @@ import { provide, inject, ComputedRef } from 'vue';
 
 const key = Symbol('basic-table');
 
+
 type Instance = TableActionType & {
   wrapRef: Ref<Nullable<HTMLElement>>;
   getBindValues: ComputedRef<Recordable>;

+ 2 - 1
src/components/chart/BarAndLine.vue

@@ -9,6 +9,7 @@
   import { useECharts } from '/@/hooks/web/useECharts';
   import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
   import EchartsUtil from '/@/utils/echartsUtil';
+  import { merge } from 'lodash-es';
 
   export default defineComponent({
     name: 'BarAndLine',
@@ -92,7 +93,7 @@
       });
 
       function initChartsOption() {
-        optionUtil = new EchartsUtil(Object.assign(option, props.option));
+        optionUtil = new EchartsUtil(merge(option, props.option));
         optionUtil.initChartOption(props.chartsType, chartsColumns);
       }
       initChartsOption();

+ 308 - 83
src/components/chart/BarSingle.vue

@@ -2,99 +2,324 @@
   <div ref="chartRef" :style="{ height, width }"></div>
 </template>
 <script lang="ts">
-  import { defineComponent, PropType, ref, Ref, reactive, watchEffect, onMounted } from 'vue';
-  import { useECharts } from '/@/hooks/web/useECharts';
-  import { toEchartsData } from '/@/utils/ventutil';
-  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
-  import EchartsUtil from '/@/utils/echartsUtil';
-
-  export default defineComponent({
-    name: 'BarAndLine',
-    props: {
-      chartsColumns: {
-        type: Array,
-        default: () => [],
-      },
-      chartsColumnsType: {
-        type: String,
-      },
-      dataSource: {
-        type: Object,
-        default: () => {},
-      },
-      xAxisData: {
-        type: Array,
-        default: () => [],
+import { defineComponent, PropType, ref, Ref, reactive, watchEffect, onMounted } from 'vue';
+import { useECharts } from '/@/hooks/web/useECharts';
+import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+import { merge } from 'lodash-es';
+
+export default defineComponent({
+  name: 'BarAndLine',
+  props: {
+    chartsColumns: {
+      type: Array,
+      default: () => [],
+    },
+    chartsColumnsType: {
+      type: String,
+    },
+    dataSource: {
+      type: Object,
+      default: () => { },
+    },
+    xAxisData: {
+      type: Array,
+      default: () => [],
+    },
+    width: {
+      type: String as PropType<string>,
+      default: '100%',
+    },
+    height: {
+      type: String as PropType<string>,
+      default: '100%',
+    },
+    option: {
+      type: Object,
+      default: () => ({}),
+    },
+    color: {
+      type: Array,
+      default: () => [],
+    },
+    fontColor: {
+      type: Array,
+      default: () => [],
+    }
+  },
+  setup(props) {
+    const chartRef = ref<HTMLDivElement | null>(null);
+    const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
+    const chartData = getTableHeaderColumns(props.chartsColumnsType) || [];
+    const chartsColumns: [] = props.chartsColumns.length > 0 ? props.chartsColumns : chartData;
+
+
+    const colors = props.color.length > 0 ? props.color : [
+      [
+        { offset: 0.5, color: 'rgba(14, 159, 255, 1)' },
+        { offset: 0.6, color: 'rgba(97, 199, 252, 0.8)' },
+        { offset: 1, color: 'rgba(97, 199, 252, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(137, 163, 164, 1)' },
+        { offset: 1, color: 'rgba(137, 163, 164, 0.8)' },
+        { offset: 1, color: 'rgba(137, 163, 164, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(44, 166, 166, 1)' },
+        { offset: 1, color: 'rgba(44, 166, 166, 0.8)' },
+        { offset: 1, color: 'rgba(44, 166, 166, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(34, 66, 186, 1)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.8)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(34, 66, 186, 1)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.8)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.08)' },
+      ],
+    ];
+
+    const option = reactive(merge({
+      grid: {
+        top: '60px',
+        bottom: 10,
+        right: 20,
+        left: 10,
+        containLabel: true,
       },
-      width: {
-        type: String as PropType<string>,
-        default: '100%',
+      xAxis: {
+        offset: 8,
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: '#006c9d',
+
+          },
+        },
+        axisLabel: {
+          color: '#ffffff',
+        },
+        axisTick: {
+          show: false
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(21,80,126,.3)',
+            type: 'dashed', //设置网格线类型 dotted:虚线   solid:实线
+          },
+          show: true,
+        },
+        showBackground: true,
+        backgroundStyle: {
+          color: 'rgba(205, 95, 255, 1)',
+        },
+        data: [],
       },
-      height: {
-        type: String as PropType<string>,
-        default: 'calc(100vh - 78px)',
+
+      yAxis: {
+        type: 'value',
+        alignTicks: true,
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: '#006c9d',
+          },
+        },
+        axisLabel: {
+          show: true,
+          color: '#ffffff',
+          // formatter: '{value}' + item.yname
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(21,80,126,.3)',
+            type: 'dashed', //设置网格线类型 dotted:虚线   solid:实线
+          },
+          // show: item.linetype == 'line' ? true : false,
+          show: true,
+        },
+        showBackground: true,
+        backgroundStyle: {
+          color: 'rgba(205, 95, 255, 1)',
+        },
       },
-    },
-    setup(props) {
-      const chartRef = ref<HTMLDivElement | null>(null);
-      const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
-      const chartData = getTableHeaderColumns(props.chartsColumnsType) || [];
-      const chartsColumns = props.chartsColumns.length > 0 ? props.chartsColumns : chartData;
-      const option = reactive({
-        name: '',
-        color: ['#7B68EE', '#0000CD', '#6495ED', '#00BFFF', '#AFEEEE', '#008080', '#00FA9A', '#2E8B57', '#FAFAD2', '#DAA520'],
-        tooltip: null,
-        grid: null,
-        toolbox: {
-          feature: {
-            saveAsImage: {
-              iconStyle: {
-                borderColor: '#ffffff',
-              },
-              show: true,
+
+      series: []
+    }, props.option));
+
+    if (props.option) {
+      merge(option, props.option)
+    }
+
+    watchEffect(() => {
+      props.dataSource && option.series && initCharts();
+    });
+
+
+    function initDta(xData: string[], yData: number[], ymax?) {
+      // 设置顶部和底部的值
+      let symbolData: number[] = [], newShadowHight: number[] = []
+      if(!ymax) ymax = Math.max(...yData)
+
+      yData.forEach(() => {
+        symbolData.push(1)
+        newShadowHight.push(ymax)
+      })
+      var color = '#fff'
+
+      option.xAxis.data = xData as never[]
+      option.series = [
+        // 底部
+        {
+          z: 2,
+          type: 'pictorialBar',
+          symbol: 'diamond',
+          symbolOffset: ['0%', '50%'],
+          symbolSize: [30, 12],
+          toolltip: {
+            show: false
+          },
+          itemStyle: {
+            color: function (params) {
+              
+              return colors[params.dataIndex][1].color;
             },
           },
+          data: symbolData, // [1,1,1,1,1]
+        },
+        // 内容区域
+        {
+          z: 1,
+          type: 'bar',
+          barWidth: 30,
+          // name: legendData[0].name,
+          itemStyle: {
+            color: function (params) {
+              return new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  // @ts-ignore
+                  color: colors[params.dataIndex][1].color,
+                },
+                {
+                  offset: 1,
+                  color: colors[params.dataIndex][2].color,
+                },
+              ]);
+            },
+          },
+          label: {
+            show: true, //开启显示
+            position: 'top', //在上方显示
+            // formatter: '{c}%',//显示百分号
+            textStyle: { //数值样式
+              // color: function (params) {
+              //   return props.fontColor.length > params ? props.fontColor[params.dataIndex] : '#fff';
+              // },
+              color: color,
+              fontSize: 13//字体大小
+            }
+          },
+          data: yData
         },
-        dataZoom: null,
-        legend: null,
-        timeline: null,
-        xAxis: null,
-        yAxis: null,
-        series: null,
-      });
-
-      watchEffect(() => {
-        props.dataSource && option.series && initCharts();
-      });
-
-      function initChartsOption() {
-
-        const optionUtil = new EchartsUtil(option);
-        optionUtil.initChartOption('listMonitor', chartsColumns);
-        console.log('option----------->', option);
-      }
 
-      initChartsOption();
+        // 内容的顶部
+        {
+          z: 3,
+          type: 'pictorialBar',
+          symbol: 'diamond',
+          symbolPosition: 'end',
+          symbolOffset: ['0%', '-50%'],
+          symbolSize: [30, 12],
+          toolltip: {
+            show: false
+          },
+          itemStyle: {
+            color: function (params) {
+              color = colors[params.dataIndex][1].color
+              return colors[params.dataIndex][0].color;
+            },
+          },
+          
+          data: yData,
+        },
+        // 阴影区域
+        {
+          z: 0,
+          type: 'bar',
+          barWidth: 30,
+          barGap: "-100%",
+          data: newShadowHight,
+          itemStyle: {
+            color: "#00365F66"
+          }
+        },
+        // 阴影的顶部
+        {
+          z: 3,
+          type: 'pictorialBar',
+          symbol: 'diamond',
+          symbolPosition: 'end',
+          symbolOffset: ['0%', '-50%'],
+          symbolSize: [30, 12],
+          toolltip: {
+            show: false
+          },
+          itemStyle: {
+            color: "#00365F66"
+          },
+          data: newShadowHight,
+        }
+      ] as never[]
+
+    }
+
+    function initCharts() {
+
+      //轴数据
+      if (option.series) {
+        const xAxisData: any[] = [];
+        const series: any[] = [];
+        const seriesData: any[] = [];
+        // const legendData: { name: string, itemStyle: Object }[] = [];
+        let ymax = 0
+        props.xAxisData.forEach((item: any, index) => {
 
-      function initCharts() {
-        
+          xAxisData.push(item['key']);
+          seriesData.push(props.dataSource[item.valueKey] || '');
 
-        //轴数据
-        if (option.series) {
-          const xAxisData: string[] = [];
-          const seriesData = [];
-          props.xAxisData.forEach((item: any) => {
-            xAxisData.push(item['key']);
-            seriesData.push(props.dataSource[item.valueKey] || '');
-          });
-          option.series[0].data = seriesData;
-          option.xAxis[0].data = xAxisData;
-          console.log('option------------->', option);
+        });
+
+        for (let i = 0; i < chartsColumns.length; i++) {
+          const item: any = chartsColumns[i]
+          // legendData.push({
+          //   name: item.legend + (item.yname ? '(' + item.yname + ')' : ''),
+          //   itemStyle: {
+          //     color: new echarts.graphic.LinearGradient(1, 0, 0, 0, colors[i]),
+          //   },
+          // })
+          if (!ymax && item['ymax']) {
+            ymax = item['ymax']
+          }
 
-          setOptions(option, false);
         }
+
+        // option.legend.data = legendData
+        // option.series[0].data = seriesData;
+
+        // option.series = series;
+        // option.xAxis.data = xAxisData;
+
+        initDta(xAxisData, seriesData)
+
+        console.log('option------------->', option);
+
+        setOptions(option, false);
       }
-      return { chartRef };
-    },
-  });
+    }
+    return { chartRef };
+  },
+});
 </script>

+ 100 - 0
src/components/chart/BarSingle1.vue

@@ -0,0 +1,100 @@
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, Ref, reactive, watchEffect, onMounted } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  import { toEchartsData } from '/@/utils/ventutil';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import EchartsUtil from '/@/utils/echartsUtil';
+
+  export default defineComponent({
+    name: 'BarAndLine',
+    props: {
+      chartsColumns: {
+        type: Array,
+        default: () => [],
+      },
+      chartsColumnsType: {
+        type: String,
+      },
+      dataSource: {
+        type: Object,
+        default: () => {},
+      },
+      xAxisData: {
+        type: Array,
+        default: () => [],
+      },
+      width: {
+        type: String as PropType<string>,
+        default: '100%',
+      },
+      height: {
+        type: String as PropType<string>,
+        default: 'calc(100vh - 78px)',
+      },
+    },
+    setup(props) {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
+      const chartData = getTableHeaderColumns(props.chartsColumnsType) || [];
+      const chartsColumns = props.chartsColumns.length > 0 ? props.chartsColumns : chartData;
+      const option = reactive({
+        name: '',
+        color: ['#7B68EE', '#0000CD', '#6495ED', '#00BFFF', '#AFEEEE', '#008080', '#00FA9A', '#2E8B57', '#FAFAD2', '#DAA520'],
+        tooltip: null,
+        grid: null,
+        toolbox: {
+          feature: {
+            saveAsImage: {
+              iconStyle: {
+                borderColor: '#ffffff',
+              },
+              show: true,
+            },
+          },
+        },
+        dataZoom: null,
+        legend: null,
+        timeline: null,
+        xAxis: null,
+        yAxis: null,
+        series: null,
+      });
+
+      watchEffect(() => {
+        props.dataSource && option.series && initCharts();
+      });
+
+      function initChartsOption() {
+
+        const optionUtil = new EchartsUtil(option);
+        optionUtil.initChartOption('listMonitor', chartsColumns);
+        console.log('option----------->', option);
+      }
+
+      initChartsOption();
+
+      function initCharts() {
+        
+
+        //轴数据
+        if (option.series) {
+          const xAxisData: string[] = [];
+          const seriesData = [];
+          props.xAxisData.forEach((item: any) => {
+            xAxisData.push(item['key']);
+            seriesData.push(props.dataSource[item.valueKey] || '');
+          });
+          option.series[0].data = seriesData;
+          option.xAxis[0].data = xAxisData;
+          console.log('option------------->', option);
+
+          setOptions(option, false);
+        }
+      }
+      return { chartRef };
+    },
+  });
+</script>

+ 389 - 0
src/components/chart/BarSingle2.vue

@@ -0,0 +1,389 @@
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts">
+import { defineComponent, PropType, ref, Ref, reactive, watchEffect, onMounted } from 'vue';
+import { useECharts } from '/@/hooks/web/useECharts';
+import { toEchartsData } from '/@/utils/ventutil';
+import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+import EchartsUtil from '/@/utils/echartsUtil';
+
+export default defineComponent({
+  name: 'BarAndLine',
+  props: {
+    chartsColumns: {
+      type: Array,
+      default: () => [],
+    },
+    chartsColumnsType: {
+      type: String,
+    },
+    dataSource: {
+      type: Object,
+      default: () => { },
+    },
+    xAxisData: {
+      type: Array,
+      default: () => [],
+    },
+    width: {
+      type: String as PropType<string>,
+      default: '100%',
+    },
+    height: {
+      type: String as PropType<string>,
+      default: 'calc(100vh - 78px)',
+    },
+  },
+  setup(props) {
+    const chartRef = ref<HTMLDivElement | null>(null);
+    const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
+    const chartData = getTableHeaderColumns(props.chartsColumnsType) || [];
+    const chartsColumns: [] = props.chartsColumns.length > 0 ? props.chartsColumns : chartData;
+
+    const labels = ['春节', '元宵节', '端午节', '中秋节'];
+    const seriesData = [
+      {
+        label: '春节',
+        value: [32],
+      },
+      {
+        label: '元宵节',
+        value: [24],
+      },
+      {
+        label: '端午节',
+        value: [42],
+      },
+      {
+        label: '中秋节',
+        value: [20],
+      }
+    ]
+
+    const colors = [
+      [
+        // { offset: 0, color: 'rgba(73, 190, 255, 1)' },
+        { offset: 0.5, color: 'rgba(14, 159, 255, 1)' },
+        { offset: 0.6, color: 'rgba(97, 199, 252, 0.8)' },
+        { offset: 1, color: 'rgba(97, 199, 252, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(137, 163, 164, 1)' },
+        { offset: 1, color: 'rgba(137, 163, 164, 0.8)' },
+        { offset: 1, color: 'rgba(137, 163, 164, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(44, 166, 166, 1)' },
+        { offset: 1, color: 'rgba(44, 166, 166, 0.8)' },
+        { offset: 1, color: 'rgba(44, 166, 166, 0.08)' },
+      ],
+      [
+        { offset: 0.5, color: 'rgba(34, 66, 186, 1)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.8)' },
+        { offset: 1, color: 'rgba(34, 66, 186, 0.08)' },
+      ],
+    ];
+
+    const option = reactive({
+      xAxis: {
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: '#006c9d',
+          },
+        },
+        axisTick: {
+          show: false
+        },
+        nameTextStyle: {
+          color: '#fff'
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(21,80,126,.3)',
+            type: 'dashed', //设置网格线类型 dotted:虚线   solid:实线
+          },
+          show: true,
+        },
+        showBackground: true,
+        backgroundStyle: {
+          color: 'rgba(205, 95, 255, 1)',
+        },
+        data: [],
+      },
+      legend: {
+        // data: getlegendData(),
+        data: [],
+        right: '0',
+        top: '18',
+        icon: 'rect',
+        itemHeight: 10,
+        itemWidth: 10,
+        textStyle: {
+          color: '#fff'
+        }
+      },
+      yAxis: {
+        type: 'value',
+        alignTicks: true,
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: '#006c9d',
+          },
+        },
+        axisLabel: {
+          show: true,
+          color: '#ffffffcc',
+          // formatter: '{value}' + item.yname
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(21,80,126,.3)',
+            type: 'dashed', //设置网格线类型 dotted:虚线   solid:实线
+          },
+          // show: item.linetype == 'line' ? true : false,
+          show: true,
+        },
+        showBackground: true,
+        backgroundStyle: {
+          color: 'rgba(205, 95, 255, 1)',
+        },
+      },
+      // series: getSeriesData()
+      series: []
+    });
+
+
+    // 定义柱状图左侧图形元素
+    const CubeLeft = echarts.graphic.extendShape({
+      buildPath: function (ctx, shape) {
+        const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+        // 侧面宽度
+        const WIDTH = 15;
+        // 斜角高度
+        const OBLIQUE_ANGLE_HEIGHT = 4;
+
+        const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+        const p2 = [basicsXAxis - WIDTH, bottomYAxis];
+        const p3 = [basicsXAxis, bottomYAxis];
+        const p4 = [basicsXAxis, topBasicsYAxis];
+
+        ctx.moveTo(p1[0], p1[1]);
+        ctx.lineTo(p2[0], p2[1]);
+        ctx.lineTo(p3[0], p3[1]);
+        ctx.lineTo(p4[0], p4[1]);
+      },
+    });
+    // 定义柱状图右侧以及顶部图形元素
+    const CubeRight = echarts.graphic.extendShape({
+
+      buildPath: function (ctx, shape) {
+        const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
+        // 侧面宽度
+        const WIDTH = 15;
+        // 斜角高度
+        const OBLIQUE_ANGLE_HEIGHT = 4;
+
+        const p1 = [basicsXAxis, topBasicsYAxis];
+        const p2 = [basicsXAxis, bottomYAxis];
+        const p3 = [basicsXAxis + WIDTH, bottomYAxis];
+        const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+
+        ctx.moveTo(p1[0], p1[1]);
+        ctx.lineTo(p2[0], p2[1]);
+        ctx.lineTo(p3[0], p3[1]);
+        ctx.lineTo(p4[0], p4[1]);
+      }
+    });
+
+    // 绘制顶面
+    const CubeTop = echarts.graphic.extendShape({
+      buildPath: function (ctx, shape) {
+        const { topBasicsYAxis, basicsXAxis } = shape;
+        // 侧面宽度
+        const WIDTH = 15;
+        // 斜角高度
+        const OBLIQUE_ANGLE_HEIGHT = 4;
+
+        const p1 = [basicsXAxis, topBasicsYAxis];
+        const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+        const p3 = [basicsXAxis, topBasicsYAxis - (OBLIQUE_ANGLE_HEIGHT * 2)];
+        const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
+
+        ctx.moveTo(p1[0], p1[1]);
+        ctx.lineTo(p2[0], p2[1]);
+        ctx.lineTo(p3[0], p3[1]);
+        ctx.lineTo(p4[0], p4[1]);
+      },
+    });
+
+    // 注册图形元素
+    echarts.graphic.registerShape('CubeLeft', CubeLeft);
+    echarts.graphic.registerShape('CubeRight', CubeRight);
+    echarts.graphic.registerShape('CubeTop', CubeTop);
+
+    function getlegendData() {
+      const data = <any[]>[];
+      labels.forEach((item, index) => {
+        data.push(
+          {
+            name: item,
+            itemStyle: {
+              color: new echarts.graphic.LinearGradient(1, 0, 0, 0, colors[index]),
+            },
+          }
+        )
+      })
+      return data
+    }
+
+    function getSeriesData() {
+      const data = <any[]>[];
+      seriesData.forEach((item, index) => {
+        data.push(
+          {
+            type: 'custom',
+            name: item.label,
+            renderItem: function (params, api) {
+              return getRenderItem(params, api);
+            },
+            data: item.value,
+            itemStyle: {
+              color: () => {
+                return new echarts.graphic.LinearGradient(0, 0, 0, 1, colors[index]);
+              },
+            },
+          }
+        )
+      })
+      return data
+    }
+
+    function getRenderItem(params, api) {
+      const index = params.seriesIndex;
+      let location = api.coord([api.value(0), api.value(1)]);
+
+      // 顶部基础 y 轴
+      const topBasicsYAxis = location[1];
+      // 基础 x 轴
+      const basicsXAxis = location[0];
+      // 底部 y 轴
+      const bottomYAxis = api.coord([api.value(0), 0])[1];
+
+      var extent = api.size([0, api.value(1)]);
+      // const location = api.coord([api.value(0), api.value(1)]);
+      let cubeLeftStyle = new echarts.graphic.LinearGradient(0, 0, 0, 1, colors[index]);
+      let cubeRightStyle = new echarts.graphic.LinearGradient(0, 0, 0, 1, colors[index]);
+      let cubeTopStyle = new echarts.graphic.LinearGradient(0, 0, 0, 1, colors[index]);
+
+      return {
+        type: 'group',
+        children: [
+          {
+            type: 'CubeLeft',
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              ...api.style(),
+              stroke: '#ffffff33',
+              fill: cubeLeftStyle,
+            }
+          },
+          {
+            type: 'CubeRight',
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              ...api.style(),
+              stroke: '#ffffff33',
+              fill: cubeRightStyle,
+            }
+          },
+          {
+            type: 'CubeTop',
+            shape: {
+              topBasicsYAxis,
+              basicsXAxis,
+              bottomYAxis,
+            },
+            style: {
+              ...api.style(),
+              stroke: '#ffffff33',
+              fill: cubeTopStyle,
+            }
+          }
+        ]
+      };
+    }
+
+    watchEffect(() => {
+      props.dataSource && option.series && initCharts();
+    });
+
+    // function initChartsOption() {
+
+    //   const optionUtil = new EchartsUtil(option);
+    //   optionUtil.initChartOption('listMonitor', chartsColumns);
+    //   console.log('option----------->', option);
+    // }
+
+    // initChartsOption();
+    function initCharts() {
+
+      //轴数据
+      if (option.series) {
+        const xAxisData: any[] = [];
+        const series: any[] = [];
+        const seriesData: any[] = [];
+        const legendData: { name: string, itemStyle: Object }[] = [];
+        props.xAxisData.forEach((item: any, index) => {
+
+          xAxisData.push(item['key']);
+          seriesData.push(props.dataSource[item.valueKey] || '');
+
+        });
+
+        for (let i = 0; i < chartsColumns.length; i++) {
+          const item: any = chartsColumns[i]
+          legendData.push({
+            name: item.legend + (item.yname ? '(' + item.yname + ')' : ''),
+            itemStyle: {
+              color: new echarts.graphic.LinearGradient(1, 0, 0, 0, colors[i]),
+            },
+          })
+
+          series.push(
+            {
+              type: 'custom',
+              name: item.legend + (item.yname ? '(' + item.yname + ')' : ''),
+              renderItem: function (params, api) {
+                return getRenderItem(params, api);
+              },
+              data: seriesData,
+            })
+
+        }
+
+        option.legend.data = legendData
+        // option.series[0].data = seriesData;
+
+        option.series = series;
+        option.xAxis.data = xAxisData;
+
+        console.log('option------------->', option);
+
+        setOptions(option, false);
+      }
+    }
+    return { chartRef };
+  },
+});
+</script>

+ 1 - 0
src/components/chart/Line.vue

@@ -75,6 +75,7 @@
         option.xAxis.data = xAxisData;
         setOptions(option);
       }
+      
       return { chartRef };
     },
   });

+ 317 - 0
src/components/vent/numberDraw.vue

@@ -0,0 +1,317 @@
+<template>
+  <div class="count" :class="[changeState, arrow]">
+    <span class="current top">{{ nextTime }}</span>
+    <span class="next top">{{ time }}</span>
+    <span class="current bottom">{{ nextTime }}</span>
+    <span class="next bottom">{{ time }}</span>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, watch, toRefs } from 'vue'
+const props = defineProps({
+  chartConfig: {
+    type: Object,
+    required: true
+  },
+  value: {
+    type: Number,
+    required: false
+  }
+})
+//动态宽高
+const { w, h } = toRefs(props.chartConfig.attr)
+let changeState = ref('')
+let time: any = ref(0) //当前数字
+let nextTime = ref(0) //下一个数字
+let duration = 1000 //延迟时间
+let arrow = 'up' //方向
+
+watch(
+  () => props.value,
+  (n: any) => {
+    if (typeof n == 'number') selfAdaption(n)
+  },
+  { deep: true, immediate: true }
+)
+
+//自适应改变数字
+function selfAdaption(n: any) {
+  changeState.value = ''
+  setTimeout(() => {
+    changeState.value = 'changing'
+  }, 20)
+  setTimeout(() => {
+    changeState.value = 'changed'
+  }, duration * 0.9)
+  time.value = n
+  nextTime.value = n - 1 < 0 ? 0 : n - 1
+}
+
+// // 倒计时改变
+// function Interval() {
+//   setTimeout(() => {
+//     changeState.value = ''
+//     setTimeout(() => {
+//       changeState.value = 'changing'
+//     }, 20)
+//     setTimeout(() => {
+//       changeState.value = 'changed'
+//     }, duration * 0.9)
+
+//     if (time.value == 0) arrow = 'up'
+//     if (time.value == 9) arrow = 'down'
+
+//     let delta = -1
+//     if (arrow == 'up') delta = 1
+
+//     if (arrow == 'down') {
+//       if (time.value == 0) {
+//         setTimeout(() => {
+//           time.value = 9
+//         }, 20)
+//       }
+//     }
+//     if (arrow == 'up') {
+//       if (time.value == 9) {
+//         setTimeout(() => {
+//           time.value = 0
+//         }, 20)
+//       }
+//     }
+
+//     time.value += delta
+//     nextTime.value = time.value - delta
+
+//     Interval()
+//   }, duration)
+// }
+// if (!(typeof props.value == 'number')) {
+//   Interval()
+// }
+
+</script>
+<style lang="less" scoped>
+.count {
+  box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2);
+  -moz-perspective: 500px;
+  -webkit-perspective: 500px;
+  perspective: 500px;
+  text-align: center;
+  -moz-transform: translateZ(0);
+  -webkit-transform: translateZ(0);
+  transform: translateZ(0);
+  width: v-bind('w + "px"');
+  height: v-bind('h + "px"');
+  line-height: v-bind('h + "px"');
+  padding: 15px;
+  box-sizing: border-box;
+}
+
+.count span {
+  background: #202020;
+  color: #f8f8f8;
+  display: block;
+  font-size: 50px;
+  left: 0;
+  position: absolute;
+  top: 0;
+  text-shadow: 0 1px 0 #282828, 0 2px 0 #1e1e1e, 0 3px 0 #141414, 0 4px 0 #0a0a0a, 0 5px 0 #000,
+    0 0 10px rgba(0, 0, 0, 0.8);
+  -moz-transform-origin: 0 v-bind('h / 2 + "px"') 0;
+  -webkit-transform-origin: 0 v-bind('h / 2 + "px"') 0;
+  transform-origin: 0 v-bind('h / 2 + "px"') 0;
+  width: 100%;
+}
+
+.count span:before {
+  border-bottom: 2px solid #000;
+  content: '';
+  left: 0;
+  position: absolute;
+  width: 100%;
+}
+
+.count span:after {
+  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.35);
+  content: '';
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+
+.count .small {
+  font-size: 175px;
+}
+
+.count .top {
+  border-top-left-radius: 11px;
+  border-top-right-radius: 11px;
+  box-shadow: inset 0 2px rgba(0, 0, 0, 0.9), inset 0 3px 0 rgba(255, 255, 255, 0.4);
+  height: 50%;
+  overflow: hidden;
+}
+
+.count .top:before {
+  bottom: 0;
+}
+
+.count .top:after {
+  background: -moz-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
+  background: -webkit-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
+  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
+  border-top-left-radius: 11px;
+  border-top-right-radius: 11px;
+}
+
+.count .bottom {
+  border-radius: 10px;
+  height: 100%;
+}
+
+.count .bottom:before {
+  top: 50%;
+}
+
+.count .bottom:after {
+  border-radius: 10px;
+  background: -moz-linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0));
+  background: -webkit-linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0));
+  background: linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0));
+}
+
+.count.down .top {
+  border-top-left-radius: 11px;
+  border-top-right-radius: 11px;
+  height: 50%;
+}
+
+.count.down .top.current {
+  -moz-transform-style: flat;
+  -webkit-transform-style: flat;
+  transform-style: flat;
+  z-index: 3;
+}
+
+.count.down .top.next {
+  -moz-transform: rotate3d(1, 0, 0, -90deg);
+  -ms-transform: rotate3d(1, 0, 0, -90deg);
+  -webkit-transform: rotate3d(1, 0, 0, -90deg);
+  transform: rotate3d(1, 0, 0, -90deg);
+  z-index: 4;
+}
+
+.count.down .bottom {
+  border-radius: 10px;
+}
+
+.count.down .bottom.current {
+  z-index: 2;
+}
+
+.count.down .bottom.next {
+  z-index: 1;
+}
+
+.count.down.changing .bottom.current {
+  box-shadow: 0 75px 5px -20px rgba(0, 0, 0, 0.3);
+  -moz-transform: rotate3d(1, 0, 0, 90deg);
+  -ms-transform: rotate3d(1, 0, 0, 90deg);
+  -webkit-transform: rotate3d(1, 0, 0, 90deg);
+  transform: rotate3d(1, 0, 0, 90deg);
+  -moz-transition: -moz-transform 0.35s ease-in, box-shadow 0.35s ease-in;
+  -o-transition: -o-transform 0.35s ease-in, box-shadow 0.35s ease-in;
+  -webkit-transition: -webkit-transform 0.35s ease-in, box-shadow 0.35s ease-in;
+  transition: transform 0.35s ease-in, box-shadow 0.35s ease-in;
+}
+
+.count.down.changing .top.next,
+.count.down.changed .top.next {
+  -moz-transition: -moz-transform 0.35s ease-out 0.35s;
+  -o-transition: -o-transform 0.35s ease-out 0.35s;
+  -webkit-transition: -webkit-transform 0.35s ease-out;
+  -webkit-transition-delay: 0.35s;
+  transition: transform 0.35s ease-out 0.35s;
+  -moz-transform: none;
+  -ms-transform: none;
+  -webkit-transform: none;
+  transform: none;
+}
+
+.count.up .top {
+  height: 50%;
+}
+
+.count.up .top.current {
+  z-index: 4;
+}
+
+.count.up .top.next {
+  z-index: 3;
+}
+
+.count.up .bottom.current {
+  z-index: 1;
+}
+
+.count.up .bottom.next {
+  box-shadow: 0 75px 5px -20px rgba(0, 0, 0, 0.3);
+  -moz-transform: rotate3d(1, 0, 0, 90deg);
+  -ms-transform: rotate3d(1, 0, 0, 90deg);
+  -webkit-transform: rotate3d(1, 0, 0, 90deg);
+  transform: rotate3d(1, 0, 0, 90deg);
+  z-index: 2;
+}
+
+.count.up.changing .top.current {
+  -moz-transform: rotate3d(1, 0, 0, -90deg);
+  -ms-transform: rotate3d(1, 0, 0, -90deg);
+  -webkit-transform: rotate3d(1, 0, 0, -90deg);
+  transform: rotate3d(1, 0, 0, -90deg);
+  -moz-transition: -moz-transform 0.2625s ease-in, box-shadow 0.2625s ease-in;
+  -o-transition: -o-transform 0.2625s ease-in, box-shadow 0.2625s ease-in;
+  -webkit-transition: -webkit-transform 0.2625s ease-in, box-shadow 0.2625s ease-in;
+  transition: transform 0.2625s ease-in, box-shadow 0.2625s ease-in;
+}
+
+.count.up.changing .bottom.next,
+.count.up.changed .bottom.next {
+  box-shadow: 0 0 0 0 transparent;
+  -moz-transition: box-shadow 0.175s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s,
+    -moz-transform 0.35s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s;
+  -o-transition: box-shadow 0.175s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s,
+    -o-transform 0.35s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s;
+  -webkit-transition: box-shadow 0.175s cubic-bezier(0.375, 1.495, 0.61, 0.78),
+    -webkit-transform 0.35s cubic-bezier(0.375, 1.495, 0.61, 0.78);
+  -webkit-transition-delay: 0.35s, 0.35s;
+  transition: box-shadow 0.175s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s,
+    transform 0.35s cubic-bezier(0.375, 1.495, 0.61, 0.78) 0.35s;
+  -moz-transform: rotate3d(1, 0, 0, 0);
+  -ms-transform: rotate3d(1, 0, 0, 0);
+  -webkit-transform: rotate3d(1, 0, 0, 0);
+  transform: rotate3d(1, 0, 0, 0);
+}
+
+.count.changed .top.current,
+.count.changed .bottom.current {
+  display: none;
+}
+
+html,
+body {
+  height: 100%;
+  width: 100%;
+}
+
+body {
+  background: #202020;
+  background-origin: 50% 50%;
+  -moz-background-size: cover;
+  -o-background-size: cover;
+  -webkit-background-size: cover;
+  background-size: cover;
+  font-family: 'Oswald';
+}
+</style>

+ 29 - 5
src/hooks/setting/index.ts

@@ -1,6 +1,27 @@
 import type { GlobConfig } from '/#/config';
 
 import { getAppEnvConfig } from '/@/utils/env';
+// 读取ip地址
+let domainUrl = '',
+  title = '';
+
+const getUrl = () => {
+  fetch(VUE_APP_URL.baseUrl + '/ventanaly-device/safety/orgParams/queryDefault', {
+    method: 'GET',
+    cache: 'no-cache',
+    headers: {
+      'Content-Type': 'application/x-www-form-urlencoded',
+    },
+  })
+    .then((response) => response.json())
+    .then((data) => (title = data.result['systemTitle']));
+};
+if (!title) {
+  try {
+    getUrl();
+  } catch (error) {}
+}
+
 
 export const useGlobSetting = (): Readonly<GlobConfig> => {
   const {
@@ -21,21 +42,24 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
     //   `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
     // );
   }
-
+  if (import.meta.env.DEV) {
+    domainUrl = VITE_GLOB_DOMAIN_URL;
+  } else if (import.meta.env.PROD && !domainUrl) {
+    domainUrl = VUE_APP_URL.baseUrl;
+  }
   // Take global configuration
   const glob: Readonly<GlobConfig> = {
-    title: VITE_GLOB_APP_TITLE,
-    domainUrl: VITE_GLOB_DOMAIN_URL,
+    title: title,
+    domainUrl: domainUrl,
     apiUrl: VITE_GLOB_API_URL,
     shortName: VITE_GLOB_APP_SHORT_NAME,
     openSso: VITE_GLOB_APP_OPEN_SSO,
     openQianKun: VITE_GLOB_APP_OPEN_QIANKUN,
     casBaseUrl: VITE_GLOB_APP_CAS_BASE_URL,
     urlPrefix: VITE_GLOB_API_URL_PREFIX,
-    uploadUrl: VITE_GLOB_DOMAIN_URL,
+    uploadUrl: domainUrl,
     viewUrl: VITE_GLOB_ONLINE_VIEW_URL,
     modalUrlArr: VITE_3D_MODAL_ARR,
   };
-  window._CONFIG['domianURL'] = VITE_GLOB_DOMAIN_URL;
   return glob as Readonly<GlobConfig>;
 };

+ 1 - 1
src/hooks/system/useListPage.ts

@@ -273,7 +273,7 @@ export function useListTable(tableProps: TableProps): [
   // 发送请求之前调用的方法
   function beforeFetch(params) {
     // 默认以 createTime 降序排序
-    return Object.assign({ column: 'createTime', order: 'desc' }, params);
+    return Object.assign({ column: 'createTime'}, params);
   }
 
   // 合并方法

+ 15 - 11
src/layouts/default/sider/bottomSideder.vue

@@ -1,18 +1,23 @@
 <template>
   <div v-if="isShowMenu == -1" class="bottom-side" :class="{ 'bottom-size-show': isShowMenu }">
     <div class="menu-container">
-      <FourBorderBg class="four-border-bg" v-for="(menu, index) in currentParentRoute.children" :key="index">
-        <div class="vent-flex-row">
-          <div class="parent-menu">
-            {{ menu.name }}
-          </div>
-          <div class="vent-flex-row-wrap child-menu">
-            <div class="child-menu-item" v-for="(childMenu, childIndex) in menu.children" :key="childIndex" @click="handleMenuClick(childMenu)">
-              {{ childMenu.name }}
+      <template v-for="(menu, index) in currentParentRoute.children" :key="index">
+        <FourBorderBg class="four-border-bg"  v-if="!menu.hideMenu">
+          <div class="vent-flex-row" >
+            <div class="parent-menu">
+              {{ menu.name }}
+            </div>
+            <div class="vent-flex-row-wrap child-menu" >
+              <template v-for="(childMenu, childIndex) in menu.children" :key="childIndex">
+                <div v-if="!childMenu.hideMenu" class="child-menu-item" @click="handleMenuClick(childMenu)">
+                  {{ childMenu.name }}
+                </div>
+              </template>
             </div>
           </div>
-        </div>
-      </FourBorderBg>
+        </FourBorderBg>
+      </template>
+      
       <div class="vent-flex-row-between menu-button-group">
         <div class="vent-flex-row program-group">
           <template v-for="(programMenu, key) in menuModules" :key="key">
@@ -76,7 +81,6 @@
           }
         } else {
           // micro-vent-3dModal 
-          debugger
 
           if(route.path.startsWith('/subSysmodal/')) {
             router.replace('/micro-vent-3dModal' + path.path)

+ 3 - 1
src/main.ts

@@ -24,10 +24,13 @@ import { registerThirdComp } from '/@/settings/registerThirdComp';
 import { useSso } from '/@/hooks/web/useSso';
 import { registerPackages } from '/@/utils/monorepo/registerPackages';
 import { initModalWorker, initTHREE } from '/@/utils/threejs/main.worker';
+
 // 在本地开发中引入的,以提高浏览器响应速度
 if (import.meta.env.DEV) {
   import('ant-design-vue/dist/antd.less');
 }
+
+
 async function bootstrap() {
   // 创建应用实例
   const app = createApp(App);
@@ -72,7 +75,6 @@ async function bootstrap() {
 
   initTHREE();
 
-
   // 挂载应用
   app.mount('#app', true);
 }

+ 7 - 23
src/qiankun/apps.ts

@@ -12,9 +12,15 @@ for (const key in import.meta.env) {
     const appList = JSON.parse(import.meta.env[key].replace(/'/g, '"'));
     appList.forEach((app) => {
       const name = app[0];
+      let utlStr;
+      if (import.meta.env.PROD) {
+        utlStr = VUE_APP_URL.baseUrl.split(':')[1] + app[1];
+      } else {
+        utlStr = app[1];
+      }
       const obj = {
         name,
-        entry: app[1],
+        entry: utlStr,
         container: '#content',
         activeRule: name,
       };
@@ -22,26 +28,4 @@ for (const key in import.meta.env) {
     });
   }
 }
-console.log('子应用路由匹配规则', _apps);
-// const _apps: object[] = [
-//   {
-//     name: 'micro-need-air', // app name registered
-//     entry: '//localhost:8099/',
-//     container: '#content',
-//     activeRule: 'micro-need-air',
-//   },
-//   {
-//     name: 'micro-vent-3dModal', // app name registered
-//     entry: '//localhost:8091/',
-//     container: '#content',
-//     activeRule: 'micro-vent-3dModal',
-//   },
-//   // {
-//   //   name: 'micro-vent-3dModal', // app name registered
-//   //   entry: '//localhost:8091/',
-//   //   container: '#content',
-//   //   activeRule: 'micro-vent-3dModal',
-//   // },
-
-// ];
 export const apps = _apps;

+ 4 - 0
src/router/helper/routeHelper.ts

@@ -229,6 +229,10 @@ function isMultipleRoute(routeModule: AppRouteModule) {
  */
 export function addSlashToRouteComponent(routeList: AppRouteRecordRaw[]) {
   routeList.forEach((route) => {
+    if (route.path.startsWith('/subSysmodal')) {
+      route.path = '/micro-vent-3dModal' + route.path;
+      route.component = 'layouts/default/index';
+    }
     let component = route.component as string;
     if (component) {
       const layoutFound = LayoutMap.get(component);

+ 3 - 2
src/store/modules/permission.ts

@@ -23,6 +23,7 @@ import { getPermCode } from '/@/api/sys/user';
 
 import { useMessage } from '/@/hooks/web/useMessage';
 import { PageEnum } from '/@/enums/pageEnum';
+import { cloneDeep } from 'lodash-es';
 
 // 系统权限
 interface AuthItem {
@@ -129,7 +130,7 @@ export const usePermissionStore = defineStore({
       this.setPermCodeList(codeList);
       this.setAuthData(systemPermission);
     },
-    
+
     async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
       const { t } = useI18n();
       const userStore = useUserStore();
@@ -226,6 +227,7 @@ export const usePermissionStore = defineStore({
             for (const menuItem of routeList) {
               // 条件1:判断组件是否是 layouts/default/index
               if (!hasIndex) {
+
                 hasIndex = menuItem.component === 'layouts/default/index' || menuItem.path === '/dashboard/analysis';
               }
               // 条件2:判断图标是否带有 冒号
@@ -269,7 +271,6 @@ export const usePermissionStore = defineStore({
           // 删除meta.ignoreRoute项
           routeList = filter(routeList, routeRemoveIgnoreFilter);
           routeList = routeList.filter(routeRemoveIgnoreFilter);
-
           routeList = flatMultiLevelRoutes(routeList);
           routes = [PAGE_NOT_FOUND_ROUTE, QIANKUN_ROUTE, , ...routeList];
           break;

+ 11 - 4
src/utils/echartsUtil.ts

@@ -37,12 +37,18 @@ export default class echartsUtil {
       if (type == 'detail' || type == 'history') {
         column.linetype = 'line';
       }
-      if (column.color)
-        // ydata.push(ylist);
-        /** 获取静态文件配置的图表样式信息 */
+      if (column.color) {
         colors.push(column.color); //获取每个图表系列的颜色
-      if (column.legend) legends.push(column.legend + (column.yname ? '(' + column.yname + ')' : '')); //获取每个图表系列的名字
+      }
+      // ydata.push(ylist);
+      /** 获取静态文件配置的图表样式信息 */
+
+      if (column.legend) {
+        legends.push(column.legend + (column.yname ? '(' + column.yname + ')' : '')); //获取每个图表系列的名字
+      }
+
       series.push(this.getSeries(column, ylist)); //获取每个图表系列的样式
+      
       if (column.seriesName || column.seriesName == undefined) {
         yAxis.push(this.getYAxis(column));
       }
@@ -91,6 +97,7 @@ export default class echartsUtil {
     }
     return null;
   }
+
   getLegend(legend) {
     const legendObj = {
       textStyle: {

+ 1 - 0
src/utils/http/axios/index.ts

@@ -95,6 +95,7 @@ const transform: AxiosTransform = {
   // 请求之前处理config
   beforeRequestHook: (config, options) => {
     const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
+    
     if (joinPrefix) {
       config.url = `${urlPrefix}${config.url}`;
     }

+ 2 - 1
src/utils/lib/echarts.ts

@@ -1,6 +1,6 @@
 import * as echarts from 'echarts/core';
 
-import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, GaugeChart, ScatterChart } from 'echarts/charts';
+import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, GaugeChart, ScatterChart, CustomChart } from 'echarts/charts';
 
 import {
   TitleComponent,
@@ -48,6 +48,7 @@ echarts.use([
   CalendarComponent,
   GraphicComponent,
   ScatterChart,
+  CustomChart,
 ]);
 
 export default echarts;

+ 1 - 1
src/utils/threejs/main.worker.ts

@@ -47,7 +47,7 @@ export function initModalWorker() {
     'fire/grout_2023-06-02.glb',
     'fire/balancePress_2023-07-20.glb',
     'yafeng/compressor_2023-07-10.glb',
-    'gas/gasPump_2023-07-25.glb',
+    'gas/gasPump_2023-08-18.glb',
   ];
 
   const db: any = new Dexie('DB');

+ 4 - 3
src/utils/threejs/useThree.ts

@@ -133,6 +133,7 @@ class UseThree {
       this.renderer = window['$renderer'];
       if (this.canvasContainer) {
         this.renderer.toneMappingExposure = 1.0;
+        this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
         // const gl = this.renderer?.getContext('webgl');
         // gl && gl.getExtension('WEBGL_lose_context')?.restoreContext();
         // this.renderer?.forceContextRestore()
@@ -241,12 +242,12 @@ class UseThree {
                           obj.material.transparent = true;
                         }
                         if (obj.material.map) {
-                          obj.material.map.encoding = THREE.sRGBEncoding;
+                          obj.material.map.colorSpace = THREE.SRGBColorSpace;
                           obj.material.map.flipY = false;
                           obj.material.map.anisotropy = 1;
                         }
                         if (obj.material.emissiveMap) {
-                          obj.material.emissiveMap.encoding = THREE.sRGBEncoding;
+                          obj.material.emissiveMap.colorSpace = THREE.SRGBColorSpace;
                           obj.material.emissiveMap.flipY = false;
                         }
 
@@ -535,7 +536,7 @@ class UseThree {
     this.clearScene();
     window.removeEventListener('resize', this.resizeRenderer);
     // this.orbitControls?.dispose();
-    this.scene?.environment?.dispose();
+    // this.scene?.environment?.dispose();
     this.scene?.clear();
     this.renderer?.dispose();
     this.renderer?.getRenderTarget()?.dispose();

+ 81 - 0
src/utils/ui.js

@@ -0,0 +1,81 @@
+// 中间背景雨
+export function rainBg(className, bgClassName) {
+  var c = document.querySelector(`.${className}`);
+  if (!c) return
+  var ctx = c.getContext('2d'); //获取canvas上下文
+  var w = (c.width = document.querySelector(`.${bgClassName}`).clientWidth);
+  var h = (c.height = document.querySelector(`.${bgClassName}`).clientHeight);
+  //设置canvas宽、高
+
+  function random(min, max) {
+    return Math.random() * (max - min) + min;
+  }
+
+  function RainDrop() { }
+  //雨滴对象 这是绘制雨滴动画的关键
+  RainDrop.prototype = {
+    init: function () {
+      this.x = random(0, w); //雨滴的位置x
+      this.y = h; //雨滴的位置y
+      this.color = 'hsl(180, 100%, 50%)'; //雨滴颜色 长方形的填充色
+      this.vy = random(4, 5); //雨滴下落速度
+      this.hit = 0; //下落的最大值
+      this.size = 2; //长方形宽度
+    },
+    draw: function () {
+      if (this.y > this.hit) {
+        var linearGradient = ctx.createLinearGradient(this.x, this.y, this.x, this.y + this.size * 30);
+        // 设置起始颜色
+        linearGradient.addColorStop(0, '#14789c');
+        // 设置终止颜色
+        linearGradient.addColorStop(1, '#090723');
+        // 设置填充样式
+        ctx.fillStyle = linearGradient;
+        ctx.fillRect(this.x, this.y, this.size, this.size * 50); //绘制长方形,通过多次叠加长方形,形成雨滴下落效果
+      }
+      this.update(); //更新位置
+    },
+    update: function () {
+      if (this.y > this.hit) {
+        this.y -= this.vy; //未达到底部,增加雨滴y坐标
+      } else {
+        this.init();
+      }
+    },
+  };
+
+  function resize() {
+    w = c.width = window.innerWidth;
+    h = c.height = window.innerHeight;
+  }
+
+  //初始化一个雨滴
+
+  var rs = [];
+  for (var i = 0; i < 10; i++) {
+    setTimeout(function () {
+      var r = new RainDrop();
+      r.init();
+      rs.push(r);
+    }, i * 300);
+  }
+
+  function anim() {
+    ctx.clearRect(0, 0, w, h); //填充背景色,注意不要用clearRect,否则会清空前面的雨滴,导致不能产生叠加的效果
+    for (var i = 0; i < rs.length; i++) {
+      rs[i].draw(); //绘制雨滴
+    }
+    requestAnimationFrame(anim); //控制动画帧
+  }
+
+  window.addEventListener('resize', resize);
+  //启动动画
+  anim();
+}
+
+// 获取图片资源路径
+export const getAssetURL = (image) => {
+  // 参数一: 相对路径
+  // 参数二: 当前路径的URL
+  return new URL(`/src/assets/images/${image}`, import.meta.url).href
+}

+ 0 - 1
src/views/system/notice/index.vue

@@ -34,7 +34,6 @@
   import { useModal } from '/@/components/Modal';
   import NoticeModal from './NoticeModal.vue';
   import DetailModal from './DetailModal.vue';
-  import { useMethods } from '/@/hooks/system/useMethods';
   import { useGlobSetting } from '/@/hooks/setting';
   import { getToken } from '/@/utils/auth';
   import { columns, searchFormSchema } from './notice.data';

+ 16 - 10
src/views/vent/deviceManager/comment/DeviceModal.vue

@@ -9,7 +9,7 @@
     :footer="null"
     destroyOnClose
   >
-    <a-tabs v-if="props.showTab" >
+    <a-tabs v-if="props.showTab" v-model:activeKey="activeKey">
       <a-tab-pane key="1" tab="基本信息" force-render>
         <FormModal :record="record" @saveOrUpdate="(values) => emit('saveOrUpdate', values)" />
       </a-tab-pane>
@@ -26,17 +26,22 @@
       <a-tab-pane key="3" tab="设备关联" v-if="deviceType == 'managesys'">
         <WorkFacePointTable :columns="pointColumns" :deviceId="deviceData.id" @save="savePointData" @delete="deletePointById" />
       </a-tab-pane>
-      <a-tab-pane key="4" :tab="deviceType !== 'managesys' ? '报警配置' : '关联设备报警配置'">
-        <WarningTable v-if="deviceType !== 'managesys'" :deviceId="deviceData.id" :pointType="record.strtype" />
-        <ManagerWarningTable v-else :deviceId="deviceData.id" :pointType="record.strtype"/>
+      <a-tab-pane v-if="deviceType == 'managesys'" key = "4" tab="预警条目管理">
+        <ManagerWarningDeviceTable  v-if="activeKey == '4'" :deviceId="deviceData.id" />
       </a-tab-pane>
-      <a-tab-pane v-if="deviceType == 'managesys'" key = "6" tab="报警控制设备配置">
-        <ManagerWarningDeviceTable :deviceId="deviceData.id" :pointType="record.strtype"/>
+      <a-tab-pane key="5" :tab="deviceType !== 'managesys' ? '报警配置' : '配置预警设备'">
+        <template v-if="activeKey == '5'">
+          <WarningTable v-if="deviceType !== 'managesys'" :deviceId="deviceData.id" :pointType="record.strtype" />
+          <BackWindDeviceTable  v-else :deviceId="deviceData.id" />
+        </template>
       </a-tab-pane>
-      <a-tab-pane v-if="deviceType == 'managesys'" key = "7" tab="一键反风控制设备配置">
-        <BackWindDeviceTable :deviceId="deviceData.id" :pointType="record.strtype"/>
+      <a-tab-pane v-if="deviceType == 'managesys'" key = "6" tab="配置控制设备">
+        <template v-if="activeKey == '6'">
+          <WarningTable v-if="deviceType !== 'managesys'" :deviceId="deviceData.id" :pointType="record.strtype" />
+          <ManagerWarningTable v-else :deviceId="deviceData.id" />
+        </template>
       </a-tab-pane>
-      <a-tab-pane key="5" tab="摄像头配置"
+      <a-tab-pane key="7" tab="摄像头配置"
         ><EditRowTable
           :columns="cameraColumns"
           :list="cameraList.bind(null, { deviceid: deviceData.id })"
@@ -49,7 +54,7 @@
   </BasicModal>
 </template>
 <script lang="ts" setup>
-  import { computed, unref, inject, reactive } from 'vue';
+  import { computed, unref, inject, reactive, ref } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import EditRowTable from '../../comment/EditRowTable.vue';
   import PointTable from './pointTabel/PointTable.vue';
@@ -74,6 +79,7 @@
   const isUpdate = inject('isUpdate');
   const deviceData = inject('formData') as any;
   const record = reactive({ strtype: '', strname: '' });
+  const activeKey = ref('1')
 
   //表单赋值
   const [registerModal, { setModalProps }] = useModalInner(async (data) => {

+ 7 - 27
src/views/vent/deviceManager/comment/NormalTable.vue

@@ -1,5 +1,5 @@
 <template>
-  <div style="position: fixed;">
+  <div >
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <template #tableTitle>
         <a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">新增</a-button>
@@ -28,6 +28,7 @@
         >
           <a class="table-action-link">删除</a>
         </a-popconfirm>
+        <slot name="action" v-bind="{ record }"></slot>
       </template>
       <template #bodyCell="{ column, record }">
         <slot name="filterCell" v-bind="{ column, record }"></slot>
@@ -61,7 +62,6 @@
     },
     searchFormSchema: {
       type: Array,
-      required: true,
       default: () => [],
     },
     formSchema: {
@@ -134,6 +134,7 @@
       // size: 'small',
       // bordered: false,
       formConfig: {
+        showAdvancedButton: true,
         // labelWidth: 100,
         labelAlign: 'left',
         labelCol: {
@@ -146,17 +147,13 @@
         },
         schemas: props.searchFormSchema as any[],
       },
+      useSearchForm: props.searchFormSchema.length > 0 ? true : false,
       striped: true,
       actionColumn: {
         width: 180,
       },
       beforeFetch: (params) => {
-        // if(params['devicetype']) {
-        //   deviceTypeId.value = params['devicetype']
-        //   params['devicetype'] = params['devicetype'] + '*'
-        // }
-        // pageType.value = params['pagetype']
-        return Object.assign({ column: 'createTime', order: 'desc' }, params);
+        return Object.assign({ column: 'createTime' }, params);
       },
     },
     exportConfig: {
@@ -241,32 +238,15 @@
         },
       },
       // {
-      //   label: '查看',wss
-      //   onClick: handleDetail.bind(null, record),
-      // },
-    ];
-  }
-  /**
-   * 下拉操作栏
-   */
-  function getDropDownAction(record) {
-    return [
-      // {
-      //   label: '删除',
-      //   popConfirm: {
-      //     title: '是否确认删除',
-      //     confirm: handleDelete.bind(null, record),
-      //   },
-      // },
-      // {
       //   label: '查看',
       //   onClick: handleDetail.bind(null, record),
       // },
     ];
   }
 
+
   defineExpose({
-    doRequest, onExportXls, onImportXls,
+    doRequest, onExportXls, onImportXls, reload
   });
 
 </script>

+ 117 - 0
src/views/vent/deviceManager/comment/warningTabel/BaseModal.vue

@@ -0,0 +1,117 @@
+<template>
+  <BasicModal @register="register" :title="title" :width="800" v-bind="$attrs" @ok="onSubmit">
+    <BasicForm @register="registerForm" >
+      <template #monitor="{ model, field }" >
+        <div class="vent-flex-row-between">
+          <Select ref="selectRef" disabled v-model:value="pointData" :options="option" style="width: calc(100% - 65px);" />
+          <a-button class="vent-margin-b-5" type="primary" @click="selectPoint(model['strtype'])" style="position: absolute; right: 0; top: 1px;">选择</a-button>
+        </div>
+      </template>
+    </BasicForm>
+  </BasicModal>
+  <DevicePointTable @register="registerModal" :data-source="devicePointList" :selection-row-keys="pointData" @reload="setPoint" />
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, defineEmits, unref, nextTick } from 'vue';
+import { BasicForm, useForm } from '/@/components/Form/index';
+import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
+import type { FormSchema } from '/@/components/Form/src/types/form';
+import { Select } from 'ant-design-vue';
+import DevicePointTable from './DevicePointTable.vue';
+import { workFacePointList } from './warning.api'
+
+  const props = defineProps({
+    formSchemas: {
+      type: Array as PropType<FormSchema[]>,
+      default: () => ([]),
+    },
+    deviceId: { type: String },
+    monitorType: {
+      type: String,
+      default: '2'
+    }
+  })
+  const emit = defineEmits(['add', 'update', 'register'])
+  const option = ref<any[]>([])
+  const devicePointList = ref<any[]>([])
+  const pointData = ref<String[]>([])
+  const title = ref('')
+  const isUpdate = ref(false)
+  // 注册 form
+  const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
+    schemas: props.formSchemas,
+    showActionButtonGroup: false,
+  });
+  
+  // 注册 modal
+  const [register, { setModalProps }] = useModalInner(async (data) => {
+    isUpdate.value = unref(data.isUpdate);
+    title.value = unref(data.title);
+    await resetFields();
+    if(data.isUpdate){
+      await setFieldsValue({ ...data.record });
+      pointData.value = [data.record['monitorId']]
+
+      // 初始打开有数据时候要查点表
+
+    }else if(data.record){
+      await setFieldsValue({ relId: data.record['id'] });
+    }
+  });
+
+  const [registerModal, { openModal }] = useModal();
+
+  async function getDevicePointList(strtype) {
+    try {
+      const result = await workFacePointList({ deviceType: strtype, valueType: props.monitorType });
+      devicePointList.value = result
+    } catch (error) {
+      devicePointList.value = []
+    }
+  };
+
+  async function onSubmit() {
+    try {
+      const data = await getFieldsValue()
+      await setFieldsValue({...data, monitorId: pointData.value[0] })
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      // 提交表单
+      if (isUpdate) {
+        emit('add', 'add', values)
+      } else {
+        emit('update', 'update',values)
+      }
+      // //关闭弹窗
+      // closeModal();
+      // //刷新列表
+      // reload()
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+
+  async function selectPoint(strtype) {
+    await getDevicePointList(strtype)
+    openModal()
+  }
+
+  function setPoint(value) {
+    const data = value[0]
+    option.value = [
+      {
+        value: data.id,
+        label: data.valuename
+      }
+    ]
+    nextTick(() => {
+      pointData.value = [data.id]
+    })
+  }
+
+  onMounted(async () => {
+    
+  });
+
+</script>
+<style scoped lang="less"></style>

+ 5 - 5
src/views/vent/deviceManager/comment/warningTabel/DevicePointTable.vue

@@ -15,7 +15,7 @@
     </div>
     <a-table
       v-if="refresh"
-      :row-selection="{ selections: true, selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
+      :row-selection="{ selections: true, selectedRowKeys: selectedRowKeys, onSelect: onSelectChange }"
       :columns="columns"
       :dataSource="dataSource"
       :row-key="(record) => record.id"
@@ -37,7 +37,7 @@
 
   const refresh = ref(true)
   const selectedRowKeys = ref<any[]>([]);
-  const selectionRows = ref([]);
+  const selectionRows = ref<any[]>([]);
 
   const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
     //重置表单
@@ -50,9 +50,9 @@
    * @param rowKeys
    * @param rows
    */
-  function onSelectChange(rowKeys, rows) {
-    selectedRowKeys.value = rowKeys;
-    selectionRows.value = rows;
+  function onSelectChange(record) {
+    selectedRowKeys.value = [record['id']];
+    selectionRows.value = [record];
   }
 
   const saveData = async () => {

+ 138 - 130
src/views/vent/deviceManager/comment/warningTabel/index1.vue

@@ -1,146 +1,154 @@
 <template>
-  <div class="vent-flex-row">
-    <span style = "color: #fff;">关联的监测设备:</span>
-    <a-select
-      v-model:value="sysDeviceId"
-      style="width: 200px"
-      placeholder="关联设备"
-      :options="sysDeviceOptions"
-      @change="handleChange"
-      :allowClear="true"
-    />
-    <a-button class="vent-margin-l-8" type="primary" @click="getDeviceWarningPointList"> 查询 </a-button>
-    <a-button class="vent-margin-l-8" type="primary" @click="addPoint"> 新增 </a-button>
+  <div class="warning-device-box">
+    <div class="warning-box">
+      <div v-for="item in warningList" class="link-item" :class="{ 'active-device-title': item.id == activeID }"
+        :key="item.id">
+        <span class="" @click="selectWarning(item.id)">{{ item.alarmName }}</span>
+      </div>
+    </div>
+    <div class="device-box">
+      <a-button class="vent-margin-b-5" type="primary" @click="handleOpen"> 新增 </a-button>
+      <a-table :columns="controlColumns" :data-source="dataSource" bordered :scroll="{ y: 500 }" :pagination="false">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.dataIndex === 'operation'">
+            <a class="action-link" @click="handleOpen(record)">编辑</a>
+            <a class="action-link vent-margin-l-10" @click="handleDelete(record)">删除</a>
+          </template>
+          <template v-if="column.dataIndex === 'operation1'">
+            <a class="action-link" @click="handleOpen()">新增</a>
+          </template>
+        </template>
+      </a-table>
+    </div>
   </div>
-
-  <EditRowTableVue 
-    v-if="refresh"
-    ref="RefComponent"
-    :columns="manageWarningPointColumns"
-    :data-source="dataSource"
-    @save-or-update="saveOrUpdate"
-    @delete-by-id="handleDelete"
-    :isAdd="false"
-    style="margin-top: 10px"
-  >
-
-  </EditRowTableVue>
-
-  <DevicePointTable @register="registerModal" :data-source="devicePointList" :selection-row-keys="selectionRowKeys" @reload="reload"/>
+  <BaseModal @register="register" @add="onSubmit" @update="onSubmit" :form-schemas="formSchemas" :monitor-type="'1'">
+  </BaseModal>
 </template>
 
-<script lang="ts" setup>  
-  import { defineProps, ref, onMounted } from 'vue';
-  import { manageWarningPointColumns } from './warning.data';
-  import EditRowTableVue from '../../../comment/EditRowTable.vue';
-  import { workFaceWarningList,  workFaceWarningEdit, workFaceWarningDelete, workFaceDeviceList, workFacePointList, workFaceWarningBatchAdd } from './warning.api';
-  import DevicePointTable from './DevicePointTable.vue';
-  import { useModal } from '/@/components/Modal';
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue'
+import { controlColumns, controlFormSchemas } from './warning.data'
+import { warningLogList, workFaceWarningList, workFaceWarningAdd, workFaceWarningEdit, workFaceWarningDelete } from './warning.api';
+import BaseModal from './BaseModal.vue'
+import { useModal } from '/@/components/Modal';
+
+const props = defineProps({
+  deviceId: { type: String },
+});
+
+const formSchemas = controlFormSchemas({ id: props.deviceId })
+const activeID = ref('')
+const warningList = ref<{ id: string, alarmName: string }[]>([])
+const dataSource = ref([])
+
+function selectWarning(id) {
+  activeID.value = id
+}
+
+async function getDataSource() {
+  const result = await workFaceWarningList({ sysId: props.deviceId, monitorType: 1, alarmId: activeID.value, pageSize: 100000 })
+  dataSource.value = result.records
+}
+
+async function handleDelete(record) {
+  await workFaceWarningDelete({ id: record.id })
+  getDataSource()
+}
+
+const [register, { openModal, closeModal }] = useModal()
+
+function handleOpen(record?) {
+  if (record) {
+    openModal(true, {
+      isUpdate: true,
+      record
+    });
+  }else{
+    openModal(true, {
+      isUpdate: false,
+    });
+  }
   
-  const props = defineProps({
-    deviceId: { type: String },
-    pointType: {
-      type: String,
-      requried: true,
-    },
-  });
-
-  const sysDeviceId = ref('') // 选中的关联设备的id
-  const sysDeviceType = ref('')
-  const devicePointList = ref<any[]>([])
-  const selectionRowKeys = ref<any[]>([])
-  const sysDeviceOptions = ref<any[]>([])
-  let sysDeviceList = <any[]>[]
-  const RefComponent = ref()
-  const dataSource = ref<any[]>([])
-
-  const refresh = ref(true)
-
-  const [registerModal, { openModal }] = useModal();
-
-  const handleChange = async (value) => {
-    sysDeviceId.value = value
-    const obj = sysDeviceList.find((element) => {
-      return element.id == value
-    })
-    sysDeviceType.value = obj['strtype']
-    await getDeviceWarningPointList()
+}
+
+async function onSubmit(flag, values) {
+  // 提交数据弹窗
+  if (flag == 'update') {
+    await workFaceWarningEdit({ ...values })
+  } else {
+    await workFaceWarningAdd({ ...values, monitorType: 1, sysId: props.deviceId, alarmId: activeID.value })
   }
+  closeModal();
+  //刷新列表
+  await getDataSource()
+}
+
+async function getWarningList() {
+  const result = await warningLogList({ sysId: props.deviceId, pageSize: 100000 }) //id: props.deviceId
+  warningList.value = result.records
+  activeID.value = warningList.value[0]['id']
+}
+
+onMounted(async () => {
+  await getWarningList()
+  await getDataSource()
+})
 
-  async function getDeviceWarningPointList() {
+</script>
 
-    const result = await workFaceWarningList({ deviceId: sysDeviceId.value})
-    if(result && result.records.length > 0){
-      dataSource.value = result.records
-    }else{
-      dataSource.value = []
-    }
-  }
+<style lang="less" scoped>
+@ventSpace: zxm;
+
+.warning-device-box {
+  width: 100%;
+  height: 600px;
+  overflow-y: auto;
+  display: flex;
+
+  .warning-box {
+    width: 200px;
+    height: 100%;
+    overflow-y: auto;
+    background: #ffffff11;
+    padding: 5px;
+    border-radius: 5px;
+    .link-item {
+      position: relative;
+      cursor: pointer;
+      line-height: 30px;
+      padding-left: 30px;
+      color: #fff;
+      span:hover {
+        color: #89ffff;
+      }
 
-  async function getDevicePointList() {
-    try {
-      const result = await workFacePointList({ deviceType: sysDeviceType.value, valueType: 2 });
-      devicePointList.value = result
-      const rowKeys = <any[]>[]
-      dataSource.value.forEach((item) => {
-        rowKeys.push(item['monitorId'])
-      })
-      selectionRowKeys.value = rowKeys
-    } catch (error) {
-      devicePointList.value = []
+      &::after {
+        content: '';
+        position: absolute;
+        display: block;
+        width: 8px;
+        height: 8px;
+        top: 12px;
+        left: 10px;
+        transform: rotateZ(45deg) skew(10deg, 10deg);
+        background: #45d3fd;
+      }
     }
-  };
-  
-  async function getDeviceOptions() {
-    const result = await workFaceDeviceList({id: props.deviceId})
-    const options = <any[]>[]
-    if(result.length > 0){
-      result.forEach(element => {
-        options.push({ value: element.id, label: element.strname })
-      });
-      sysDeviceOptions.value = options
-      sysDeviceList = result
+    .active-device-title {
+      color: aqua;
     }
-    
   }
 
-  async function saveOrUpdate(record) {
-    try {
-      if (record.id) {
-        await workFaceWarningEdit({ ...record });
-      }
-      RefComponent.value.reload()
-    } catch (error) { }
+  .device-box {
+    flex: 1;
+    margin-left: 10px;
+    overflow: hidden;
   }
-
-  function handleDelete(id) {
-    workFaceWarningDelete({id}).then(() => {
-      getDeviceWarningPointList()
-    })
-  }
-
-  async function reload(list) {
-    const monitorList = <any[]>[]
-    list.forEach(item => {
-      monitorList.push({ deviceId: sysDeviceId.value, monitorId: item['id'], monitorName: item['valuename'], sysId: props.deviceId })
-    })
-    await workFaceWarningBatchAdd(monitorList)
-    getDeviceWarningPointList()
-  }
-
-  function addPoint() {
-    getDevicePointList().then(() => {
-      openModal();
-    })
+  .action-link{
+    color: #00e7ff;
+    &:hover{
+      color: #00e7ff;
+    }
   }
-
-  onMounted(async () => {
-    await getDeviceOptions()
-    await getDeviceWarningPointList()
-  });
-</script>
-
-<style lang="less" scoped>
-  @ventSpace: zxm;
-
+}
 </style>

+ 62 - 195
src/views/vent/deviceManager/comment/warningTabel/index2.vue

@@ -1,208 +1,75 @@
 <template>
-  <div class="vent-flex-row">
-    <div class="vent-flex-row ">
-      <span style = "color: #fff;">预警监测设备:</span>
-      <a-select
-        v-model:value="sysDeviceId"
-        style="width: 180px"
-        :options="sysDeviceOptions"
-        @change="handleChange"
-        :allowClear="true"
-      />
-    </div>
-    <div class="vent-flex-row vent-padding-lr-5">
-      <span style = "color: #fff;">预警条目:</span>
-      <a-select
-        v-model:value="sysWarningId"
-        style="width: 180px"
-        :options="warningOptions"
-        @change="handleWarningChange"
-        :allowClear="true"
-      />
-    </div>
-    <div class="vent-flex-row">
-      <span style = "color: #fff;">预警控制设备:</span>
-      <a-select
-        v-model:value="controlDeviceId"
-        style="width: 180px"
-        :options="sysDeviceOptions"
-        @change="handleDeviceChange"
-        :allowClear="true"
-      />
-    </div>
-    <a-button class="vent-margin-l-8" type="primary" @click="getWarningControlDevicePointList"> 查询 </a-button>
-    <a-button class="vent-margin-l-8" type="primary" @click="addPoint"> 新增 </a-button>
+  <div class="">
+    <BasicTable @register="registerTable" :columns="workFaceWarningColumns">
+      <template #tableTitle>
+        <a-button preIcon="ant-design:plus-outlined" type="primary" @click="openModal()">新增</a-button>
+      </template>
+      <template #action="{ record }">
+        <a class="table-action-link" @click="openModal()">编辑</a>
+        <a-popconfirm
+          title="确定删除?"
+          @confirm="handleDelete(record)"
+        >
+          <a class="table-action-link">删除</a>
+        </a-popconfirm>
+      </template>
+    </BasicTable>
+    <BaseModal @register="register" @add="onSubmit" @update="onSubmit" :form-schemas="workFaceWarningFormSchemas" > </BaseModal>
   </div>
-
-  <EditRowTableVue
-    v-if="refresh"
-    ref="RefComponent"
-    :columns="controlDevicePointColumns"
-    :data-source="dataSource"
-    @save-or-update="saveOrUpdate"
-    @delete-by-id="handleDelete"
-    :isAdd="false"
-    style="margin-top: 10px"
-  >
-
-  </EditRowTableVue>
-
-  <DevicePointTable @register="registerModal" :data-source="controlDevicePointList" :selection-row-keys="selectionRowKeys" @reload="reload"/>
 </template>
 
-<script lang="ts" setup>  
-  import { defineProps, ref, onMounted } from 'vue';
-  import { controlDevicePointColumns } from './warning.data';
-  import EditRowTableVue from '../../../comment/EditRowTable.vue';
-  import { workFaceWarningList, warkFaceControlDevicePointEdit, workFacePointList, warkFaceControlDevicePointDelete, workFaceDeviceList, warkFaceControlDevicePointBatchAdd, warkFaceControlDevicePointList } from './warning.api';
-  import DevicePointTable from './DevicePointTable.vue';
-  import { useModal } from '/@/components/Modal';
-  import { useMessage } from '/@/hooks/web/useMessage';
-  
-  const props = defineProps({
-    deviceId: { type: String },
-    pointType: {
-      type: String,
-      requried: true,
+<script lang="ts" name="system-user" setup>
+import { BasicTable, TableAction } from '/@/components/Table';
+import { useListPage } from '/@/hooks/system/useListPage';
+import BaseModal from './BaseModal.vue' 
+import { useModal } from '/@/components/Modal';
+import { workFaceWarningColumns, workFaceWarningFormSchemas } from './warning.data'
+import { warningLogList, warningLogAdd, warningLogEdit, warningLogDeleteById } from './warning.api'
+
+const props = defineProps({
+  deviceId: { type: String },
+});
+
+const { tableContext } = useListPage({
+  tableProps: {
+    api: warningLogList.bind(null, { sysId: props.deviceId }),
+    scroll: { y: 390 },
+    size: 'small',
+    // expandRowByClick: true,
+    clickToRowSelect: true,
+    useSearchForm: false,
+    rowSelection: {
+      columnWidth: 20,
     },
-  });
-  const { createMessage } = useMessage();
-
-  const sysDeviceId = ref('') // 选中的关联设备的id
-  const sysWarningId = ref('') // 选中的预警条目的id
-  const controlDeviceId = ref('') //预警条目控制的设备id
-  const controlDeviceType = ref('')
-  const selectionRowKeys = ref<any[]>([])
-  const sysDeviceOptions = ref<any[]>([])
-  const warningOptions = ref<any[]>([]) //设备预警条目列表
-  let sysDeviceList = <any[]>[]
-  const RefComponent = ref()
-  const dataSource = ref<any[]>([])
-  const controlDevicePointList = ref<any[]>([])
-
-  const refresh = ref(true)
-
-  const [registerModal, { openModal }] = useModal();
-
-  const handleChange = async () => {
-    sysWarningId.value = ''
-    controlDeviceId.value = ''
-    dataSource.value = []
-    await getWarningPointList()
-  }
-
-  const handleWarningChange = () => {
-    if(!sysDeviceId.value){
-      createMessage.warning('请先选择预警监测设备。。。')
-    }
-    controlDeviceId.value = ''
-    dataSource.value = []
-  }
-
-  const handleDeviceChange = async(value) => {
-    if (!sysWarningId.value) {
-      createMessage.warning('请先选择预警条目。。。')
-      controlDeviceId.value = ''
-      return
-    }
-    const obj = sysDeviceList.find((element) => {
-      return element.id == value
-    })
-    controlDeviceType.value = obj['strtype']
-    await getWarningControlDevicePointList()
-  }
-
-  async function getWarningPointList() {
-    const result = await workFaceWarningList({ deviceId: sysDeviceId.value })
-    const options = <any[]>[]
-    if (result && result.records.length > 0) {
-      result.records.forEach(element => {
-        options.push({ value: element.id, label: element.monitorName })
-      });
-      warningOptions.value = options
-    }else {
-      warningOptions.value = []
-    }
-  }
-
-  async function getWarningControlDevicePointList() {;
-    const result = await warkFaceControlDevicePointList({ alarmId: sysWarningId.value, deviceId: controlDeviceId.value })
-    dataSource.value = result.records
-  }
-
-  async function getControlDevicePointList() {
-    try {
-      const result = await workFacePointList({ deviceType: controlDeviceType.value, valueType: 1 });
-      controlDevicePointList.value = result
-      const rowKeys = <any[]>[]
-      dataSource.value.forEach((item) => {
-        rowKeys.push(item['monitorId'])
-      })
-      selectionRowKeys.value = rowKeys
-    } catch (error) {
-      controlDevicePointList.value = []
-    }
-  };
-  
-  async function getDeviceOptions() {
-    const result = await workFaceDeviceList({id: props.deviceId})
-    const options = <any[]>[]
-    if(result.length > 0){
-      result.forEach(element => {
-        options.push({ value: element.id, label: element.strname })
-      });
-      sysDeviceOptions.value = options
-      sysDeviceList = result
-    }
-    
-  }
-
-  async function saveOrUpdate(record) {
-    try {
-      if (record.id) {
-        await warkFaceControlDevicePointEdit({ ...record });
-      }
-      RefComponent.value.reload()
-    } catch (error) { }
-  }
+    showTableSetting: false,
+    formConfig: {
+      disabled: true,
+      showResetButton: false,
+      showSubmitButton: false,
+    },
+  },
+});
+const [registerTable, { reload }] = tableContext;
 
-  function handleDelete(id) {
-    warkFaceControlDevicePointDelete({id}).then(() => {
-      getWarningControlDevicePointList()
-    })
-  }
+const [register, { openModal, closeModal }] = useModal()
 
-  async function reload(list) {
-    const monitorList = <any[]>[]
-    const obj = sysDeviceOptions.value.find(item => {
-      return item.id === controlDeviceId.value
-    })
-    list.forEach(item => {
-      monitorList.push({ alarmId: sysWarningId.value,  monitorId: item['id'], monitorName: item['valuename'], deviceId: controlDeviceId.value, devicePos: obj ? obj['strinstallpos']: null })
-    })
-    warkFaceControlDevicePointBatchAdd(monitorList).then(() => {
-      getWarningControlDevicePointList()
-    })
-  }
 
+async function handleDelete(record) {
+  await warningLogDeleteById(record.id)
+}
 
-  function addPoint() {
-    if (!controlDeviceId.value) {
-      createMessage.warning('请先选择报警控制设备。。。')
-      return
+async function onSubmit(flag, values) {
+  // 提交数据弹窗
+    if (flag == 'update') {
+      await warningLogEdit(values)
+    } else {
+      await warningLogAdd({...values, sysId: props.deviceId })
     }
-    getControlDevicePointList().then(() => {
-      openModal();
-    })
-  }
+    closeModal();
+    //刷新列表
+    reload()
+}
 
-  onMounted(async () => {
-    await getDeviceOptions()
-    await getWarningControlDevicePointList()
-  });
 </script>
 
-<style lang="less" scoped>
-  @ventSpace: zxm;
-
-</style>
+<style scoped></style>

+ 216 - 109
src/views/vent/deviceManager/comment/warningTabel/index3.vue

@@ -1,132 +1,239 @@
 <template>
-  <div class="vent-flex-row">
-    <span style="color: #fff;">关联的控制设备:</span>
-    <a-select v-model:value="sysDeviceId" style="width: 200px" placeholder="关联的控制设备" :options="sysDeviceOptions"
-      @change="handleChange" :allowClear="true" />
-    <a-button class="vent-margin-l-8" type="primary" @click="getDeviceWarningPointList"> 查询 </a-button>
-    <a-button class="vent-margin-l-8" type="primary" @click="addPoint"> 新增 </a-button>
+  <div class="warning-device-box">
+    <div class="warning-box">
+      <div v-for="item in warningList" class="link-item" :class="{ 'active-device-title': item.id == activeID }" :key="item.id">
+        <span class="" @click="selectWarning(item.id)">{{ item.alarmName }}</span>
+      </div>
+    </div>
+    <div class="device-box">
+      <a-button class="vent-margin-b-5" type="primary" @click="handleOpen"> 新增 </a-button>
+      <a-table v-if="show" :columns="MonitorColumns" :data-source="dataSource" bordered :scroll="{ y: 500 }" :pagination="false">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.dataIndex === 'operation'">
+            <a class="action-link" @click="handleOpen('update', record)">编辑</a>
+            <a class="action-link vent-margin-l-10" @click="handleDelete(record)">删除</a>
+          </template>
+          <template v-if="column.dataIndex === 'operation1'">
+            <a class="action-link" @click="handleOpen('add', record)">新增</a>
+          </template>
+        </template>
+      </a-table>
+    </div>
   </div>
-
-  <EditRowTableVue v-if="refresh" ref="RefComponent" :columns="BackWindDevicePointColumns" :data-source="dataSource"
-    @save-or-update="saveOrUpdate" @delete-by-id="handleDelete" :isAdd="false" style="margin-top: 10px">
-
-  </EditRowTableVue>
-
-  <DevicePointTable @register="registerModal" :data-source="devicePointList" :selection-row-keys="selectionRowKeys"
-    @reload="reload" />
+  <BaseModal @register="register" @add="onSubmit" @update="onSubmit" :form-schemas="formSchemas" > </BaseModal>
+  
 </template>
 
 <script lang="ts" setup>
-import { defineProps, ref, onMounted } from 'vue';
-import { BackWindDevicePointColumns } from './warning.data';
-import EditRowTableVue from '../../../comment/EditRowTable.vue';
-import { backWindControlDevicePointList, backWindControlDevicePointEdit, backWindControlDevicePointDelete, workFaceDeviceList, workFacePointList, backWindControlDevicePointBatchAdd } from './warning.api';
-import DevicePointTable from './DevicePointTable.vue';
-import { useModal } from '/@/components/Modal';
-
-const props = defineProps({
-  deviceId: { type: String },
-  pointType: {
-    type: String,
-    requried: true,
-  },
-});
-
-const sysDeviceId = ref('') // 选中的关联设备的id
-const sysDeviceType = ref('')
-const devicePointList = ref<any[]>([])
-const selectionRowKeys = ref<any[]>([])
-const sysDeviceOptions = ref<any[]>([])
-let sysDeviceList = <any[]>[]
-const RefComponent = ref()
-const dataSource = ref<any[]>([])
-
-const refresh = ref(true)
-
-const [registerModal, { openModal }] = useModal();
+  import { ref, onMounted } from 'vue'
+  import { rowOrColSpanMap, MonitorColumns, monitorWarningFormSchemas } from './warning.data'
+  import { warningLogList, workFaceWarningList, workFaceWarningAdd, workFaceWarningEdit, workFaceWarningDelete  } from './warning.api';
+  import BaseModal from './BaseModal.vue'
+  import { useModal } from '/@/components/Modal';
+ 
+  const emit = defineEmits(['register'])
+  
+  const props = defineProps({
+    deviceId: { type: String },
+  });
+
+  const show = ref(false)
+  const formSchemas = monitorWarningFormSchemas({ id: props.deviceId })
+  const activeID = ref('')
+  const warningList = ref<{ id: string, alarmName: string }[]>([])
+  const dataSource = ref([])
+
+  function selectWarning(id) {
+    activeID.value = id
+  }
 
-const handleChange = async (value) => {
-  sysDeviceId.value = value
-  const obj = sysDeviceList.find((element) => {
-    return element.id == value
-  })
-  sysDeviceType.value = obj['strtype']
-  await getDeviceWarningPointList()
-}
+  async function getWarningList() {
+    const result = await warningLogList({ sysId: props.deviceId, pageSize: 100000  }) //id: props.deviceId
+    warningList.value = result.records
+    activeID.value = warningList.value[0]['id']
+  }
 
-async function getDeviceWarningPointList() {
+  async function getDataSource() {
+    show.value = false
+    rowOrColSpanMap.clear()
+    const result = await workFaceWarningList({ sysId: props.deviceId, monitorType: 2, alarmId: activeID.value, pageSize: 100000 })
+    let flag = 0
+    let groupNum = 0
+    //合并数据
+    const resetRowOrColSpanMap = (i) => {
+      
+      let j = i - flag
+      const value0 = [
+        {
+          code: 'key',
+          rowSpan: flag + 1,
+          colSpan: 1
+        },
+        {
+          code: 'alarmName',
+          rowSpan: flag + 1,
+          colSpan: 1
+        },
+        {
+          code: 'operation1',
+          rowSpan: flag + 1,
+          colSpan: 1
+        },
+      ]
+      const value1 = [
+        {
+          code: 'key',
+          rowSpan: 0,
+          colSpan: 1
+        },
+        {
+          code: 'alarmName',
+          rowSpan: 0,
+          colSpan: 1
+        },
+        {
+          code: 'operation1',
+          rowSpan: 0,
+          colSpan: 1
+        },
+      ]
+      if(i == result.records.length - 1){
+        rowOrColSpanMap.set(result.records[j]['id'], value0)
+        j += 1
+        while (j <= i) {
+          rowOrColSpanMap.set(result.records[j]['id'], value1)
+          ++j
+        }
+      }else{
+        rowOrColSpanMap.set(result.records[j - 1]['id'], value0)
+        while (j < i) {
+          rowOrColSpanMap.set(result.records[j]['id'], value1)
+          ++j
+        }
+      }
+    }
 
-  const result = await backWindControlDevicePointList({ deviceId: sysDeviceId.value })
-  if (result && result.records.length > 0) {
+    for(let i=0; i< result.records.length; i++){
+      const item = result.records[i]
+      item['key'] = `${groupNum}`
+      if(item.relId){
+        
+        ++flag
+        if(i == result.records.length - 1){
+          resetRowOrColSpanMap(i)
+        }
+      }else{
+        groupNum++
+        if(flag){
+          resetRowOrColSpanMap(i)
+        }
+        flag = 0
+      }
+    }
+    console.log(rowOrColSpanMap)
+    show.value = true
     dataSource.value = result.records
-  } else {
-    dataSource.value = []
   }
-}
 
-async function getDevicePointList() {
-  try {
-    const result = await workFacePointList({ deviceType: sysDeviceType.value, valueType: 1 });
-    devicePointList.value = result
-    const rowKeys = <any[]>[]
-    dataSource.value.forEach((item) => {
-      rowKeys.push(item['monitorId'])
-    })
-    selectionRowKeys.value = rowKeys
-  } catch (error) {
-    devicePointList.value = []
+  async function handleDelete(record) {
+    await workFaceWarningDelete({id: record.id })
+    getDataSource()
   }
-};
 
-async function getDeviceOptions() {
-  const result = await workFaceDeviceList({ id: props.deviceId })
-  const options = <any[]>[]
-  if (result.length > 0) {
-    result.forEach(element => {
-      options.push({ value: element.id, label: element.strname })
+  const [register, { openModal, closeModal }] = useModal()
+
+  function handleOpen(flag?, record?) {
+    if (record) {
+      if(flag == 'update'){
+        openModal(true, {
+          isUpdate: true,
+          record
+        });
+      }else {
+        // 新增并关系数据
+        openModal(true, {
+          isUpdate: false,
+          record
+        });
+      }
+      
+    }else{
+      openModal(true, {
+      isUpdate: false,
     });
-    sysDeviceOptions.value = options
-    sysDeviceList = result
+    }
+    
   }
 
-}
-
-async function saveOrUpdate(record) {
-  try {
-    if (record.id) {
-      await backWindControlDevicePointEdit({ ...record });
+  async function onSubmit(flag, values) {
+    if(activeID.value){
+      // 提交数据弹窗
+      if (flag == 'update') {
+        await workFaceWarningEdit({ ...values })
+      } else {
+        await workFaceWarningAdd({ ...values, monitorType: 2, sysId: props.deviceId, alarmId: activeID.value})
+      }
+      getDataSource()
     }
-    RefComponent.value.reload()
-  } catch (error) { }
-}
-
-function handleDelete(id) {
-  backWindControlDevicePointDelete({ id }).then(() => {
-    getDeviceWarningPointList()
-  })
-}
-
-async function reload(list) {
-  const monitorList = <any[]>[]
-  list.forEach(item => {
-    monitorList.push({ deviceId: sysDeviceId.value, monitorId: item['id'], monitorName: item['valuename'], sysId: props.deviceId })
-  })
-  await backWindControlDevicePointBatchAdd(monitorList)
-  getDeviceWarningPointList()
-}
+    closeModal();
+  }
 
-function addPoint() {
-  getDevicePointList().then(() => {
-    openModal();
+  onMounted(async() => {
+    await getWarningList()
+    await getDataSource()
   })
-}
-
-onMounted(async () => {
-  await getDeviceOptions()
-  await getDeviceWarningPointList()
-});
 </script>
 
 <style lang="less" scoped>
-@ventSpace: zxm;
+  @ventSpace: zxm;
+
+  .warning-device-box{
+    width: 100%;
+    height: 600px;
+    overflow-y: auto;
+    display: flex;
+    .warning-box{
+      width: 200px;
+      height: 100%;
+      overflow-y: auto;
+      background: #ffffff11;
+      padding: 5px;
+      border-radius: 5px;
+      
+      .link-item{
+        position: relative;
+        cursor: pointer;
+        line-height: 30px;
+        padding-left: 30px;
+        color: #fff;
+        span:hover{
+          color: #89ffff;
+        }
+        &::after{
+          content: '';
+          position: absolute;
+          display: block;
+          width: 8px;
+          height: 8px;
+          top: 12px;
+          left: 10px;
+          transform: rotateZ(45deg) skew(10deg, 10deg);
+          background: #45d3fd;
+        }
+      }
+      .active-device-title {
+        color: aqua;
+      }
+    }
+    .device-box{
+      flex: 1;
+      margin-left: 10px;
+    }
+
+    .action-link{
+      color: #00e7ff;
+    }
+  }
+  
+
 </style>

+ 12 - 1
src/views/vent/deviceManager/comment/warningTabel/warning.api.ts

@@ -16,7 +16,12 @@ enum Api {
   exportXls = '/sys/user/exportXls',
 
   workFaceDeviceList = '/safety/managesysDevice/getManagesDeviceInfo',
-  
+
+  warningLogList = '/safety/managesysAlarmInfo/list',
+  warningLogAdd = '/safety/managesysAlarmInfo/add',
+  warningLogEdit = '/safety/managesysAlarmInfo/edit',
+  warningLogDeleteById = '/safety/managesysAlarmInfo/delete',
+
   workFaceWarningList = '/safety/managesysAlarm/list',
   workFaceWarningAdd = '/safety/managesysAlarm/add',
   workFaceWarningBatchAdd = '/safety/managesysAlarm/addBatch',
@@ -122,3 +127,9 @@ export const backWindControlDevicePointDelete = (params) => {
   return defHttp.delete({ url: Api.backWindControlDevicePointDelete, params }, { joinParamsToUrl: true }).then(() => {});
 };
 
+// 预警条目管理
+export const warningLogList = (params) => defHttp.get({ url: Api.warningLogList, params });
+export const warningLogAdd = (params) => defHttp.post({ url: Api.warningLogAdd, params });
+export const warningLogEdit = (params) => defHttp.put({ url: Api.warningLogEdit, params });
+export const warningLogDeleteById = (params) => defHttp.delete({ url: Api.warningLogDeleteById, params })
+

+ 396 - 9
src/views/vent/deviceManager/comment/warningTabel/warning.data.ts

@@ -1,19 +1,21 @@
 import { BasicColumn, FormSchema } from '/@/components/Table';
 import { initDictOptions } from '/@/utils/dict';
+import type { TableColumnType } from 'ant-design-vue';
+import { workFaceDeviceList } from './warning.api';
 
 export const levelColumns: BasicColumn[] = [
   {
-    title: '上限',
-    dataIndex: 'fmax',
+    title: '下限',
     width: 100,
     editRow: true,
+    dataIndex: 'fmin',
     editComponent: 'InputNumber',
   },
   {
-    title: '下限',
+    title: '上限',
+    dataIndex: 'fmax',
     width: 100,
     editRow: true,
-    dataIndex: 'fmin',
     editComponent: 'InputNumber',
   },
   {
@@ -187,6 +189,50 @@ export const workFaceColumns: BasicColumn[] = [
   },
 ];
 
+export const workFaceWarningColumns: BasicColumn[] = [
+  {
+    title: '报警条目',
+    dataIndex: 'alarmName',
+    align: 'center',
+  },
+  {
+    title: '预警等级',
+    dataIndex: 'alarmLevel',
+    align: 'center',
+  },
+  {
+    title: '创建人',
+    dataIndex: 'createBy',
+    align: 'center',
+  },
+  {
+    title: '创建日期',
+    dataIndex: 'createTime',
+    align: 'center',
+  },
+];
+
+export const workFaceWarningFormSchemas: FormSchema[] = [
+  {
+    label: 'ID',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+  {
+    label: '预警名称',
+    field: 'alarmName',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '预警等级',
+    field: 'alarmLevel',
+    component: 'JDictSelectTag',
+    componentProps: { dictCode: 'leveltype' },
+  },
+];
+
 export const manageWarningPointColumns: BasicColumn[] = [
   {
     title: '设备名称',
@@ -195,7 +241,7 @@ export const manageWarningPointColumns: BasicColumn[] = [
   },
   {
     title: '报警条目',
-    dataIndex: 'monitorName',
+    dataIndex: 'alarmName',
     align: 'center',
   },
   {
@@ -227,6 +273,51 @@ export const manageWarningPointColumns: BasicColumn[] = [
   },
 ];
 
+export const controlFormSchemas = (param) =>
+  <FormSchema[]>[
+    {
+      label: 'ID',
+      field: 'id',
+      component: 'Input',
+      show: false,
+    },
+    {
+      label: '点表',
+      field: 'strtype',
+      component: 'Input',
+      show: false,
+    },
+    {
+      label: '设备名称',
+      field: 'deviceId',
+      component: 'ApiSelect',
+      required: true,
+      componentProps: ({ formModel }) => {
+        return {
+          api: workFaceDeviceList.bind(null, param),
+          labelField: 'strname',
+          valueField: 'id',
+          onChange: (e, option) => {
+            if (option) formModel['strtype'] = option['strtype'];
+          },
+        };
+      },
+    },
+
+    {
+      label: '点位',
+      field: 'monitorId',
+      component: 'Select',
+      slot: 'monitor',
+      required: true,
+    },
+    {
+      label: '执行顺序',
+      field: 'orderNum',
+      component: 'InputNumber',
+    },
+  ];
+
 export const controlDevicePointColumns: BasicColumn[] = [
   {
     title: '报警条目',
@@ -246,8 +337,6 @@ export const controlDevicePointColumns: BasicColumn[] = [
   {
     title: '值',
     dataIndex: 'value',
-    edit: true,
-    editComponent: 'Input',
     align: 'center',
   },
   // {
@@ -273,8 +362,6 @@ export const controlDevicePointColumns: BasicColumn[] = [
     title: '执行顺序',
     dataIndex: 'orderNum',
     align: 'center',
-    edit: true,
-    editComponent: 'InputNumber',
   },
   {
     title: '更新人',
@@ -282,6 +369,101 @@ export const controlDevicePointColumns: BasicColumn[] = [
     align: 'center',
     width: 100,
   },
+  {
+    title: '操作',
+    dataIndex: 'operation',
+    width: 100,
+  },
+];
+
+export const monitorWarningFormSchemas = (param) =>
+  <FormSchema[]>[
+    {
+      label: 'ID',
+      field: 'id',
+      component: 'Input',
+      show: false,
+    },
+    {
+      label: '点表',
+      field: 'strtype',
+      component: 'Input',
+      show: false,
+    },
+    {
+      label: '设备名称',
+      field: 'deviceId',
+      component: 'ApiSelect',
+      required: true,
+      componentProps: ({ formModel }) => {
+        return {
+          api: workFaceDeviceList.bind(null, param),
+          labelField: 'strname',
+          valueField: 'id',
+          onChange: (e, option) => {
+            if (option) formModel['strtype'] = option['strtype'];
+          },
+        };
+      },
+    },
+    {
+      label: '且关系关联id',
+      field: 'relId',
+      component: 'Input',
+      show: false,
+    },
+    {
+      label: '点位',
+      field: 'monitorId',
+      component: 'Select',
+      slot: 'monitor',
+      required: true,
+    },
+    {
+      label: '报警上限值',
+      field: 'fmax',
+      component: 'InputNumber',
+    },
+    {
+      label: '报警下限值',
+      field: 'fmin',
+      component: 'InputNumber',
+    },
+    {
+      label: '报警持续时间(s)',
+      field: 'cxTime',
+      component: 'InputNumber',
+    },
+  ];
+
+export const controlWarningFormSchemas: FormSchema[] = [
+  {
+    label: 'ID',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+  {
+    label: '设备名称',
+    field: 'deviceName',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '控制点位描述',
+    field: 'monitorName',
+    component: 'Input',
+  },
+  {
+    label: '值',
+    field: 'value',
+    component: 'Input',
+  },
+  {
+    label: '执行顺序',
+    field: 'orderNum',
+    component: 'Input',
+  },
 ];
 
 export const BackWindDevicePointColumns: BasicColumn[] = [
@@ -340,3 +522,208 @@ export const BackWindDevicePointColumns: BasicColumn[] = [
     width: 100,
   },
 ];
+
+export const rowOrColSpanMap = new Map();
+
+const sharedOnCell = (record, rowIndex, column) => {
+  const arr = rowOrColSpanMap.get(record.id);
+  if (arr) {
+    const col = arr.find((item) => {
+      return item.code === column.dataIndex;
+    });
+    if (col) return { rowSpan: col.rowSpan, colSpan: col.colSpan };
+  }
+};
+
+export const MonitorColumns: TableColumnType[] = [
+  {
+    title: '组合',
+    dataIndex: 'key',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '预警名称',
+    dataIndex: 'alarmName',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '设备名称',
+    dataIndex: 'deviceName',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '监测点位',
+    dataIndex: 'monitorName',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '报警下限值',
+    dataIndex: 'fmin',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '报警上限值',
+    dataIndex: 'fmax',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '报警持续时间',
+    dataIndex: 'cxTime',
+    customCell: sharedOnCell,
+  },
+  {
+    title: '操作',
+    colSpan: 2,
+    dataIndex: 'operation',
+    width: 120,
+    customCell: sharedOnCell,
+  },
+  {
+    title: '操作',
+    colSpan: 0,
+    dataIndex: 'operation1',
+    width: 80,
+    customCell: sharedOnCell,
+  },
+];
+
+export const controlColumns: TableColumnType[] = [
+  {
+    title: '预警名称',
+    dataIndex: 'alarmName',
+    align: 'center',
+  },
+  {
+    title: '设备名称',
+    dataIndex: 'deviceName',
+    align: 'center',
+  },
+  {
+    title: '控制点位',
+    dataIndex: 'monitorName',
+    align: 'center',
+  },
+  {
+    title: '执行顺序',
+    dataIndex: 'orderNum',
+    align: 'center',
+  },
+  {
+    title: '操作',
+    colSpan: 2,
+    dataIndex: 'operation',
+    width: 120,
+    align: 'center',
+  },
+];
+
+export const testData = [
+  {
+    key: '1',
+    name: '气源压力超限',
+    age: 32,
+    tel: '0571-22098909',
+    phone: 18889898989,
+    address: 'New York No. 1 Lake Park',
+    rowSpan: 1,
+    colSpan: 1,
+  },
+  {
+    key: '2',
+    name: '气源压力超限',
+    tel: '0571-22098333',
+    phone: 18889898888,
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    rowSpan: 1,
+    colSpan: 1,
+  },
+  {
+    key: '3',
+    name: '气源压力超限',
+    age: 32,
+    tel: '0575-22098909',
+    phone: 18900010002,
+    address: 'Sidney No. 1 Lake Park',
+    rowSpan: 1,
+    colSpan: 1,
+  },
+  {
+    key: '4',
+    name: '气源压力超限',
+    age: 18,
+    tel: '0575-22098909',
+    phone: 18900010002,
+    address: 'London No. 2 Lake Park',
+    rowSpan: 1,
+    colSpan: 1,
+  },
+  {
+    key: '5',
+    name: '气源压力超限',
+    age: 18,
+    tel: '0575-22098909',
+    phone: 18900010002,
+    address: 'Dublin No. 2 Lake Park',
+    rowSpan: 1,
+    colSpan: 1,
+  },
+];
+
+export const testData1 = [
+  {
+    key: '1',
+    alarmName: '气源压力超限',
+    devicePos: 32,
+    monitorName: '0571-22098909',
+    value: 18889898989,
+    orderNum: '1',
+    updateBy: 'New York No. 1 Lake Park',
+  },
+  {
+    key: '2',
+    alarmName: '气源压力超限',
+    devicePos: 42,
+    monitorName: '0571-22098333',
+    value: 18889898888,
+    orderNum: '2',
+    updateBy: 'London No. 1 Lake Park',
+  },
+  {
+    key: '3',
+    alarmName: '气源压力超限',
+    devicePos: 32,
+    monitorName: '0575-22098909',
+    value: 18900010002,
+    orderNum: '3',
+    updateBy: 'Sidney No. 1 Lake Park',
+  },
+  {
+    key: '4',
+    alarmName: '气源压力超限',
+    devicePos: 18,
+    monitorName: '0575-22098909',
+    value: 18900010002,
+    orderNum: '4',
+    updateBy: 'London No. 2 Lake Park',
+  },
+  {
+    key: '5',
+    alarmName: '气源压力超限',
+    devicePos: 18,
+    monitorName: '0575-22098909',
+    value: 18900010002,
+    orderNum: '5',
+    updateBy: 'Dublin No. 2 Lake Park',
+  },
+  {
+    key: '6',
+    alarmName: '气源压力超限',
+    devicePos: 18,
+    monitorName: '0575-22098909',
+    value: 18900010002,
+    orderNum: '5',
+    updateBy: 'Dublin No. 2 Lake Park',
+  },
+];
+

+ 1 - 1
src/views/vent/deviceManager/deviceTable/index.vue

@@ -137,7 +137,7 @@
     searchFormSchema[0].componentProps['dictCode'] =  `${deviceType.value}kind`
     columns.value = getTableHeaderColumns(`${deviceType.value}_list`) || []
     const formSchemaColumns = getFormSchemaColumns(`${deviceType.value}_edit`) || []
-
+    
     arrToFormColumns(formSchemaColumns)
     isRefresh.value = true
   })

+ 34 - 13
src/views/vent/deviceManager/pointTabel/point.data.ts

@@ -15,7 +15,11 @@ export const columns: BasicColumn[] = [
     dataIndex: 'devicekind_dictText',
     width: 120,
   },
-
+  {
+    title: '点表类型',
+    dataIndex: 'devicetype',
+    width: 120,
+  },
   {
     title: '值名称',
     dataIndex: 'valuename',
@@ -26,16 +30,16 @@ export const columns: BasicColumn[] = [
     dataIndex: 'valuecode',
     width: 100,
   },
-  // {
-  //   title: '值类型',
-  //   width: 150,
-  //   dataIndex: 'valuetype_dictText',
-  // },
-  // {
-  //   title: '最小值',
-  //   width: 100,
-  //   dataIndex: 'fmin',
-  // },
+  {
+    title: '点位地址',
+    width: 150,
+    dataIndex: 'plcaddr',
+  },
+  {
+    title: '数据类型',
+    width: 100,
+    dataIndex: 'datakind_dictText',
+  },
   // {
   //   title: '最大值',
   //   dataIndex: 'fmax',
@@ -70,7 +74,16 @@ export const searchFormSchema: FormSchema[] = [
     component: 'MTreeSelect',
     colProps: { span: 6 },
   },
-
+  {
+    label: '值类型',
+    field: 'valuetype',
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'valuetype',
+      placeholder: '请选择设备类型',
+      stringToNumber: true,
+    },
+  },
   {
     label: '值名称',
     field: 'valuename',
@@ -141,6 +154,11 @@ export const formSchema: FormSchema[] = [
     },
   },
   {
+    label: '功能码',
+    field: 'fanctionCode',
+    component: 'Input',
+  },
+  {
     label: '值名称',
     field: 'valuename',
     component: 'Input',
@@ -166,7 +184,7 @@ export const formSchema: FormSchema[] = [
     component: 'Input',
   },
   {
-    label: 'plc地址',
+    label: '点位地址',
     field: 'plcaddr',
     component: 'Input',
   },
@@ -227,6 +245,7 @@ export const formSchema: FormSchema[] = [
     componentProps: {
       dictCode: 'booltype',
       placeholder: '是否保存',
+      stringToNumber: true,
     },
   },
   {
@@ -236,6 +255,7 @@ export const formSchema: FormSchema[] = [
     componentProps: {
       dictCode: 'booltype',
       placeholder: '是否报警',
+      stringToNumber: true,
     },
   },
   {
@@ -245,6 +265,7 @@ export const formSchema: FormSchema[] = [
     componentProps: {
       dictCode: 'booltype',
       placeholder: '是否计算平均值',
+      stringToNumber: true,
     },
   },
   {

+ 29 - 2
src/views/vent/deviceManager/substationTabel/index.vue

@@ -19,6 +19,10 @@
         <a-tag v-if="column.dataIndex === 'linkstatus'" :color="record.linkstatus == 0 ? '#999' : '#87d068'">{{
           record.linkstatus == 1 ? '链接' : '断开'
         }}</a-tag>
+      
+      </template>
+      <template #action="{ record }">
+        <a v-if="record['strtype'] == 'http'" class="table-action-link" @click="addDevices(record)">同步设备</a>
       </template>
     </NormalTable>
   </div>
@@ -26,11 +30,34 @@
 
 <script lang="ts" name="system-user" setup>
   //ts语法
-  import { ref } from 'vue';
+  import { ref, onMounted, onUnmounted } from 'vue';
   import NormalTable from '../comment/NormalTable.vue';
   import { columns, searchFormSchema, formSchema } from './substation.data';
-  import { list, getImportUrl, getExportUrl, deleteById, batchDeleteById, saveOrUpdate } from './substation.api';
+  import { list, getImportUrl, getExportUrl, deleteById, batchDeleteById, saveOrUpdate, addDevice } from './substation.api';
+  import { message } from 'ant-design-vue';
   const normalTabel = ref();
+  let timer = undefined ;
+  function reload() {
+    timer = setInterval(() => {
+      if(normalTabel.value)normalTabel.value.reload()
+    }, 30000)
+  }
+
+  function addDevices(record) {
+    addDevice({ id: record.id }).then((result) => {
+      // message.success('同步生成')
+    }).catch(() => {
+      message.success('同步失败')
+    })
+  }
+  onMounted(() => {
+    reload()
+  })
+
+  onUnmounted(() => {
+    clearInterval(timer)
+  })
+  
 </script>
 
 <style scoped></style>

+ 4 - 1
src/views/vent/deviceManager/substationTabel/substation.api.ts

@@ -9,6 +9,7 @@ enum Api {
   deleteBatch = '/sys/user/deleteBatch',
   importExcel = '/sys/user/importExcel',
   exportXls = '/sys/user/exportXls',
+  addDevice = '/safety/ventanalySubStation/addDeviceBystation',
 }
 /**
  * 导出api
@@ -29,7 +30,7 @@ export const list = (params) => defHttp.get({ url: Api.list, params });
  * 删除用户
  */
 export const deleteById = (params, handleSuccess) => {
-  return defHttp.delete({ url: Api.deleteById, params: params.nsubstationid }, { joinParamsToUrl: true }).then(() => {
+  return defHttp.delete({ url: Api.deleteById, params: { id: params.nsubstationid } }, { joinParamsToUrl: true }).then(() => {
     handleSuccess();
   });
 };
@@ -58,3 +59,5 @@ export const saveOrUpdate = (params, isUpdate) => {
   const url = isUpdate ? Api.edit : Api.save;
   return isUpdate ? defHttp.put({ url: url, params }) : defHttp.post({ url: url, params });
 };
+
+export const addDevice = (params) => defHttp.post({ url: Api.addDevice, params });

+ 101 - 23
src/views/vent/deviceManager/substationTabel/substation.data.ts

@@ -91,20 +91,53 @@ export const formSchema: FormSchema[] = [
     label: '名称',
     field: 'strname',
     component: 'Input',
+    required: true,
   },
   {
     label: '安装位置',
     field: 'strinstallpos',
     component: 'Input',
+    required: true,
   },
   {
+    label: '监测类型',
+    field: 'monitorparam',
+    component: 'MTreeSelect',
+    colProps: { span: 6 },
+    required: true,
+  },
+  {
+    label: '分站类型',
+    field: 'ntype',
+    component: 'JDictSelectTag',
+    componentProps: {
+      dictCode: 'stationtype',
+      placeholder: '请选择分站类型',
+      stringToNumber: true,
+    },
+    required: true,
+  },
+  {
+    label: '分站站号',
+    field: 'code   ',
+    component: 'Input',
+  },
+  {
+    label: '分站IP地址',
+    field: 'strip',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '分站端口',
+    field: 'nport',
+    component: 'InputNumber',
+  },
+
+  {
     label: '读取数据方式',
     field: 'strtype',
     component: 'JDictSelectTag',
-    // componentProps: {
-    //   dictCode: 'getdatatype',
-    //   placeholder: '请选择分站用途',
-    // },
     componentProps: ({ formModel, formActionType, schema }) => {
       return {
         dictCode: 'getdatatype',
@@ -121,6 +154,15 @@ export const formSchema: FormSchema[] = [
               field: 'plcType',
               show: false,
             });
+            if (e == 'http') {
+              updateSchema({
+                label: 'reqHeader请求头',
+                field: 'reqparam',
+                component: 'Input',
+                required: true,
+                show: true,
+              });
+            }
           }
         },
       };
@@ -137,35 +179,71 @@ export const formSchema: FormSchema[] = [
     },
   },
   {
-    label: '分站用途',
-    field: 'nkj980use',
+    label: '系统缓存点表下标',
+    field: 'plcTable',
+    component: 'Input',
+  },
+  {
+    label: '请求方式',
+    field: 'reqtype',
     component: 'JDictSelectTag',
     componentProps: {
-      dictCode: 'nkj980use',
-      placeholder: '请选择分站用途',
+      dictCode: 'reqtype',
+      placeholder: '请选择请求方式',
       stringToNumber: true,
     },
   },
   {
-    label: '分站IP地址',
-    field: 'strip',
+    label: 'reqHeader请求头',
+    field: 'reqparam',
     component: 'Input',
+    show: false,
   },
   {
-    label: '分站端口',
-    field: 'nport',
-    component: 'InputNumber',
+    label: '请求参数',
+    field: 'reqparam',
+    component: 'Input',
+  },
+  {
+    label: '数据属性字段',
+    field: 'dataproperty',
+    component: 'Input',
+  },
+  {
+    label: 'ID字段',
+    field: 'idcode',
+    component: 'Input',
+  },
+  {
+    label: '名称code',
+    field: 'namecode',
+    component: 'Input',
+  },
+  {
+    label: '数据格式化',
+    field: 'dataformat',
+    component: 'Input',
+  },
+  {
+    label: '字符串分割符',
+    field: 'splitchar',
+    component: 'Input',
+  },
+  {
+    label: '连接用户名',
+    field: 'remoteUser',
+    component: 'Input',
+  },
+  {
+    label: '连接密码',
+    field: 'remotePass',
+    component: 'Input',
+  },
+  {
+    label: '图层ID',
+    field: 'nlayerid',
+    component: 'Input',
   },
-  // {
-  //   label: '链接状态',
-  //   field: 'linkstatus',
-  //   component: 'JDictSelectTag',
-  //   componentProps: {
-  //     dictCode: 'linkstatus',
-  //     placeholder: '请选择链接状态',
-  //     stringToNumber: true,
-  //   },
-  // },
   {
     label: '备注',
     field: 'strremark',

+ 55 - 0
src/views/vent/deviceManager/workingFace/workingFace.data.ts

@@ -60,6 +60,16 @@ export const searchFormSchema: FormSchema[] = [
     component: 'Input',
     colProps: { span: 6 },
   },
+  {
+    label: '系统类型',
+    field: 'strtype',
+    component: 'JDictSelectTag',
+    colProps: { span: 6 },
+    componentProps: {
+      dictCode: 'syskind',
+      placeholder: '请选择系统型号',
+    },
+  },
 ];
 
 export const formSchema: FormSchema[] = [
@@ -181,4 +191,49 @@ export const formSchema: FormSchema[] = [
       };
     },
   },
+  {
+    label: '是否绑定采空区',
+    field: 'linkEmptyFlag',
+    component: 'RadioGroup',
+    defaultValue: 0,
+    componentProps: ({ formActionType }) => {
+      return {
+        options: [
+          { label: '绑定采空区', value: 1, key: '1' },
+          { label: '不绑定', value: 0, key: '2' },
+        ],
+        onChange: (e: any) => {
+          const { updateSchema } = formActionType;
+          updateSchema({
+            field: 'linkEmpty',
+            label: '绑定采空区ID',
+            component: 'ApiSelect',
+            show: e.target.value == 1 ? true : false,
+            componentProps: () => {
+              return {
+                api: list.bind(null, { strtype: 'sys_empty', pageSize: 1000 }),
+                labelField: 'systemname',
+                valueField: 'id',
+                resultField: 'records',
+              };
+            },
+          });
+        },
+      };
+    },
+  },
+  {
+    label: '绑定采空区ID',
+    field: 'linkEmpty',
+    component: 'ApiSelect',
+    show: false,
+    componentProps: () => {
+      return {
+        api: list.bind(null, { strtype: 'sys_empty', pageSize: 1000 }),
+        labelField: 'systemname',
+        valueField: 'id',
+        resultField: 'records',
+      };
+    },
+  },
 ];

+ 203 - 0
src/views/vent/monitorManager/alarmMonitor/AlarmHistoryTable.vue

@@ -0,0 +1,203 @@
+<template>
+  <div class="history-table">
+    <BasicTable ref="historyTable" @register="registerTable" >
+      <template #bodyCell="{ column, record }">
+        <slot name="filterCell" v-bind="{ column, record }"></slot>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+
+<script lang="ts" name="system-user" setup>
+  //ts语法
+  import { watchEffect, ref, watch, defineExpose } from 'vue';
+  import { FormSchema } from '/@/components/Form/index';
+  import { BasicTable } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { warningHistoryList } from './alarm.api'
+  import dayjs from 'dayjs';
+
+  const historyTable = ref();
+
+  const emit = defineEmits(['change']);
+  const props = defineProps({
+    columnsType: {
+      type: String,
+    },
+    columns: {
+      type: Array,
+      // required: true,
+      default: () => [],
+    },
+    deviceType: {
+      type: String,
+    },
+    designScope: {
+      type: String,
+    },
+    sysId: {
+      type: String,
+    },
+    scroll: {
+      type: Object,
+      default: { y: 0 }
+    },
+    formSchemas: {
+      type: Array<FormSchema>,
+      default: () => []
+    }
+  });
+  const columns = ref([])
+  const tableScroll = props.scroll.y ? ref({ y: props.scroll.y - 100 }) : ref({})
+
+  watch(
+    () => {
+      return props.columnsType;
+    },
+    (newVal) => {
+      if (!newVal) return
+      const column = getTableHeaderColumns(newVal + '_history')
+      if (column && column.length < 1) {
+        const arr = newVal.split('_')
+        const columnKey = arr.reduce((prev, cur, index) => {
+          if (index !== arr.length - 2) {
+            return prev + '_' + cur
+          } else {
+            return prev
+          }
+        })
+        columns.value = getTableHeaderColumns(arr[0] + '_history');
+      } else {
+        columns.value = column
+      }
+      if(historyTable.value) reload()
+    },
+    {
+      immediate: true
+    }
+  );
+
+  watch(() => props.scroll.y, (newVal) => {
+    if(newVal){
+      tableScroll.value = {y: newVal - 100 }
+    }else{
+      tableScroll.value = {}
+    }
+  })
+
+  // 列表页面公共参数、方法
+  const { tableContext } = useListPage({
+    tableProps: {
+      api: warningHistoryList,
+      columns: props.columnsType ? columns : (props.columns as any[]),
+      canResize: true,
+      showTableSetting: false,
+      showActionColumn: false,
+      bordered: false,
+      size: 'small',
+      scroll: tableScroll,
+      formConfig: {
+        labelAlign: 'left',
+        showAdvancedButton: false,
+        // autoAdvancedCol: 2,
+
+        baseColProps: {
+          // offset: 0.5,
+          xs: 24,
+          sm: 24,
+          md: 24,
+          lg: 9,
+          xl: 7,
+          xxl: 4,
+        },
+        schemas: props.formSchemas.length > 0 ? props.formSchemas : [
+          {
+            label: '报警时间区间',
+            field: 'tickectDate',
+            component: 'TimeRangePicker',
+            componentProps: {
+              placeholder: ['报警开始时间', '报警结束时间'],
+              valueFormat: 'HH:mm:ss',
+            },
+          },
+        ],
+        fieldMapToTime: [['tickectDate', ['starttime', 'endtime'], '']],
+      },
+      fetchSetting: {
+        listField: 'list.records',
+        pageField: 'list.pages',
+        sizeField: 'list.size',
+        totalField: 'list.total',
+      },
+      beforeFetch(params) {
+        params.strtype = props.deviceType + '*';
+        if(props.sysId){
+          params.sysId = props.sysId;
+        }
+        if(params['datalist.pages'])params['pages'] = params['datalist.pages']
+        if (params['datalist.size']) params['pageSize'] = params['datalist.size']
+      },
+      afterFetch(resultItems) {
+        resultItems.map((item) => {
+          Object.assign(item, item['readData']);
+        });
+        return resultItems;
+      },
+    },
+  });
+  //注册table数据
+  const [registerTable, { getDataSource, reload, setLoading }] = tableContext;
+
+  watchEffect(() => {
+    if (historyTable.value && getDataSource) {
+      const data = getDataSource() || [];
+      emit('change', data);
+      console.log('[ data ] >', data);
+    }
+  });
+
+  
+
+  defineExpose({ setLoading })
+</script>
+
+<style scoped lang="less">
+  @import '/@/design/vent/color.less';
+  
+
+  :deep(.@{ventSpace}-table-body) {
+    height: auto !important;
+  }
+  :deep(.zxm-picker){
+      height: 30px !important;
+  }
+  .history-table {
+    width: 100%;
+    :deep(.jeecg-basic-table-form-container) {
+      .@{ventSpace}-form {
+        padding: 0 !important;
+        border: none !important;
+        margin-bottom: 0 !important;
+        .@{ventSpace}-picker,
+        .@{ventSpace}-select-selector {
+          width: 100% !important;
+          height: 100%;
+          background: #00000017;
+          border: 1px solid #b7b7b7;
+          input,
+          .@{ventSpace}-select-selection-item,
+          .@{ventSpace}-picker-suffix {
+            color: #fff;
+          }
+          .@{ventSpace}-select-selection-placeholder {
+            color: #ffffffaa;
+          }
+        }
+      }
+      .@{ventSpace}-table-title {
+        min-height: 0 !important;
+      }
+    }
+  }
+</style>

+ 210 - 0
src/views/vent/monitorManager/alarmMonitor/DetailModal.vue

@@ -0,0 +1,210 @@
+<template>
+  <BasicModal @register="register" title="预警详情" width="100%" v-bind="$attrs" @ok="onSubmit" @cancel="onSubmit" :defaultFullscreen="true">
+    <div class="alarm-modal">
+      <a-tabs class="tabs-box" type="card" v-model:activeKey="activeKey" @change="tabChange">
+        <a-tab-pane key="1" tab="实时预警">
+          <div v-if="activeKey == '1'" class="box-bg table-box" style="margin-bottom: 10px">
+            <MonitorTable
+              ref="SensorMonitorRef"
+              :columns="levelColumns"
+              :dataSource="dataSource"
+              design-scope="alarm"
+              :isShowSelect="false"
+              :formConfig="formConfig"
+              title="预警监测"
+            >
+              <template #filterCell="{ column, record }">
+                <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == 0 ? 'green' : 'red'">{{
+                  record.warnFlag == 0 ? '正常' : '报警'
+                }}</a-tag>
+                <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == 0 ? 'default' : 'green'">{{
+                  record.netStatus == 0 ? '断开' : '连接'
+                }}</a-tag>
+              </template>
+            </MonitorTable>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="2" tab="报警历史">
+          <div class="tab-item box-bg">
+            <AlarmHistoryTable v-if="activeKey == '2'" :columns="levelColumns" designScope="alarm-history" />
+          </div>
+        </a-tab-pane>
+      </a-tabs>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+
+import { onMounted, ref, defineEmits, reactive, onUnmounted, watch } from 'vue';
+import MonitorTable from '../comment/MonitorTable.vue';
+import AlarmHistoryTable from './AlarmHistoryTable.vue';
+import { warningList } from './alarm.api';
+import { formConfig, levelColumns } from './alarm.data'
+import { BasicModal, useModalInner } from '/@/components/Modal';
+import { array } from 'vue-types';
+
+const props = defineProps({
+  deviceId: { type: String },
+  monitorData: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const emit = defineEmits(['close','register'])
+
+// 默认初始是第一行
+const activeKey = ref('1');
+const dataSource = ref([]);
+
+const tabChange = (activeKeyVal) => {
+  activeKey.value = activeKeyVal;
+
+};
+
+
+// 注册 modal
+const [register, { closeModal }] = useModalInner();
+
+async function onSubmit() {
+  emit('close')
+  closeModal();
+}
+
+watch(() => props.monitorData, (data:never[]) => {
+  dataSource.value = data
+})
+
+onMounted(async () => {
+
+});
+onUnmounted(() => {
+
+});
+</script>
+<style scoped lang="less">
+
+  @import '/@/design/vent/color.less';
+  @import '/@/design/vent/modal.less';
+  .padding-0 {
+    padding: 10px 0 !important;
+  }
+  .alarm-modal {
+    position: relative;
+    padding: 10px;
+    z-index: 999;
+    max-height: calc(100vh - 150px);
+    .@{ventSpace}-tabs {
+      max-height: calc(100vh - 100px);
+      .tab-item {
+        max-height: calc(100vh - 170px);
+        overflow-y: auto;
+      }
+    }
+    .title-text {
+      position: absolute;
+      top: -14px;
+      left: 0;
+      width: 100%;
+      text-align: center;
+      color: #fff;
+    }
+    .table-box {
+      height: calc(60vh - 150px);
+      padding: 20px 10px;
+      overflow-y: auto;
+    }
+
+    .box-bg {
+      border: 1px solid #4d7ad855;
+      border-radius: 2px;
+      // background-color: #001d3055;
+      // -webkit-backdrop-filter: blur(8px);
+      // backdrop-filter: blur(8px);
+      box-shadow: 0 0 10px #5984e055 inset;
+      // background-color: #00b3ff12;
+    }
+    .charts-box {
+      height: calc(40vh - 80px);
+      padding: 5px 10px;
+      margin-top: 10px;
+    }
+  }
+  :deep(.@{ventSpace}-tabs-tabpane-active) {
+    height: 100%;
+  }
+  :deep(.@{ventSpace}-tabs-card) {
+    .@{ventSpace}-tabs-tab {
+      background: linear-gradient(#2cd1ff55, #1eb0ff55);
+      border-color: #74e9fe;
+      border-radius: 0%;
+      &:hover {
+        color: #64d5ff;
+      }
+    }
+    .@{ventSpace}-tabs-tab.@{ventSpace}-tabs-tab-active .@{ventSpace}-tabs-tab-btn {
+      color: aqua;
+    }
+    .@{ventSpace}-tabs-nav::before {
+      border-color: #74e9fe;
+    }
+    .@{ventSpace}-picker,
+    .@{ventSpace}-select-selector {
+      width: 100% !important;
+      background: #00000017 !important;
+      border: 1px solid @vent-form-item-boder !important;
+      input,
+      .@{ventSpace}-select-selection-item,
+      .@{ventSpace}-picker-suffix {
+        color: #fff !important;
+      }
+      .@{ventSpace}-select-selection-placeholder {
+        color: #b7b7b7 !important;
+      }
+    }
+    .@{ventSpace}-pagination-next,
+    .action,
+    .@{ventSpace}-select-arrow,
+    .@{ventSpace}-picker-separator {
+      color: #fff !important;
+    }
+    .@{ventSpace}-table-cell-row-hover {
+      background: #264d8833 !important;
+    }
+    .@{ventSpace}-table-row-selected {
+      background: #00c0a311  !important;
+      td {
+        background-color: #00000000 !important;
+      }
+    }
+    .@{ventSpace}-table-thead {
+      // background: linear-gradient(#004a8655 0%, #004a86aa 10%) !important;
+      background: #3d9dd45d!important;
+
+      & > tr > th,
+      .@{ventSpace}-table-column-title {
+        // color: #70f9fc !important;
+        border-color: #84f2ff  !important;
+        border-left: none !important;
+        border-right: none !important;
+        padding: 7px;
+      }
+    }
+
+    .@{ventSpace}-table-tbody {
+      tr > td {
+        padding: 12px;
+      }
+    }
+    .@{ventSpace}-table-tbody > tr:hover.@{ventSpace}-table-row > td {
+      background-color: #26648855 !important;
+    }
+
+    .jeecg-basic-table-row__striped {
+      // background: #97efff11 !important;
+      td {
+        background-color: #97efff11 !important;
+      }
+    }
+  }
+</style>

Some files were not shown because too many files changed in this diff