feat:新增功能
1.panel图表的创建 2.panel图表的展示 3.create+增加了创建panel菜单
This commit is contained in:
@@ -529,3 +529,7 @@ html {
|
||||
color: #F98D9A;
|
||||
}
|
||||
/* end--自定义可编辑的el-select下拉框样式*/
|
||||
.right-box-add-chart {
|
||||
width: 520px;
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
|
||||
328
nezha-fronted/src/components/charts/chart-list.vue
Normal file
328
nezha-fronted/src/components/charts/chart-list.vue
Normal file
@@ -0,0 +1,328 @@
|
||||
<style scoped>
|
||||
.chartBox {
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 10px;
|
||||
float:left;
|
||||
}
|
||||
.noData{
|
||||
text-align: center
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<div>
|
||||
<div class="chartBox" v-for="(item, index) in dataList" :key="item.id">
|
||||
|
||||
<line-chart-block v-if="item.type === 'line' || item.type === 'bar' || item.type === 4" :key="'inner' + item.id"
|
||||
ref="editChart"
|
||||
@on-refresh-data=""
|
||||
@on-remove-chart-block="removeChart"
|
||||
@on-edit-chart-block=""
|
||||
:panel-id="filter.panelId"
|
||||
:editChartId="'editChartId' + item.id"></line-chart-block>
|
||||
<!--
|
||||
<chart-table v-if="item.type === 'table'" ref="editChart" :key="'inner' + item.id"
|
||||
@on-refresh-data="refreshChart"
|
||||
@on-remove-chart-block="removeChart"
|
||||
@on-edit-chart-block="editData"
|
||||
:panel-id="filter.panelId"
|
||||
:editChartId="'editChartId' + item.id"></chart-table>
|
||||
-->
|
||||
</div>
|
||||
<el-row v-if="dataList.length === 0" class="noData"></el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
//import bus from '../../libs/bus';
|
||||
import lineChartBlock from './line-chart-block';
|
||||
//import chartTable from './chart-table';
|
||||
|
||||
export default {
|
||||
name: 'chartList',
|
||||
props: {
|
||||
},
|
||||
components: {
|
||||
lineChartBlock,
|
||||
//chartTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: {},
|
||||
dataList: [], // 看板中所有图表信息
|
||||
time: {
|
||||
start: '',
|
||||
end: '',
|
||||
},
|
||||
panelId: '',
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
methods: {
|
||||
initData(filter) {
|
||||
this.dataList = [];
|
||||
// 内含 panelId,开始时间,结束时间
|
||||
this.filter = filter;
|
||||
this.getData(this.filter);
|
||||
},
|
||||
// 获取panel详情数据,获取panel下所有chart列表
|
||||
getData(params) {
|
||||
if (this.dataList.length > 0) {
|
||||
this.$refs.editChart.forEach((item) => {
|
||||
//item.showLoad();//之后要实现
|
||||
});
|
||||
}
|
||||
//param 目前没有用
|
||||
const param = {
|
||||
panelId: params.panelId,
|
||||
query: params.query,
|
||||
};
|
||||
if (!param.query) delete param.query;
|
||||
//根据panelId获得panel下的所有图表
|
||||
this.$get('panel/'+ params.panelId+'/charts').then(response => {
|
||||
if (response.code === 200) {
|
||||
if(response.data.list){
|
||||
this.dataList = response.data.list;
|
||||
}else {
|
||||
this.dataList = response.data;
|
||||
}
|
||||
this.dataSetFirst(this.dataList);
|
||||
}
|
||||
});
|
||||
},
|
||||
// arr: 该panel下图表list,生成该看板下所有图表
|
||||
dataSetFirst(arr) {
|
||||
if (arr.length) {
|
||||
arr.forEach((item, index) => {
|
||||
this.getChartData(item, index);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 获取一个图表具体数据,图表信息,图表位置index
|
||||
getChartData(chartInfo, pos, filterType) {
|
||||
const chartItem = chartInfo;
|
||||
const index = pos; // 指标
|
||||
const len = chartItem.elements.length;
|
||||
//this.setSize(chartItem.span, index); // 设置该图表宽度
|
||||
this.setSize(chartItem.span,chartItem.height, index); // 设置该图表宽度,高度
|
||||
// 没有数据的设置提示信息暂无数据-针对每一个图
|
||||
if (len === 0) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.editChart[index].setData(chartItem, [], this.filter.panelId, this.filter);//????怎么设置的无数据??
|
||||
});
|
||||
} else {
|
||||
let startTime = '';
|
||||
let endTime = '';
|
||||
if (filterType === 'refresh') {//刷新:需要根据刷新周期来???
|
||||
const now = new Date();
|
||||
const origin = new Date(this.filter.end_time);
|
||||
const numInterval = now.getTime() - origin.getTime();
|
||||
if (numInterval >= 6000) {
|
||||
startTime = this.getNewTime(this.filter.start_time, numInterval);
|
||||
endTime = bus.timeFormate(now, 'yyyy/MM/dd-hh:mm:ss');
|
||||
} else {
|
||||
startTime = this.filter.start_time;
|
||||
endTime = this.filter.end_time;
|
||||
}
|
||||
} else {
|
||||
startTime = this.filter.start_time;
|
||||
endTime = this.filter.end_time;
|
||||
}
|
||||
//测试数据----之后会要去掉
|
||||
startTime='2020-01-02T10:10:30.781Z';
|
||||
endTime = '2020-01-02T20:10:30.781Z';
|
||||
|
||||
|
||||
this.$nextTick(() => {
|
||||
const axiosArr = chartItem.elements.map((ele) => {
|
||||
const filterItem = ele;
|
||||
return this.$get('http://192.168.40.247:9090/api/v1/query_range?query='+filterItem.expression+"&start="+startTime+"&end="+endTime+'&step=3600s');
|
||||
});
|
||||
// 一个图表的所有element单独获取数据
|
||||
axios.all(axiosArr).then((res) => {
|
||||
if (res.length > 0) {
|
||||
const series = [];
|
||||
const legend = [];
|
||||
const tableData = [];
|
||||
const sumData = {
|
||||
name: 'sum',
|
||||
data: [],
|
||||
visible: true,
|
||||
threshold: null,
|
||||
};
|
||||
|
||||
res.forEach((response, innerPos) => {
|
||||
if (response.status === 'success') {
|
||||
if (response.data.result) {
|
||||
// 循环处理每个elements下获取的数据列
|
||||
response.data.result.forEach((queryItem) => {
|
||||
const seriesItem = {
|
||||
theData: {
|
||||
name: '',
|
||||
data: [],
|
||||
type:chartInfo.type,
|
||||
//visible: true,
|
||||
//threshold: null,
|
||||
},
|
||||
metric_name: '',
|
||||
};
|
||||
// 图表中每条线的名字,后半部分
|
||||
let host = `${queryItem.metric.__name__}, `;//up,
|
||||
const tagsArr = Object.keys(queryItem.metric);//["__name__","asset","idc","instance","job","module","project"]
|
||||
// 设置时间-数据格式对
|
||||
const dpsArr = Object.entries(queryItem.values);//[ ["0",[1577959830.781,"0"]], ["1",[1577959845.781,"0"]] ]
|
||||
// 判断是否有数据
|
||||
if (dpsArr.length > 0 && tagsArr.length > 0) {
|
||||
tagsArr.forEach((tag, i) => {
|
||||
if (tag !== '__name__') {
|
||||
host += i === 0 ? `${tag}=${queryItem.metric[tag]}` : ` ${tag}=${queryItem.metric[tag]},`;
|
||||
}
|
||||
});
|
||||
if(host.endsWith(',')){host = host.substr(0,host.length-1);}
|
||||
legend.push(host);
|
||||
// 图表中每条线的名字,去掉最后的逗号与空格:metric名称, 标签1=a,标签2=c
|
||||
seriesItem.theData.name = host;
|
||||
seriesItem.metric_name = seriesItem.theData.name;
|
||||
// 将秒改为毫秒
|
||||
seriesItem.theData.data = queryItem.values.map((dpsItem, dpsIndex) => {
|
||||
/*曲线汇总暂不需要
|
||||
if (sumData.data[dpsIndex]) {
|
||||
const sumNum = sumData.data[dpsIndex][1] || 0;
|
||||
sumData.data[dpsIndex][1] = sumNum + dpsItem[1];
|
||||
} else {
|
||||
sumData.data[dpsIndex] = [dpsItem[0] * 1000, dpsItem[1]];
|
||||
}
|
||||
*/
|
||||
return [dpsItem[0] * 1000, dpsItem[1]];
|
||||
});
|
||||
tableData.push({//表格数据
|
||||
name: host.slice(host.indexOf(', ') + 1),
|
||||
metric: queryItem.metric.__name__,
|
||||
time: (dpsArr[dpsArr.length - 1][0]) * 1000,
|
||||
value: dpsArr[dpsArr.length - 1][1],
|
||||
});
|
||||
series.push(seriesItem.theData);
|
||||
} else if (chartItem.elements && chartItem.elements[innerPos]) {
|
||||
// 无数据提示
|
||||
/*
|
||||
const currentInfo = chartItem.elements[innerPos];
|
||||
const errorMsg = `图表 ${chartItem.title} 中 ${currentInfo.metric},${currentInfo.tags} 无数据`;
|
||||
this.$message.warning({
|
||||
duration: 15,
|
||||
content: errorMsg,
|
||||
closable: true,
|
||||
});
|
||||
*/
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// chartData, seriesItem, panelsId, filter
|
||||
if (chartItem.type === 'table') {//表格
|
||||
this.$refs.editChart[index].setData(chartItem, tableData,
|
||||
this.filter.panelId, this.filter);
|
||||
} else if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 4) {
|
||||
if (series.length && chartItem.type === 4) {//曲线汇总
|
||||
series.push(sumData);
|
||||
}
|
||||
this.$refs.editChart[index].setData(chartItem, series,
|
||||
this.filter.panelId, this.filter,legend);
|
||||
}
|
||||
} else {
|
||||
const type = chartItem.type;
|
||||
if (type === 'table') {
|
||||
this.$refs.editChart[index].setData(chartItem, [], this.filter.panelId,
|
||||
this.filter);
|
||||
} else if (type === 'line' || type === 'bar' || chartItem.type === 4) {
|
||||
this.$refs.editChart[index].setData(chartItem, [], this.filter.panelId,
|
||||
this.filter);
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
this.$message.warning({
|
||||
content: this.$t("tip.refreshLater"),//'Please refesh later',//请稍后刷新
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
// 设置图表的宽度
|
||||
setSize(size,height, index) {
|
||||
this.$nextTick(() => {
|
||||
const chartBox = document.getElementsByClassName('chartBox');
|
||||
chartBox[index].style.width = `${(size / 12) * 100}%`;
|
||||
chartBox[index].style.height = height;
|
||||
});
|
||||
},
|
||||
|
||||
getNewTime(time, num) {
|
||||
const date = new Date(time);
|
||||
const newDate = new Date(parseInt(date.getTime(), 10) + num);
|
||||
return bus.timeFormate(newDate, 'yyyy/MM/dd-hh:mm:ss');
|
||||
},
|
||||
// 删除图表
|
||||
removeChart(chartId) {
|
||||
/*
|
||||
const chart = this.dataList.find(item => item.id === chartId);
|
||||
if (chart) {
|
||||
this.$confirm(this.$t("tip.confirmDelete"), {
|
||||
confirmButtonText: this.$t("tip.yes"),
|
||||
cancelButtonText: this.$t("tip.no"),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$delete("panel?ids=" + u.id).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.deleteSuccess")});
|
||||
if(this.showPanel.id===u.id){
|
||||
this.showPanel.id ='';
|
||||
}
|
||||
this.getTableData();
|
||||
this.rightBox.show = false;
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
//this.$emit('on-remove-chart', chart);
|
||||
}*/
|
||||
},
|
||||
/*
|
||||
// 刷新列表中的一个图表
|
||||
refreshChart(chartId) {
|
||||
this.dataList.forEach((item, index) => {
|
||||
if (item.id === chartId) {
|
||||
this.getChartData(item, index, 'refresh');
|
||||
}
|
||||
});
|
||||
},
|
||||
// 刷新数据
|
||||
refreshData() {
|
||||
this.getData(this.filter);
|
||||
},
|
||||
|
||||
// 编辑图表
|
||||
editData(chartId) {
|
||||
// 获取该id下chart的相关信息
|
||||
const chart = this.dataList.find(item => item.id === chartId);
|
||||
if (chart) {
|
||||
this.$emit('on-edit-chart', chart);
|
||||
}
|
||||
},
|
||||
|
||||
*/
|
||||
},
|
||||
|
||||
mounted() {
|
||||
},
|
||||
beforeDestroy() {
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
63
nezha-fronted/src/components/charts/chart-table.scss
Normal file
63
nezha-fronted/src/components/charts/chart-table.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ---------edit-chart-move--------- */
|
||||
.chart-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// min-height: 500px;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
border: 1px solid #d8dce1;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
.table-title {
|
||||
font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
.edit {
|
||||
// position: absolute;
|
||||
// right: 40px;
|
||||
// top: 5px;
|
||||
// z-index: 10;
|
||||
padding-right: 40px;
|
||||
.set-icon {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-left: 15px;
|
||||
color: #5aacff;
|
||||
border: 0 none;
|
||||
background: transparent;
|
||||
}
|
||||
.list-icon {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.show-icon {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.chart-select {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 25px;
|
||||
z-index: 10;
|
||||
font-size: 14px;
|
||||
.chart-select-btn {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
color: #5aacff;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*没有数据显示*/
|
||||
.null {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
187
nezha-fronted/src/components/charts/chart-table.vue
Normal file
187
nezha-fronted/src/components/charts/chart-table.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<style lang="scss">
|
||||
@import './chart-table.scss';
|
||||
</style>
|
||||
<template>
|
||||
<div class="chart-table">
|
||||
<div class="clearfix">
|
||||
<div class="float-left table-title">
|
||||
{{data.title}}
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<div class="edit">
|
||||
<!-- v-if="firstShow" -->
|
||||
<div class="list-icon">
|
||||
<span @click="refreshChart" title="刷新" class="set-icon" v-if="showSetting">
|
||||
<Icon type="refresh"></Icon>
|
||||
</span>
|
||||
<span @click="editChart" title="编辑" class="set-icon" v-if="showSetting">
|
||||
<Icon type="compose"></Icon>
|
||||
</span>
|
||||
<span @click="removeChart" title="删除" class="set-icon" v-if="showSetting">
|
||||
<Icon type="trash-a"></Icon>
|
||||
</span>
|
||||
<span @click="showAllScreen" title="全屏" class="set-icon">
|
||||
<Icon type="arrow-expand"></Icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<Table border size="small" :height="350" :data="seriesItem" :columns="columns" :loading="tableLoading"></Table>
|
||||
</div>
|
||||
<Modal title="查看" v-model="screenModal" width="96%">
|
||||
<div class="clearfix">
|
||||
<div class="float-left table-title">
|
||||
{{data.title}}
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<Icon style="cursor:pointer;" type="refresh" size="16" color="#5aacff" @click.native="refreshChart"></Icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<Table border size="small" :height="350" :data="seriesItem" :columns="columns" :loading="tableLoading"></Table>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import axios from 'axios';
|
||||
import bus from '../../libs/bus';
|
||||
// import { getQueryChart } from '../../models/service';
|
||||
|
||||
export default {
|
||||
name: 'chartTable',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
// 看板id
|
||||
panelId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// 展示设置内容
|
||||
showSetting: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {}, // 该图表信息,chartItem
|
||||
seriesItem: [], // 保存信息
|
||||
images: '',
|
||||
toolbox: false,
|
||||
items: {
|
||||
metric_name: [], // 每条数据列名称
|
||||
xAxis: [],
|
||||
theData: [], // series数据组
|
||||
},
|
||||
panelIdInner: '', // 看板id=panelId,原写作chart,由set_data获取
|
||||
firstLoad: false, // 是否第一次加载
|
||||
chartType: 'table', // 图表类型
|
||||
screenModal: false,
|
||||
// 查询数据使用
|
||||
filter: {
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
},
|
||||
stableFilter: {}, // 保存数据使用,初始化起止时间,单图or多图等
|
||||
firstShow: false, // 默认不显示操作按钮,
|
||||
tableLoading: false,
|
||||
columns: [{
|
||||
title: 'metric',
|
||||
key: 'metric',
|
||||
width: 200,
|
||||
sortable: true,
|
||||
}, {
|
||||
title: 'tag',
|
||||
key: 'name',
|
||||
minWidth: 200,
|
||||
sortable: true,
|
||||
}, {
|
||||
title: '采集时间',
|
||||
key: 'time',
|
||||
width: 160,
|
||||
render: (h, params) => h('span', bus.timeFormate(params.row.time, 'yyyy-MM-dd hh:mm:ss')),
|
||||
}, {
|
||||
title: '数值',
|
||||
key: 'value',
|
||||
width: 160,
|
||||
sortable: true,
|
||||
render: (h, params) => h('span', this.getNumStr(params.row.value)),
|
||||
}],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
showLoad() {},
|
||||
// 展示图表编辑区
|
||||
showTool() {
|
||||
this.toolbox = !this.toolbox;
|
||||
},
|
||||
// 重新请求数据 刷新操作
|
||||
refreshChart() {
|
||||
this.tableLoading = true;
|
||||
this.$emit('on-refresh-data', this.data.id);
|
||||
},
|
||||
// 编辑图表
|
||||
editChart() {
|
||||
this.$emit('on-edit-chart-block', this.data.id);
|
||||
},
|
||||
// 删除该图表
|
||||
removeChart() {
|
||||
this.$emit('on-remove-chart-block', this.data.id);
|
||||
},
|
||||
// 全屏查看
|
||||
showAllScreen() {
|
||||
this.screenModal = true;
|
||||
},
|
||||
// 设置数据, filter区分
|
||||
setData(chartItem, seriesItem, panelId, filter) {
|
||||
this.firstShow = true; // 展示操作按键
|
||||
this.tableLoading = false;
|
||||
if (filter) { // 保存数据,用于同步时间
|
||||
this.stableFilter = filter;
|
||||
}
|
||||
this.panelIdInner = panelId;
|
||||
this.data = chartItem;
|
||||
this.seriesItem = seriesItem;
|
||||
},
|
||||
// 获取格式
|
||||
getNumStr(num) {
|
||||
if (num) {
|
||||
if (num >= 1000) {
|
||||
const kbNum = num / 1000;
|
||||
if (kbNum >= 1000) {
|
||||
const mbNum = kbNum / 1000;
|
||||
if (mbNum > 1000) {
|
||||
const gbNum = mbNum / 1000;
|
||||
if (gbNum > 1000) {
|
||||
const tbNum = gbNum / 1000;
|
||||
if (tbNum > 1000) {
|
||||
const pbNum = tbNum / 1000;
|
||||
return `${pbNum.toFixed(2)}PB`;
|
||||
}
|
||||
return `${tbNum.toFixed(2)}TB`;
|
||||
}
|
||||
return `${gbNum.toFixed(2)}GB`;
|
||||
}
|
||||
return `${mbNum.toFixed(2)}MB`;
|
||||
}
|
||||
return `${kbNum.toFixed(2)}KB`;
|
||||
}
|
||||
return num.toFixed(2);
|
||||
}
|
||||
return num;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.firstLoad = false;
|
||||
},
|
||||
beforeDestroy() {},
|
||||
};
|
||||
|
||||
</script>
|
||||
86
nezha-fronted/src/components/charts/line-chart-block.scss
Normal file
86
nezha-fronted/src/components/charts/line-chart-block.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
/* ---------edit-chart-move--------- */
|
||||
.line-chart-block {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// min-height: 500px;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
border: 1px solid #d8dce1;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 3px;
|
||||
.line-area {
|
||||
box-sizing: border-box;
|
||||
background: #FFF;
|
||||
min-height: 400px;
|
||||
span.highcharts-title {
|
||||
display: block !important;
|
||||
width: 50%;
|
||||
font-size: 14px !important;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word !important;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
}
|
||||
.edit {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 15px;
|
||||
z-index: 10;
|
||||
.set-icon {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-left: 15px;
|
||||
color: #5aacff;
|
||||
border: 0 none;
|
||||
background: transparent;
|
||||
}
|
||||
.list-icon {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.show-icon {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.chart-select {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 25px;
|
||||
z-index: 10;
|
||||
font-size: 14px;
|
||||
.chart-select-btn {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
color: #5aacff;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*没有数据显示*/
|
||||
.null {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.line-chart-block-modal {
|
||||
.line-area {
|
||||
box-sizing: border-box;
|
||||
background: #FFF;
|
||||
min-height: 400px;
|
||||
span.highcharts-title {/*针对highcharts设置的样式,echarts需要修改??*/
|
||||
display: block !important;
|
||||
width: 50%;
|
||||
font-size: 14px !important;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word !important;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
599
nezha-fronted/src/components/charts/line-chart-block.vue
Normal file
599
nezha-fronted/src/components/charts/line-chart-block.vue
Normal file
@@ -0,0 +1,599 @@
|
||||
<style lang="scss">
|
||||
@import './line-chart-block.scss';
|
||||
</style>
|
||||
<template>
|
||||
<div class="line-chart-block" >
|
||||
|
||||
<div class="edit">
|
||||
<div class="list-icon" v-if="firstShow">
|
||||
<span @click="refreshChart" :title="$t('dashboard.refresh')" class="set-icon" v-if="showSetting">
|
||||
<i class="el-icon-refresh-right"></i>
|
||||
</span>
|
||||
<span @click="editChart" :title="$t('dashboard.edit')" class="set-icon" v-if="showSetting">
|
||||
<i class="el-icon-edit-outline"></i>
|
||||
</span>
|
||||
<span @click="removeChart" :title="$t('dashboard.delete')" class="set-icon" v-if="showSetting">
|
||||
<i class="el-icon-delete"></i>
|
||||
</span>
|
||||
<span @click="showAllScreen" :title="$t('dashboard.screen')" class="set-icon">
|
||||
<i class="el-icon-full-screen"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="line-area" ref="lineChartArea" style=""></div>
|
||||
<!--
|
||||
<Modal title="查看" v-model="screenModal" width="96%" class="line-chart-block-modal">-
|
||||
<el-dialog class="line-chart-block-modal"
|
||||
title="查看"
|
||||
:visible.sync="screenModal"
|
||||
width="90%"
|
||||
:before-close="handleClose">
|
||||
<el-row>
|
||||
<div class="float-right">
|
||||
全屏,日期选择组件
|
||||
<calendar-select placement="bottom-end" ref="dateSelect" @on-date-change="dateChange"></calendar-select>
|
||||
|
||||
</div>
|
||||
</el-row>>
|
||||
<div class="line-area" ref="screenShowArea" id="lineChartArea"></div>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
-->
|
||||
<!--</Modal>-->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
//import highstock from 'highcharts/highstock';
|
||||
import echarts from 'echarts';
|
||||
import bus from '../../libs/bus';
|
||||
//import calendarSelect from '../page/calendar-select';
|
||||
//import { getQueryChart } from '../../models/service';
|
||||
|
||||
//require('highcharts/modules/no-data-to-display')(highstock);
|
||||
|
||||
export default {
|
||||
name: 'lineChartBlock',
|
||||
components: {
|
||||
//calendarSelect,
|
||||
},
|
||||
props: {
|
||||
editChartId: {
|
||||
type: String,
|
||||
default: 'editChartId',
|
||||
},
|
||||
// 看板id
|
||||
panelId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// 展示设置内容
|
||||
showSetting: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {}, // 该图表信息,chartItem
|
||||
seriesItem: [], // 保存信息
|
||||
images: '',
|
||||
toolbox: false,
|
||||
items: {
|
||||
metric_name: [], // 每条数据列名称
|
||||
xAxis: [],
|
||||
theData: [], // series数据组
|
||||
},
|
||||
panelIdInner: '', // 看板id=panelId,原写作chart,由set_data获取
|
||||
chartName: '',
|
||||
firstLoad: false, // 是否第一次加载
|
||||
highchartStore: null, // 保存图表数据
|
||||
echartStore:null,// 保存图表数据
|
||||
highchartModalStore: null, // 全屏查看时数据
|
||||
chartType: 'line', // 图表类型
|
||||
screenModal: false,
|
||||
// 查询数据使用
|
||||
filter: {
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
},
|
||||
stableFilter: {}, // 保存数据使用,初始化起止时间,单图or多图等
|
||||
firstShow: false, // 默认不显示操作按钮,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/*
|
||||
typeVisible() {
|
||||
if (this.data.type === 'line' || this.data.type === 'bar' || this.data.type === 4) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
*/
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
// chartSite用于区分是全屏显示还是局部显示
|
||||
initChart(chartInfo, dataArg, ele, chartSite,legend) {
|
||||
this.firstShow = true; // 展示操作按键
|
||||
const self = this;
|
||||
this.chartType = ''; // 图表类型
|
||||
if ( chartInfo.type === 4) {//line,bar
|
||||
this.chartType = 'line';
|
||||
}
|
||||
|
||||
//var myEchart = echarts.init(document.getElementById('lineChartArea'));
|
||||
this.echartStore = echarts.init(ele);
|
||||
var option = {
|
||||
title: {
|
||||
text: chartInfo.title || null,
|
||||
textAlign: 'left',
|
||||
useHTML: true,
|
||||
textStyle: {
|
||||
//display: 'inline-block',//无此属性
|
||||
width: '300px',
|
||||
}
|
||||
},
|
||||
color: ['#7bbfea', '#b3424a', '#f05b72', '#596032', '#bd6758',
|
||||
'#cd9a5b', '#918597', '#70a19f', '#005344', '#FF00FF',
|
||||
'#f7acbc', '#5f5d46', '#66ffff', '#ccFF66', '#f47920',
|
||||
'#769149', '#1d953f', '#abc88b', '#7f7522', '#9b95c9',
|
||||
'#f3715c', '#ea66a6', '#d1c7b7', '#9d9087', '#77787b',
|
||||
'#f58220', '#c37e00', '#00ae9d', '#f26522', '#76becc',
|
||||
'#76624c', '#d71345', '#2468a2', '#ca8687', '#1b315e',
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
confine:true
|
||||
},
|
||||
legend: {
|
||||
show:true,
|
||||
formatter:function(name){
|
||||
if(!name){
|
||||
return '';
|
||||
}
|
||||
if(name.length>5){
|
||||
return name.slice(0,5)+'...';
|
||||
}
|
||||
},
|
||||
tooltip:{show:true},
|
||||
data: legend,
|
||||
//orient:'vertical',
|
||||
x:'center',
|
||||
y:'top',
|
||||
top:'5%',
|
||||
//bottom:0
|
||||
},
|
||||
grid: {
|
||||
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
height:15
|
||||
}, {
|
||||
start: 0,
|
||||
end: 100,
|
||||
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: {
|
||||
color: '#fff',
|
||||
shadowBlur: 3,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.6)',
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2
|
||||
}
|
||||
}],
|
||||
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
//boundaryGap: false,//line-false; bar-true;
|
||||
//data: ['20190101', '20190102', '周三', '周四', '周五', '周六', '周日']
|
||||
axisLabel: {
|
||||
intervale: 0,
|
||||
rotate: 0,
|
||||
formatter: function (value) {
|
||||
var t_date = new Date(value);
|
||||
return [t_date.getFullYear(), t_date.getMonth() + 1, t_date.getDate()].join('-') + "\n"
|
||||
+ [t_date.getHours(), t_date.getMinutes()].join(':');
|
||||
}
|
||||
},
|
||||
axisPointer: {//y轴上显示指针对应的值
|
||||
show: true,
|
||||
},
|
||||
splitLine:{
|
||||
show:false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine:{
|
||||
show:true
|
||||
}
|
||||
},
|
||||
useUTC: false,//使用本地时间
|
||||
series: dataArg
|
||||
};
|
||||
|
||||
// params.series = dataArg;
|
||||
if (chartSite === 'local') { // 本地显示
|
||||
/*
|
||||
option.series = dataArg.map((item) => {// params.series = dataArg.map((item) => {
|
||||
const obj = Object.assign(item);
|
||||
/*
|
||||
obj.events = {
|
||||
// eslint-disable-next-line
|
||||
legendItemClick: function(event) {//点击metric的事件,可以用默认的先
|
||||
event.preventDefault();
|
||||
let visibleAll = true; // 全部显示
|
||||
let visibleThis = true; // 点击是否是当前显示的那个条目
|
||||
self.highchartStore.series.forEach((serie, i) => {
|
||||
// 最后一个为导航,排除。
|
||||
if (i !== self.highchartStore.series.length - 1) {
|
||||
// 查看是否为显示一条状态数据,如果有非显示状态的,表示当前只显示一条数据
|
||||
if (!serie.visible) {
|
||||
visibleAll = false;
|
||||
// 点击前显示单个,判断是否点击为正在显示那个,该种情况表示点击了其他隐藏状态数据
|
||||
if (event.target.index === i) { // one to another
|
||||
visibleThis = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// 根据visibleAll和visibleThis判断是要显示一个还是全显示
|
||||
// 当visibleAll为true(表示all => one)
|
||||
// visibleAll为false: visibleThis为false(one => another one)。
|
||||
// visibleAll为false: visibleThis为true(one => all)。
|
||||
if (visibleAll || !visibleThis) { // 隐藏显示其中一个
|
||||
self.highchartStore.series.forEach((serie, i) => {
|
||||
const operateObj = serie;
|
||||
// 除显示条目以外,还有一条serie是导航栏
|
||||
if (i !== self.highchartStore.series.length - 1) {
|
||||
if (event.target.index === i) {
|
||||
operateObj.setVisible(true, false);
|
||||
} else {
|
||||
operateObj.setVisible(false, false);
|
||||
}
|
||||
} else {
|
||||
operateObj.setVisible(true, false);
|
||||
}
|
||||
});
|
||||
self.highchartStore.redraw();
|
||||
} else { // 全部显示
|
||||
self.highchartStore.series.forEach((serie) => {
|
||||
const operateObj = serie;
|
||||
operateObj.setVisible(true, false);
|
||||
});
|
||||
self.highchartStore.redraw();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return obj;
|
||||
});
|
||||
*/
|
||||
this.echartStore.setOption(option);//创建图表
|
||||
} else if (chartSite === 'screen') { // 全屏显示
|
||||
option.series = dataArg.map((item) => {// params.series = dataArg.map((item) => {
|
||||
const obj = Object.assign(item);
|
||||
obj.events = {
|
||||
// eslint-disable-next-line
|
||||
legendItemClick: function(event) {
|
||||
event.preventDefault();
|
||||
let visibleAll = true; // 全部显示
|
||||
let visibleThis = true; // 点击是否是当前显示的那个条目
|
||||
self.highchartModalStore.series.forEach((serie, i) => {
|
||||
if (i !== self.highchartModalStore.series.length - 1) {
|
||||
if (!serie.visible) {
|
||||
visibleAll = false;
|
||||
if (event.target.index === i) {
|
||||
visibleThis = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (visibleAll || !visibleThis) {
|
||||
self.highchartModalStore.series.forEach((serie, i) => {
|
||||
const operateObj = serie;
|
||||
if (i !== self.highchartModalStore.series.length - 1) {
|
||||
if (event.target.index === i) {
|
||||
operateObj.setVisible(true, false);
|
||||
} else {
|
||||
operateObj.setVisible(false, false);
|
||||
}
|
||||
} else {
|
||||
operateObj.setVisible(true, false);
|
||||
}
|
||||
});
|
||||
self.highchartModalStore.redraw();
|
||||
} else {
|
||||
self.highchartModalStore.series.forEach((serie) => {
|
||||
const operateObj = serie;
|
||||
operateObj.setVisible(true, false);
|
||||
});
|
||||
self.highchartModalStore.redraw();
|
||||
}
|
||||
},
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
this.echartStore.setOption(option);//显示全屏界面
|
||||
}
|
||||
},
|
||||
// 设置数据, filter区分
|
||||
setData(chartItem, seriesItem, panelId, filter,legend) {
|
||||
if (filter) { // 保存数据,用于同步时间
|
||||
this.stableFilter = filter;
|
||||
}
|
||||
this.panelIdInner = panelId;
|
||||
this.data = chartItem;
|
||||
this.seriesItem = seriesItem;
|
||||
this.initChart(chartItem, seriesItem, this.$refs.lineChartArea, 'local',legend);
|
||||
if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 4) {
|
||||
this.initChart(chartItem, seriesItem, this.$refs.lineChartArea, 'local',legend);
|
||||
}
|
||||
},
|
||||
// 删除该图表
|
||||
removeChart() {
|
||||
this.$emit('on-remove-chart-block', this.data.id);
|
||||
}
|
||||
/*
|
||||
handleClose(done) {
|
||||
/*
|
||||
this.$confirm('确认关闭?')
|
||||
.then(_ => {
|
||||
done();
|
||||
})
|
||||
.catch(_ => {});
|
||||
|
||||
},*/
|
||||
/*
|
||||
// 展示图表编辑区
|
||||
showTool() {
|
||||
this.toolbox = !this.toolbox;
|
||||
},
|
||||
// 修改图表类型
|
||||
changeChart(type) {
|
||||
this.chartType = type;
|
||||
this.highchartStore.update({
|
||||
chart: {
|
||||
type: this.chartType,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 重新请求数据 刷新操作-> console, create-board
|
||||
refreshChart() {
|
||||
this.highchartStore.showLoading();
|
||||
this.$emit('on-refresh-data', this.data.id);
|
||||
},
|
||||
// 编辑图表
|
||||
editChart() {
|
||||
this.$emit('on-edit-chart-block', this.data.id);
|
||||
},
|
||||
},
|
||||
// 全屏查看
|
||||
showAllScreen() {
|
||||
// 初始化同步时间
|
||||
this.filter.start_time = this.stableFilter.start_time;
|
||||
this.filter.end_time = this.stableFilter.end_time;
|
||||
this.screenModal = true;
|
||||
// 全屏绘图
|
||||
this.initChart(this.data, this.seriesItem, this.$refs.screenShowArea, 'screen');
|
||||
// 日期组件同步时间
|
||||
this.$refs.dateSelect.initTime({
|
||||
start: this.stableFilter.start_time,
|
||||
end: this.stableFilter.end_time,
|
||||
});
|
||||
},
|
||||
dateChange(time) {
|
||||
this.filter.start_time = `${time[0]}:00`;
|
||||
this.filter.end_time = `${time[1]}:59`;
|
||||
if (this.showSetting) {
|
||||
this.getQueryChart('list');
|
||||
} else {
|
||||
this.getQueryChart('dashboard');
|
||||
}
|
||||
},
|
||||
// 查询数据,修改日期查询全屏数据
|
||||
getQueryChart(type) {
|
||||
if (this.highchartModalStore) {
|
||||
this.highchartModalStore.showLoading();
|
||||
}
|
||||
let axiosArr = [];
|
||||
if (type === 'list') { // 普通模式,主控台使用
|
||||
axiosArr = this.data.elements.map((ele) => {
|
||||
const filterItem = ele;
|
||||
return getQueryChart({
|
||||
product_id: this.productId,
|
||||
metric: filterItem.metric,
|
||||
tags: filterItem.tags,
|
||||
start: this.filter.start_time,
|
||||
end: this.filter.end_time,
|
||||
});
|
||||
});
|
||||
} else if (type === 'dashboard') { // 概览模式,指标概览中使用
|
||||
// 概览模式,需要区分单独一个和多个
|
||||
if (this.stableFilter.chartCount === 'multiple') {
|
||||
// 所有tag标签
|
||||
const tagAllArr = this.proTags(this.data.tags);
|
||||
// 根据title格式化的标签
|
||||
const titles = this.data.title.split(',');
|
||||
const titleArr = titles.map(item => item.trim());
|
||||
// 获取所需标签
|
||||
const tagArr = this.getCompilation(tagAllArr, titleArr);
|
||||
axiosArr = [getQueryChart({
|
||||
product_id: this.productId,
|
||||
metric: this.data.metric,
|
||||
tags: tagArr.toString(),
|
||||
start: this.filter.start_time,
|
||||
end: this.filter.end_time,
|
||||
})];
|
||||
} else {
|
||||
axiosArr = [getQueryChart({
|
||||
product_id: this.productId,
|
||||
metric: this.data.metric,
|
||||
tags: this.data.tags,
|
||||
start: this.filter.start_time,
|
||||
end: this.filter.end_time,
|
||||
})];
|
||||
}
|
||||
}
|
||||
// 一个图表
|
||||
axios.all(axiosArr).then((res) => {
|
||||
if (res.length > 0) {
|
||||
const series = [];
|
||||
const sumData = {
|
||||
name: 'sum',
|
||||
data: [],
|
||||
visible: true,
|
||||
threshold: null,
|
||||
};
|
||||
res.forEach((response) => {
|
||||
if (response.data.code === 200) {
|
||||
if (response.data.data) {
|
||||
// 循环处理每个elements下获取的数据列
|
||||
response.data.data.forEach((queryItem) => {
|
||||
const seriesItem = {
|
||||
theData: {
|
||||
name: '',
|
||||
data: [],
|
||||
visible: true,
|
||||
threshold: null,
|
||||
},
|
||||
metric_name: '',
|
||||
};
|
||||
// 图表中每条线的名字,后半部分
|
||||
let host = `${queryItem.metric}, `;
|
||||
const tagsArr = Object.keys(queryItem.tags);
|
||||
// 设置时间-数据格式对
|
||||
const dpsArr = Object.entries(queryItem.dps);
|
||||
if (tagsArr.length > 0 && dpsArr.length > 0) {
|
||||
tagsArr.forEach((tag, i) => {
|
||||
if (tag !== 'uuid') {
|
||||
host += i === 0 ? `${tag}=${queryItem.tags[tag]}` : `, ${tag}=${queryItem.tags[tag]}`;
|
||||
}
|
||||
});
|
||||
// 图表中每条线的名字,去掉最后的逗号与空格
|
||||
seriesItem.theData.name = host;
|
||||
seriesItem.metric_name = seriesItem.theData.name;
|
||||
// 将秒改为毫秒
|
||||
seriesItem.theData.data = dpsArr.map((dpsItem, dpsIndex) => {
|
||||
if (sumData.data[dpsIndex]) {
|
||||
const sumNum = sumData.data[dpsIndex][1] || 0;
|
||||
sumData.data[dpsIndex][1] = sumNum + dpsItem[1];
|
||||
} else {
|
||||
sumData.data[dpsIndex] = [dpsItem[0] * 1000, dpsItem[1]];
|
||||
}
|
||||
return [dpsItem[0] * 1000, dpsItem[1]];
|
||||
});
|
||||
series.push(seriesItem.theData);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (series.length && this.data.type === 4) {
|
||||
series.push(sumData);
|
||||
}
|
||||
this.initChart(this.data, series, this.$refs.screenShowArea, 'screen');
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
this.$Message.warning({
|
||||
content: '请稍后刷新',
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showLoad() {
|
||||
if (this.highchartStore) {
|
||||
this.highchartStore.showLoading();
|
||||
}
|
||||
},
|
||||
// 获取格式
|
||||
getNumStr(num) {
|
||||
if (num >= 1000) {
|
||||
const kbNum = num / 1000;
|
||||
if (kbNum >= 1000) {
|
||||
const mbNum = kbNum / 1000;
|
||||
if (mbNum > 1000) {
|
||||
const gbNum = mbNum / 1000;
|
||||
if (gbNum > 1000) {
|
||||
const tbNum = gbNum / 1000;
|
||||
if (tbNum > 1000) {
|
||||
const pbNum = tbNum / 1000;
|
||||
return `${pbNum.toFixed(2)}PB`;
|
||||
}
|
||||
return `${tbNum.toFixed(2)}TB`;
|
||||
}
|
||||
return `${gbNum.toFixed(2)}GB`;
|
||||
}
|
||||
return `${mbNum.toFixed(2)}MB`;
|
||||
}
|
||||
return `${kbNum.toFixed(2)}KB`;
|
||||
}
|
||||
return num.toFixed(2);
|
||||
},
|
||||
// 获取tag数组
|
||||
proTags(data) {
|
||||
const dou = data.indexOf(',');
|
||||
const tmp = [];
|
||||
if (dou === -1) {
|
||||
const set = data.split('=');
|
||||
if (set[1].indexOf('|') > -1) {
|
||||
const valueArr = set[1].split('|');
|
||||
valueArr.forEach((keyV) => {
|
||||
tmp.push(`${set[0]}=${keyV}`);
|
||||
});
|
||||
} else {
|
||||
tmp.push(`${set[0]}=${set[1]}`);
|
||||
}
|
||||
} else {
|
||||
const mid = data.split(','); // ['key=v1','key=v2',....]
|
||||
mid.forEach((item) => {
|
||||
const setInner = item.split('=');
|
||||
if (setInner[1].indexOf('|') > -1) {
|
||||
const valueArr = setInner[1].split('|');
|
||||
valueArr.forEach((keyV) => {
|
||||
tmp.push(`${setInner[0]}=${keyV}`);
|
||||
});
|
||||
} else {
|
||||
tmp.push(`${setInner[0]}=${setInner[1]}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
return tmp;
|
||||
},
|
||||
// 获取交集,取得所需tags进行查询,k1=v1,k2=v2
|
||||
getCompilation(arr1, arr2) {
|
||||
const arr = [];
|
||||
if (arr1.length && arr2.length) {
|
||||
arr1.forEach((item) => {
|
||||
if (arr2.indexOf(item) > -1) {
|
||||
arr.push(item);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
*/
|
||||
},
|
||||
mounted() {
|
||||
this.firstLoad = false;
|
||||
},
|
||||
beforeDestroy() {},
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -204,7 +204,7 @@
|
||||
</el-popover>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<panel-box :panel="editPanel" @reload="panelListReload" @reloadForDel="" ref="panelBox"></panel-box>
|
||||
<project-box :project="editProject" ref="projectBox"></project-box>
|
||||
<module-box :currentProject="currentProject" :module="editModule" @reload="" ref="moduleBox"></module-box>
|
||||
<add-endpoint-box :currentProject="currentProject" :currentModule="currentModule" @reload=""
|
||||
@@ -221,6 +221,10 @@
|
||||
username: sessionStorage.getItem("nz-username"),
|
||||
language: localStorage.getItem("nz-language"),
|
||||
assetData: [],
|
||||
editPanel:{//新增or编辑的panel
|
||||
id:'',
|
||||
name: ''
|
||||
},
|
||||
projectData: [], //顶部菜单project列表中的数据
|
||||
editProject: {id: '', name: '', remark: ''}, //新增/编辑的project
|
||||
currentProject: {id: '', name: '', remark: ''}, //module/endpoint弹框用来回显project
|
||||
@@ -252,6 +256,11 @@
|
||||
receiver: '',
|
||||
},
|
||||
createMenu: [ //新增按钮内容
|
||||
{
|
||||
label: this.$t('dashboard.panel.createPanelTitle'),
|
||||
url: 'panel',
|
||||
type: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('project.project.createProject'),
|
||||
url: 'project',
|
||||
@@ -300,7 +309,10 @@
|
||||
},
|
||||
|
||||
createBox(item) {
|
||||
if (item.type == 1) {
|
||||
if (item.type == 0) {
|
||||
this.$refs.panelBox.show(true);
|
||||
this.editPanel = {id: '', name: ''};
|
||||
}else if (item.type == 1) {
|
||||
this.$refs.projectBox.show(true);
|
||||
this.editProject = {id: '', name: '', remark: ''};
|
||||
} else if (item.type == 2) {
|
||||
@@ -428,6 +440,11 @@
|
||||
this.$refs.projectBox.show(true);
|
||||
this.editProject = Object.assign({}, p);
|
||||
},
|
||||
panelListReload(){
|
||||
if(this.$route.path==='/panel'){
|
||||
this.$store.commit('panelListChange',true);//新增panel之后,且当前页面为panel页面,则更新panel列表
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
this.$get('logout');
|
||||
this.jumpTo('login');
|
||||
|
||||
@@ -20,18 +20,53 @@ const cn = {
|
||||
title:'仪表盘',
|
||||
panel:{
|
||||
title:'看板',
|
||||
//侧滑框
|
||||
//面板-侧滑框
|
||||
createPanelTitle: "创建面板",
|
||||
createPanelTitleSec:"创建新面板",
|
||||
editPanelTitle: "编辑面板",
|
||||
panelForm:{
|
||||
panelName:"面板名称" ,
|
||||
panelId:"ID"
|
||||
} ,
|
||||
//图表-侧滑框
|
||||
createChartTitle: "Create Chart",
|
||||
editChartTitle: "Edit Chart",
|
||||
chartForm:{
|
||||
chartId:"ID",
|
||||
chartName:"图表名称",
|
||||
type:"类型",
|
||||
typeVal:{
|
||||
line:{
|
||||
label:"曲线"
|
||||
},
|
||||
bar:{
|
||||
label:"柱状图"
|
||||
},
|
||||
table:{
|
||||
label:"表格",
|
||||
}
|
||||
},
|
||||
width:"宽度",
|
||||
high:"高度",
|
||||
metric:"指标",
|
||||
addMetric:"添加指标",
|
||||
},
|
||||
},
|
||||
metric:{
|
||||
name:"指标名称",
|
||||
normal:"Normal",
|
||||
expert:"Expert",
|
||||
label:"指标标签",
|
||||
addMetric:"添加指标",
|
||||
expertTip:"请输入表达式"
|
||||
},
|
||||
metricPreview:{
|
||||
title:'指标预览',
|
||||
}
|
||||
},
|
||||
refresh:'刷新',
|
||||
edit:'编辑',
|
||||
delete:'删除',
|
||||
screen:'全屏'
|
||||
},
|
||||
asset: {
|
||||
tableTitle: {
|
||||
|
||||
@@ -25,24 +25,57 @@ const en = {
|
||||
title:'Dashboard',
|
||||
panel:{
|
||||
title:'Panel',
|
||||
//侧滑框
|
||||
//面板-侧滑框
|
||||
createPanelTitle: "Create Panel",
|
||||
createPanelTitleSec:"Create New Panel",
|
||||
editPanelTitle: "Panel",
|
||||
panelForm:{
|
||||
panelName:"Panel Name",
|
||||
panelId:"ID"
|
||||
},
|
||||
//图表-侧滑框
|
||||
createChartTitle: "Create Chart",
|
||||
editChartTitle: "Edit Chart",
|
||||
chartForm:{
|
||||
chartId:"ID",
|
||||
chartName:"Chart Name",
|
||||
type:"Type",
|
||||
typeVal:{
|
||||
line:{
|
||||
label:"Line"
|
||||
},
|
||||
bar:{
|
||||
label:"Bar"
|
||||
},
|
||||
table:{
|
||||
label:"Table"
|
||||
}
|
||||
},
|
||||
width:"Width",
|
||||
high:"High",
|
||||
metric:"Metric",
|
||||
addMetric:"Add Metric",
|
||||
},
|
||||
},
|
||||
metric:{
|
||||
name:"Metric",
|
||||
normal:"Normal",
|
||||
expert:"Expert",
|
||||
label:"MetricLabel",
|
||||
addMetric:"Create Metric",
|
||||
expertTip:"Please enter an expression"
|
||||
},
|
||||
metricPreview:{
|
||||
title:'MetricPreview',
|
||||
}
|
||||
},
|
||||
refresh:'refresh',
|
||||
edit:'edit',
|
||||
delete:'delete',
|
||||
screen:'fullScreen'
|
||||
},
|
||||
validate: { //校验规则
|
||||
required: 'Required',
|
||||
number: 'Must be a number',
|
||||
email:'E-mail is invalide',
|
||||
tel:'Phone number is invalide',
|
||||
config: {
|
||||
account: {
|
||||
|
||||
@@ -60,7 +93,8 @@ const en = {
|
||||
no: "No",
|
||||
deleteSuccess: "Successfully Deleted",
|
||||
saveSuccess: "Successfully Saved",
|
||||
coverSuccess: 'Successfully Covered'
|
||||
coverSuccess: 'Successfully Covered',
|
||||
refreshLater:'Please refresh later'
|
||||
},
|
||||
asset:{
|
||||
asset: 'Asset',
|
||||
|
||||
145
nezha-fronted/src/components/common/rightBox/panelBox.vue
Normal file
145
nezha-fronted/src/components/common/rightBox/panelBox.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template key="panelBox">
|
||||
<transition name="right-box">
|
||||
<div class="right-box right-box-panel" v-if="rightBox.show">
|
||||
<!-- begin--顶部按钮-->
|
||||
<div class="right-box-top-btns">
|
||||
<button type="button" v-if="panel.id != ''" @click="del(panel)" class="nz-btn nz-btn-size-normal nz-btn-style-light">
|
||||
<span class="top-tool-btn-txt">{{$t('overall.delete')}}</span>
|
||||
</button>
|
||||
<button type="button" @click="save" class="nz-btn nz-btn-size-normal nz-btn-style-normal">
|
||||
<span class="top-tool-btn-txt">{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
<button type="button" @click="esc" class="nz-btn nz-btn-size-normal nz-btn-style-light nz-btn-style-square">
|
||||
<span class="top-tool-btn-txt"><i class="el-icon-close"></i></span>
|
||||
</button>
|
||||
<!--
|
||||
<div class="right-box-top-btn right-box-top-btn-full" @click="esc">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
<span>{{$t('overall.esc')}}</span>
|
||||
</div>
|
||||
<div class="right-box-top-btn right-box-top-btn-full" @click="save">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-edit-outline"></i>
|
||||
</div>
|
||||
<span >{{$t('overall.save')}}</span>
|
||||
</div>
|
||||
<div @click="del(panel)" class="right-box-top-btn" v-if="panel.id != ''">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-delete"></i>
|
||||
</div>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
<!-- end--顶部按钮-->
|
||||
|
||||
<!-- begin--标题-->
|
||||
<div class="right-box-title">{{rightBox.title}}</div>
|
||||
<!-- end--标题-->
|
||||
|
||||
<!-- begin--表单-->
|
||||
<div class="right-box-form">
|
||||
<div class="right-box-form-row">
|
||||
<div class="right-box-form-label">{{$t('dashboard.panel.panelForm.panelName')}}</div>
|
||||
<div class="right-box-form-content">
|
||||
<el-input placeholder="" maxlength="64" show-word-limit v-model="panel.name" size="small"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end--表单-->
|
||||
|
||||
<!-- begin--底部按钮-->
|
||||
<!--
|
||||
<div class="right-box-bottom-btns">
|
||||
<div @click="esc()" :class="{'right-box-bottom-btn-50': true}" class="right-box-bottom-btn right-box-bottom-btn-cancel">{{$t('overall.cancel')}}</div><div @click="save()" class="right-box-bottom-btn right-box-bottom-btn-50">{{panel.id == '' ? $t('overall.create') : $t('overall.save')}}</div>
|
||||
</div>
|
||||
-->
|
||||
<!-- end--底部按钮-->
|
||||
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "panelBox",
|
||||
props: {
|
||||
panel: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rightBox: { //面板弹出框相关
|
||||
show: false,
|
||||
title: this.$t('dashboard.panel.createPanelTitle')
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show(show) {
|
||||
this.rightBox.show = show;
|
||||
},
|
||||
setTitle(title) {
|
||||
this.rightBox.title = title;
|
||||
},
|
||||
//面板相关方法
|
||||
del: function(u) {
|
||||
this.$confirm(this.$t("tip.confirmDelete"), {
|
||||
confirmButtonText: this.$t("tip.yes"),
|
||||
cancelButtonText: this.$t("tip.no"),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$delete("panel?ids=" + u.id).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.deleteSuccess")});
|
||||
this.$emit("reloadForDel");
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
save: function() {
|
||||
if (this.panel.id) {
|
||||
this.$put('panel', this.panel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
this.$emit("reload");
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$post('panel', this.panel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
this.$emit("reload");
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
esc: function() {
|
||||
this.rightBox.show = false;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
mounted: function() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
496
nezha-fronted/src/components/page/dashboard/chartBox.vue
Normal file
496
nezha-fronted/src/components/page/dashboard/chartBox.vue
Normal file
@@ -0,0 +1,496 @@
|
||||
|
||||
<style scoped>
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.bg-purple {
|
||||
background: white;
|
||||
}
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.common-float-left {
|
||||
float: left;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.el-inner {
|
||||
width: 200px;
|
||||
border-top-width: 0px;
|
||||
border-left-width: 0px;
|
||||
border-right-width: 0px;
|
||||
border-bottom-width: 10px;
|
||||
border-color:red;
|
||||
/*outline: medium;*/
|
||||
}
|
||||
|
||||
.element-bottom-border {
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #dfe7f2;
|
||||
margin-top:-25px;
|
||||
}
|
||||
/*metric样式--begin*/
|
||||
.element-item {
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px dashed #dfe7f2;
|
||||
}
|
||||
/*metric样式--end*/
|
||||
.label-center{
|
||||
margin-top:6px;
|
||||
}
|
||||
</style>
|
||||
<template key="chartBox">
|
||||
<transition name="right-box">
|
||||
<div class="right-box right-box-add-chart" v-if="rightBox.show" >
|
||||
<!-- begin--顶部按钮-->
|
||||
<div class="right-box-top-btns">
|
||||
<button type="button" v-if="chart.id != ''" @click="del(chart)" class="nz-btn nz-btn-size-normal nz-btn-style-light">
|
||||
<span class="top-tool-btn-txt">{{$t('overall.delete')}}</span>
|
||||
</button>
|
||||
<button type="button" @click="confirmAdd" class="nz-btn nz-btn-size-normal nz-btn-style-normal">
|
||||
<span class="top-tool-btn-txt">{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
<button type="button" @click="esc" class="nz-btn nz-btn-size-normal nz-btn-style-light nz-btn-style-square">
|
||||
<span class="top-tool-btn-txt"><i class="el-icon-close"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- end--顶部按钮-->
|
||||
|
||||
<!-- begin--标题-->
|
||||
<div class="right-box-title">{{rightBox.title}}</div>
|
||||
<!-- end--标题-->
|
||||
|
||||
<!-- begin--表单-->
|
||||
<el-scrollbar class="right-box-form-box">
|
||||
<el-form class="right-box-form" :model="chart" label-position="top" :rules="rules" ref="chartForm">
|
||||
<el-form-item :label='$t("dashboard.panel.chartForm.chartName")' prop="title">
|
||||
<el-input size="mini" maxlength="64" show-word-limit v-model="chart.title"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="2" >
|
||||
<div class="label-center" >{{$t('dashboard.panel.chartForm.type')}}</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div class="grid-content ">
|
||||
<el-form-item prop="type">
|
||||
<el-select class="right-box-row-with-btn" value-key="chartType" popper-class="" v-model="chart.type" placeholder="" size="small">
|
||||
<el-option v-for="item in chartTypeList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span class="panel-dropdown-label-txt" >{{item.name}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2.1">
|
||||
<div class="label-center" >{{$t('dashboard.panel.chartForm.width')}}</div>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<div class="grid-content ">
|
||||
<el-form-item prop="span">
|
||||
<el-select class="right-box-row-with-btn" value-key="chartSpan" popper-class="" v-model="chart.span" placeholder="" size="small">
|
||||
<el-option v-for="item in spanList" :key="item" :label="'span-' + item" :value="item">
|
||||
<span class="panel-dropdown-label-txt" > span-{{item}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<div class="label-center" >{{$t('dashboard.panel.chartForm.high')}}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="grid-content ">
|
||||
<el-form-item prop="heigh">
|
||||
<el-input-number :min="10" label="" v-model="chart.height" placeholder="" size="small"></el-input-number>px
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="element-bottom-border" >
|
||||
<div >{{$t('dashboard.panel.chartForm.metric')}}</div>
|
||||
</el-row>
|
||||
|
||||
<el-row class="element-item" v-for="(elem, index) in elements" :key="'ele' + index">
|
||||
<chart-metric ref="chartTag"
|
||||
:product-id="productId"
|
||||
:pointer="index"
|
||||
:metric-list="metricList"
|
||||
:count-total="elements.length"
|
||||
@on-delete-target="deleteTarget"
|
||||
@sub-save-ok="subOk"
|
||||
@on-add-target-success="getTarget"
|
||||
></chart-metric>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="20">
|
||||
<button type="button" @click="addTarget" class="nz-btn nz-btn-size-normal nz-btn-style-normal">
|
||||
<span class="top-tool-btn-txt">{{$t('dashboard.panel.chartForm.addMetric')}}</span>
|
||||
</button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="20">
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
<!--
|
||||
<div class="right-box-form">
|
||||
|
||||
<div class="right-box-form-row">
|
||||
<div class="right-box-form-label">{{$t('dashboard.panel.chartForm.chartName')}}</div>
|
||||
<div class="right-box-form-content">
|
||||
<el-input placeholder="" maxlength="64" show-word-limit v-model="chart.title" size="small"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-box-form-row">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"><div class="grid-content bg-purple">1</div></el-col>
|
||||
<el-col :span="8"><div class="grid-content bg-purple">2</div></el-col>
|
||||
<el-col :span="8"><div class="grid-content bg-purple">3</div></el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
-->
|
||||
<!-- end--表单-->
|
||||
|
||||
<!-- begin--底部按钮-->
|
||||
<!--
|
||||
<div class="right-box-bottom-btns">
|
||||
<div @click="esc" class="right-box-bottom-btn right-box-bottom-btn-cancel right-box-bottom-btn-50">{{$t('overall.cancel')}}</div><div @click="save" class="right-box-bottom-btn right-box-bottom-btn-50">{{$t('overall.create')}}</div>
|
||||
</div>
|
||||
-->
|
||||
<!-- end--底部按钮-->
|
||||
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bus from '../../../libs/bus';
|
||||
import ChartMetric from "./chartMetric";
|
||||
|
||||
export default {
|
||||
name: "chartBox",
|
||||
props: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rightBox: {
|
||||
show: false,
|
||||
title: this.$t('dashboard.panel.createChartTitle'),
|
||||
},
|
||||
chart: {
|
||||
id:'',
|
||||
title: '',
|
||||
type:'line',
|
||||
span:12,
|
||||
height:400,
|
||||
elements:{
|
||||
id: '',
|
||||
expression: '',
|
||||
type: ''
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{required: true, trigger: 'blur'}
|
||||
],
|
||||
/*
|
||||
heigh: [
|
||||
{required: true, message: this.$t('validate.required'), trigger: 'blur'}
|
||||
]
|
||||
*/
|
||||
},
|
||||
chartTypeList: [
|
||||
{
|
||||
id:"line",
|
||||
name:this.$t("dashboard.panel.chartForm.typeVal.line.label")
|
||||
},
|
||||
{
|
||||
id:"bar",
|
||||
name:this.$t("dashboard.panel.chartForm.typeVal.bar.label")
|
||||
},
|
||||
{
|
||||
id:"table",
|
||||
name:this.$t("dashboard.panel.chartForm.typeVal.table.label")
|
||||
}
|
||||
],
|
||||
elements: [1], // 指标部分 tarNum
|
||||
elementTarget: [], // 本地保存数据
|
||||
spanList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
// 是否为编辑已有信息
|
||||
isedit: false,
|
||||
productId: 0,//不需要这个参数,可以删除
|
||||
panelId: 0,
|
||||
metricList: [], // metric列表
|
||||
deleteIndex: '', // 要删除的指标模块
|
||||
subCount: 0, // subSave保存data到bus计数器
|
||||
}
|
||||
},
|
||||
components:{
|
||||
'chart-metric':ChartMetric
|
||||
},
|
||||
methods: {
|
||||
show(show) {
|
||||
this.rightBox.show = show;
|
||||
},
|
||||
save() {
|
||||
this.$refs['chartForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.chart.id) {//修改
|
||||
this.$put('panel/'+this.chart.id+'/charts', this.project).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
});
|
||||
} else {//新增
|
||||
this.$post('panel/charts', this.chart).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
del: function(u) {
|
||||
this.$confirm(this.$t("tip.confirmDelete"), {
|
||||
confirmButtonText: this.$t("tip.yes"),
|
||||
cancelButtonText: this.$t("tip.no"),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$delete("panel/"+u.id+"/charts").then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.deleteSuccess")});
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
//----------------------------------
|
||||
|
||||
/*关闭弹框*/
|
||||
esc() {
|
||||
this.rightBox.show = false;
|
||||
//this.editParamBox.show = false;
|
||||
},
|
||||
|
||||
/*metric部分相关方法--begin*/
|
||||
// 增加指标,tarNum
|
||||
addTarget() {
|
||||
this.elements.push(1);
|
||||
},
|
||||
// 删除指标,第一步, 新方法
|
||||
deleteTarget(index) {
|
||||
this.deleteIndex = index;
|
||||
this.$refs.chartTag.forEach((item) => {
|
||||
// 子组件保存内容到bus
|
||||
item.subSave();
|
||||
});
|
||||
},
|
||||
// subSave保存成功后回调,第二步
|
||||
subOk() {
|
||||
// 每个模块均有返回,当全部模块返回完成时,将sub计数器重置
|
||||
this.subCount += 1;
|
||||
if (this.subCount === this.elements.length) {
|
||||
this.subCount = 0;
|
||||
// 保存完成,进行删除操作
|
||||
this.elements.splice(this.deleteIndex, 1);
|
||||
this.elementTarget.splice(this.deleteIndex, 1);
|
||||
bus.chartAddInfo.metricTarget.splice(this.deleteIndex, 1);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.chartTag.forEach((item, index) => {
|
||||
item.setSubdata(index); // 将数据从bus重新赋值
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
// 格式化tag为字符串表达式
|
||||
tagsToString(metric,arr) {
|
||||
let str = metric;
|
||||
arr.forEach((item, index) => {
|
||||
if (index === 0) {
|
||||
str +="{"
|
||||
if(item.value.length===1){
|
||||
str += `${item.name}='${item.value.join('|')}'`;
|
||||
}else if(item.value.length>1){
|
||||
str += `${item.name}=~'${item.value.join('|')}'`;
|
||||
}
|
||||
} else {
|
||||
if(item.value.length===1){
|
||||
str += `,${item.name}='${item.value.join('|')}'`;
|
||||
}else if(item.value.length>1){
|
||||
str += `,${item.name}=~'${item.value.join('|')}'`;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
str +="}";
|
||||
return str;
|
||||
},
|
||||
// 新建图表
|
||||
addCharts(params) {
|
||||
this.$post('panel/'+this.panelId+'/charts', params).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.esc();
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
this.$refs.chartForm.resetFields();//清空表单
|
||||
this.$emit('on-create-success', 'create', response.data,params);
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 更新图表
|
||||
updateCharts(params) {
|
||||
/*
|
||||
const obj = params;
|
||||
obj.id = this.chartId;
|
||||
updateCharts(obj).then((res) => {
|
||||
if (res.status === 200) {
|
||||
this.chartModal = false;
|
||||
this.initInfo();
|
||||
this.$refs.chartInfo.resetFields();
|
||||
this.$emit('on-create-success', 'update', res.data);
|
||||
}
|
||||
});
|
||||
*/
|
||||
},
|
||||
// 获取每个tag组件内部校验后数据,点击生成图表时触发
|
||||
getTarget(target) {
|
||||
this.elementTarget.push(target);
|
||||
if (this.elementTarget.length === this.elements.length) {
|
||||
this.$refs.chartForm.validate((valid) => {
|
||||
const params = {
|
||||
// productId: this.productId,
|
||||
//panelId: this.panelId,
|
||||
title: this.chart.title,//this.chart
|
||||
span: this.chart.span,
|
||||
height: this.chart.height,
|
||||
type: this.chart.type,
|
||||
};
|
||||
|
||||
//生成指标数组
|
||||
const elements = [];
|
||||
this.elementTarget.forEach((elem,index) => {
|
||||
if(elem.type==='normal'){
|
||||
const metricStr = this.tagsToString(elem.metric,elem.selectedTagList);
|
||||
elements.push({
|
||||
//id:index+1,
|
||||
//metric: elem.metric,//指标名称
|
||||
expression: metricStr,//指标对应Label及Value组成的表达式字符串
|
||||
type:elem.type,//指标类型
|
||||
});
|
||||
}else if(elem.type==='expert'){
|
||||
elements.push({
|
||||
//id:index+1,
|
||||
//metric: elem.metric,//指标名称
|
||||
expression: elem.expression,//指标对应Label及Value组成的表达式字符串
|
||||
type:elem.type,//指标类型
|
||||
});
|
||||
}
|
||||
});
|
||||
params.elements = elements;
|
||||
if (valid) {
|
||||
if (this.isedit) {
|
||||
params.id = this.chartId;
|
||||
this.updateCharts(params);
|
||||
} else {
|
||||
this.addCharts(params);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 生成图表
|
||||
confirmAdd() {
|
||||
this.elementTarget = []; // 初始化清空参数
|
||||
this.$refs.chartTag.forEach((item, index) => {//循环指标列表
|
||||
// 触发每个tag组件内部进行校验
|
||||
item.saveTarget(index);
|
||||
});
|
||||
this.$refs.chartForm.validate();
|
||||
},
|
||||
|
||||
// 获取metric列表
|
||||
getSuggestMetric() {
|
||||
this.$get('metric', {pageNo: 1, pageSize: 9999}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.metricList = response.data.list;
|
||||
}else {
|
||||
this.metricList = [];
|
||||
}
|
||||
})
|
||||
},
|
||||
// 创建打开
|
||||
createData(panelId) {
|
||||
this.panelId = panelId;
|
||||
this.isedit = false;
|
||||
this.initInfo(); // 初始化图表信息
|
||||
//this.chartModal = true;//????控制弹出框显示和隐藏的,不需要了
|
||||
this.initOpen(); // 获取metric, productId数据
|
||||
},
|
||||
|
||||
// 初始化信息
|
||||
initInfo() {
|
||||
this.chart.title = '';
|
||||
this.chart.type = 'line';
|
||||
this.chart.span = 12;
|
||||
this.chart.height = 400;
|
||||
this.elements = [1];
|
||||
this.elementTarget = [];
|
||||
bus.chartAddInfo.metricTarget = [];
|
||||
},
|
||||
// 创建时打开,用于清空chart-tag数据
|
||||
initOpen() {
|
||||
bus.$emit('clear_history');
|
||||
/* 项目不需要produce
|
||||
if (this.$route.params.productId) {
|
||||
this.productId = parseInt(this.$route.params.productId, 10);
|
||||
}
|
||||
*/
|
||||
this.getSuggestMetric();//this.getSuggestMetric(this.productId);
|
||||
},
|
||||
|
||||
/*metric部分相关方法--end*/
|
||||
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
418
nezha-fronted/src/components/page/dashboard/chartMetric.vue
Normal file
418
nezha-fronted/src/components/page/dashboard/chartMetric.vue
Normal file
@@ -0,0 +1,418 @@
|
||||
<style scoped>
|
||||
.without-bottom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ivu-select-dropdown {
|
||||
max-height: 100px;
|
||||
}
|
||||
.error-info-text {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
line-height: 1;
|
||||
padding-top: 6px;
|
||||
color: #ed3f14;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
}
|
||||
.error-text {
|
||||
color: #ed3f14;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.nz-btn-style-higher{
|
||||
line-height: 22px;
|
||||
}
|
||||
.li-list-part {
|
||||
width: 90%;
|
||||
height: 130px;
|
||||
border: 1px solid #cfcfcf;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.li-list-part-label-val-list {
|
||||
width: 100%;
|
||||
height: 165px;
|
||||
border: 1px solid #cfcfcf;
|
||||
overflow-y: auto;
|
||||
margin-top: -10px;/*21*/
|
||||
border: 0px;
|
||||
}
|
||||
.no-list-style{
|
||||
list-style: none;
|
||||
}
|
||||
.li-cursor{
|
||||
cursor: pointer;
|
||||
}
|
||||
.activeColor {
|
||||
background-color: #409EFF;
|
||||
}
|
||||
.metric-title-label{
|
||||
margin-bottom:8px;
|
||||
}
|
||||
.metric-title-position{
|
||||
margin-bottom:-5px;
|
||||
}
|
||||
.metric-title-row-position{
|
||||
margin-top:-10px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<el-form :model="elementInfo" ref="elementInfo">
|
||||
<el-row>
|
||||
<el-col span="16">
|
||||
{{$t('dashboard.panel.chartForm.metric')}}
|
||||
</el-col>
|
||||
<el-col span="8">
|
||||
<div class="nz-btn-group float-left" v-show="tableShow == 2">
|
||||
<button @click="clickTabelShow(1,'normal')" class="nz-btn nz-btn-size-small nz-btn-style-light float-left">
|
||||
<span>{{$t('dashboard.metric.normal')}}</span>
|
||||
</button>
|
||||
<button @click="" class="nz-btn nz-btn-size-small nz-btn-disabled nz-btn-style-normal float-left">
|
||||
<span>{{$t('dashboard.metric.expert')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="nz-btn-group float-left" v-show="tableShow == 1">
|
||||
<button @click="" class="nz-btn nz-btn-size-small nz-btn-disabled nz-btn-style-normal float-left">
|
||||
<span>{{$t('dashboard.metric.normal')}}</span>
|
||||
</button>
|
||||
<button @click="clickTabelShow(2,'expert')" class="nz-btn nz-btn-size-small nz-btn-style-light float-left">
|
||||
<span>{{$t('dashboard.metric.expert')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show="tableShow == 2">
|
||||
<el-col span="22">
|
||||
<el-form-item prop="expression" ><!--expression和metric的验证只能有一个,不能同时存在??:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
|
||||
<el-input size="mini" type="textarea" maxlength="1024" show-word-limit v-model="elementInfo.expression" :placeholder="this.$t('dashboard.metric.expertTip')" ></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col span="2">
|
||||
<button type="button" v-if="countTotal > 1" @click="deleteTarget" class="nz-btn nz-btn-size-normal nz-btn-style-light nz-btn-style-square nz-btn-style-higher"><span class="top-tool-btn-txt"><i class="el-icon-close"></i></span></button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show="tableShow == 1">
|
||||
<el-col span="22">
|
||||
<el-form-item :label-width="80" prop="metric" ><!--:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
|
||||
<el-select ref="metricSelect" placeholder="" popper-class="" size="small" v-model="elementInfo.metric" @change="selectMetric">
|
||||
<el-option v-for="(item, index) in metricShowList.arr" :key="item.metric + index"
|
||||
:value="item.metric">{{item.metric}}</el-option>
|
||||
</el-select>
|
||||
<span v-if="metricShowList.text" class="error-info-text">{{metricShowList.text}}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col span="2">
|
||||
<button type="button" v-if="countTotal > 1" @click="deleteTarget" class="nz-btn nz-btn-size-normal nz-btn-style-light nz-btn-style-square nz-btn-style-higher"><span class="top-tool-btn-txt"><i class="el-icon-close"></i></span></button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row v-if="elementInfo.metric" v-show="tableShow == 1"><!--v-if="elementInfo.tagList.length > 0"-->
|
||||
<el-col span="12">
|
||||
<div class="metric-title-label">{{elementInfo.metric}}</div>
|
||||
<div class="li-list-part">
|
||||
<ul class="no-list-style">
|
||||
<li class="li-cursor" v-if="!item.isSelect"
|
||||
v-for="(item,index) in elementInfo.tagList"
|
||||
@click="getLidata(index,item)"
|
||||
:key="index">
|
||||
<div>
|
||||
<span >{{ item.name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col span="12">
|
||||
<div class="li-list-part-label-val-list" >
|
||||
<el-form-item class="metric-title-position" v-for="(item, index) in elementInfo.selectedTagList" :key="index" :label="item.name" :label-width="100" :ref="'tagItem' + index" :prop="'tagList.' + index + '.value'" >
|
||||
<el-row class="metric-title-row-position" >
|
||||
<!--多选列表 -->
|
||||
<el-col span="20" >
|
||||
<el-select v-model="item.value" ref="tagSelect" size="small"
|
||||
placeholder=""
|
||||
collapse-tags
|
||||
multiple>
|
||||
<el-option v-for="(op, j) in elementInfo.selectedTagList[index].list" :key="op + j" :value="op">{{op}}</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col span="4" >
|
||||
<button type="button" @click="deleteMetricLabel(item,index)" class="nz-btn nz-btn-size-normal nz-btn-style-light nz-btn-style-square nz-btn-style-higher"><span class="top-tool-btn-txt"><i class="el-icon-close"></i></span></button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
<script>
|
||||
import bus from '../../../libs/bus';
|
||||
|
||||
export default {
|
||||
name: 'chartTag',
|
||||
props: {
|
||||
// 序号
|
||||
pointer: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// metric列表
|
||||
metricList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
countTotal: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
//multipleSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableShow: 1, // 1.normal; 2.expert
|
||||
// 指标信息
|
||||
elementInfo: {
|
||||
metric: '',//当前选中的metric名称
|
||||
type:'normal',
|
||||
// name: '',
|
||||
tagList: [], // 标签列表
|
||||
selectedTagList:[],//已选中的标签列表
|
||||
expression:''
|
||||
},
|
||||
metricLoading: false,
|
||||
keydataList: [], // tag标签键列表
|
||||
target: null, // 获取到的数据
|
||||
tagSet: null, // 根据你metric获取的tags信息
|
||||
setDataFlag: false, // true时为获取数据,编辑状态
|
||||
vendorCount: '',
|
||||
};
|
||||
},
|
||||
watch: {},
|
||||
beforeDestroy() {},
|
||||
methods: {
|
||||
// 删除该选项,第一步,传递要删除的参数
|
||||
deleteTarget() {
|
||||
this.$emit('on-delete-target', this.pointer);
|
||||
},
|
||||
// 第二步,on-delete-target回调,保存数据
|
||||
subSave() {
|
||||
bus.chartAddInfo.metricTarget[this.pointer] = this.elementInfo;
|
||||
this.$emit('sub-save-ok');
|
||||
},
|
||||
// 第三步,将数据重新赋值,sub-save-ok回调
|
||||
setSubdata(index) {
|
||||
if (index === this.pointer) {
|
||||
this.elementInfo.metric = bus.chartAddInfo.metricTarget[this.pointer].metric;
|
||||
// 当该项metric为空时,重置一下
|
||||
if (!this.elementInfo.metric && this.$refs.metricSelect) {
|
||||
this.$refs.metricSelect.reset();
|
||||
}
|
||||
this.elementInfo.tagList = bus.chartAddInfo.metricTarget[this.pointer].tagList;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// (最后整体保存添加的图标的时候执行)保存, chartdata点击确认后保存本身数据并返回给chartdata
|
||||
saveTarget(pointer) {
|
||||
if (this.pointer === pointer) {
|
||||
this.$refs.elementInfo.validate((valid) => {
|
||||
if (valid) {//根据设置的rules进行验证,验证通过,则返回,继续进行保存(每个el-form-item都需要验证)
|
||||
this.$emit('on-add-target-success', this.elementInfo, pointer);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 选择metric
|
||||
selectMetric() {
|
||||
if (this.elementInfo.metric) {//选择了metric,则设置tagList,否则设置为空
|
||||
this.getSuggestTags(this.elementInfo.metric);
|
||||
} else {
|
||||
this.elementInfo.tagList = [];
|
||||
}
|
||||
},
|
||||
// 选择主机
|
||||
/*
|
||||
selectHost(arr, index) {
|
||||
this.elementInfo.tagList[index].value = arr;
|
||||
if (this.$refs.elementInfo && this.$refs[`tagItem${index}`]) {
|
||||
this.$refs.elementInfo.validateField(`tagList.${index}.value`);
|
||||
}
|
||||
},
|
||||
*/
|
||||
/*
|
||||
selectTag(index) {//多选列表改变时的操作:为了* 的操作,此处不需要
|
||||
const arr = this.elementInfo.tagList[index].value;
|
||||
if (arr.length > 0 && arr.indexOf('*') > -1) {
|
||||
this.elementInfo.tagList[index].value = ['*'];
|
||||
}
|
||||
},
|
||||
*/
|
||||
// 获取tags列表
|
||||
getSuggestTags(metric) {
|
||||
this.$get('metric/labelName?metric='+metric).then(response => {
|
||||
this.elementInfo.selectedTagList = [];
|
||||
this.elementInfo.tagList = [];
|
||||
if (response.code === 200) {
|
||||
if(response.data.list){
|
||||
//this.elementInfo.tagList = response.data.list;
|
||||
response.data.list.forEach((item) => {
|
||||
const tagObj = {
|
||||
name: item.name,
|
||||
isSelect:false//当前元素是否被选中,默认都未选中
|
||||
};
|
||||
this.elementInfo.tagList.push(tagObj);
|
||||
});
|
||||
}else{
|
||||
response.data.forEach((item) => {
|
||||
const tagObj = {
|
||||
name: item.name,
|
||||
isSelect:false //当前元素是否被选中,默认都未选中
|
||||
};
|
||||
this.elementInfo.tagList.push(tagObj);
|
||||
});
|
||||
}
|
||||
//this.elementInfo.tagList = response.data.list;
|
||||
}else {
|
||||
this.elementInfo.tagList = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
getStyles(width) {
|
||||
return `width: ${width}px;`;
|
||||
},
|
||||
filterMethod(value, option) {
|
||||
return option.toUpperCase().indexOf(value.toUpperCase()) !== -1;
|
||||
},
|
||||
|
||||
|
||||
// 编辑已有图表状态时,先填充数据
|
||||
setMdata(data) {
|
||||
this.setDataFlag = true;
|
||||
this.target = Object.assign({}, data);
|
||||
this.elementInfo.metric = data.metric;
|
||||
this.selectMetric(); // 获取tag
|
||||
// this.proTags(data.tags);
|
||||
},
|
||||
clearHistory() {
|
||||
this.elementInfo.metric = '';
|
||||
this.setDataFlag = false;
|
||||
if (this.$refs.elementInfo) {
|
||||
this.$refs.elementInfo.resetFields();//???
|
||||
}
|
||||
if (this.$refs.metricSelect) {
|
||||
this.$refs.metricSelect.reset();
|
||||
}
|
||||
this.elementInfo.tagList = [];
|
||||
},
|
||||
// 获取文本宽度
|
||||
getWidth(str) {
|
||||
const sensor = document.createElement('pre');
|
||||
sensor.innerHTML = str;
|
||||
sensor.style.display = 'inline-block';
|
||||
sensor.style.width = 'auto';
|
||||
sensor.style.visibility = 'hidden';
|
||||
sensor.style.height = 0;
|
||||
sensor.style.position = 'relative';
|
||||
sensor.style['z-index'] = -10;
|
||||
document.body.appendChild(sensor);
|
||||
const width = sensor.offsetWidth;
|
||||
document.body.removeChild(sensor);
|
||||
const widthL = width > 180 ? width : 180;
|
||||
return widthL;
|
||||
},
|
||||
// 将tag添加到相应框内
|
||||
proTags(data) {
|
||||
const dou = data.indexOf(',');
|
||||
// 只有一组tag
|
||||
if (dou === -1) {
|
||||
const set = data.split('=');
|
||||
const tagValueArr = set[1].indexOf('|') > -1 ? set[1].split('|') : [set[1]];
|
||||
const tagIndex = this.elementInfo.tagList.findIndex(t => t.name === set[0]);
|
||||
if (tagIndex > -1) {
|
||||
this.elementInfo.tagList[tagIndex].value = tagValueArr;
|
||||
}
|
||||
} else { // 多组tag
|
||||
const mid = data.split(','); // ['key=v1','key=v2|v3',....]
|
||||
mid.forEach((item) => {
|
||||
const setInner = item.split('=');
|
||||
const innerValueArr = setInner[1].indexOf('|') > -1 ?
|
||||
setInner[1].split('|') : [setInner[1]];
|
||||
const tagIndex = this.elementInfo.tagList.findIndex(t => t.name === setInner[0]);
|
||||
if (tagIndex > -1) {
|
||||
this.elementInfo.tagList[tagIndex].value = innerValueArr;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
//metricLabelName单击事件
|
||||
getLidata(index, item) {
|
||||
if(!item.isSelect){
|
||||
this.vendorCount = index;
|
||||
//查询metricLabel名称对应的LabelValue
|
||||
this.$get('metric/labelVal?metric='+this.elementInfo.metric+"&name="+item.name).then(response => {
|
||||
const tagObj = {
|
||||
name: item.name,//选中的metricLabel名称
|
||||
list:[],//metricLabel名称对应的LabelValue
|
||||
value:[]//最终选择的值
|
||||
};
|
||||
//this.elementInfo.selectedTagList = [];
|
||||
if (response.code === 200) {
|
||||
if(response.data.list){
|
||||
response.data.list.forEach((item) => {
|
||||
tagObj.list.push(item.value)
|
||||
});
|
||||
this.elementInfo.selectedTagList.push(tagObj);
|
||||
}else{
|
||||
response.data.forEach((item) => {
|
||||
tagObj.list.push(item.value)
|
||||
});
|
||||
this.elementInfo.selectedTagList.push(tagObj);
|
||||
}
|
||||
}else {
|
||||
this.elementInfo.selectedTagList.push(tagObj);
|
||||
}
|
||||
});
|
||||
item.isSelect = true;
|
||||
}
|
||||
},
|
||||
//删除MetricLabel时,需要将tagList中的isSelect设置为false,并删除elementInfo.selectedTagList里对应的元素
|
||||
deleteMetricLabel(item,index) {
|
||||
this.elementInfo.tagList.forEach((tagItem) => {
|
||||
if(tagItem.name===item.name){
|
||||
tagItem.isSelect = false;
|
||||
}
|
||||
});
|
||||
this.elementInfo.selectedTagList.splice(index,1);
|
||||
//this.$emit('on-delete-target', this.pointer);
|
||||
},
|
||||
clickTabelShow(val,type){
|
||||
this.tableShow = val;
|
||||
this.elementInfo.type = type;
|
||||
if(val===2){
|
||||
this.elementInfo.metric='';
|
||||
}
|
||||
if(val===1){
|
||||
this.elementInfo.expression='';
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
metricShowList() {
|
||||
const obj = {
|
||||
arr: [...this.metricList],
|
||||
text: '',
|
||||
};
|
||||
return obj;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
bus.$on('clear_history', () => {
|
||||
this.clearHistory();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -22,11 +22,15 @@
|
||||
.content-right-option .el-icon-view:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/* begin-chart list*/
|
||||
.table-list {
|
||||
margin-top: 100px;
|
||||
}
|
||||
margin-top: 10px;
|
||||
|
||||
}
|
||||
.box-content {
|
||||
position: relative;
|
||||
}
|
||||
/* end-chart list*/
|
||||
/* begin--Panel-自定义可编辑的el-select下拉框样式*/
|
||||
.panel-dropdown-btn {
|
||||
display: inline-block;
|
||||
@@ -74,11 +78,11 @@
|
||||
</div>
|
||||
<div class="content-right">
|
||||
<div class="top-tools">
|
||||
<button @click="toAdd" class="nz-btn nz-btn-size-normal nz-btn-style-normal float-right">
|
||||
<button @click="toAddChart" class="nz-btn nz-btn-size-normal nz-btn-style-normal float-right">
|
||||
<span class="top-tool-btn-txt">{{$t('overall.add')}}</span>
|
||||
</button>
|
||||
<div class="top-tool-search float-right"><search-input :searchMsg="searchMsg" @search="search"></search-input></div>
|
||||
<el-select class="right-box-row-with-btn" popper-class="" v-model="showPanel.id" placeholder="" size="small">
|
||||
<el-select class="right-box-row-with-btn" popper-class="" v-model="showPanel.id" placeholder="" size="small" @change="panelChange">
|
||||
<el-option >
|
||||
<span class="panel-dropdown-btn-create" @click.stop="toAdd" >{{$t('dashboard.panel.createPanelTitleSec')}}</span>
|
||||
</el-option>
|
||||
@@ -96,73 +100,29 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="table-list">
|
||||
<!--<div class="box-content">
|
||||
<div class="table-list" style="overflow-y:auto;height: 100%;">
|
||||
<div class="box-content">
|
||||
<chart-list
|
||||
@on-edit-chart="editData"
|
||||
@on-remove-chart="removeData" ref="chartList"></chart-list>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="right-box">
|
||||
<div class="right-box right-box-panel" v-if="rightBox.show">
|
||||
<!-- begin--顶部按钮-->
|
||||
<div class="right-box-top-btns">
|
||||
<div class="right-box-top-btn right-box-top-btn-full" @click="esc">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
<span>{{$t('overall.esc')}}</span>
|
||||
</div>
|
||||
<div class="right-box-top-btn right-box-top-btn-full" @click="save">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-edit-outline"></i>
|
||||
</div>
|
||||
<span >{{$t('overall.save')}}</span>
|
||||
</div>
|
||||
<div @click="del(panel)" class="right-box-top-btn" v-if="panel.id != ''">
|
||||
<div class="right-box-btn-icon">
|
||||
<i class="el-icon-delete"></i>
|
||||
</div>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end--顶部按钮-->
|
||||
|
||||
<!-- begin--标题-->
|
||||
<div class="right-box-title">{{rightBox.title}}</div>
|
||||
<!-- end--标题-->
|
||||
|
||||
<!-- begin--表单-->
|
||||
<div class="right-box-form">
|
||||
<div class="right-box-form-row">
|
||||
<div class="right-box-form-label">{{$t('dashboard.panel.panelForm.panelName')}}</div>
|
||||
<div class="right-box-form-content">
|
||||
<el-input placeholder="" maxlength="64" show-word-limit v-model="panel.name" size="small"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end--表单-->
|
||||
|
||||
<!-- begin--底部按钮-->
|
||||
<div class="right-box-bottom-btns">
|
||||
<div @click="esc()" :class="{'right-box-bottom-btn-50': true}" class="right-box-bottom-btn right-box-bottom-btn-cancel">{{$t('overall.cancel')}}</div><div @click="save()" class="right-box-bottom-btn right-box-bottom-btn-50">{{panel.id == '' ? $t('overall.create') : $t('overall.save')}}</div>
|
||||
</div>
|
||||
<!-- end--底部按钮-->
|
||||
|
||||
</div>
|
||||
</transition>
|
||||
<panel-box :panel="panel" @reload="panelReload" @reloadForDel="panelReloadForDel" ref="panelBox"></panel-box>
|
||||
<chart-box ref="addChartModal" @on-create-success="createSuccess"></chart-box>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
import ChartBox from "./chartBox";
|
||||
import ChartList from '../../charts/chart-list';
|
||||
|
||||
export default {
|
||||
name: "panel",
|
||||
data() {
|
||||
return {
|
||||
rightBox: { //弹出框相关
|
||||
rightBox: { //面板弹出框相关
|
||||
show: false,
|
||||
title: this.$t('dashboard.panel.createPanelTitle')
|
||||
},
|
||||
@@ -174,6 +134,18 @@ export default {
|
||||
id:'',
|
||||
name: ''
|
||||
},
|
||||
chart: {
|
||||
id:'',
|
||||
title: '',
|
||||
type:'line',
|
||||
span:12,
|
||||
height:400,
|
||||
elements:{
|
||||
id: '',
|
||||
expression: '',
|
||||
type: ''
|
||||
}
|
||||
},
|
||||
pageObj: {
|
||||
pageNo: 1,
|
||||
pageSize: 9999,//此处获取所有数据,所以设置一个较大的值
|
||||
@@ -198,9 +170,32 @@ export default {
|
||||
}],
|
||||
},
|
||||
searchLabel: {}, //搜索参数
|
||||
|
||||
//---图表相关参数--start
|
||||
dataList: [], // 数据列表
|
||||
searchName: '', // 搜索名称
|
||||
filter: { // 过滤条件
|
||||
//productId: 0,
|
||||
panelId: 0,
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
},
|
||||
panelId: 0,
|
||||
removeModal: false, // 删除弹出
|
||||
deleteObj: {}, // 删除对象
|
||||
//---图表相关参数--end
|
||||
}
|
||||
},
|
||||
components:{
|
||||
'chart-box':ChartBox,
|
||||
'chart-list':ChartList
|
||||
},
|
||||
methods: {
|
||||
//面板相关操作
|
||||
panelChange(){
|
||||
this.filter.panelId = this.showPanel.id;
|
||||
this.getData(this.filter);
|
||||
},
|
||||
del: function(u) {
|
||||
this.$confirm(this.$t("tip.confirmDelete"), {
|
||||
confirmButtonText: this.$t("tip.yes"),
|
||||
@@ -223,40 +218,61 @@ export default {
|
||||
},
|
||||
toEdit: function(u) {
|
||||
this.panel = Object.assign({}, u);
|
||||
this.rightBox.title = this.$t("dashboard.panel.editPanelTitle") ;
|
||||
this.rightBox.show = true;
|
||||
this.$refs.panelBox.setTitle(this.$t("dashboard.panel.editPanelTitle"));
|
||||
this.$refs.panelBox.show(true);
|
||||
},
|
||||
toAdd: function() {
|
||||
this.cleanPanel();
|
||||
this.rightBox.title = this.$t("dashboard.panel.createPanelTitle");
|
||||
this.rightBox.show = true;
|
||||
this.$refs.panelBox.show(true);
|
||||
this.panel = {id: '', name: ''};
|
||||
this.$refs.panelBox.setTitle(this.$t("dashboard.panel.createPanelTitle"));
|
||||
},
|
||||
save: function() {
|
||||
if (this.panel.id) {
|
||||
this.$put('panel', this.panel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
panelReload(){
|
||||
this.getTableData();
|
||||
this.esc();
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
},
|
||||
panelReloadForDel:function(){
|
||||
if(this.showPanel.id===this.panel.id){
|
||||
this.showPanel.id ='';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$post('panel', this.panel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
|
||||
this.getTableData();
|
||||
this.esc();
|
||||
},
|
||||
/*图表相关操作--start*/
|
||||
toAddChart:function(){
|
||||
this.$refs.addChartModal.show(true);
|
||||
this.$refs.addChartModal.createData(this.showPanel.id);//初始化创建图表需要的初始数据
|
||||
},
|
||||
|
||||
// 编辑图表信息,打开编辑弹窗
|
||||
editData(data) {
|
||||
this.$refs.addChartModal.editData(data, this.panelId, this.filter.productId);
|
||||
},
|
||||
// 移除图表
|
||||
removeData(data) {
|
||||
this.deleteObj = data;
|
||||
this.removeModal = true;
|
||||
},
|
||||
// 图表创建成功,回调panel页面,进行图表的刷新
|
||||
createSuccess(msg, data,params) {
|
||||
/*
|
||||
if (data && data.code === 200) {
|
||||
|
||||
if (msg === 'create') {
|
||||
//this.$message.success('添加成功');
|
||||
} else {
|
||||
this.$message.error(response.msg);
|
||||
//this.$message.success('编辑成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
this.filter.panelId = this.showPanel.id;
|
||||
this.getData(this.filter);
|
||||
},
|
||||
esc: function() {
|
||||
this.rightBox.show = false;
|
||||
// 获取数据,用在子页面
|
||||
getData(params) {
|
||||
this.$refs.chartList.initData(params);
|
||||
},
|
||||
|
||||
/*图表相关操作--end*/
|
||||
|
||||
//公用操作
|
||||
jumpTo(data,id) {
|
||||
this.$store.state.assetData.moduleData = data
|
||||
this.$store.state.assetData.selectedData = id
|
||||
@@ -273,18 +289,19 @@ export default {
|
||||
this.$get('panel', this.searchLabel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.panelData = response.data.list;
|
||||
if(this.panelData.length>0 ){
|
||||
this.filter.panelId = this.panelData[0].id;
|
||||
}
|
||||
if( response.data.list.length>0 && this.showPanel.id===''){
|
||||
this.showPanel.id = response.data.list[0].id;
|
||||
this.filter.panelId = this.showPanel.id;
|
||||
}
|
||||
this.pageObj.total = response.data.total
|
||||
|
||||
this.getData(this.filter);
|
||||
}
|
||||
})
|
||||
},
|
||||
cleanPanel: function() {
|
||||
this.panel = {
|
||||
id: '',
|
||||
name: ''
|
||||
}
|
||||
|
||||
},
|
||||
pageNo(val) {
|
||||
this.pageObj.pageNo = val;
|
||||
@@ -309,10 +326,19 @@ export default {
|
||||
this.getTableData();
|
||||
},
|
||||
mounted: function() {
|
||||
|
||||
},
|
||||
computed: {
|
||||
refreshPanel() {
|
||||
return this.$store.state.panelListReload;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
refreshPanel(n, o) {
|
||||
if (n) {
|
||||
this.getTableData();
|
||||
this.$store.commit('panelListChange', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
175
nezha-fronted/src/libs/bus.js
Normal file
175
nezha-fronted/src/libs/bus.js
Normal file
@@ -0,0 +1,175 @@
|
||||
import Vue from 'vue';
|
||||
//import Cookies from 'js-cookie';
|
||||
//import md5 from 'md5';
|
||||
|
||||
Date.prototype.setStart = function() {
|
||||
this.setHours(0);
|
||||
this.setMinutes(0);
|
||||
this.setSeconds(0);
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
Date.prototype.setEnd = function() {
|
||||
this.setHours(23);
|
||||
this.setMinutes(59);
|
||||
this.setSeconds(59);
|
||||
};
|
||||
|
||||
export default new Vue({
|
||||
data() {
|
||||
return {
|
||||
selectDate: [], // 选中的时间段
|
||||
emailReg: /^[a-zA-Z0-9]{1,10}@[a-zA-Z0-9]{1,5}\.[a-zA-Z0-9]{1,5}$/,
|
||||
// 创建策略信息
|
||||
buildRuleInfo: {
|
||||
triggers: [],
|
||||
actions: [],
|
||||
},
|
||||
backtoRulelist: '', // 返回策略列表页信息
|
||||
//role: md5(1),
|
||||
role:1,
|
||||
// 创建图表信息
|
||||
chartAddInfo: {
|
||||
metricTarget: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 获取初始化时间,默认最近一周
|
||||
getDefaultDate() {
|
||||
let start = this.getDays(-7);
|
||||
let end = this.getDays(0);
|
||||
start.setStart();
|
||||
end.setEnd();
|
||||
// let start = this.getHoursTime(-1);
|
||||
// let end = this.getHoursTime(0);
|
||||
start = this.timeFormate(start, 'yyyy-MM-dd hh:mm:ss');
|
||||
end = this.timeFormate(end, 'yyyy-MM-dd hh:mm:ss');
|
||||
this.selectDate = [start, end];
|
||||
},
|
||||
getHoursTime(hours) {
|
||||
const today = new Date().getTime();
|
||||
const date = new Date(today + (hours * 60 * 60 * 1000));
|
||||
return date;
|
||||
},
|
||||
// 初始化日期
|
||||
getDays(days) {
|
||||
const today = new Date().getTime();
|
||||
const date = new Date(today + (days * 24 * 60 * 60 * 1000));
|
||||
return date;
|
||||
},
|
||||
formatDate(date, type) {
|
||||
const yy = date.getFullYear();
|
||||
const dateM = date.getMonth() + 1;
|
||||
const mm = dateM > 9 ? dateM : `0${dateM}`;
|
||||
const dateD = date.getDate();
|
||||
const dd = dateD > 9 ? dateD : `0${dateD}`;
|
||||
if (type) {
|
||||
return `${yy}${type}${mm}${type}${dd}`;
|
||||
}
|
||||
return `${yy}${mm}${dd}`;
|
||||
},
|
||||
timeFormate(date, fmt) {
|
||||
const time = new Date(date);
|
||||
let fm = fmt;
|
||||
// fmt 自定义格式,如:yy-MM-dd
|
||||
let week = '';
|
||||
switch (time.getDay()) {
|
||||
case 0:
|
||||
week = '周日';
|
||||
break;
|
||||
case 1:
|
||||
week = '周一';
|
||||
break;
|
||||
case 2:
|
||||
week = '周二';
|
||||
break;
|
||||
case 3:
|
||||
week = '周三';
|
||||
break;
|
||||
case 4:
|
||||
week = '周四';
|
||||
break;
|
||||
case 5:
|
||||
week = '周五';
|
||||
break;
|
||||
case 6:
|
||||
week = '周六';
|
||||
break;
|
||||
default:
|
||||
week = '';
|
||||
break;
|
||||
}
|
||||
const o = {
|
||||
'M+': time.getMonth() + 1, // 月份
|
||||
'd+': time.getDate(), // 日
|
||||
'h+': time.getHours(), // 小时
|
||||
'm+': time.getMinutes(), // 分
|
||||
's+': time.getSeconds(), // 秒
|
||||
'q+': Math.floor((time.getMonth() + 3) / 3), // 季度
|
||||
S: time.getMilliseconds(), // 毫秒
|
||||
w: week,
|
||||
};
|
||||
if (/(y+)/.test(fm)) {
|
||||
fm = fm.replace(RegExp.$1, (time.getFullYear().toString()).substr(4 - RegExp.$1.length));
|
||||
}
|
||||
Object.keys(o).forEach((k) => {
|
||||
if (new RegExp(`(${k})`).test(fm)) {
|
||||
fm = fm.replace(RegExp.$1, (RegExp.$1.length === 1) ?
|
||||
(o[k]) : ((`00${o[k]}`).substr((`${o[k]}`).length)));
|
||||
}
|
||||
});
|
||||
return fm;
|
||||
},
|
||||
isEmptyObject(obj) {
|
||||
if (obj) {
|
||||
let name = '';
|
||||
// eslint-disable-next-line
|
||||
for (name in obj) { return false; }
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateEmail(rule, value, callback) {
|
||||
if (value === '') {
|
||||
callback(new Error('请输入邮箱'));
|
||||
} else if (!this.emailReg.test(value)) {
|
||||
callback(new Error('邮箱格式不正确'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
getNumStr(num) {
|
||||
if (num >= 1000) {
|
||||
const kbNum = num / 1000;
|
||||
if (kbNum >= 1000) {
|
||||
const mbNum = kbNum / 1000;
|
||||
if (mbNum > 1000) {
|
||||
const gbNum = mbNum / 1000;
|
||||
if (gbNum > 1000) {
|
||||
const tbNum = gbNum / 1000;
|
||||
if (tbNum > 1000) {
|
||||
const pbNum = tbNum / 1000;
|
||||
return `${pbNum.toFixed(2)}PB`;
|
||||
}
|
||||
return `${tbNum.toFixed(2)}TB`;
|
||||
}
|
||||
return `${gbNum.toFixed(2)}GB`;
|
||||
}
|
||||
return `${mbNum.toFixed(2)}MB`;
|
||||
}
|
||||
return `${kbNum.toFixed(2)}KB`;
|
||||
}
|
||||
return num.toFixed(2);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDefaultDate();
|
||||
},
|
||||
computed: {
|
||||
/*
|
||||
isAdmin() {
|
||||
return this.role === Cookies.get('owl_role');
|
||||
},
|
||||
*/
|
||||
},
|
||||
});
|
||||
@@ -25,6 +25,7 @@ import assetAddUnit from "./components/page/asset/assetAddUnit"; //资产添加
|
||||
import assetEditUnit from "./components/page/asset/assetEditUnit"; //资产添加组件
|
||||
import alertConfigBox from "./components/common/rightBox/alertConfigBox"; //告警规则弹框组件
|
||||
import dcConfigBox from "./components/common/dcConfig"; //idc配置弹框组件
|
||||
import panelBox from "./components/common/rightBox/panelBox"; //面板弹框组件
|
||||
|
||||
Vue.component("Pagination", Pagination);
|
||||
Vue.component("searchInput", searchInput);
|
||||
@@ -36,6 +37,7 @@ Vue.component("assetAddUnit", assetAddUnit);
|
||||
Vue.component("assetEditUnit", assetEditUnit);
|
||||
Vue.component("alert-config-box", alertConfigBox);
|
||||
Vue.component("idc-config-box", dcConfigBox);
|
||||
Vue.component("panel-box", panelBox);
|
||||
|
||||
Vue.prototype.$axios = axios;
|
||||
Vue.prototype.$post = post;
|
||||
|
||||
@@ -17,6 +17,7 @@ const store = new Vuex.Store({
|
||||
projectListChange: 0,
|
||||
moduleListChange: 0,
|
||||
flushDataSign: false,
|
||||
panelListReload:false
|
||||
},
|
||||
getters: {
|
||||
},
|
||||
@@ -32,6 +33,9 @@ const store = new Vuex.Store({
|
||||
},
|
||||
moduleListChange(state) {
|
||||
state.moduleListChange = state.moduleListChange + 1;
|
||||
},
|
||||
panelListChange(state,status){
|
||||
state.panelListReload = status;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
Reference in New Issue
Block a user