import PerfectScrollbar from "perfect-scrollbar"; import {getChart} from "./common"; import MessageBox from "element-ui/packages/message-box/src/main"; import vuex from 'vuex' import i18n from "../i18n"; /*弹窗点击外部后关闭*/ const exceptClassName = ["config-dropdown", "nz-pop", "el-picker", "chart-box-dropdown", 'metric-dropdown', 'el-cascader__dropdown', "asset-dropdown", "no-style-class", 'el-message-box','nz-dashboard-dropdown', "el-autocomplete-suggestion",'nz-temp-box']; //clickoutside排除的class(白名单) no-style-class:没有任何样式的class export const clickoutside = { // 初始化指令 bind(el, binding, vnode) { if(!binding.expression) return; let unsavedChange = localStorage.getItem('nz-unnsaved-change'); let oldValue; try{ oldValue= JSON.parse(JSON.stringify(binding.value.obj)); }catch (e) { } function documentHandler(e) { if (el.contains(e.target)) { return false; } else { let flag = true; let path = e.path || (e.composedPath && e.composedPath()); 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 && path[i].className.indexOf(exceptClassName[j]) != -1) { flag = false; break top; } } } if (!flag) { return false; } if(oldValue){ let newValue = JSON.parse(JSON.stringify(binding.value.obj)); if(unsavedChange=='on'&&!isEqual(oldValue,newValue)){ MessageBox.confirm(i18n.t("tip.confirmCancel"), { confirmButtonText: i18n.t("tip.yes"), cancelButtonText: i18n.t("tip.no"), 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('mousedown', documentHandler); }, unbind(el, binding) { // 解除事件监听 document.removeEventListener('mousedown', el.__vueClickOutside__); delete el.__vueClickOutside__; }, }; function isEqual (o1, o2) { var isEqualForInner = function (obj1, obj2) { var o1 = obj1 instanceof Object; var o2 = obj2 instanceof Object; if (!o1 || !o2) { return obj1 === obj2; } if (Object.keys(obj1).length !== Object.keys(obj2).length) { return false; } for (var attr of Object.keys(obj1)) { var t1 = obj1[attr] instanceof Object; var 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); } export const cancelWithChange={ bind:function(el,binding){ if(!binding.value||!binding.value.obj) return; let unsavedChange = localStorage.getItem('nz-unnsaved-change'); let oldValue = JSON.parse(JSON.stringify(binding.value.obj)); function domClick(e){ let newValue = JSON.parse(JSON.stringify(binding.value.obj)); if(unsavedChange=='on'&&!isEqual(oldValue,newValue)){ MessageBox.confirm(i18n.t("tip.confirmCancel"), { confirmButtonText: i18n.t("tip.yes"), cancelButtonText: i18n.t("tip.no"), type: 'warning' }).then(() => { if(binding.value.func){ binding.value.func(); } }) }else{ binding.value.func(); } } el.__vueDomClick__ = domClick el.addEventListener('click',domClick) }, unbind:function(el,binding){ // 解除事件监听 document.removeEventListener('click', el.__vueDomClick__); delete el.__vueDomClick__; } } // 底部上滑框窗口控制 export const bottomBoxWindow = { // 鼠标拖动二级列表 listResize(vm, e) { window.resizing = true; let mainListDom = document.querySelector(".main-list"); //主列表 let subBoxDom = document.querySelector(".sub-box"); //副列表 let subListDom = document.querySelector(".sub-list"); //副列表 let contentRightDom = document.querySelector(".content-right"); //右侧内容区 let resizeBarHeight = 9; //resize横条高度 let minHeight = 15; //主、副列表最小高度限制为15 let contentHideHeight = 100; //主、副列表高度低于100时隐藏内容 let mainModalDom = document.querySelector(".main-modal"); //主列表遮罩 let resizeModalDom = document.querySelector(".resize-modal"); //副列表遮罩 let resizeBarDom = document.querySelector(".sub-list-resize"); //拖动条 let contentRightHeight = contentRightDom.offsetHeight;//可视高度 //点击时俩dom的初始高度: let subInitialHeight = subListDom.offsetHeight+resizeBarHeight; mainModalDom.style.display = "block"; resizeModalDom.style.cssText = `height: ${subInitialHeight}px; display: block;`; resizeBarDom.style.display = "none"; let resizeModalEndHeight; //点击时鼠标的Y轴位置 let mouseInitialY = e.clientY; document.onmousemove = (e) => { window.resizing = true; e.preventDefault(); //得到鼠标拖动的距离 let mouseMoveY = e.clientY-mouseInitialY; resizeModalEndHeight = subInitialHeight-mouseMoveY; // 主、副列表高度限制 if(resizeModalEndHeight > contentRightHeight-minHeight){ resizeModalEndHeight = contentRightHeight-minHeight; } if(resizeModalEndHeight < minHeight){ resizeModalEndHeight = minHeight; } resizeModalDom.style.height = `${resizeModalEndHeight}px`; }; document.onmouseup = () => { window.resizing = false; mainListDom.style.height = `${contentRightHeight-resizeModalEndHeight}px`; subBoxDom.style.height = `${resizeModalEndHeight}px`; subListDom.style.height = `${resizeModalEndHeight-resizeBarHeight}px`; resizeModalDom.style.display = "none"; mainModalDom.style.display = "none"; resizeBarDom.style.display = ""; //当主副列表可视区域小于一定值时,不展示内容 if(contentRightHeight-resizeModalEndHeight <= contentHideHeight){ if (vm.bottomBox.mainResizeShow) { vm.bottomBox.mainResizeShow = false; } } else { if (!vm.bottomBox.mainResizeShow) { vm.bottomBox.mainResizeShow = true; } } if(resizeModalEndHeight < contentHideHeight){ if (vm.bottomBox.subResizeShow) { vm.bottomBox.subResizeShow = false; } } else { if (!vm.bottomBox.subResizeShow) { vm.bottomBox.subResizeShow = true; } } document.onmousemove = null; document.onmouseup = null; } }, exitFullScreen(vm) { window.resizing = true; let contentRightDom = document.querySelector(".content-right"); //右侧内容区 let contentRightHeight = contentRightDom.offsetHeight;//可视高度 //主列表 document.querySelector(".main-list-with-sub").style.height = vm.bottomBox.mainListHeight ? vm.bottomBox.mainListHeight + 'px' : 'calc(50% - 4px)'; //副列表 document.querySelector(".sub-list").style.height = vm.bottomBox.mainListHeight ? contentRightHeight-vm.bottomBox.mainListHeight-9 + 'px' : 'calc(50% - 4px)'; setTimeout(() => { if (document.querySelector(".main-list-with-sub").offsetHeight >= 100) { vm.bottomBox.mainResizeShow = true; } if (document.querySelector(".sub-list").offsetHeight >= 100) { vm.bottomBox.subResizeShow = true; } vm.bottomBox.isFullScreen = false; window.resizing = false; }, 210); }, fullScreen(vm) { window.resizing = true; let contentRightDom = document.querySelector(".content-right"); //右侧内容区 let contentRightHeight = contentRightDom.offsetHeight;//可视高度 vm.bottomBox.isFullScreen = true; //主列表 vm.bottomBox.mainListHeight = document.querySelector(".main-list-with-sub").offsetHeight; //记录全屏前主列表的高度 document.querySelector(".main-list-with-sub").style.height = '0'; vm.bottomBox.mainResizeShow = false; //副列表 document.querySelector(".sub-list").style.height = contentRightHeight + 'px'; window.resizing = false; }, showSubListWatch(vm, n) { vm.bottomBox.inTransform = n; if (!n) { vm.mainTableHeight = vm.$tableHeight.normal; //重置table的高度 vm.tools.toTopBtnTop = vm.$tableHeight.toTopBtnTop; vm.bottomBox.isFullScreen = false; //移动分页组件的位置 let paginationTop = document.querySelector(".pagination-top"); let paginationBottom = document.querySelector(".pagination-bottom"); paginationTop.classList.remove("display-none"); if (paginationTop.classList.contains("pagination-top-show")) { paginationTop.classList.remove("pagination-top-show"); } if (!paginationTop.classList.contains("pagination-top-hide")) { paginationTop.classList.add("pagination-top-hide"); } setTimeout(() => { paginationTop.classList.add("display-none"); paginationBottom.appendChild(paginationTop.removeChild(document.querySelector(".pagination"))); }, 210); // 主列表恢复全屏 vm.bottomBox.mainResizeShow = vm.bottomBox.subResizeShow = true; document.querySelector('.main-list').style.height = ""; //副列表高度清空 document.querySelector(".sub-list").style.height = ""; } else { vm.mainTableHeight = vm.$tableHeight.openSubList.mainList; //重置table高度 vm.tools.toTopBtnTop = vm.$tableHeight.openSubList.toTopBtnTop; //移动分页组件的位置 let paginationTop = document.querySelector(".pagination-top"); paginationTop.appendChild(document.querySelector(".pagination-bottom").removeChild(document.querySelector(".pagination"))); paginationTop.classList.remove("display-none"); setTimeout(() => { if (paginationTop.classList.contains("pagination-top-hide")) { paginationTop.classList.remove("pagination-top-hide"); } if (!paginationTop.classList.contains("pagination-top-show")) { paginationTop.classList.add("pagination-top-show"); } }, 210); } } }; export function stringTimeParseToUnix(stringTime){ let time=new Date(stringTime).getTime(); return time/1000; } export function calcDurationByStringTime(startTime, endTime) { let durationSecond = stringTimeParseToUnix(endTime)-stringTimeParseToUnix(startTime); let result = `${durationSecond%60}s`; if (durationSecond > 60) { result = `${(Math.floor(durationSecond/60))%60}m ${result}`; } if (durationSecond > 60*60) { result = `${Math.floor(durationSecond/(60*60))}h ${result}`; } return result; } export function calcDurationByStringTimeB(startTime, endTime) { let durationSecond = stringTimeParseToUnix(endTime)-stringTimeParseToUnix(startTime); let result = `${durationSecond%60}s`; if(durationSecond >= 60*60 *24){ result = `${(Math.floor(durationSecond/3600))%24}h` result = `${Math.floor(durationSecond/(60*60*24))}d ${result}`; } else if (durationSecond >= 60*60) { result = `${(Math.floor(durationSecond/60))%60}m` result = `${Math.floor(durationSecond/(60*60))}h ${result}`; }else if(durationSecond >= 60) { result = `${(Math.floor(durationSecond/60))%60}m ${result}`; } return result; } export function unixTimeParseToString(unixTime,fmt='yyyy-MM-dd hh:mm:ss'){ let date=new Date(unixTime * 1000); var o = { "M+" : date.getMonth()+1, //月份 "d+" : date.getDate(), //日 "h+" : date.getHours(), //小时 "m+" : date.getMinutes(), //分 "s+" : date.getSeconds(), //秒 "q+" : Math.floor((date.getMonth()+3)/3), //季度 "S" : date.getMilliseconds() //毫秒 }; if(/(y+)/.test(fmt)) fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); for(var k in o) if(new RegExp("("+ k +")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); return fmt; } //chart-resize工具 export const chartResizeTool = { minHeight: 200, //图表最小高度 chartPaddingTop: 2, //图表padding-top chartBlankHeight:4, //图表空白占位高度(padding-top + border) chartTableBlankHeight: 6, //表格型图表额外空白占位高度 chartBlankWidth: 2, //图表空白占位宽度 containerBlankWidth: 5, //容器空白占位宽度(#listContainer的padding) titleHeight: 28, //标题dom高度 stepHeight: 50, //单元高度 timeouter:null, stepWidth(containerWidth) { //单元宽度,参数为容器总宽度 return (containerWidth-this.containerBlankWidth)/12; }, calculateHeight(original) { let height = Math.floor(original/this.stepHeight)*this.stepHeight; if (height < this.minHeight) { height = this.minHeight; } return height-this.chartBlankHeight; }, start(vm, originalData, event, chartIndexs) { let $self=this; let data = JSON.parse(JSON.stringify(originalData)); //将初始对象复制,后续操作使用复制对象 let shadow = vm.$refs.resizeShadow; //缩放时底部阴影dom if (!shadow) { shadow = vm.$refs[0].resizeShadow; } let box = vm.$refs.resizeBox; //图表内容dom if (!box) { box = vm.$refs[0].resizeBox; } let chartPaddingTop = this.chartPaddingTop; let chartBlankWidth = this.chartBlankWidth; let chartBlankHeight = this.chartBlankHeight; let titleHeight = this.titleHeight; let stepWidth = this.stepWidth(document.getElementById('listContainer').offsetWidth); let stepHeight = this.stepHeight; let mouseOriginalX = event.clientX; //鼠标初始坐标 let mouseOriginalY = event.clientY; let originalWidth = stepWidth*data.span; //图表、阴影初始宽高 let shadowNewWidth = originalWidth; let originalHeight = data.height; let shadowNewHeight = originalHeight; //1.激活背景阴影 shadow.classList.add("resize-shadow-active"); //2.鼠标移动时调整resize-box的宽高,同时监听宽高的变化,变化量每达到step的50%时改变resize-shadow的尺寸并重绘resize-box内容 document.addEventListener("mousemove", moveListener); //3.鼠标松开,将resize-box宽高改为resize-shadow宽高,结束 document.addEventListener("mouseup", mouseupListener); function moveListener(e) { let mouseX = e.clientX; let mouseY = e.clientY; //调整resize-box的宽高 box.style.width = `${originalWidth+(mouseX-mouseOriginalX)-chartBlankWidth}px`; box.style.height = `${originalHeight+(mouseY-mouseOriginalY)-chartBlankHeight}px`; //监听宽高的变化,变化量每达到step的50%时改变resize-shadow的尺寸并重绘resize-box内容 let remainderWidth = (box.offsetWidth+chartBlankWidth-shadowNewWidth)%stepWidth; //宽的余数 let remainderHeight = (box.offsetHeight+chartBlankHeight-shadowNewHeight)%stepHeight; //高的余数 boxStepHandler(remainderWidth, remainderHeight); } function mouseupListener(e) { //将resize-box的宽高设为resize-shadow的宽高 box.style.width = `${shadow.offsetWidth}px`; box.style.height = `${Math.round((shadow.offsetHeight )/chartPaddingTop)*chartPaddingTop}px`; data.height = Math.round((box.offsetHeight+chartPaddingTop)/stepHeight)*stepHeight; data.span = Math.round((box.offsetWidth+chartBlankWidth)/stepWidth); //请求后台,保存变更 if (data.height != originalData.height || data.span != originalData.span) { originalData.height = data.height; originalData.span = data.span; vm.$put("panel/" + vm.panelIdInner + "/charts/modify", originalData); } //关闭背景阴影 shadow.classList.remove("resize-shadow-active"); document.removeEventListener("mousemove", moveListener); document.removeEventListener("mouseup", mouseupListener); } function boxStepHandler(width, height, chartIndex) { //param: 宽高变化量,大于0放大,小于0缩小 let widthChange = false; let heightChange = false; if (width > stepWidth/2) { //放大shadow宽 widthChange = true; //判断是否因为百分数计算的宽度有小数的原因导致宽度超过当前行,使图表错误换行 let currentWidth = shadow.offsetWidth+stepWidth; let currentSpan = Math.round((currentWidth+chartBlankWidth)/stepWidth); let calcWidth = currentSpan*stepWidth-chartBlankWidth; //不会换行的宽度 shadow.style.width = `${calcWidth}px`; } else if (width <= 0-(stepWidth/2)) { //缩小shadow宽 widthChange = true; let currentWidth = shadow.offsetWidth-stepWidth; let currentSpan = Math.round(currentWidth/stepWidth); let calcWidth = currentSpan*stepWidth-chartBlankWidth; //不会换行的宽度 shadow.style.width = `${calcWidth}px`; } if (widthChange) { shadowNewWidth = shadow.offsetWidth; } if (height > stepHeight/2) { //放大shadow高 heightChange = true; shadow.style.height = `${Math.round(shadow.offsetHeight/chartPaddingTop)*chartPaddingTop+stepHeight}px`; } else if (height <= 0-(stepHeight/2)) { //缩小shadow高 heightChange = true; shadow.style.height = `${Math.round(shadow.offsetHeight/chartPaddingTop)*chartPaddingTop-stepHeight}px`; } if (heightChange) { shadowNewHeight = shadow.offsetHeight+chartBlankHeight; } if (widthChange || heightChange) { clearTimeout($self.timeouter) $self.timeouter=setTimeout(()=>{ //step时chart重绘 if (box.classList.contains("resize-box-echarts")) { //echarts类型 getChart(chartIndexs).resize({width: shadow.offsetWidth-16, height: shadow.offsetHeight-titleHeight-vm.$refs.legendArea.offsetHeight}); } },500) //chart外层宽高调整 let outerBox = document.getElementById(`chart-${data.id}`); outerBox.style.height = `${shadow.offsetHeight+chartPaddingTop}px`; outerBox.style.width = `${parseFloat(shadow.style.width.split("px")[0])+chartBlankWidth}px`; } } } }; //table 方法 export const tableSet = { //是否需要排序 sortableShow(prop,from){ switch(prop){ case 'state': if( from === 'operationlog' ||from === 'alertSilence'){ return false } case 'id': case 'startAt': if( from === 'alertSilence' ){ return false } case 'state': case 'id': case 'alertRule': case 'severity': case 'startAt': 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: break; } }, // 本地正序 asce (prop) { return function (obj1, obj2) { let val1 = obj1[prop]; let val2 = obj2[prop]; if (!isNaN(val1) && !isNaN(val2) && prop==='value') { val1 = Number(val1); val2 = Number(val2); } if(prop==='time'){ val1 = tableSet.strTodate(val1); val2 = tableSet.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,""); } } if (val1 < val2) { return -1; } else if (val1 > val2) { return 1; } else { return 0; } } }, //本地倒序 desc (prop) { return function (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 = tableSet.strTodate(val1); val2 = tableSet.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,""); } } if (val1 < val2) { return 1; } else if (val1 > val2) { return -1; } else { return 0; } } }, // 转化时间字符串为时间戳 strTodate(str){ let date = str.trim(); date = date.substring(0,19); date = date.replace(/-/g,'/'); //必须把日期'-'转为'/' return new Date(date).getTime(); } } export function getMetricTypeValue(queryItem,type){ let copy=JSON.parse(JSON.stringify(queryItem)); switch (type) { case 'min': let min =copy.sort((x,y)=>{return parseFloat(x[1]) - parseFloat(y[1])})[0][1]; return min; case 'max': let max =copy.sort((x,y)=>{return parseFloat(y[1]) - parseFloat(x[1])})[0][1]; return max; case 'avg': copy = copy.map(t=>parseFloat(t[1])); let sum = eval(copy.join('+')); let avg = sum / copy.length; return avg; case 'last': let last =copy.sort((x,y)=>{return parseFloat(y[0]) - parseFloat(x[0])})[0][1]; return last; case 'first': let first =copy.sort((x,y)=>{return parseFloat(y[0]) - parseFloat(x[0])})[copy.length-1][1]; return first; case 'total': copy = copy.map(t=>parseFloat(t[1])); let total = eval(copy.join('+')); return total; } } export function blankPromise() { return new Promise(resolve => {resolve();}); } export function clickLegend(echart,legendName,index){ if(echart){ } } export function showTableTooltip(content, show = true, e) { //console.info(content, e) if (show) { let dom = document.querySelector(".table-tooltip"); dom.innerHTML = content; dom.setAttribute("style", `visibility: visible; top: ${e.clientY-e.offsetY+e.target.offsetHeight*0.5-dom.offsetHeight*0.5}px; left: ${e.clientX-e.offsetX+e.target.offsetWidth}px`); } } export function hideTableTooltip() { let dom = document.querySelector(".table-tooltip"); dom.setAttribute("style", "visibility: hidden;"); dom.innerHTML = ""; } /*function getTdDom(dom) { let tagName = dom.tagName; if (tagName.toLowerCase() === 'td') { return dom; } else { return getTdDom(dom.parentElement); } }*/