import { ElMessageBox, ElMessage } from 'element-plus' import i18n from '@/i18n' import _ from 'lodash' import { storageKey, iso36112, topDomain } from '@/utils/constants' import { getIso36112JsonData } from '@/utils/api' import { format } from 'echarts' import router from '@/router' export const tableSort = { // 是否需要排序 sortableShow (prop, from) { switch (prop) { case 'state': { if (from === 'operationlog' || from === 'alertSilence') { return false } break } case 'startAt': { if (from === 'alertSilence') { return false } break } case 'id': case 'alertRule': case 'severity': case 'endAt': case 'ID': case 'HOST': case 'SN': case 'assetType': case 'purchaseDate': case 'pingStatus': case 'dataCenter': case 'cabinet': case 'model': case 'principal': case 'asset': case 'port': case 'project': case 'module': case 'type': case 'name': case 'area': case 'vendor': case 'filename': case 'updateAt': case 'username': case 'ip': case 'operation': case 'createDate': case 'time': case 'host': case 'protocol': case 'user': case 'cmd': case 'alertName': case 'threshold': case 'idc': case 'alertNum': case 'gname': return 'custom' default : return false } }, // prop字段 propTitle (prop, from) { switch (from) { case 'asset': switch (prop) { case 'ID': return 'ass.id' case 'HOST': return 'ass.host' case 'SN': return 'ass.sn' case 'assetType': return 'sdtt.value' case 'purchaseDate': return 'ass.purchase_date' case 'state': return 'ass.state' case 'pingStatus': return 'assp.rtt' case 'dataCenter': return 'idc.name' case 'cabinet': return 'cab.name' case 'model': return 'mo.name' case 'vendor': return 'sdt.value' case 'principal': return 'su.username' default : return prop } case 'alertMessage': switch (prop) { case 'id': return 'am.id' case 'state': return 'am.state' case 'alertRule': return 'ar.alert_name' case 'severity': return 'am.severity' case 'startAt': return 'am.start_at' case 'endAt': return 'am.end_at' default : return prop } case 'project': switch (prop) { case 'id': return 'e.id' case 'asset': return 'a.host' case 'port': return 'e.port' case 'project': return 'p.name' case 'module': return 'm.name' case 'type': return 'm.type' case 'state' :return 'es.state' // case 'path': return'e.path'; default : return prop } case 'dc': switch (prop) { case 'id': return 'i.id' case 'name': return 'i.name' case 'area': return 'sa.name' default : return prop } case 'endpointTab': switch (prop) { case 'id': return 'e.id' case 'asset': return 'a.host' case 'port': return 'e.port' case 'project': return 'p.name' case 'module': return 'm.name' case 'type': return 'm.type' case 'state' :return 'es.state' // case 'path': return'e.path'; default : return prop } case 'model': switch (prop) { case 'id': return 'mo.id' case 'name': return 'mo.name' case 'type': return 'dictt.value' case 'vendor': return 'dict.value' default : return prop } case 'promServer': switch (prop) { case 'id': return 'id' case 'idc': return 'idc_id' case 'host': return 'host' case 'port': return 'port' case 'type': return 'type' default : return prop } case 'mib': switch (prop) { case 'id': return 'sm.id' case 'name': return 'sm.name' case 'filename': return 'sm.file_name' case 'updateAt': return 'sm.update_at' default : return prop } case 'operationlog': switch (prop) { case 'id': return 'sl.id' case 'username': return 'su.username' case 'ip': return 'sl.ip' case 'operation': return 'sl.operation' case 'type': return 'sl.type' case 'createDate': return 'sl.create_date' case 'time': return 'sl.time' default : return prop } case 'temrminallog': switch (prop) { case 'protocol': return 'protocol' case 'startTime': return 'startTime' default : return prop } case 'alertRules': switch (prop) { case 'id': return 'ar.id' case 'alertName': return 'ar.alert_name' case 'threshold': return 'ar.threshold' case 'severity': return 'ar.severity' default : return prop } case 'exprTemp': switch (prop) { case 'id': return 'id' case 'name': return 'name' case 'gname': return 'gname' default : return prop } default: return prop } }, // 本地正序 asce (prop) { return function (obj1, obj2) { const { val1, val2 } = this.format(prop, obj1, obj2) if (val1 < val2) { return -1 } else if (val1 > val2) { return 1 } else { return 0 } } }, // 本地倒序 desc (prop) { return function (obj1, obj2) { const { val1, val2 } = this.format(prop, obj1, obj2) if (val1 < val2) { return 1 } else if (val1 > val2) { return -1 } else { return 0 } } }, format (prop, obj1, obj2) { let val1 = obj1[prop] let val2 = obj2[prop] if (!isNaN(Number(val1)) && !isNaN(Number(val2)) && prop !== 'time') { val1 = Number(val1) val2 = Number(val2) } if (prop === 'time') { val1 = tableSort.strTodate(val1) val2 = tableSort.strTodate(val2) } if (prop === 'element') { if (val1.alias) { val1 = JSON.stringify(obj1[prop].alias).replace(/\s*/g, '') } else { val1 = JSON.stringify(obj1[prop].element).replace(/\s*/g, '') } if (val2.alias) { val2 = JSON.stringify(obj2[prop].alias).replace(/\s*/g, '') } else { val2 = JSON.stringify(obj2[prop].element).replace(/\s*/g, '') } } return { val1, val2 } }, // 转化时间字符串为时间戳 strToDate (str) { let date = str.trim() date = date.substring(0, 19) date = date.replace(/-/g, '/') // 必须把日期'-'转为'/' return new Date(date).getTime() } } /* cancel提醒保存指令 */ export const cancelWithChange = { mounted (el, binding) { if (!binding.value || !binding.value.object) return const oldValue = JSON.parse(JSON.stringify(binding.value.object)) function domClick (e) { const newValue = JSON.parse(JSON.stringify(binding.value.object)) if (!isEqual(oldValue, newValue)) { ElMessageBox.confirm(i18n.global.t('tip.confirmCancel'), { confirmButtonText: i18n.global.t('tip.yes'), cancelButtonText: i18n.global.t('tip.no'), type: 'warning' }).then(() => { if (binding.value.func) { binding.value.func() } }) } else { binding.value.func() } } el.__vueDomClick__ = domClick el.addEventListener('click', domClick) }, unmounted (el, binding) { // 解除事件监听 document.removeEventListener('click', el.__vueDomClick__) delete el.__vueDomClick__ } } /* clickOutside指令 */ const exceptClassName = ['prevent-click-outside'] // clickOutside排除的class(白名单) export const clickOutside = { // 初始化指令 beforeMount (el, binding) { /* let oldValue try { oldValue = JSON.parse(JSON.stringify(binding.value.object)) } catch (e) { } */ function documentHandler (e) { if (el.contains(e.target)) { return false } else { let flag = true const path = e.path || (e.composedPath && e.composedPath()) // eslint-disable-next-line no-labels top: for (let i = 0; i < path.length; i++) { for (let j = 0; j < exceptClassName.length; j++) { if (path[i].className && path[i].className.indexOf(exceptClassName[j]) !== -1) { flag = false // eslint-disable-next-line no-labels break top } } } if (!flag) { return false } binding.value() /* if (oldValue) { const newValue = JSON.parse(JSON.stringify(binding.value.oldValue)) if (!isEqual(oldValue, newValue)) { ElMessageBox.confirm('Confirm?', { // TODO 国际化 confirmButtonText: 'Yes', // TODO 国际化 cancelButtonText: 'No', // TODO 国际化 type: 'warning' }).then(() => { if (binding.value.func) { binding.value.func() } }) } else { binding.value.func() } } else { if (binding.arg) { binding.value(e, binding.arg) } else { if (binding.value) { binding.value(e) } } } */ } } // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听 el.__vueClickOutside__ = documentHandler document.addEventListener('click', documentHandler) }, unmounted (el, binding) { // 解除事件监听 document.removeEventListener('click', el.__vueClickOutside__) delete el.__vueClickOutside__ } } function noDataDomFactory () { const noDataDom = document.createElement('div') noDataDom.setAttribute('class', 'no-data') noDataDom.innerText = 'No data' return noDataDom } export const noData = { updated (el, binding) { if (el) { if (binding.value) { setTimeout(() => { // 是否已有no data let alreadyHasNoData = false el.childNodes.forEach(node => { if (node.classList && node.classList.value.indexOf('no-data') > -1) { alreadyHasNoData = true } else { node.style && (node.style.display = 'none') } }) if (!alreadyHasNoData) { el.insertBefore(noDataDomFactory(), el.childNodes[0]) } }) } else { setTimeout(() => { for (let i = 0; i < el.childNodes.length; i++) { const node = el.childNodes[i] if (node.classList && node.classList.value.indexOf('no-data') > -1) { el.removeChild(node) break } } el.childNodes.forEach(node => { node.style && (node.style.display = '') }) }) } } } } export function isEqual (o1, o2) { const isEqualForInner = function (obj1, obj2) { const o1 = obj1 instanceof Object const o2 = obj2 instanceof Object if (!o1 || !o2) { return obj1 === obj2 } if (Object.keys(obj1).length !== Object.keys(obj2).length) { return false } for (const attr of Object.keys(obj1)) { const t1 = obj1[attr] instanceof Object const t2 = obj2[attr] instanceof Object if (t1 && t2) { if (!isEqualForInner(obj1[attr], obj2[attr])) { return false } } else if (obj1[attr] !== obj2[attr]) { return false } } return true } return isEqualForInner(o1, o2) } /* 计算文本的实际width,而不是length */ export function getTextRect (text, fontSize = 14) { return format.getTextRect(text, `${fontSize}`) } /* url占位符处理 */ export function replaceUrlPlaceholder (url, params) { _.forIn(params, (value, key) => { url = url.replace('{{' + key + '}}', value) }) url = url.replace(/{{(.*?)}}/g, '') return url } // 双引号替换为单引号 export function doubleQuotationToSingle (content) { return content.replace(/\"/g, "'") } // 双引号加斜杠转义 export function doubleQuotationEscape (content) { return content.replace(/\"/g, '\\\"') } // 下划线转换驼峰 export function lineToHump (name) { return name.replace(/\_(\w)/g, function (all, letter) { return letter.toUpperCase() }) } // 驼峰转空格,首字母小写 export function humpToSpace (name) { const str = name.replace(/([A-Z])/g, ' $1') return str.split(' ').map(s => _.lowerFirst(s)).join(' ') } // 下划线转换空格 export function lineToSpace (name) { if (_.isEmpty(name)) { return '' } return name.replace(/\_(\w)/g, ' ') } // 驼峰转换下划线 export function humpToLine (name) { if (_.isEmpty(name)) { return '' } return name.replace(/([A-Z])/g, '_$1').toLowerCase() } // 排序功能:从大到小,降序排列 export function reverseSortBy (i) { return function (a, b) { return b[i] - a[i] } } // 排序功能:从小到大,升序排列 export function sortBy (i) { return function (a, b) { return a[i] - b[i] } } // echart图标,y轴鼠标悬浮时,显示标签所有内容 export function extensionEchartY (chart) { // 判断是否创建过div框,如果创建过就不再创建了 // 该div用来盛放文本显示内容的,方便对其悬浮位置进行处理 const id = document.getElementById('extension') if (!id) { const div = "
" const contentDiv = document.createElement('div') contentDiv.setAttribute('id', 'extension') contentDiv.setAttribute('style', 'display:block') document.documentElement.append(contentDiv) } chart.on('mouseover', function (params) { // 注意这里,我是以Y轴显示内容过长为例,如果是x轴的话,需要改为xAxis if (params.componentType === 'yAxis') { // 设置悬浮文本的位置以及样式 const extEle = document.getElementById('extension') extEle.style.cssText = 'display:inline;position:absolute;' + ' padding: 12px;' + ' max-width: 400px !important;' + ' color: #666;' + ' background-color: rgb(255, 255, 255);' + ' font-size: 14px;' + ' line-height: 20px;' + ' font-weight:400; ' + ' font-family: "Microsoft YaHei"' + ' border-style: solid;' + ' border-width: 1px;' + ' border-radius: 4px;' + ' border-color: transparent !important;' + ' box-shadow: rgb(0 0 0 / 30%) 0px 0px 3px;' + ' white-space: nowrap;' + ' z-index: 99999999;' extEle.innerHTML = params.value document.documentElement.onmousemove = function (event) { const extEle = document.getElementById('extension') const xx = event.pageX - extEle.offsetWidth - 20 const yy = event.pageY + 20 extEle.style.cssText = extEle.style.cssText + 'top:' + yy + 'px;left:' + xx + 'px;' } } }) chart.on('mouseout', function (params) { // 注意这里,我是以Y轴显示内容过长为例,如果是x轴的话,需要改为xAxis if (params.componentType == 'yAxis') { const extEle = document.getElementById('extension') extEle.style.cssText = 'display:none;' } }) } // 搜索功能:对象转字符串 export function objToStr (obj) { return Object.keys(obj).map(k => { return `${k}='${obj[k]}'` }).join(' AND ') } // 搜索功能:字符串转对象 export function strToObj (n) { const paramsArr = n.split(/\sAND\s/) const paramsObj = {} paramsArr.forEach(string => { const param = string.split('=') if (param.length > 1) { let value = param[1].trim() const valueArr = value.split(/[\"\']/g) if (valueArr.length > 2) { value = valueArr[1].trim() } paramsObj[param[0].trim()] = value } }) return paramsObj } // 加载geo数据 export function loadGeoData (key) { const keys = [] if (key) { keys.push(key) } else { keys.push(storageKey.iso36112Capital) keys.push(storageKey.iso36112WorldLow) } keys.forEach(async k => { if (!localStorage.getItem(k)) { const data = await getIso36112JsonData(iso36112[k]) if (data) { localStorage.setItem(k, JSON.stringify(data)) } } }) return localStorage.getItem(key) } /* 返回geodata对象 */ export function getGeoData (key) { const data = localStorage.getItem(key) if (data) { return JSONParse(data) } else { return JSONParse(loadGeoData(key)) } } export function getCapitalGeo (countryId) { const data = getGeoData(storageKey.iso36112Capital) return data[countryId] } function JSONParse (data) { const firstParse = JSON.parse(data) if (typeof firstParse === 'string') { return JSON.parse(firstParse) } else { return firstParse } } export function copyValue (item) { const str = item const domUrl = document.createElement('input') domUrl.value = JSON.stringify(str) domUrl.id = 'creatDom' document.body.appendChild(domUrl) domUrl.select() // 选择对象 document.execCommand('Copy') // 执行浏览器复制命令 const creatDom = document.getElementById('creatDom') creatDom.parentNode.removeChild(creatDom) ElMessage.success(i18n.global.t('tip.copySuccess')) } export function computeSecondaryDomain (name) { // 命中的顶级域名 let hitTopDomain = '' // 同顶级域名比对 const hits = [] topDomain.forEach(td => { const hitIndex = name.lastIndexOf(td) if (hitIndex > -1 && hitIndex + td.length === name.length) { hits.push(td) } }) if (hits.length > 0) { hits.sort((a, b) => { return b.split('.').length - a.split('.').length }) hitTopDomain = hits[0] } else { const arr = name.split('.') hitTopDomain = arr[arr.length - 1] } const index = name.lastIndexOf(hitTopDomain) const preArr = name.substring(0, index).split('.') return [preArr[preArr.length - 2], hitTopDomain].join('.') } export function getCurrentRoute () { return router.currentRoute && router.currentRoute.path } // 判断数据相等 export function arrayIsEqual (arr1, arr2) { if (arr1 === arr2) { // 如果2个数组对应的指针相同,那么肯定相等,同时也对比一下类型 return true } else { if (arr1.length !== arr2.length) { return false } else { for (const i in arr1) { // 循环遍历对比每个位置的元素 if (arr1[i] !== arr2[i]) { // 只要出现一次不相等,那么2个数组就不相等 return false } } // for循环完成,没有出现不相等的情况,那么2个数组相等 return true } } } // 字体长度缓存记录,{ 'fontSize': { 'limitWidth': { 'text': xxx } } } const fontCache = {} // 处理文本超长 export function truncateText (text, limitWidth, fontSize = 12, ellipsis = '...') { if (!text || !limitWidth) { return null } // hit cache const cache = fontCache[`${fontSize}`] && fontCache[`${fontSize}`][`${limitWidth}`] if (cache) { const hit = Object.keys(cache).find(k => k === text) if (hit) { return cache[hit] } } // 计算 const dom = document.createElement('span') dom.classList.add('temp-dom') dom.style.fontSize = `${fontSize}px` dom.innerText = text return dom.offsetWidth } export function scrollToTop (dom, toTop, duration, direction) { const clientHeight = dom.clientHeight const currentTop = dom.scrollTop const totalScrollDistance = Math.abs(currentTop - toTop) let scrollY = currentTop let oldTimestamp = null function step (newTimestamp) { if (oldTimestamp !== null) { if (direction === 'up') { scrollY -= totalScrollDistance * (newTimestamp - oldTimestamp) / duration console.info(scrollY) if (scrollY < 0) { dom.scrollTop = 0 return } dom.scrollTop = scrollY } else if (direction === 'down') { scrollY += totalScrollDistance * (newTimestamp - oldTimestamp) / duration if (scrollY > clientHeight) { dom.scrollTop = clientHeight return } dom.scrollTop = scrollY } } oldTimestamp = newTimestamp window.requestAnimationFrame(step) } window.requestAnimationFrame(step) }