SunZehao 4 ماه پیش
والد
کامیت
a3e8abe029

+ 3 - 0
src/App.vue

@@ -154,6 +154,9 @@ export default {
           deviceType: "windturbine",
         },
       ],
+      realList: [],
+      dialogList: [],
+      alarmList: []
     };
   },
   computed: {

+ 3 - 0
src/main.js

@@ -18,6 +18,8 @@ import "./permission";
 import axios from "@api/axios";
 import * as echarts from 'echarts'
 
+import websocket from "./utils/ws.js";
+
 // 引入基础工具
 import basicTool from "@tools/basicTool";
 
@@ -106,6 +108,7 @@ window.__STATICVUE__.use(animated);
 window.__STATICVUE__.config.globalProperties.API = axios; //全局注册
 window.__STATICVUE__.config.globalProperties.BASE = basicTool; //全局注册
 window.__STATICVUE__.config.globalProperties.$echarts = echarts; //全局注册
+window.__STATICVUE__.config.globalProperties.$ws = websocket;
 
 // window.__STATICVUE__.config.globalProperties.$Cesium = Cesium;
 

+ 10 - 10
src/router/index.js

@@ -73,25 +73,25 @@ export const asyncRoutes = [
                         },
                     },
                     {
-                        path: "historyWarning", // 报警查询
-                        name: "historyWarning",
+                        path: "customWarning", // 预警查询
+                        name: "customWarning",
                         component: () =>
-                        import("@/views/IntegratedAlarm/safe/historyWarning"),
+                        import("@/views/IntegratedAlarm/safe/customWarning"),
                         meta: {
-                            title: "警查询",
+                            title: "警查询",
                             icon: "",
-                            permissions: ["jn_safe_bjcx"],
+                            permissions: ["jn_safe_yjcx"],
                         },
                     },
                     {
-                        path: "customWarning", // 预警查询
-                        name: "customWarning",
+                        path: "historyWarning", // 报警查询
+                        name: "historyWarning",
                         component: () =>
-                        import("@/views/IntegratedAlarm/safe/customWarning"),
+                        import("@/views/IntegratedAlarm/safe/historyWarning"),
                         meta: {
-                            title: "警查询",
+                            title: "警查询",
                             icon: "",
-                            permissions: ["jn_safe_yjcx"],
+                            permissions: ["jn_safe_bjcx"],
                         },
                     },
                     {

+ 22 - 0
src/utils/auth.js

@@ -0,0 +1,22 @@
+/*
+ * @Date: 2023-06-18 16:51:52
+ * @LastEditors: zhubj
+ * @LastEditTime: 2023-06-18 16:53:05
+ * @Description: 头部注释
+ * @FilePath: \own-vue3-vuecli-template\src\utils\auth.js
+ */
+import Cookies from 'js-cookie'
+
+const TokenKey = 'Admin-Token'
+
+export function getToken() {
+  return Cookies.get(TokenKey)
+}
+
+export function setToken(token) {
+  return Cookies.set(TokenKey, token)
+}
+
+export function removeToken() {
+  return Cookies.remove(TokenKey)
+}

+ 36 - 0
src/utils/baiduMap.js

@@ -0,0 +1,36 @@
+/*
+ * @Date: 2023-06-18 11:21:18
+ * @LastEditors: zhubj
+ * @LastEditTime: 2023-06-18 15:15:01
+ * @Description: 头部注释
+ * @FilePath: \own-vue3-vuecli-template\src\utils\baiduMap.js
+ */
+// baiduMap.js
+
+const baiduMapKey = process.env.VUE_APP_BAIDU_MAP_KEY;
+
+export function getBMap () {
+  if (!baiduMapKey) {
+    return;
+  }
+  const BMap_URL = `https://api.map.baidu.com/api?v=3.0&ak=${baiduMapKey}&s=1&callback=onBMapCallback`;
+  return new Promise((resolve, reject) => {
+    // 如果已加载直接返回
+    if (typeof BMap !== 'undefined') {
+      resolve(BMap)
+      return true;
+    }
+
+    // 百度地图异步加载回调处理
+    window.onBMapCallback = () => {
+      console.log('百度地图脚本初始化成功...');
+      resolve(BMap)
+    }
+
+    // 插入script脚本
+    const scriptElement = document.createElement('script');
+    scriptElement.type = 'text/javascript'
+    scriptElement.src = BMap_URL
+    document.head.appendChild(scriptElement);
+  })
+}

+ 13 - 0
src/utils/errorCode.js

@@ -0,0 +1,13 @@
+/*
+ * @Date: 2023-06-18 17:27:57
+ * @LastEditors: zhubj
+ * @LastEditTime: 2023-06-18 17:28:11
+ * @Description: 头部注释
+ * @FilePath: \own-vue3-vuecli-template\src\utils\errorCode.js
+ */
+export default {
+  '401': '认证失败,无法访问系统资源',
+  '403': '当前操作没有权限',
+  '404': '访问资源不存在',
+  'default': '系统未知错误,请反馈给管理员'
+}

+ 629 - 0
src/utils/index.js

@@ -0,0 +1,629 @@
+// import http from '@/api/http.js'
+// import { downloadXlsx } from "../utils/xlsx";
+import { saveAs } from 'file-saver'
+import {apiGetExportMsg, apiGetModel,} from '../api/api'
+// import XLSXD from 'xlsx-style'
+// import Papa from 'papaparse'
+// import { isObject } from "xe-utils"
+
+//检查空
+const checkNull = val => val === undefined || val === null
+
+const until = {
+    // 计算比率
+    ratioCalculation(a, b) {
+        let num = null
+        if (a !== '-' && b !== '-') {
+            if (a === '0.00' || a === 0) {
+                num = 0
+            } else if (b === '0.00' || b === 0) {
+                num = '-'
+            } else {
+                num = Math.round((Number(a)/Number(b)*100))+'%'
+            }
+        } else {
+            num = '-'
+        }
+        return num
+    },
+    // 导出所有
+    downloadPer(url,fileName, idss, idsmo) {
+        let params = null
+        if (!idsmo) {
+            params = {
+                ids: idss ? idss.join(',') : ''
+            }
+        } else {
+            params = {
+                idsMv: idss ? idss.join(',') : '',
+                idsBcr: idsmo ? idsmo.join(',') : '',
+            }
+        }
+        apiGetExportMsg(url,params).then(datas =>{
+            let blob = new Blob([datas])
+            saveAs(blob, fileName)
+        }).catch((r) => {
+            console.error(r)
+        })
+    },
+    // 下载模板
+    downloadTemplate(url, params, proName) {
+        apiGetModel(url,params).then(datas =>{
+            let blob = new Blob([datas])
+            saveAs(blob, proName)
+        }).catch((r) => {
+            console.error(r)
+        })
+    },
+    getTime(date){
+        var y = date.getFullYear();  
+        var m = date.getMonth() + 1;  
+        m = m < 10 ? ('0' + m) : m;  
+        var d = date.getDate();  
+        d = d < 10 ? ('0' + d) : d;  
+        var h = date.getHours();  
+        h=h < 10 ? ('0' + h) : h;  
+        var minute = date.getMinutes();  
+        minute = minute < 10 ? ('0' + minute) : minute;  
+        var second=date.getSeconds();  
+        second=second < 10 ? ('0' + second) : second;  
+        return y + '-' + m + '-' + d+' '+h+':'+minute+':'+second; 
+        // timeF = y + '-' + m + '-' + d
+        // return timeF
+    },
+    changePowerPickDate(val) {
+        let endss = val.substring(val.length-2, val.length) * 1
+        let startTime = val.substring(0, val.length-2)
+        let allTime = ''
+        if (0 <= endss &&  endss < 15) {
+            allTime = startTime + '00'
+        } else if (15 <= endss &&  endss  < 30) {
+            allTime = startTime + '15'
+        } else if (30 <= endss &&  endss  < 45) {
+            allTime = startTime + '30'
+        } else if (45 <= endss &&  endss  < 60) {
+            allTime = startTime + '45'
+        }
+        return allTime
+    },
+    changeElectricPickDate(val) {
+        let endss = val.substring(val.length-2, val.length) * 1
+        let startTime = val.substring(0, val.length-2)
+        let allTime = ''
+        if (0 <= endss &&  endss < 10) {
+            allTime = startTime + '00'
+        } else if (10 <= endss &&  endss  < 20) {
+            allTime = startTime + '10'
+        } else if (20 <= endss &&  endss  < 30) {
+            allTime = startTime + '20'
+        } else if (30 <= endss &&  endss  < 40) {
+            allTime = startTime + '30'
+        } else if (40 <= endss &&  endss  < 50) {
+            allTime = startTime + '40'
+        } else if (50 <= endss &&  endss  < 60) {
+            allTime = startTime + '50'
+        }
+        return allTime
+    },
+    oninput(str, limit, type, zero) {
+        if (zero) {
+            if (str.substr(0, 1) === '.' || str.substr(0, 1) === '-') {
+                str = ''
+            }
+        } else {
+            if (str.substr(0, 1) === '.' || str.substr(0, 1) === '-' || str.substr(0, 1) === '0') {
+                str = ''
+            }
+        }
+        if (type === 'float') {
+            str = str.replace(/[^\d^\.]+/g, '') // 保留数字和小数点
+            if (limit === 1) {
+                str = str.replace(/^\D*([0-9]\d*\.?\d{0,1})?.*$/, '$1') // 小数点后只能输 1 位
+            } else if (limit === 2) {
+                str = str.replace(/^\D*([0-9]\d*\.?\d{0,2})?.*$/, '$1') // 小数点后只能输 2 位
+            } else if (limit === 3) {
+                str = str.replace(/^\D*([0-9]\d*\.?\d{0,3})?.*$/, '$1') // 小数点后只能输 3 位
+            } else if (limit === 4) {
+                str = str.replace(/^\D*([0-9]\d*\.?\d{0,4})?.*$/, '$1') // 小数点后只能输 4 位
+            } else if (limit === 6) {
+                str = str.replace(/^\D*([0-9]\d*\.?\d{0,6})?.*$/, '$1') // 小数点后只能输 6 位
+            }
+        } else {
+            str = str.replace(/[^\d^]+/g, '')
+            str = str.substring(0, limit)
+        }
+        return str
+    },
+    sortBy(attr,rev){
+        //第二个参数没有传递 默认升序排列
+        if(rev ==  undefined){
+            rev = 1;
+        }else{
+            rev = (rev) ? 1 : -1;
+        }
+        return function(a,b){
+            a = a[attr];
+            b = b[attr];
+            if(a < b){
+                return rev * -1;
+            }
+            if(a > b){
+                return rev * 1;
+            }
+            return 0;
+        }
+    },
+    /**
+ * 字母大小写切换
+ * @param str 要处理的字符串
+ * @param type 1:首字母大写其余小写 2:首子母小写其余大写 3:大小写转换 4:全部大写 5:全部小写
+ */
+    strChangeCase(str, type) {
+        function ToggleCase(str) {
+            var itemText = ""
+            str.split("").forEach(
+                function (item) {
+                    if (/^([a-z]+)/.test(item)) {
+                        itemText += item.toUpperCase();
+                    } else if (/^([A-Z]+)/.test(item)) {
+                        itemText += item.toLowerCase();
+                    } else {
+                        itemText += item;
+                    }
+                });
+            return itemText;
+        }
+
+        switch (type) {
+            case 1:
+                return str.replace(/^(\w)(\w+)/, function (v, v1, v2) {
+                    return v1.toUpperCase() + v2.toLowerCase();
+                });
+            case 2:
+                return str.replace(/^(\w)(\w+)/, function (v, v1, v2) {
+                    return v1.toLowerCase() + v2.toUpperCase();
+                });
+            case 3:
+                return ToggleCase(str);
+            case 4:
+                return str.toUpperCase();
+            case 5:
+                return str.toLowerCase();
+            default:
+                return str;
+        }
+    },
+    /*
+ *数字每千位加逗号
+ * 
+ */
+    commafy(num) {
+        return num && num.toString()
+        .replace(/\d+/, function(s){
+            return s.replace(/(\d)(?=(\d{3})+$)/g, '$1,')
+        })
+    },
+
+/*
+ *手机号码中间4位隐藏花号(*)显示
+ *
+ */
+    hideMobile(mobile) {
+        return mobile && mobile.toString().replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
+    },
+    /*
+ * 验证是否为数字
+ */
+    isNumber(n) {
+        return !isNaN(parseFloat(n)) && isFinite(n);
+    },
+
+    /*
+    * 是否为数组
+    */
+    isArray(obj) {
+        return Object.prototype.toString.call(obj) === '[object Array]';
+    },
+
+    /*
+    * 递归深拷贝
+    */
+    deepCopy(obj) {
+        let result = Array.isArray(obj) ? [] : {};
+        for (let key in obj) {
+            if (obj.hasOwnProperty(key)) {
+            if (typeof obj[key] === 'object' && obj[key] !== null) {
+                result[key] = deepCopy(obj[key]);
+            } else {
+                result[key] = obj[key];
+            }
+            }
+        }
+        return result;
+    },
+    exportTable(name, columnData, tableDatas) {
+        let datalist = [];
+        //表头数据
+        let cluData = ''
+        for(let i=0; i<columnData.length;i++){
+            cluData += columnData[i].label+','
+        }
+        cluData.slice(0, cluData.length-1)
+        datalist.push(cluData.split(','));
+        //这里的tableData为你的表格数据
+        for(let j=0;j<tableDatas.length;j++) {
+            let item = tableDatas[j]
+            let tabelDatas = ''
+            for(let k=0; k<columnData.length;k++){
+                let its = columnData[k]
+                if (item[its.value] !==null) {
+                    tabelDatas += item[its.value] + ','
+                } else {
+                    tabelDatas +=  '-,'
+                }
+            }
+            datalist.push(tabelDatas.split(','));
+        }
+        downloadXlsx(datalist, `${name}.xlsx`);
+        // let blob = new Blob([datalist])
+        // saveAs(blob, `${name}.xlsx`)
+        // this.exportCsv(datalist)
+    },
+
+    exportExcel() {
+        // let time = this.$utils.getTime(new Date())
+        // let titleName = this.title + ' ' + time
+        // this.$utils.exportTable(titleName,this.columData, this.tableData)
+
+        let wb = XLSX.utils.book_new();
+        let headers = {
+            name: '姓名',
+            age: '年龄',
+            sex: '性别'
+        }
+        this.columData.unshift(headers)
+        let contentWs = XLSX.utils.json_to_sheet(this.columData, {
+            header: ['name', 'age', 'sex'], // 可自定义表头顺序
+            skipHeader: true, //是否忽略表头,默认为false
+            origin: 'A2' // 设置插入位置
+        })
+        contentWs['!merges'] = [{s:{r:0,c:0}, e:{r:1,c:0}}, {s:{r:0,c:1}, e:{r:1,c:1}}]
+        // contentWs['!merges'] = [{s:{r:1,c:2}, e:{r:2,c:2}}]
+        // contentWs['A1'] = {
+        //     t: 's',
+        //     v: '人员',
+        //     s: {
+        //         font:{
+        //             name: '微软雅黑',
+        //             sz: 16,
+        //             bold: true,
+        //             color: {rgb: 'ffffff'}
+        //         },
+        //         alignment: {
+        //             horizontal: 'center',
+        //             vertical: 'center'
+        //         },
+        //         fill:{bgcolor: {rgb: '4472c4'}}
+        //     }
+        // }
+        // 设置合并单元格 !merges为一个对象数组,每个对象设定了单元格合并的规则,
+        // {s:{r:0,c:0}, e:{r:0,c:2}}为一个规则,s:起始位置,e:结束位置,r:行,c:列
+        // contentWs['!merges'] = [{s:{r:0,c:0}, e:{r:0,c:2}}]
+
+        
+        // contentWs['A3'] = {
+        //     t: 's',
+        //     v: '人员ss',
+        //     s: {
+        //         font:{
+        //             name: '微软雅黑',
+        //             sz: 16,
+        //             bold: true,
+        //             color: {rgb: 'ffffff'}
+        //         },
+        //         alignment: {
+        //             horizontal: 'center',
+        //             vertical: 'center'
+        //         },
+        //         fill:{bgcolor: {rgb: '4472c4'}}
+        //     }
+        // }
+        // contentWs['!merges'] = [{s:{r:0,c:2}, e:{r:0,c:4}}]
+        //设置列宽
+        contentWs['!cols'] = [{wch: 30},{wch: 20},{wch: 40}]
+        XLSX.utils.book_append_sheet(wb, contentWs, '明细')
+        // XLSX.writeFile(wb, '明细.xlsx')
+        const tmpDown = new Blob([
+            this.s2ab(
+                XLSXD.write(wb, {
+                    bookType: 'xlsx',
+                    bookSST: true,
+                    type: 'binary',
+                    cellStyles: true
+                })
+            )
+        ])
+        this.downExcel(tmpDown, '明细.xlsx')
+    },
+    downExcel(obj, fileName) {
+        const a_node = document.createElement('a')
+        a_node.download = fileName
+        if ('msSaveOrOpenBlob'in navigator) {
+            window.navigator.msSaveOrOpenBlob(obj, fileName)
+        } else {
+            a_node.href = URL.createObjectURL(obj)
+        }
+        a_node.click()
+        setTimeout(() =>{
+            URL.createObjectURL(obj)
+        }, 2000)
+    },
+    s2ab(s) {
+        if (typeof ArrayBuffer !== 'undefined') {
+            const buf = new ArrayBuffer(s.length)
+            const view = new Uint8Array(buf)
+            for(let i =0; i != s.length; ++i) {
+                view[i] = s.charCodeAt(i) & 0xff
+            }
+            return buf
+        } else {
+            const buf = new Array(s.length)
+            for(let i =0; i != s.length; ++i) {
+                buf[i] = s.charCodeAt(i) & 0xff
+            }
+            return buf
+        }
+    },
+    setTooltip(myChart1, myChart2, num) {
+        // const myChart1 = this.$echarts.init(document.getElementById(name1))
+        // const myChart2 = this.$echarts.init(document.getElementById(name2))
+        myChart1.getZr().on('mousemove', (params) => {
+            const pointInPixel = [params.offsetX, params.offsetY];
+            // 判断当前鼠标移动的位置是否在图表中
+            if (myChart1.containPixel('grid', pointInPixel)) {
+                //使用 convertFromPixel方法 转换像素坐标值到逻辑坐标系上的点。获取点击位置对应的x轴数据的索引值
+                const pointInGrid = myChart1.convertFromPixel({ seriesIndex: 0 }, pointInPixel);
+                // x轴数据的索引值
+                const xIndex = pointInGrid[0];
+                // 使用getOption() 获取图表的option
+                const op = myChart1.getOption();
+                // 获取当前点击位置要的数据
+                const xDate = op.xAxis[0].data[xIndex];
+                // 这里不直接用params.dataIndex是因为可能两个图表X轴的月份数据点不一致
+                const dataIndex = op.xAxis[0].data.findIndex(x => x === xDate);
+                myChart2.dispatchAction({
+                    type: 'showTip',
+                    seriesIndex: num,
+                    // 我用的echarts版本是4.8.0,用name而不用dataIndex时,不知道为什么tooltip不显示,所以这里用dataIndex
+                    // name: params.name
+                    dataIndex: dataIndex,
+                    position: '15%'
+                });
+            } else { 
+                myChart2.dispatchAction({
+                    type: 'hideTip'
+                });
+            }
+        })
+        myChart2.getZr().on('mousemove', (params) => {
+            const pointInPixel = [params.offsetX, params.offsetY];
+            // 判断当前鼠标移动的位置是否在图表中
+            if (myChart2.containPixel('grid', pointInPixel)) {
+                //使用 convertFromPixel方法 转换像素坐标值到逻辑坐标系上的点。获取点击位置对应的x轴数据的索引值
+                const pointInGrid = myChart2.convertFromPixel({ seriesIndex: 0 }, pointInPixel);
+                // x轴数据的索引值
+                const xIndex = pointInGrid[0];
+                // 使用getOption() 获取图表的option
+                const op = myChart2.getOption();
+                // 获取当前点击位置要的数据
+                const xDate = op.xAxis[0].data[xIndex];
+                // 这里不直接用params.dataIndex是因为可能两个图表X轴的月份数据点不一致
+                const dataIndex = op.xAxis[0].data.findIndex(x => x === xDate);
+                myChart1.dispatchAction({
+                    type: 'showTip',
+                    seriesIndex: num,
+                    // 我用的echarts版本是4.8.0,用name而不用dataIndex时,不知道为什么tooltip不显示,所以这里用dataIndex
+                    // name: params.name
+                    dataIndex: dataIndex,
+                    position: '15%'
+                });
+            } else { 
+                myChart1.dispatchAction({
+                    type: 'hideTip'
+                });
+            }
+        })
+    },
+    // 数据是否为null,NaN,0的判断
+    isHasNum(data) {
+        let num = null
+        if (data && data !== 0) {
+            if (data !== 'NaN' && data !== null) {
+                num = Number(data).toFixed(2)
+            } else {
+                num = '-'
+            }
+        } else if(data === 0) {
+            num = 0
+        } else {
+            num = '-'
+        }
+        return num
+    },
+    //获取当前月天数
+    hasYearMonthDate(a, b) {
+        let d = new Date(a, b, 0)
+        let de = d.getDate()
+        return de
+    },
+    // ------------------------------------------------------------------------
+    // 冒泡排序
+    sortBubble(arr) {
+        for(let i=0; i<arr.length; i++){
+            for(let j=0; j<arr.length-i; j++){
+                if (arr[j] > arr[j+1]) {
+                    let temp = arr[j]
+                    arr[j] = arr[j+1]
+                    arr[j+1] = temp
+                }
+            }
+        }
+        return arr
+    },
+    //数组去重
+    // 方法一
+    //利用for 循环 搭配 indexOf 去重
+    unique(arr) {
+        let newArr = []
+        for(let i=0; i<arr.length; i++) {
+            if (newArr.indexOf(arr[i]) === -1) {
+                newArr.push(arr[i])
+            }
+        }
+        return arr
+    },
+    //方法二
+    //借助ES6提供的Set结构 new Set() 
+    // var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
+    // var arr2 = noRepeat(arr)
+    noRepeatT(arr){
+        var newArr = [...new Set(arr)]; //利用了Set结构不能接收重复数据的特点
+        return newArr
+    },
+    //方法三
+    //利用 filter() 去重
+    //eg: var arr = ['apple','apps','pear','apple','orange','apps']
+    useFilter(arr) {
+        return arr.filter(function(item,index){
+            return arr.indexOf(item) === index;  // 因为indexOf 只能查找到第一个  
+        })
+    },
+    //方法四
+    //将数组的每一个元素依次与其他元素做比较,发现重复元素,删除 
+    //eg: var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
+    noRepeatF(arr) {
+        for(var i = 0; i < arr.length-1; i++){
+            for(var j = i+1; j < arr.length; j++){
+                if(arr[i]===arr[j]){
+                    arr.splice(j,1);
+                    j--;
+                }
+            }
+        }
+        return arr;
+    },
+    //方法五
+    //借助新数组 通过 indexOf 方法判断当前元素在数组中的索引,如果与循环的下标相等则添加到新数组中
+    // var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22]
+    noRepeatFi(arr) {
+        var newArr = [];
+        for (var i = 0; i < arr.length; i++) {
+            if (arr.indexOf(arr[i]) == i) {
+            newArr.push(arr[i]);
+            }
+        }
+        return newArr;
+    },
+    //方法六
+    //利用双重for循环
+    // var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22]
+    noRepeatS(arr){
+    for (var i = 0; i < arr.length; i++) {
+        for (var j = 0; j < arr.length; j++) {
+            if (arr[i] == arr[j] && i != j) { //将后面重复的数删掉
+                arr.splice(j, 1);
+                }
+        }
+        }
+        return arr;
+    },
+    //方法七
+    //利用includes实现数组去重
+    // var arr = [1,9,8,8,7,2,5,3,3,3,2,3,1,4,5,444,55,22];
+    noRepeatSe(arr) {
+        let newArr = [];
+        for(i=0; i<arr.length; i++){
+            if(!newArr.includes(arr[i])){
+                newArr.push(arr[i])
+            }
+        }
+        return newArr
+    },
+    //把url中的参数解析为一个对象
+    // var url = 'http://www.demo.cn/index.html?key1=val1&key2=val2'
+    parseQueryString(argu) {
+        let str = argu.split('?')[1]
+        let result = {}
+        let temp = str.split('&')
+        for(let i=0; i<temp.length; i++) {
+            let temp2 = temp[i].split('=')
+            result[temp2[0]] = temp2[1]
+        }
+        return result
+    },
+    //统计字符串中出现最多的字母
+    // let str = 'jdjsajdjasdasdakss'
+    countStr(str) {
+        let json = {}
+        //循环完毕后会得到一个对象 如{a:0,b:1,c:2}
+        for(let i=0; i<str.length; i++) {
+            if (!json[str.charAt(i)]) {
+                json[str.charAt(i)] = 1
+            } else {
+                json[str.charAt(i)]++
+            }
+        }
+    },
+    //对象深拷贝
+    deepClone(obj) {
+        if (obj instanceof Object) {
+            let isArray = Array.isArray(obj)
+            let cloneObj = isArray ? [] : {}
+            for(let key in obj) {
+                cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
+            }
+            return cloneObj
+        } else {
+            throw new Error('obj不是一个对象')
+        }
+    },
+    // 防抖
+    debounce(fn, delay) {
+    
+        var delay = delay || 200;
+        var timer;
+        return function () {
+            var th = this;
+            var args = arguments;
+            if (timer) {
+                clearTimeout(timer);
+            }
+            timer = setTimeout(function () {
+                timer = null;
+                fn.apply(th, args);
+            }, delay);
+        };
+    },
+    // 节流
+    throttle(fn, interval) {
+        var last;
+        var timer;
+        var interval = interval || 200;
+        return function () {
+            var th = this;
+            var args = arguments;
+            var now = +new Date();
+            if (last && now - last < interval) {
+                clearTimeout(timer);
+                timer = setTimeout(function () {
+                    last = now;
+                    fn.apply(th, args);
+                }, interval);
+            } else {
+                last = now;
+                fn.apply(th, args);
+            }
+        }
+    }
+}
+export default until

+ 111 - 0
src/utils/request.js

@@ -0,0 +1,111 @@
+/*
+ * @Date: 2023-06-18 16:29:47
+ * @LastEditors: zhubj
+ * @LastEditTime: 2023-06-19 13:12:11
+ * @Description: 头部注释
+ * @FilePath: \own-vue3-vuecli-template\src\utils\request.js
+ */
+import axios from 'axios'
+import { getToken } from './auth'
+import errorCode from './errorCode'
+import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
+import store from '@/store';
+
+// 是否显示重新登录
+export let isRelogin = { show: false };
+
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
+// 创建axios实例
+const service = axios.create({
+    // axios中请求配置有baseURL选项,表示请求URL公共部分
+    baseURL: process.env.VUE_APP_GENERAT_URL,
+    // 超时
+    timeout: 180000,
+    headers: {
+        // 设置后端需要的传参类型
+        'Content-Type': 'application/json;charset=utf-8',
+        'X-Requested-With': 'XMLHttpRequest',
+    },
+})
+
+// request拦截器
+service.interceptors.request.use(config => {
+  // 是否需要设置 token (默认是存在的,只有传值 false 的时候是不需要token)
+  config.headers.token = getToken()
+  return config
+}, error => {
+    console.log(error)
+    Promise.reject(error)
+})
+
+// 响应拦截器
+service.interceptors.response.use(
+    res => {
+    // 未设置状态码则默认成功状态
+        const code = res.data.code || 200;
+    // 获取错误信息
+    const msg = errorCode[code] || res.data.msg || errorCode['default']
+    // 二进制数据则直接返回
+    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
+      return res.data
+    }
+    if (code === 401) {
+        ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录','系统提示', {
+            confirmButtonText: '重新登录',
+            cancelButtonText: '取消',
+            type: 'warning'
+        }).then(() => {
+            this.$route.replace({path: '/login'})
+        })
+    //   if (!isRelogin.show) {
+    //     isRelogin.show = true;
+    //     ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录','系统提示', {
+    //       confirmButtonText: '重新登录',
+    //       cancelButtonText: '取消',
+    //       type: 'warning'
+    //     }).then(() => {
+    //         isRelogin.show = false;
+    //       this.$route.push({path: '/login'})
+    //     }).catch(() => {
+    //       isRelogin.show = false
+    //     })
+    //   }
+    //   return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
+    } else if (code === 500) {
+      ElMessage({ message: msg, type: 'error' })
+      return Promise.reject(new Error(msg))
+    } else if (code !== 200) {
+      ElNotification({ type: 'error', title: msg})
+      return Promise.reject('error')
+    } else {
+      return res.data
+    }
+  },
+  error => {
+      // 响应失败要处理的地方
+    console.log('err' + error)
+        let { message } = error;
+        if (message.indexOf('401') != -1) {
+            ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
+                confirmButtonText: '重新登录',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                // this.$route.replace({ path: '/login' })
+                let str = `${window.location.origin}/#/login`
+                window.location = str
+            })
+        }
+        if (message == "Network Error") {
+            message = "后端接口连接异常";
+        } else if (message.includes("timeout")) {
+            message = "系统接口请求超时";
+        } else if (message.includes("Request failed with status code")) {
+            message = "系统接口" + message.substr(message.length - 3) + "异常";
+        }
+        ElMessage({ message: message, type: 'error', duration: 3 * 1000 })
+        return Promise.reject(error)
+  }
+)
+
+export default service

+ 126 - 0
src/utils/ws.js

@@ -0,0 +1,126 @@
+import { ElNotification } from "element-plus";
+
+let socket = null; // 实例对象
+let lockReconnect = false; // 是否真正建立连接
+let timeout = 5 * 1000; // 5秒一次心跳|
+let timeoutObj = null; // 心跳倒计时
+let serverTimeoutObj = null; // 服务心跳倒计时
+let timeoutnum = null; // 断开 重连倒计时
+let reLinkNum = 0;
+let reLinkMaxNum = 3;
+
+const initWebSocket = async () => {
+    if ("WebSocket" in window) {
+        if (reLinkNum < reLinkMaxNum) {
+            const wsUrl = `${process.env.VUE_APP_GENERAT_URL}/ws/powerfitting/12345`;
+            socket = new WebSocket(wsUrl);
+            socket.onerror = webSocketOnError;
+            socket.onmessage = webSocketOnMessage;
+            socket.onclose = closeWebsocket;
+            socket.onopen = openWebsocket;
+        }
+    } else {
+        ElNotification.error({
+            title: "错误",
+            message: "您的浏览器不支持websocket,请更换Chrome或者Firefox",
+        });
+    }
+}
+
+//建立连接
+const openWebsocket = (e) => {
+    start();
+}
+
+const start = () => {
+    // 开启心跳
+    timeoutObj && clearTimeout(timeoutObj);
+    serverTimeoutObj && clearTimeout(serverTimeoutObj);
+    timeoutObj = setTimeout(() => {
+        // 这里发送一个心跳,后端收到后,返回一个心跳消息
+        if (socket.readyState === WebSocket.OPEN) {
+            if (reLinkNum > 0) {
+                reLinkNum = 0;
+            }
+            // 如果连接正常,发送心跳
+            socket.send(JSON.stringify({ type: 'heartbeat' }));
+        } else {
+            // 否则重连
+            reconnect();
+        }
+        serverTimeoutObj = setTimeout(() => {
+            // 超时关闭
+            socket.close();
+        }, timeout);
+    }, timeout);
+}
+
+//重新连接
+const reconnect = () => {
+    if (lockReconnect) {
+        return;
+    }
+    close();
+    lockReconnect = true;
+    //没连接上会一直重连,设置延迟避免请求过多
+    timeoutnum && clearTimeout(timeoutnum);
+    if (reLinkNum < reLinkMaxNum) {
+        reLinkNum++;
+        timeoutnum = setTimeout(() => {
+            console.log(`websocket断线重连第${reLinkNum}次`)
+            //新连接
+            initWebSocket();
+            lockReconnect = false;
+        }, 1000);
+    } else {
+        console.log(`websocket超过最大重连限制,已手动关闭`);
+
+    }
+}
+
+//重置心跳
+const reset = () => {
+    // 清除时间
+    clearTimeout(timeoutObj);
+    clearTimeout(serverTimeoutObj);
+    // 重启心跳
+    start();
+}
+
+const sendWebsocket = (e) => {
+    // socket.send(`我发消息了`);
+}
+
+const webSocketOnError = (e) => {
+    initWebSocket();
+    reconnect();
+}
+
+//服务器返回的数据
+const webSocketOnMessage = (e) => {
+    const data = JSON.parse(JSON.parse(e.data));
+    reset();
+    // console.log(11111, data);
+    // 处理其他类型的消息
+    window.dispatchEvent(
+        new CustomEvent("onmessageWS", {
+            detail: {
+                data,
+            },
+        })
+    );
+}
+
+const closeWebsocket = (e) => {
+    // reconnect();
+    close();
+}
+
+//断开连接
+const close = () => {
+    //WebSocket对象也有发送和关闭的两个方法,只需要在自定义方法中分别调用send()和close()即可实现。
+    socket && socket?.close && socket?.close();
+    socket = null;
+}
+//具体问题具体分析,把需要用到的方法暴露出去
+export default { initWebSocket, sendWebsocket, webSocketOnMessage, close };

+ 3 - 12
src/views/IntegratedAlarm/safe/customWarning/index.vue

@@ -191,11 +191,7 @@
             :width="item.width || ''"
             show-overflow-tooltip
             header-align="center"
-            :align="
-              item.code == 'description' || item.code == 'faultCause'
-                ? 'left'
-                : 'center'
-            "
+            align="center"
           >
             <template #default="scope">
               <p :style="item.style && item.style(scope.row)">
@@ -462,7 +458,7 @@ const state = reactive({
     // },
   ],
   c: "windturbine",
-  stationId: "GJNY_SXGS_ZZ_FDC_STA",
+  stationId: "NX_FGS_HA_FDC_STA",
   alarmId: "",
   typeVal: "windturbine",
   windturbineList: [],
@@ -479,7 +475,7 @@ const state = reactive({
   isshowwindturbineName: true,
   tableHeader: [
     { title: "时间", code: "ts", width: "150" },
-    { title: "场站", code: "stationname", width: "150" },
+    { title: "场站", code: "wpName", width: "150" },
     { title: "机组", code: "code", width: "150" },
     { title: "部件", code: "components", width: "200" },
     { title: "预警信息", code: "characteristic" },
@@ -630,14 +626,9 @@ const getAlarmHistoryt = async () => {
     ele.isCloseName = ele.endts ? "已解除" : "未解除";
     ele.deviceTypeName = tableFilter(ele.deviceType);
     ele.endtsName = ele.endts > 0 ? formatTime(ele.endts) : "--";
-    ele.devicename = changName(ele)
   });
   state.tableData = data?.ls;
 };
-const changName = (ele) => {
-  let str = ele.stationname.substring(0, ele.stationname.indexOf('风电场'))
-  return ele.devicename.substring(str.length, ele.devicename.length)
-}
 //报警类型变化
 const typechange = () => {
   state.alarmId = "";

+ 3 - 12
src/views/IntegratedAlarm/safe/historyWarning/index.vue

@@ -186,11 +186,7 @@
             :key="item.code"
             :width="item.width || ''"
             show-overflow-tooltip
-            :align="
-              item.code == 'description' || item.code == 'faultCause'
-                ? 'left'
-                : 'center'
-            "
+            align="center"
           >
             <template #default="scope">
               <p :style="item.style && item.style(scope.row)">
@@ -483,7 +479,7 @@ const state = reactive({
     // },
   ],
   typeVal: "windturbine",
-  stationId: "GJNY_SXGS_ZZ_FDC_STA",
+  stationId: "NX_FGS_HA_FDC_STA",
   stationName: "",
   alarmId: "",
   windturbineList: [],
@@ -501,7 +497,7 @@ const state = reactive({
   ts: "",
   tableHeader: [
     { title: "时间", code: "ts", width: "150" },
-    { title: "场站", code: "stationname", width: "120" },
+    { title: "场站", code: "wpName", width: "120" },
     { title: "机组", code: "code", width: "100" },
     { title: "报警信息", code: "description" },
     // { title: "故障原因", code: "faultCause" },
@@ -674,14 +670,9 @@ const getAlarmHistoryt = async () => {
         ? "光伏"
         : "";
     ele.endtsName = ele.endts > 0 ? formatTime(ele.endts) : "--";
-    ele.devicename = changName(ele)
   });
   state.tableData = data?.ls;
 };
-const changName = (ele) => {
-  let str = ele.stationname.substring(0, ele.stationname.indexOf('风电场'))
-  return ele.devicename.substring(str.length, ele.devicename.length)
-}
 //报警类型变化
 const typechange = () => {
   state.alarmId = "";