index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <template>
  2. <div class="alarmBox" :class="alarmList?.length ? 'notEmpty' : ''">
  3. <div
  4. :class="`${index < 6 ? item.class + ' alarmItem animate__animated' : ''}`"
  5. v-for="(item, index) in alarmList"
  6. :key="index"
  7. >
  8. <template v-if="index < 6">
  9. <div class="alarmTitle">{{ item.alarmName }}</div>
  10. <div class="alarmContent">
  11. <div class="contentItem">报警描述: {{ item.description }}</div>
  12. <div class="contentItem">报警时间: {{ item.tsName }}</div>
  13. </div>
  14. <div class="btnBox" :class="`lv${item.lv}BdColor`">
  15. <div class="btnItem" :class="`lv${item.lv}BdColor lv${item.lv}`">
  16. <el-button
  17. class="comfirmBtn"
  18. size="small"
  19. type="text"
  20. @click="comfirm(item)"
  21. >确认本条</el-button
  22. >
  23. </div>
  24. <div class="btnItem" :class="`lv${item.lv}`">
  25. <el-button
  26. class="comfirmBtn"
  27. size="small"
  28. type="text"
  29. @click="comfirmAll"
  30. >全部确认</el-button
  31. >
  32. </div>
  33. </div>
  34. </template>
  35. </div>
  36. </div>
  37. </template>
  38. <script>
  39. import { confirmAlart } from "@api/api.js";
  40. export default {
  41. data() {
  42. return {
  43. alarmList: [],
  44. seriousWarning: false,
  45. audioElement: null,
  46. ws: null,
  47. timeConnect: 0,
  48. limitConnect: 5,
  49. };
  50. },
  51. created() {
  52. this.webSocketInit(
  53. `ws://10.81.3.154:6014/websocket/${this.$store.state.user.authToken}`
  54. );
  55. },
  56. unmounted() {
  57. this.ws?.close();
  58. },
  59. methods: {
  60. getAlarmName(alarmItem) {
  61. let alarmName = "";
  62. if (alarmItem.deviceType === "booststation") {
  63. alarmName = "升压站报警";
  64. } else if (alarmItem.deviceType === "inverter") {
  65. alarmName = "逆变器报警";
  66. } else if (alarmItem.deviceType === "windturbine") {
  67. alarmName = "设备报警";
  68. } else if (alarmItem.deviceType === "station") {
  69. alarmName = "场站";
  70. }
  71. if (alarmItem.alarmType === "custom") {
  72. alarmName = "自定义报警";
  73. }
  74. return alarmName;
  75. },
  76. getLvName(alarmItem) {
  77. if (alarmItem.rank === 1) {
  78. return "低级";
  79. } else if (alarmItem.rank === 2) {
  80. return "低中级";
  81. } else if (alarmItem.rank === 3) {
  82. return "中级";
  83. } else if (alarmItem.rank === 4) {
  84. return "中高级";
  85. } else if (alarmItem.rank === 5) {
  86. return "高级";
  87. }
  88. },
  89. comfirm(item) {
  90. this.$confirm("您确定要执行此操作吗?", "提示", {
  91. confirmButtonText: "确定",
  92. cancelButtonText: "取消",
  93. type: "warning",
  94. }).then(() => {
  95. item.class = `animate__bounceOutRight lv${item.lv}`;
  96. item.confirm = true;
  97. setTimeout(() => {
  98. item.class = `animate__bounceOutRight hidden lv${item.lv}`;
  99. confirmAlart([item]).then((res) => {
  100. if (res.code === 200) {
  101. this.BASE.showMsg({
  102. type: "success",
  103. msg: "确认成功",
  104. });
  105. this.$store.commit("removeWarning", item);
  106. this.playAudioEffect();
  107. }
  108. });
  109. }, 500);
  110. });
  111. },
  112. comfirmAll() {
  113. this.$confirm("您确定要执行此操作吗?", "提示", {
  114. confirmButtonText: "确定",
  115. cancelButtonText: "取消",
  116. type: "warning",
  117. }).then(() => {
  118. for (let i = 0; i < this.alarmList.length; i++) {
  119. if (!this.alarmList[i].comfirm) {
  120. this.alarmList[
  121. i
  122. ].class = `animate__bounceOutRight lv${this.alarmList[i].lv}`;
  123. this.alarmList[i].confirm = true;
  124. setTimeout(() => {
  125. this.alarmList[
  126. i
  127. ].class = `animate__bounceOutRight hidden lv${this.alarmList[i].lv}`;
  128. }, 500);
  129. }
  130. }
  131. confirmAlart(this.alarmList).then((res) => {
  132. if (res.code === 200) {
  133. this.BASE.showMsg({
  134. type: "success",
  135. msg: "全部确认成功",
  136. });
  137. this.$store.commit("emptyWarning");
  138. this.playAudioEffect();
  139. }
  140. });
  141. this.playAudioEffect();
  142. });
  143. },
  144. getRandomNumber(min, max) {
  145. return Math.floor(Math.random() * (max - min + 1)) + min;
  146. },
  147. playAudioEffect() {
  148. const fiveLvWarning = this.alarmList.some((ele) => {
  149. return ele.lv === 5 && !ele.confirm;
  150. });
  151. const fourLvWarning = this.alarmList.some((ele) => {
  152. return ele.lv === 4 && !ele.confirm;
  153. });
  154. if (fiveLvWarning && !this.seriousWarning) {
  155. this.seriousWarning = true;
  156. this.audioElement = new Audio();
  157. this.audioElement.src = "./static/sound/lv5.mp3";
  158. this.audioElement.loop = true;
  159. this.audioElement?.play();
  160. } else if (fourLvWarning && !this.seriousWarning) {
  161. this.audioElement = new Audio();
  162. this.audioElement.src = "./static/sound/lv4.mp3";
  163. this.audioElement.addEventListener("ended", () => {
  164. this.audioElement.removeEventListener(
  165. "ended",
  166. this.stopPlayAudioEffect
  167. );
  168. });
  169. this.audioElement?.play();
  170. } else {
  171. if (!this.seriousWarning) {
  172. this.stopPlayAudioEffect();
  173. }
  174. }
  175. },
  176. stopPlayAudioEffect() {
  177. this.seriousWarning = false;
  178. if (this.audioElement) {
  179. this.audioElement.pause();
  180. this.audioElement.currentTime = 0;
  181. this.audioElement.loop = false;
  182. }
  183. this.audioElement = null;
  184. },
  185. webSocketInit(serveIP) {
  186. if ("WebSocket" in window) {
  187. this.ws = new WebSocket(serveIP);
  188. this.ws.onmessage = (res) => {
  189. let alarmItem = JSON.parse(res.data);
  190. if (alarmItem) {
  191. const alarmOption = {
  192. id: alarmItem.id,
  193. lv: alarmItem.rank,
  194. lvName: this.getLvName(alarmItem),
  195. rank: alarmItem.rank,
  196. class: `animate__bounceInRight lv${alarmItem.rank}`,
  197. isClose: alarmItem.isClose,
  198. isCloseName: alarmItem.isClose ? "是" : "否",
  199. alarmType: alarmItem.alarmType,
  200. alarmName: this.getAlarmName(alarmItem),
  201. description: alarmItem.description,
  202. deviceType: alarmItem.deviceType,
  203. oval: alarmItem.oval,
  204. triggerType: alarmItem.triggerType,
  205. ts: alarmItem.ts,
  206. tsName: new Date(alarmItem.ts).formatDate("yyyy-MM-dd hh:mm:ss"),
  207. deviceId: alarmItem.deviceId,
  208. deviceType: alarmItem.deviceType,
  209. faultCause: alarmItem.faultCause,
  210. resolvent: alarmItem.resolvent,
  211. characteristic: alarmItem.characteristic,
  212. code: alarmItem.code,
  213. };
  214. this.alarmList.push(alarmOption);
  215. this.$store.commit("setWarning", alarmOption);
  216. this.alarmList.sort((a, b) => {
  217. return b.lv - a.lv;
  218. });
  219. this.playAudioEffect();
  220. }
  221. };
  222. this.ws.onclose = () => {
  223. this.ws = null;
  224. };
  225. this.ws.onopen = () => {
  226. this.timeConnect = 0;
  227. console.log("WebSocket 服务已建立");
  228. };
  229. this.ws.onerror = () => {
  230. this.reconnect(serveIP);
  231. };
  232. } else {
  233. this.BASE.showMsg({
  234. msg: "当前浏览器不支持 WebSocket ,请更换浏览器后重试",
  235. });
  236. }
  237. },
  238. reconnect(serveIP) {
  239. if (this.timeConnect < this.limitConnect) {
  240. console.log(`webSocket 连接失败,第 ${++this.timeConnect} 次重连`);
  241. setTimeout(() => {
  242. this.webSocketInit(serveIP);
  243. }, 2000);
  244. } else {
  245. console.log("webSocket 连接已超时");
  246. this.BASE.showMsg({
  247. showClose: true,
  248. duration: 0,
  249. msg: `webSocket 连接超时,实时报警获取失败`,
  250. });
  251. this.ws?.close();
  252. }
  253. },
  254. },
  255. };
  256. </script>
  257. <style lang="scss" scoped>
  258. .alarmBox {
  259. width: 240px;
  260. height: calc(100% - 85px);
  261. padding: 0 12px 0 30px;
  262. position: absolute;
  263. right: 0;
  264. bottom: 0;
  265. z-index: 5000;
  266. display: flex;
  267. flex-direction: column-reverse;
  268. align-items: center;
  269. font-size: 12px;
  270. overflow-y: scroll;
  271. pointer-events: none;
  272. border-radius: 8px;
  273. transition: 0.2s;
  274. .alarmItem {
  275. width: 100%;
  276. box-sizing: border-box;
  277. border-radius: 8px;
  278. border: 1px solid #ebeef5;
  279. background: #1890ff;
  280. margin-bottom: 4px;
  281. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  282. color: #fff;
  283. pointer-events: auto;
  284. cursor: pointer;
  285. .alarmTitle {
  286. display: flex;
  287. justify-content: flex-start;
  288. align-content: center;
  289. width: calc(100% - 16px);
  290. overflow: hidden;
  291. text-overflow: ellipsis;
  292. white-space: nowrap;
  293. padding: 0 8px;
  294. margin-top: 8px;
  295. }
  296. .alarmContent {
  297. width: calc(100% - 16px);
  298. display: flex;
  299. justify-content: flex-start;
  300. align-items: flex-start;
  301. flex-wrap: wrap;
  302. margin-top: 4px;
  303. padding: 0 8px;
  304. .contentItem {
  305. width: 100%;
  306. display: flex;
  307. justify-content: flex-start;
  308. align-items: center;
  309. margin-bottom: 2px;
  310. word-wrap: break-word;
  311. &:last-child {
  312. margin-bottom: 0;
  313. }
  314. }
  315. }
  316. .btnBox {
  317. display: flex;
  318. width: 100%;
  319. justify-content: center;
  320. align-items: center;
  321. margin-top: 4px;
  322. border-top: 1px solid red;
  323. .btnItem {
  324. width: 50%;
  325. display: flex;
  326. justify-content: center;
  327. align-items: center;
  328. padding: 4px 0;
  329. .el-button {
  330. padding: 0;
  331. width: 45%;
  332. height: 20px;
  333. min-height: 20px;
  334. }
  335. &.lv1 .el-button,
  336. &.lv2 .el-button,
  337. &.lv3 .el-button {
  338. color: var(--el-color-info) !important;
  339. }
  340. &.lv4 .el-button {
  341. color: rgb(50, 65, 87) !important;
  342. }
  343. &.lv5 .el-button {
  344. color: #242f42;
  345. }
  346. &:first-child {
  347. border-right: 1px solid red;
  348. }
  349. }
  350. }
  351. &.lv5 {
  352. background: #fef0f0;
  353. border: 1px solid #242f42;
  354. color: #242f42;
  355. }
  356. &.lv4 {
  357. background: #f0f9eb;
  358. border: 1px solid rgb(50, 65, 87);
  359. color: rgb(50, 65, 87);
  360. }
  361. &.lv1,
  362. &.lv2,
  363. &.lv3 {
  364. background: #fdf6ec;
  365. border: 1px solid var(--el-color-info);
  366. color: var(--el-color-info);
  367. }
  368. .lv1BdColor,
  369. .lv2BdColor,
  370. .lv3BdColor {
  371. border-color: var(--el-color-info) !important;
  372. }
  373. .lv4BdColor {
  374. border-color: rgb(50, 65, 87) !important;
  375. }
  376. .lv5BdColor {
  377. border-color: #242f42 !important;
  378. }
  379. &.hidden {
  380. height: 0;
  381. padding: 0;
  382. margin-bottom: 0;
  383. border: 0;
  384. transition: 0.2s;
  385. overflow: hidden;
  386. }
  387. }
  388. &::-webkit-scrollbar {
  389. width: 0; /* 隐藏Webkit浏览器的滚动条宽度 */
  390. height: 0; /* 隐藏Webkit浏览器的滚动条高度 */
  391. }
  392. &.notEmpty:hover {
  393. background: rgba(0, 0, 0, 0.12);
  394. box-shadow: 0 0 12px rgba(0, 0, 0, 0.12);
  395. transition: 0.2s;
  396. }
  397. }
  398. </style>