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 @@
>