diff --git a/nezha-fronted/src/components/charts/d3-line-chart2.vue b/nezha-fronted/src/components/charts/d3-line-chart2.vue new file mode 100644 index 000000000..d70cb36de --- /dev/null +++ b/nezha-fronted/src/components/charts/d3-line-chart2.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/nezha-fronted/src/components/charts/d3-line.js b/nezha-fronted/src/components/charts/d3-line.js new file mode 100644 index 000000000..0456834a7 --- /dev/null +++ b/nezha-fronted/src/components/charts/d3-line.js @@ -0,0 +1,630 @@ +import * as d3 from "d3"; +import './d3-line.scss' +import {randomcolor} from "../common/js/radomcolor/randomcolor"; + +export function D3LineChart(selector,option){ + return { + selector:selector, //选择器 + width:option.width, + height:option.height, + timeFormat:option.timeFormat?option.timeFormat:'%Y-%m-%d', + _timeFormat:null, + datas:option.datas, + legends:option.legends, + title:option.title?option.title:'', + subTitle:option.subTitle?option.subTitle:'', + colors:option.colors, + padding:option.padding?option.padding:{top:40,left:40,bottom:40,right:20}, + duration:option.duration?option.duration:800, + _head_height:0, + _foot_height:0, + currentLineNum:0, + showXAxisTick:option.showXAxisTick?option.showXAxisTick:false, + showYAxisTick:option.showYAxisTick?option.showYAxisTick:false, + tooltipFormatter:option.tooltipFormatter, + + init:function(){ + //定义画布 + this.svg=d3.select(this.selector) + .append('svg') + .attr('width',this.width) + .attr('height',this.height) + .on('mousemove', drawTooltip) + .on('mouseout', removeTooltip); + + this.initOption(); + this.constomAction(); + + this.drawTitle(); + this.creatScale(); + this.createDefs(); + this.createXInnerBar(); + this.createYInnderBar(); + this.createXAxis(); + this.createYAxis(); + + // this.createLegends(); + this.drawLines(); + + this.createZoom() + + let $self=this; + let oldToolVal=null; + function removeTooltip() { + if ($self.tooltip) $self.tooltip.style('display', 'none'); + if ($self.tooltipLine) $self.tooltipLine.attr('stroke', 'none'); + } + function drawTipLine(time){ + if ($self.currentTransform) + $self.tooltipLine + .attr('stroke', 'black') + .attr("clip-path", "url(#clip)") + .attr('x1', $self.currentTransform.applyX($self.xScale(time))) + .attr('x2', $self.currentTransform.applyX($self.xScale(time))) + .attr('y1', $self._head_height) + .attr('y2', $self.height - $self._foot_height); + else + + $self.tooltipLine + .attr('stroke', 'black') + .attr("clip-path", "url(#clip)") + .attr('x1', $self.xScale(time)) + .attr('x2', $self.xScale(time)) + .attr('y1', $self._head_height) + .attr('y2', $self.height - $self._foot_height); + } + function drawTooltip(){ + var x=d3.mouse($self.svg.node())[0]; + if(x<$self.padding.left||x>$self.width-$self.padding.right){ + return false; + } + if ($self.currentTransform) + var time = $self.currentTransform.rescaleX($self.xScale).invert(d3.mouse($self.svg.node())[0]); + else + var time = $self.xScale.invert(d3.mouse($self.svg.node())[0]); + + // drawTipLine(time) + + $self.tooltip.html(d3.timeFormat('%Y-%m-%d %H:%M:%S')(time)) + .style('display', 'block') + .style('left', d3.event.pageX + 20 + 'px') + .style('top', d3.event.pageY - 20 + 'px') + .selectAll() + .data($self.datas).enter() + .append('div') + .html(function(d, i,g) { + let toolVal=d[0]; + let min=Math.abs(+toolVal[0] - +time ) + d.forEach(item=>{ + let temp=Math.abs(+item[0] - +time) + if(temp - min < 0){ + min=temp; + toolVal=item; + } + }) + if(toolVal){ + oldToolVal=toolVal; + let legend=$self.legends[i]; + if(!legend.isGray){ + return `
+
${legend.alias?legend.alias:legend.name}:
+
${toolVal[1]}
+
` + } + } + }); + } + }, + initOption:function(){ + this._timeFormat=d3.timeFormat(this.timeFormat) + this._head_height=this.padding.top + this._foot_height=this.padding.bottom + this.currentLineNum=this.datas.length; + this.tooltip = d3.select('body') + .append('div') + .attr('style',"position: absolute; background-color: rgba(221,228,237,1);border-color:rgba(221,228,237,1); padding: 5px; display: none; left: 983px; top: 89px;") + this.tooltipLine = this.svg.append('line'); + + this.minMax=getMinMax(this.datas); + + if(this.padding.left < computeDistance(this.minMax.max+'')){ + this.padding.left = computeDistance(this.minMax.max+'') + } + + if(!this.colors || this.colors.length{return d[1]})]) + min=d3.min([min,d3.min(arr[i],d=>{return d[1]})]) + } + return{min:min,max:max} +} +function computeDistance(str){ + var width = 0; + var html = document.createElement('span'); + html.innerText = str; + html.className = 'getTextWidth'; + document.querySelector('body').appendChild(html); + width = document.querySelector('.getTextWidth').offsetWidth; + document.querySelector('.getTextWidth').remove(); + return Number((width+5)); +} + +function CrystalLineObject(chart) { + this.group = null; + this.path = null; + this.oldData = []; + let dataset=chart.datas; + let svg=chart.svg; + let xScale=chart.xScale; + let yScale=chart.yScale; + let lineColor=chart.colors; + const dispatch=chart.dispatch; + + this.init = function(id) { + var arr = dataset[id]; + let legend=chart.legends[id]; + this.group = svg.append("g"); + let $self=this; + var line = d3.line() + .x(function(d, i) { + return xScale(d[0]); + }) + .y(function(d) { + return yScale(d[1]); + }) + .curve(d3.curveCatmullRom.alpha(0.3)); //折线曲度 + + //添加折线 + this.path = this.group.append("path") + .attr("d", line(arr)) + .attr('class','chart-line') + .style("fill", "none") + .style("stroke-width", 1) + .attr("clip-path", "url(#clip)") + .style("stroke", lineColor[id]) + .style("stroke-opacity", 0.9) + .on('line-single-show',function(d,i,group){ + let event=d3.event; + let name=event.detail?event.detail.name:"" + if(legend.name != name){ + $self.group + .transition() + .duration(chart.duration) + .style('opacity','0') + legend.isGray=true; + }else{ + $self.group.transition() + .duration(chart.duration).style('opacity','1') + legend.isGray=false; + } + }) + .on('line-all-show',function(){ + $self.group.transition() + .duration(chart.duration).style('opacity','1') + chart.legends.forEach(item=>{ + item.isGray=false; + }) + }) + + //添加系列的小圆点 + /* this.group.selectAll("circle") + .data(arr) + .enter() + .append("circle") + .attr("clip-path", "url(#clip)") + .attr("cx", function(d, i) { + return xScale(d[0]); + }) + .attr("cy", function(d) { + return yScale(d[1]); + }) + .attr("r", 5) + .attr("fill", lineColor[id]);*/ + this.oldData = arr; + }; + + this.scale = function(id, _duration, transform) { + var arr = dataset[id]; + + var line = d3.line() + .x(function(d, i) { + + return transform.applyX(xScale(d[0])) + }) + .y(function(d) { + return yScale(d[1]); + }) + + //添加折线 + this.group.select("path") + .attr("d", line(arr)) + .style("fill", "none") + .style("stroke-width", 1) + .style("stroke", lineColor[id]) + .style("stroke-opacity", 0.9); + + this.group.selectAll("circle") + .attr("cx", function(d, i) { + return transform.applyX(xScale(d[0])); + }) + .attr("cy", function(d) { + return yScale(d[1]); + }) + + } + + //动画初始化方法 + this.movieBegin = function(id) { + var arr = dataset[id]; + //补足/删除路径 + var olddata = this.oldData; + var line = d3.line() + .x(function(d, i) { + if (i >= olddata.length) return chart.width - chart.padding.left; + else return xScale(d[0]); + }) + .y(function(d, i) { + if (i >= olddata.length) return chart.height - chart._foot_height; + else return yScale(olddata[i].value); + }); + + //路径初始化 + this.path.attr("d", line(arr)); + + //截断旧数据 + var tempData = olddata.slice(0, arr.length); + /*var circle = this.group.selectAll("circle").data(tempData); + + //删除多余的圆点 + circle.exit().remove();*/ + + //圆点初始化,添加圆点,多出来的到右侧底部 + /*this.group.selectAll("circle") + .data(arr) + .enter() + .append("circle") + .attr("cx", function(d, i) { + if (i >= olddata.length) return chart.width - chart.padding; + else return xScale(d[0]); + }) + .attr("cy", function(d, i) { + if (i >= olddata.length) return chart.height - chart._foot_height; + else return yScale(d[1]); + }) + .attr("r", 5) + .attr("fill", lineColor[id]);*/ + + this.oldData = arr; + }; + + //重绘加动画效果 + this.reDraw = function(id, _duration) { + var arr = dataset[id]; + var line = d3.line() + .x(function(d, i) { + return xScale(d[0]); + }) + .y(function(d) { + return yScale(d[1]); + }); + + //路径动画 + this.path.transition().duration(_duration).attr("d", line(arr)); + + //圆点动画 + /* this.group.selectAll("circle") + .transition() + .duration(_duration) + .attr("cx", function(d, i) { + return xScale(d[0]); + }) + .attr("cy", function(d) { + return yScale(d[1]); + })*/ + }; + + //从画布删除折线 + this.remove = function() { + this.group.remove(); + }; + +} diff --git a/nezha-fronted/src/components/charts/d3-line.scss b/nezha-fronted/src/components/charts/d3-line.scss new file mode 100644 index 000000000..c2b5c5283 --- /dev/null +++ b/nezha-fronted/src/components/charts/d3-line.scss @@ -0,0 +1,72 @@ +.chart{ + position: relative; +} +.title { + font-family: Arial, 微软雅黑; + font-size: 18px; + text-anchor: middle; +} + +.subTitle { + font-family: Arial, 宋体; + font-size: 12px; + text-anchor: middle; + fill: #666 +} + +.axis path, +.axis line { + fill: none; + stroke: black; + shape-rendering: crispEdges; +} + +.axis text { + font-family: sans-serif; + font-size: 11px; + fill: #999; +} + +.inner_line path, +.inner_line line { + fill: none; + stroke: #ccc; + shape-rendering: crispEdges; + opacity: .5; +} + +.legend { + font-size: 12px; + font-family: Arial, Helvetica, sans-serif; + text-align:left; + max-height:80px; + min-height:25px; + left: 10px; + line-height: 18px; + position: absolute; + padding-bottom:3px; +} + +.legend-shape{ + display:inline-block; + margin-right:5px; + border-radius:10px; + width:15px; + height:5px; + vertical-align: middle; +} +.ft-gr{ + color:lightgray; +} + +.legend-item{ + text-overflow:ellipsis; + white-space:nowrap; + /*width:100%;*/ + margin-right:10px; + overflow-x:hidden; + cursor:pointer; + display:inline-block; + float:left; + line-height: 20px; +} diff --git a/nezha-fronted/src/components/common/header.vue b/nezha-fronted/src/components/common/header.vue index 59bc334bd..77684bcd5 100644 --- a/nezha-fronted/src/components/common/header.vue +++ b/nezha-fronted/src/components/common/header.vue @@ -9,7 +9,7 @@ > -
+
{{$store.state.consoleCount<=10?$store.state.consoleCount:'10+'}}
@@ -150,16 +150,12 @@ - - - - + + - - - + +
@@ -174,6 +170,13 @@ }, data() { return { + rightBox: { + project: {show: false}, + module: {show: false}, + endpoint: {show: false}, + asset: {show: false}, + alertRule: {show: false}, + }, username: sessionStorage.getItem("nz-username"), language: localStorage.getItem("nz-language") ? localStorage.getItem("nz-language") : 'en', assetData: [], @@ -181,10 +184,6 @@ activeItemIndex:'', activeItemIndexes: [], hoverItemIndex: '', - editPanel:{//新增or编辑的panel - id:'', - name: '' - }, projectData: [], //顶部菜单project列表中的数据 editProject: {id: '', name: '', remark: ''}, //新增/编辑的project currentProject: {id: '', name: '', remark: ''}, //module/endpoint弹框用来回显project @@ -257,15 +256,6 @@ } }, methods: { - closeAsset() { - this.addUnitShow = false; - this.assetBoxShow = false; - }, - refreshAsset(flag) { - if (flag && this.$route.path == "/asset") { - window.location.reload(); - } - }, cli(){ this.$store.commit('openConsole'); }, @@ -284,14 +274,11 @@ }, createBox(item) { - if (item.type == 0) { - this.$refs.panelBox.show(true); - this.editPanel = {id: '', name: ''}; - }else if (item.type == 1) { - this.$refs.projectBox.show(true,true); + if (item.type == 1) { + this.rightBox.project.show = true; this.editProject = {id: '', name: '', remark: ''}; } else if (item.type == 2) { - this.$refs.moduleBox.show(true,true); + this.rightBox.module.show = true; this.editModule = { id: '', name: '', @@ -437,8 +424,8 @@ }) }, toEditProject(p) { - this.$refs.projectBox.show(true,true); this.editProject = Object.assign({}, p); + this.rightBox.project.show = true; }, indOf(a, b) { let c = []; diff --git a/nezha-fronted/src/components/common/language/en.js b/nezha-fronted/src/components/common/language/en.js index 51a4e207c..13d038c06 100644 --- a/nezha-fronted/src/components/common/language/en.js +++ b/nezha-fronted/src/components/common/language/en.js @@ -418,6 +418,7 @@ const en = { vendor:'Vendor', ping:'Ping', }, + featureTitle:'Attribute', /*createAsset:{ title:'New asset',//'新增资产' sn:'SN',//SN diff --git a/nezha-fronted/src/components/common/rightBox/addEndpointBox.vue b/nezha-fronted/src/components/common/rightBox/addEndpointBox.vue index da8392f75..f08737075 100644 --- a/nezha-fronted/src/components/common/rightBox/addEndpointBox.vue +++ b/nezha-fronted/src/components/common/rightBox/addEndpointBox.vue @@ -1,229 +1,226 @@ diff --git a/nezha-fronted/src/components/common/rightBox/editEndpointBox.vue b/nezha-fronted/src/components/common/rightBox/editEndpointBox.vue index 3f593337c..c2d6f81f2 100644 --- a/nezha-fronted/src/components/common/rightBox/editEndpointBox.vue +++ b/nezha-fronted/src/components/common/rightBox/editEndpointBox.vue @@ -1,206 +1,119 @@ diff --git a/nezha-fronted/src/components/page/project/project.vue b/nezha-fronted/src/components/page/project/project.vue index 12c089bf2..8f1af08de 100644 --- a/nezha-fronted/src/components/page/project/project.vue +++ b/nezha-fronted/src/components/page/project/project.vue @@ -26,10 +26,10 @@