diff --git a/nezha-fronted/src/assets/stylus/main.scss b/nezha-fronted/src/assets/stylus/main.scss
index da620227e..3edfd880f 100644
--- a/nezha-fronted/src/assets/stylus/main.scss
+++ b/nezha-fronted/src/assets/stylus/main.scss
@@ -902,15 +902,19 @@ li{
}
25%{
opacity: 0;
- max-height: 125px;
+ max-height: 100px;
}
50%{
opacity: 0;
- max-height: 250px;
+ max-height: 200px;
}
- 75%{
- opacity: 0.4;
- max-height: 375px;
+ 66%{
+ opacity: 0.2;
+ max-height: 300px;
+ }
+ 83%{
+ opacity: 0.5;
+ max-height: 400px;
}
100% {
visibility: visible;
@@ -924,18 +928,21 @@ li{
max-height: 500px;
visibility: visible;
}
- 25%{
-
- opacity: 0.4;
- max-height: 375px;
+ 16% {
+ opacity: 0.5;
+ max-height: 400px;
+ }
+ 33%{
+ opacity: 0.2;
+ max-height: 300px;
}
50%{
opacity: 0;
- max-height: 250px;
+ max-height: 200px;
}
75%{
opacity: 0;
- max-height: 125px;
+ max-height: 100px;
}
100% {
visibility: hidden;
diff --git a/nezha-fronted/src/components/charts/chart-detail.vue b/nezha-fronted/src/components/charts/chart-detail.vue
new file mode 100644
index 000000000..73a19495d
--- /dev/null
+++ b/nezha-fronted/src/components/charts/chart-detail.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+ {{errorContent}}
+
+
+
+
+
+
{{data.title}}
+
+
+
+
+
+
+ {{item.title}}
+
+
+
+
+
+
+
+
+
+ {{value}}
+ {{value}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nezha-fronted/src/components/charts/chart-list.vue b/nezha-fronted/src/components/charts/chart-list.vue
index cbc6f2b6c..f233031f9 100644
--- a/nezha-fronted/src/components/charts/chart-list.vue
+++ b/nezha-fronted/src/components/charts/chart-list.vue
@@ -25,6 +25,7 @@
cursor: se-resize;
box-sizing: border-box;
user-select: none;
+ z-index: 2000;
}
.vue-resizable-handle:after {
border-right: 2px solid #555;
@@ -52,8 +53,8 @@
scroll:true,
filter: '.drag-disabled',
scrollFn:function(offsetX,offsetY,originalEvent,touchEvt,hoverTargetEI){},
- animation:150,
- handle:'.chartTitle'
+ animation: 150,
+ handle: '.chart-title'
}" >
+ @on-refresh-data="refreshChart"
+ @on-search-data="searchData"
+ @on-remove-chart-block="removeChart"
+ @on-duplicate-chart-block="duplicateChart"
+ @on-drag-chart="editChartForDrag"
+ @on-edit-chart-block="editData"
+ :panel-id="filter.panelId"
+ :chart-data="item"
+ :chart-index="index">
-
+ >-->
+
-
+ >-->
-
+ >-->
-
+ >-->
0 ) {
- this.dataList.forEach((item) => {
- if (this.$refs['editChart'+item.id] && this.$refs['editChart'+item.id].length > 0) {
- this.$refs['editChart'+item.id][0].clearData();
- }
- });
- }*/
this.dataList = [];
this.chartDataCacheGroup.clear();
},
@@ -549,7 +536,6 @@
},
// 获取panel详情数据,获取panel下所有chart列表
getData(params) {
- //param 目前没有用
const param = {
panelId: params.panelId,
query: params.query,
@@ -567,7 +553,10 @@
type: "alertRuleInfo",
prev: 0,
next: -9,
- buildIn: 1
+ buildIn: 1,
+ draggable: false,
+ resizable: false,
+ editable: false,
});
this.dataList.push({
id: -9,
@@ -610,7 +599,10 @@
buildIn: 1,
elements: [{
expression: `up{endpoint="${this.additionalInfo.id}"}`
- }]
+ }],
+ draggable: false,
+ resizable: false,
+ editable: false,
});
this.dataList.push({
id: -9,
@@ -621,7 +613,10 @@
type: "assetInfo",
prev: -10,
next: -8,
- buildIn: 1
+ buildIn: 1,
+ draggable: false,
+ resizable: false,
+ editable: false,
});
this.dataList.push({
id: -8,
@@ -643,6 +638,7 @@
});
return;
}
+
if (!param.query) delete param.query;
//根据panelId获得panel下的所有图表
this.$get('panel/'+ params.panelId+'/charts').then(response => {
@@ -675,6 +671,17 @@
if (this.dataList.length > 0 ) {
this.dataList.forEach((item,index) => {
this.setChartSize(item, index);//设置该图表宽度
+ if (param.from == "asset") {
+ if (item.type == "assetInfo") {
+ this.$set(item, "draggable", true);
+ this.$set(item, "resizable", true);
+ }
+ } else if (param.from == "project") {
+ if (item.type == "projectInfo") {
+ this.$set(item, "draggable", true);
+ this.$set(item, "resizable", true);
+ }
+ }
if(!item.isLoaded){
//获得当前显示在浏览器的图表,从后台获取数据
let chartBox = document.getElementById('chart-' + item.id);
@@ -914,7 +921,7 @@
dpsArr = Object.entries(queryItem.values);//[ ["0",[1577959830.781,"0"]], ["1",[1577959845.781,"0"]] ]
dpsArr = dpsArr.map(item => {
return [item[0], [item[1][0], Number(item[1][1])]]
- })
+ });
// 判断是否有数据, && tagsArr.length > 0
if (dpsArr.length > 0 && this.$refs['editChart' + chartItem.id] && this.$refs['editChart' + chartItem.id].length > 0) {
tagsArr.forEach((tag, i) => {
@@ -1173,18 +1180,6 @@
return [dpsItem[0] * 1000, dpsItem[1]];
});
series.push(seriesItem.theData);
-
- } else if (chartInfo.elements && chartInfo.elements[0]) {
- // 无数据提示
- /*
- const currentInfo = chartItem.elements[innerPos];
- const errorMsg = `图表 ${chartItem.title} 中 ${currentInfo.metric},${currentInfo.tags} 无数据`;
- this.$message.warning({
- duration: 15,
- content: errorMsg,
- closable: true,
- });
- */
}
}
if (this.$refs['editChart' + chartInfo.id] && this.$refs['editChart' + chartInfo.id].length > 0) {
@@ -1232,90 +1227,71 @@
}
},
getEndpointInfoChartData(chartInfo) {
+ let vm = this;
+ let detail = [];
+ chartInfo.title = this.$t("project.chart.endpointInfo");
+ detail.push({title: this.$t("project.chart.basicTitle"), data: function() {
+ let data = {};
+ vm.detail.forEach(item => {
+ data[item.label] = item.value;
+ });
+ data[vm.$t("alert.list.state")] = "";
+ return data;
+ }()});
let endpointId = this.additionalInfo.id;
let alertMsg = new Promise((resolve, reject) => {
- this.$get('/alert/message?endpointId='+endpointId).then(response=>{
- if(response.code == 200){
- resolve(response.data);
- } else {
- reject(false);
+ this.$get('/alert/message?endpointId=' + endpointId).then(response => {
+ if (response.code == 200) {
+ let alerts = {};
+ response.data && function () {
+ response.data.list.forEach(item => {
+ let alertName = item.alertRule.alertName;
+ let hasAlert = false;
+ for (let alert in alerts) {
+ if (alertName == alert) {
+ hasAlert = true;
+ }
+ }
+ if (hasAlert) {
+ alerts[alertName]++;
+ } else {
+ alerts[alertName] = 1;
+ }
+ });
+ }();
+ detail.push({title: this.$t("overall.alert"), data: alerts});
+ resolve(true);
}
});
});
- /*let endpointState = new Promise((resolve, reject) => {
- let query = `up{endpoint="${endpointId}"}`;
- this.$get('/prom/api/v1/query_range?query=' + query + "&start=" + this.$stringTimeParseToUnix(this.filter.start_time) + "&end=" + this.$stringTimeParseToUnix(this.filter.end_time) + '&step=' + bus.getStep(this.filter.start_time, this.filter.end_time)).then(response=>{
- if(response.status == "success"){
- resolve(response.data);
- } else {
- reject(false);
- }
- });
- });*/
- Promise.all([alertMsg]).then(result => {
- //endpointInfo的告警信息
- let alerts = [];
- let data = result[0];
- if (data.list) {
- data.list.forEach(item => {
- let index = -1;
- let hasLabel = alerts.some((alert, i) => {
- if (alert.label == item.alertRule.alertName) {
- index = i;
- }
- return alert.label == item.alertRule.alertName;
- });
- if (hasLabel) {
- alerts[index].value++;
- } else {
- alerts.push({label: item.alertRule.alertName, value: 1});
- }
- });
- }
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, this.detail, alerts);
- //endpointInfo的state
- /*data = result[1];
- let state = [];
- if (data.result && data.result[0] && data.result[0].values) {
- state = data.result[0].values;
- }
-
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, this.detail, alerts, state);*/
+ alertMsg.then(result => {
+ this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, detail);
}, err => {
});
- /*req.then(data => {
- let alerts = [];
- if (data && data.list) {
- data.list.forEach(item => {
- let index = -1;
- let hasLabel = alerts.some((alert, i) => {
- if (alert.label == item.alertRule.alertName) {
- index = i;
- }
- return alert.label == item.alertRule.alertName;
- });
- if (hasLabel) {
- alerts[index].value++;
- } else {
- alerts.push({label: item.alertRule.alertName, value: 1});
- }
- });
- }
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, this.detail, alerts);
- });*/
},
getAssetInfoChartData(chartInfo){
+ let vm = this;
+ chartInfo.title = this.$t("asset.createAssetTab.assetInfo");
+ let detail = [];
if(!this.isModel){
- this.$refs['editChart'+chartInfo.id][0].showLoad(chartInfo);
+ this.$refs['editChart'+chartInfo.id][0].showLoad();
let assetId = this.additionalInfo.assetId ? this.additionalInfo.assetId : this.additionalInfo.id;
this.$get('/asset/info?id=' + assetId).then(response=>{
if(response.code == 200){
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, response.data, this.filter.panelId, this.filter);
- }else{
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, {}, this.filter.panelId, this.filter, response.msg);
+ response.data && function() {
+ response.data.Basic && detail.push({
+ title: vm.$t('asset.createAssetTab.basicTitle'),
+ data: response.data.Basic
+ });
+ response.data.Feature && detail.push({
+ title: vm.$t('asset.createAssetTab.featureTitle'),
+ data: response.data.Feature
+ });
+ }();
}
- })
+ this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, detail, this.filter.panelId, this.filter);
+ });
}else {
let data={
Basic:{
@@ -1353,62 +1329,112 @@
}
},
getProjectInfoChartData(chartInfo){
+ let vm = this;
+ let detail = [];
if(!this.isModel){
this.$refs['editChart'+chartInfo.id][0].showLoad(chartInfo);
this.$get('/project/info?id='+this.additionalInfo.id).then(response=>{
if(response.code == 200){
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, response.data, this.filter.panelId, this.filter);
- }else{
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, {}, this.filter.panelId, this.filter, response.msg);
+ response.data && function() {
+ response.data.basic && detail.push({
+ title: vm.$t('project.chart.basicTitle'),
+ data: response.data.basic
+ });
+ response.data.module && function() {
+ response.data.module.forEach(d => {
+ detail.push({
+ title: `${vm.$t('project.module.module')}:${d.name}`,
+ data: d
+ });
+ });
+ }();
+ }();
}
+ this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, detail, this.filter.panelId, this.filter, response.msg);
})
}else {
- let data={
- basic: {
+ detail.push({
+ title: "system",
+ data: {
id: 1,
name: "system",
remark: "描述信息",
alertStat: [1,2,3],
- },
- module: [
- {
- id: 1,
- name: "kafka",
- type: "http",
- remark: "描述信息",
- endpointStat: [3,23],
- alertStat: [2,3,4],
- },
- {
- id: 2,
- name: "kafkakafkakafkakafkakafkakafkakafka",
- type: "http",
- remark: "描述信息",
- endpointStat: [3,23],
- alertStat: [2,0,4],
- },
- {
- id: 3,
- name: "kafkakafka",
- type: "snmp",
- remark: "描述信息",
- endpointStat: [3,0],
- alertStat: [2,3,4],
- },
- ]
- };
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, data, this.filter.panelId, this.filter);
+ }
+ });
+ detail.push({
+ title: `${this.$t("project.module.module")}:kafka`,
+ data: {
+ id: 1,
+ name: "kafka",
+ type: "http",
+ remark: "描述信息",
+ endpointStat: [3,23],
+ alertStat: [2,3,4],
+ }
+ });
+ detail.push({
+ title: `${this.$t("project.module.module")}:kafkakafkakafkakafkakafkakafkakafka`,
+ data: {
+ id: 2,
+ name: "kafkakafkakafkakafkakafkakafkakafka",
+ type: "http",
+ remark: "描述信息",
+ endpointStat: [3,23],
+ alertStat: [2,0,4],
+ }
+ });
+ detail.push({
+ title: `${this.$t("project.module.module")}:kafkakafka`,
+ data: {
+ id: 3,
+ name: "kafkakafka",
+ type: "snmp",
+ remark: "描述信息",
+ endpointStat: [3,0],
+ alertStat: [2,3,4],
+ }
+ });
+ this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, detail, this.filter.panelId, this.filter);
}
},
getAlertListChartData:function(chartInfo,filterType){
this.$refs['editChart'+chartInfo.id][0].getAlertList(filterType);
},
getAlertRuleChartData(chartInfo) {
- this.$get("alert/rule/stat?id=" + this.additionalInfo.id).then(response => {
- if (response.code === 200) {
- this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, response.data);
- }
+ let vm = this;
+ let detail = [];
+ let req = new Promise((resolve, reject) => {
+ this.$get("alert/rule/stat?id=" + this.additionalInfo.id).then(response => {
+ if (response.code == 200) {
+ response.data && function () {
+ for (let type in response.data) {
+ if (type == "project" && response.data.project.length > 0) {
+ detail.push({title: vm.$t("project.project.project"), data: convert(response.data.project)});
+ } else if (type == "module" && response.data.module.length > 0) {
+ detail.push({title: vm.$t("project.module.module"), data: convert(response.data.module)});
+ } else if (type == "asset" && response.data.asset.length > 0) {
+ detail.push({title: vm.$t("asset.asset"), data: convert(response.data.asset)});
+ } else if (type == "endpoint" && response.data.endpoint.length > 0) {
+ detail.push({title: vm.$t("project.endpoint.endpoint"), data: convert(response.data.endpoint)});
+ }
+ }
+ }();
+ resolve(true);
+ }
+ });
});
+ req.then(result => {
+ this.$refs['editChart'+chartInfo.id][0].setData(chartInfo, detail);
+ }, err => {});
+
+ function convert(d) {
+ let data = {};
+ d.forEach(item => {
+ data[item.name] = item.nums;
+ });
+ return data;
+ }
},
// 设置图表的尺寸
@@ -1552,7 +1578,7 @@
//1.元素距离页面顶部的距离
var mainOffsetTop = ele.offsetTop;//offsetTop: 当前元素顶部距离最近父元素顶部的距离,和有没有滚动条没有关系。单位px,只读元素。
//2.元素的高度
- var mainHeight = itemHeight//ele.offsetHeight;//itemHeight;
+ var mainHeight = itemHeight;//ele.offsetHeight;//itemHeight;
//3.页面滚动的距离
var windowScrollTop = scrollTop;//document.documentElement.scrollTop || document.body.scrollTop;
//4.浏览器可见区域的高度
@@ -1575,7 +1601,7 @@
let chartType = item.type;
item.isLoaded = true;
if(chartType!=='url'){
- that.getChartDataForSearch(item,index);
+ that.getChartDataForSearch(item, index);
} else {
that.$refs['editChart'+item.id][0].showLoad(item);
}
diff --git a/nezha-fronted/src/components/charts/chart.scss b/nezha-fronted/src/components/charts/chart.scss
index 6468bf8b0..0aa69b826 100644
--- a/nezha-fronted/src/components/charts/chart.scss
+++ b/nezha-fronted/src/components/charts/chart.scss
@@ -198,63 +198,68 @@
}
}
}
- .chart-asset-info, .chart-project-info, .chart-alert-rule-info {
- .chart-info-container {
+ .chart-container {
+ height: 100%;
+ position: relative;
+ background-color: white;
+ .chart-title:hover {
+ background-color:#d8dce1;
+ }
+ .chart-title:not(.drag-disabled) {
+ cursor: move;
+ }
+ .chart-title {
+ text-align: center;
+ width: 100%;
+ line-height: 30px;
+ height: 28px;
+ padding: 1px 3px 0 3px;
+ box-sizing: border-box;
+ .nz-chart-top{
+ width:100%;
+ }
+ .el-dropdown-link {
+ cursor: move;
+ }
+ .el-icon-arrow-down {
+ font-size: 12px;
+ }
+ .chart-title-text {
+ font-weight: bold;
+ font-size: 18px;
+ line-height: 26px;
+ color: #333;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ max-width:calc(100% - 20px);
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ .chart-title-icon{
+ display: inline-block;
+ cursor: pointer;
+ }
+ }
+ }
+ .chart-info {
padding-top: 6px;
+ width: 100%;
+ height: calc(100% - 10px);
}
.active-icon {
margin: 0;
}
- .asset-info-content {
- width: 100%;
- height: calc(100% - 10px);
- }
- .asset-info-content-title {
+ .chart-sub-title {
background-color: #efefef;
font-size: 13px;
color: #505255;
padding-left: 2px;
height: 25px;
line-height: 25px;
+ user-select: none;
}
- .basic-content-asset.fold, .feature-content-asset.fold {
- animation-name: fold-500; //该动画定义在main.scss里
- animation-duration: 0.3s;
- animation-iteration-count:1;
- opacity: 0;
- max-height: 0;
- visibility: hidden;
- }
- .basic-content-project.init-no-animation {
- opacity: 1;
- max-height: 200px;
- visibility: visible;
- }
- .basic-content-project.fold, .feature-content-project.fold {
- animation-name: fold-200; //该动画定义在main.scss里
- animation-duration: 0.3s;
- animation-iteration-count:1;
- opacity: 0;
- max-height: 0;
- visibility: hidden;
- }
- .basic-content-asset.unfold, .feature-content-asset.unfold {
- animation-name: unfold-500;
- animation-duration: 0.3s;
- animation-iteration-count:1;
- opacity: 1;
- max-height: 500px;
- visibility: visible;
- }
- .basic-content-project.unfold, .feature-content-project.unfold {
- animation-name: unfold-200;
- animation-duration: 0.3s;
- animation-iteration-count:1;
- opacity: 1;
- max-height: 200px;
- visibility: visible;
- }
- .basic-content, .feature-content {
+ .chart-sub-content {
opacity: 0;
max-height: 0;
visibility: hidden;
@@ -360,6 +365,43 @@
}
}
}
+ .init-no-animation {
+ opacity: 1;
+ max-height: 200px;
+ visibility: visible;
+ }
+ .fold-500 {
+ animation-name: fold-500; //该动画定义在main.scss里
+ animation-duration: 0.2s;
+ animation-iteration-count:1;
+ opacity: 0;
+ max-height: 0;
+ visibility: hidden;
+ }
+ .fold-200 {
+ animation-name: fold-200; //该动画定义在main.scss里
+ animation-duration: 0.2s;
+ animation-iteration-count:1;
+ opacity: 0;
+ max-height: 0;
+ visibility: hidden;
+ }
+ .unfold-500 {
+ animation-name: unfold-500;
+ animation-duration: 0.2s;
+ animation-iteration-count:1;
+ opacity: 1;
+ max-height: 500px;
+ visibility: visible;
+ }
+ .unfold-200 {
+ animation-name: unfold-200;
+ animation-duration: 0.2s;
+ animation-iteration-count:1;
+ opacity: 1;
+ max-height: 200px;
+ visibility: visible;
+ }
}
.chart-url {
.url-container {