feat:新增功能

1.指标预览->预览图表功能
2.指标预览->单图展示的创建看板和选择已有看板
3.指标预览->单图展示和多图展示的切换及对应曲线展示
说明:多图展示的创建看板和选择已有看板未实现,国际化未实现,样式需要进一步调整,图表展示中tip信息显示位置需要调整
fix:修改BUG
1.dashboard无需分页pageSize为-1
2.panel图表line类型曲线设置为平滑
3.panel图表y轴数据格式化
This commit is contained in:
hanyuxia
2020-01-14 21:20:30 +08:00
parent 92a9ef2afd
commit 65646c0fb3
14 changed files with 1913 additions and 80 deletions

View File

@@ -166,6 +166,8 @@ export default {
const seriesItem = { const seriesItem = {
theData: { theData: {
name: '', name: '',
symbol:'none', //去掉点
smooth:true, //曲线变平滑
data: [], data: [],
type:chartInfo.type, type:chartInfo.type,
//visible: true, //visible: true,

View File

@@ -5,7 +5,7 @@
<div class="line-chart-block" > <div class="line-chart-block" >
<div class="edit"> <div class="edit">
<div class="list-icon" > <div class="list-icon" v-if="firstShow">
<span @click="refreshChart" :title="$t('dashboard.refresh')" class="set-icon" v-if="showSetting"> <span @click="refreshChart" :title="$t('dashboard.refresh')" class="set-icon" v-if="showSetting">
<i class="el-icon-refresh-right"></i> <i class="el-icon-refresh-right"></i>
</span> </span>
@@ -108,8 +108,9 @@ export default {
}, },
stableFilter: {}, // 保存数据使用,初始化起止时间,单图or多图等 stableFilter: {}, // 保存数据使用,初始化起止时间,单图or多图等
legend:[], legend:[],
// firstShow: false, // 默认不显示操作按钮, firstShow: false, // 默认不显示操作按钮,
searchTime:[new Date().setHours(new Date().getHours()-1),new Date()], searchTime:[new Date().setHours(new Date().getHours()-1),new Date()],
oldSearchTime:[],
pickerOptions: { pickerOptions: {
shortcuts: [ shortcuts: [
{ {
@@ -207,7 +208,7 @@ export default {
methods: { methods: {
// chartSite用于区分是全屏显示还是局部显示 // chartSite用于区分是全屏显示还是局部显示
initChart(chartInfo, dataArg, ele, chartSite,legend) { initChart(chartInfo, dataArg, ele, chartSite,legend) {
//this.firstShow = true; // 展示操作按键 this.firstShow = true; // 展示操作按键
const self = this; const self = this;
this.chartType = ''; // 图表类型 this.chartType = ''; // 图表类型
if ( chartInfo.type === 4) {//line,bar if ( chartInfo.type === 4) {//line,bar
@@ -227,6 +228,8 @@ export default {
textStyle: { textStyle: {
//display: 'inline-block',//无此属性 //display: 'inline-block',//无此属性
width: '300px', width: '300px',
fontStyle:'normal',
fontWeight:'normal'
} }
}, },
color: ['#7bbfea', '#b3424a', '#f05b72', '#596032', '#bd6758', color: ['#7bbfea', '#b3424a', '#f05b72', '#596032', '#bd6758',
@@ -344,6 +347,35 @@ export default {
splitLine:{ splitLine:{
show:true show:true
}, },
axisLabel: {
formatter: function(num,index) {
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 `${parseFloat(gbNum.toFixed(2))}B`;
}
return `${parseFloat(mbNum.toFixed(2))}M`;
}
return `${parseFloat(kbNum.toFixed(2))}K`;
}
return parseFloat(num.toFixed(2));
//alert(1);
//return value;
//return this.getNumStr(value);
},
},
//boundaryGap:[0,0.2] //boundaryGap:[0,0.2]
}, },
useUTC: false,//使用本地时间 useUTC: false,//使用本地时间
@@ -406,6 +438,7 @@ export default {
return obj; return obj;
}); });
*/ */
this.echartStore.clear();
this.echartStore.setOption(option);//创建图表 this.echartStore.setOption(option);//创建图表
this.echartStore.hideLoading(); this.echartStore.hideLoading();
this.echartStore.resize({height:chartInfo.height});//,width:`${ele.clientWidth-100}`} this.echartStore.resize({height:chartInfo.height});//,width:`${ele.clientWidth-100}`}
@@ -473,6 +506,7 @@ export default {
this.seriesItem = seriesItem; this.seriesItem = seriesItem;
this.searchTime[0] = filter.start_time; this.searchTime[0] = filter.start_time;
this.searchTime[1] = filter.end_time; this.searchTime[1] = filter.end_time;
this.oldSearchTime = this.searchTime;
this.initChart(chartItem, seriesItem, this.$refs.lineChartArea, 'local',legend); this.initChart(chartItem, seriesItem, this.$refs.lineChartArea, 'local',legend);
/* /*
if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 4) { if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 4) {
@@ -505,12 +539,20 @@ export default {
}); });
*/ */
}, },
changeDate(st){
this.oldSearchTime=st;
this.searchTime = st;
this.filter.start_time = st[0];
this.filter.end_time = st[1];
},
// 全屏查看 // 全屏查看
showAllScreen() { showAllScreen() {
// 初始化同步时间 // 初始化同步时间
this.filter.start_time = this.stableFilter.start_time; this.filter.start_time = this.stableFilter.start_time;
this.filter.end_time = this.stableFilter.end_time; this.filter.end_time = this.stableFilter.end_time;
this.searchTime = this.oldSearchTime;
this.screenModal = true; this.screenModal = true;
this.dateChange();
}, },
dateChange(time) { dateChange(time) {
this.filter.start_time = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss'); this.filter.start_time = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss');
@@ -528,49 +570,28 @@ export default {
} }
let axiosArr = []; let axiosArr = [];
this.$nextTick(() => { this.$nextTick(() => {
let startTime = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss');
let endTime = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss');
if(!startTime || !endTime){//如果时间为空则默认取最近1小时
let now = new Date();
startTime = bus.timeFormate(now, 'yyyy-MM-dd hh:mm:ss');
endTime = bus.timeFormate(now.setHours(now.getHours()-1), 'yyyy-MM-dd hh:mm:ss');
this.searchTime[0] = startTime;
this.searchTime[1] = endTime;
}
let step = bus.getStep(startTime,endTime);
if (type === 'list') { // 普通模式,主控台使用 if (type === 'list') { // 普通模式,主控台使用
let startTime = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss');
let endTime = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss');
if(!startTime || !endTime){//如果时间为空则默认取最近1小时
let now = new Date();
startTime = bus.timeFormate(now, 'yyyy-MM-dd hh:mm:ss');
endTime = bus.timeFormate(now.setHours(now.getHours()-1), 'yyyy-MM-dd hh:mm:ss');
this.searchTime[0] = startTime;
this.searchTime[1] = endTime;
}
let step = bus.getStep(startTime,endTime);
axiosArr = this.data.elements.map((ele) => { axiosArr = this.data.elements.map((ele) => {
const filterItem = ele; const filterItem = ele;
return this.$get('/prom/api/v1/query_range?query='+filterItem.expression+"&start="+startTime+"&end="+endTime+'&step='+step); return this.$get('/prom/api/v1/query_range?query='+filterItem.expression+"&start="+startTime+"&end="+endTime+'&step='+step);
}); });
} else if (type === 'dashboard') { // 概览模式,指标概览中使用 } else if (type === 'dashboard') { // 概览模式,指标概览中使用
/*
// 概览模式,需要区分单独一个和多个 // 概览模式,需要区分单独一个和多个
if (this.stableFilter.chartCount === 'multiple') { if (this.stableFilter.chartCount === 'multiple') {
// 所有tag标签 axiosArr = [this.$get('/prom/api/v1/query_range?query='+this.data.title+"&start="+startTime+"&end="+endTime+'&step='+step)];
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 { } else {
axiosArr = [getQueryChart({ axiosArr = [this.$get('/prom/api/v1/query_range?query='+this.data.elements[0].expression+"&start="+startTime+"&end="+endTime+'&step='+step)];
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) => { axios.all(axiosArr).then((res) => {
@@ -591,6 +612,8 @@ export default {
const seriesItem = { const seriesItem = {
theData: { theData: {
name: '', name: '',
symbol:'none', //去掉点
smooth:true, //曲线变平滑
data: [], data: [],
type:this.data.type, type:this.data.type,
//visible: true, //visible: true,
@@ -669,6 +692,31 @@ export default {
this.echartStore.showLoading(); this.echartStore.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)}B`;
}
return `${mbNum.toFixed(2)}M`;
}
return `${kbNum.toFixed(2)}K`;
}
return num.toFixed(2);
},
/* /*
handleClose(done) { handleClose(done) {
/* /*
@@ -816,30 +864,7 @@ export default {
}); });
}, },
// 获取格式
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数组 // 获取tag数组
proTags(data) { proTags(data) {
const dou = data.indexOf(','); const dou = data.indexOf(',');

View File

@@ -25,6 +25,7 @@ const cn = {
}, },
//面板-侧滑框 //面板-侧滑框
createPanelTitle: "创建面板", createPanelTitle: "创建面板",
selectPanelTitle:"选择面板",
createPanelTitleSec:"创建新面板", createPanelTitleSec:"创建新面板",
editPanelTitle: "编辑面板", editPanelTitle: "编辑面板",
panelForm:{ panelForm:{

View File

@@ -34,6 +34,7 @@ const en = {
}, },
//面板-侧滑框 //面板-侧滑框
createPanelTitle: "Create Panel", createPanelTitle: "Create Panel",
selectPanelTitle:"Select Panel",
createPanelTitleSec:"Create New Panel", createPanelTitleSec:"Create New Panel",
editPanelTitle: "Panel", editPanelTitle: "Panel",
panelForm:{ panelForm:{

View File

@@ -0,0 +1,279 @@
<template key="metricPrePanelBox">
<transition name="right-box">
<div class="right-box right-box-panel z-top" v-if="rightBox.show">
<!-- begin--顶部按钮-->
<div class="right-box-top-btns">
<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>
<!-- end--顶部按钮-->
<!-- begin--标题-->
<div class="right-box-title">{{rightBox.title}}</div>
<!-- end--标题-->
<!-- begin--表单-->
<div class="right-box-form">
<el-form :model="chartSaveInfo" ref="chartSaveInfo">
<el-form-item label="Chart Name" prop="title" :rules="{ required: true, type: 'string', message: '请输入图表名称', trigger: 'change' }">
<el-input size="small" maxlength="64" show-word-limit v-model="chartSaveInfo.title"></el-input>
</el-form-item>
<el-form-item v-if="isCreatePanel" label="Panel Name" prop="name" :rules="{ required: true, type: 'string', message: '请输入看板名称', trigger: 'change' }">
<el-input size="small" maxlength="64" show-word-limit v-model="chartSaveInfo.name"></el-input>
</el-form-item>
<el-form-item v-else label="Select Panel" prop="panelId" :rules="{ required: true, type: 'number', message: '请选择看板', trigger: 'change' }">
<el-select ref="panelSelect" width="100%" v-model="chartSaveInfo.panelId"
popper-class="" placeholder="" size="small"
@change="panelSelect"
filterable>
<el-option v-for="(item, index) in panelList"
:key="index"
:value="item.id"
:label="item.name">{{item.name}}</el-option>
</el-select>
</el-form-item>
</el-form>
</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>
import bus from '../../../libs/bus';
export default {
name: "metricPrePanelBox",
props: {
chartInfo: Object,
elementTarget:Object,
seriesData:Object,
chartCount:Object
},
data() {
return {
rightBox: { //面板弹出框相关
show: false,
title: this.$t('dashboard.panel.createPanelTitle')
},
isCreatePanel: false,
// 创建看板信息
chartSaveInfo: {
title: '', // 图表名称
name: '', // panel名称
panelId: '', // 选择以后panelId
},
panelList: [],
panelItem: {}, // 保存看板信息
panelId: 0,
createChartList: [], // 创建多个图表的图表信息
}
},
methods: {
show(show) {
this.rightBox.show = show;
},
setTitle(title) {
this.rightBox.title = title;
},
setIsCreatePanel(isCreate) {
this.isCreatePanel = isCreate;
},
setChartTitle(title){
this.chartSaveInfo.title = title;
},
// 选择看板
panelSelect(id) {
if (id) {
const panel = this.panelList.find(p => p.id === id);
if (panel) {
this.panelItem = panel;
}
}
},
// 生成图表的时候,使用该函数,汇总创建图表参数
getOptions() {
//this.$refs.chartInfo.validate((valid) => {
const params = {
//panelId: this.panelId,
// title: this.chartSaveInfo.title,
type: this.chartInfo.type,
span: this.chartInfo.span,
height: this.chartInfo.height,
};
// 多图模式
if (this.chartCount === 'multiple') {
this.createChartList = [];
//alert(JSON.stringify(this.seriesData));
this.seriesData.forEach((queryItem) => {
const soleParam = Object.assign({}, params);
const elements = [];
let tagStr = ''; // tag字符串
let host = `${queryItem.metric}, `; // 名称
const tagsArr = Object.keys(queryItem.tags);
// 根据图表展示时获取的tag进行组装名称以及tag字符串
tagsArr.forEach((tag) => {
if (tag !== 'uuid') {
host += `${tag}=${queryItem.tags[tag]}, `;
if (tagStr) {
tagStr += `,${tag}=${queryItem.tags[tag]}`;
} else {
tagStr += `${tag}=${queryItem.tags[tag]}`;
}
}
});
// 图表中每条线的名字,去掉最后的逗号与空格
host = host.substring(0, host.length - 2);
soleParam.title = host;
elements.push({
expression: this.elementTarget.metric,
type: this.elementTarget.type,
});
soleParam.elements = elements;
//if (valid) {
this.addMultipleChart(soleParam);
//}
});
} else if (this.chartCount === 'single') {
params.title = this.chartSaveInfo.title;
const elements = [];
if(this.elementTarget.type==='normal'){
const metricStr = bus.tagsToString(this.elementTarget.metric,this.elementTarget.selectedTagList);
elements.push({
expression: metricStr,
type: this.elementTarget.type,
});
}else if(this.elementTarget.type==='expert'){
elements.push({
expression: this.elementTarget.expression,
type: this.elementTarget.type,
});
}
params.elements = elements;
//if (valid) {
this.addCharts(params);
//}
}
//});
},
// 新建单个图表single
addCharts(params) {
this.$post('panel/'+this.panelId+'/charts', params).then(response => {
if (response.code === 200) {
this.elementTarget = {};
//this.saveDisabled = true; // 创建成功后,不能再次创建??
this.$emit('reload');
this.esc();
this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
/*
this.$confirm(this.$t("tip.confirmDelete"), {
confirmButtonText: this.$t("tip.yes"),
cancelButtonText: this.$t("tip.no"),
type: 'warning'
}).then(() => {
});
*/
/*
this.$Modal.confirm({
title: '创建成功',
content: `是否前去看板:${this.panelItem.name},查看创建的图表`,
onOk: () => {
this.$router.push({
path: `/console/panel/detail/${this.panelId}/${this.productId}`,
query: {
product: this.$route.query.product,
panel: this.panelItem.name,
},
});
},
onCancel: () => {
this.cancel();
},
});
*/
} else {
this.$message.error(response.msg);
}
});
},
save: function() {
this.$refs.chartSaveInfo.validate((flag) => {
if (flag) { // 再判断弹出窗口内内容是否正确
if (this.isCreatePanel) { // 创建看板
this.panelItem.name = this.chartSaveInfo.name;
this.$post('panel', this.panelItem).then(response => {
if (response.code === 200) {
this.panelId = response.data.panel.id;
this.panelItem = response.data.panel;
this.getOptions();
//this.esc();
//this.$message({duration: 1000, type: 'success', message: this.$t("tip.saveSuccess")});
//this.$emit("reload");----询问是否去查看
} else {
this.$message.error(response.msg);
}
});
} else { // 选择看板
this.panelId = this.chartSaveInfo.panelId;
this.getOptions();
}
}
});
},
esc: function() {
this.rightBox.show = false;
// 取消创建modal
this.panelList = [];
this.chartSaveInfo.panelId = ''; // 选择panel
this.chartSaveInfo.name = ''; // panel名称创建
if (this.$refs.chartSaveInfo) {
this.$refs.chartSaveInfo.resetFields();
}
if (this.$refs.panelSelect) {
this.$refs.panelSelect.reset();
}
},
getTableData: function() {
this.$get('panel?pageNo=1&pageSize=-1').then(response => {
if (response.code === 200) {
this.panelList = response.data.list;
}else {
this.panelList = [];
}
})
},
},
created() {
},
mounted: function() {
},
watch: {
}
}
</script>
<style scoped>
.z-top{
z-index: 50;
}
</style>

View File

@@ -354,6 +354,7 @@
} }
}, },
// 格式化tag为字符串表达式 // 格式化tag为字符串表达式
/*
tagsToString(metric,arr) { tagsToString(metric,arr) {
let str = metric; let str = metric;
let sepStr = ''; let sepStr = '';
@@ -385,6 +386,7 @@
} }
return str; return str;
}, },
*/
// 新建图表 // 新建图表
addCharts(params) { addCharts(params) {
this.$post('panel/'+this.panelId+'/charts', params).then(response => { this.$post('panel/'+this.panelId+'/charts', params).then(response => {
@@ -429,7 +431,7 @@
const elements = []; const elements = [];
this.elementTarget.forEach((elem,index) => { this.elementTarget.forEach((elem,index) => {
if(elem.type==='normal'){ if(elem.type==='normal'){
const metricStr = this.tagsToString(elem.metric,elem.selectedTagList); const metricStr = bus.tagsToString(elem.metric,elem.selectedTagList);
elements.push({ elements.push({
//id:index+1, //id:index+1,
//metric: elem.metric,//指标名称 //metric: elem.metric,//指标名称
@@ -469,7 +471,7 @@
// 获取metric列表 // 获取metric列表
getSuggestMetric() { getSuggestMetric() {
this.$get('metric', {pageNo: 1, pageSize: 9999}).then(response => { this.$get('metric', {pageNo: 1, pageSize: -1}).then(response => {
if (response.code === 200) { if (response.code === 200) {
this.metricList = response.data.list; this.metricList = response.data.list;
}else { }else {

View File

@@ -57,10 +57,16 @@
.star-red{ .star-red{
color:#ff1818; color:#ff1818;
} }
.full-width{
width:100%;
}
.mt1{
margin-top:1px;
}
</style> </style>
<template> <template>
<el-form :model="elementInfo" ref="elementInfo" > <el-form :model="elementInfo" ref="elementInfo" >
<el-row> <el-row >
<el-col span="16"> <el-col span="16">
<span class="star-red">*</span>&nbsp;{{$t('dashboard.panel.chartForm.metric')}} <span class="star-red">*</span>&nbsp;{{$t('dashboard.panel.chartForm.metric')}}
</el-col> </el-col>
@@ -84,9 +90,9 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row v-if="tableShow == 2"> <el-row v-if="tableShow == 2">
<el-col span="22"> <el-col span="22" class="mt1">
<el-form-item prop="expression" :rules="{ required: true, message:$t('validate.required'), trigger: 'blur' }"><!--expression和metric的验证只能有一个不能同时存在:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"--> <el-form-item prop="expression" :rules="{ required: true, message:$t('validate.required'), trigger: 'blur' }"><!--expression和metric的验证只能有一个不能同时存在:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
<el-input size="mini" ref="metricExpression" type="textarea" maxlength="1024" show-word-limit v-model="elementInfo.expression" :placeholder="this.$t('dashboard.metric.expertTip')" ></el-input> <el-input size="mini" class="full-width" ref="metricExpression" type="textarea" maxlength="1024" show-word-limit v-model="elementInfo.expression" :placeholder="this.$t('dashboard.metric.expertTip')" ></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col span="2"> <el-col span="2">
@@ -94,9 +100,9 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row v-if="tableShow == 1"> <el-row v-if="tableShow == 1">
<el-col span="22"> <el-col span="22" >
<el-form-item :label-width="80" prop="metric" :rules="{ required: true, message: $t('validate.required'), trigger: 'blur' }"><!--:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"--> <el-form-item :label-width="80" prop="metric" :rules="{ required: true, message: $t('validate.required'), trigger: 'blur' }"><!--:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
<el-select ref="metricSelect" filterable placeholder="" popper-class="" size="small" v-model="elementInfo.metric" @change="selectMetric"> <el-select ref="metricSelect" class="full-width" filterable placeholder="" popper-class="" size="small" v-model="elementInfo.metric" @change="selectMetric">
<el-option v-for="(item, index) in metricShowList.arr" :key="item.metric + index" <el-option v-for="(item, index) in metricShowList.arr" :key="item.metric + index"
:value="item.metric">{{item.metric}}</el-option> :value="item.metric">{{item.metric}}</el-option>
</el-select> </el-select>
@@ -243,7 +249,6 @@ export default {
} }
}, },
// 选择metric // 选择metric
selectMetric() { selectMetric() {
if (this.elementInfo.metric) {//选择了metric则设置tagList否则设置为空 if (this.elementInfo.metric) {//选择了metric则设置tagList否则设置为空

View File

@@ -0,0 +1,534 @@
<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;
}
.star-red{
color:#ff1818;
}
.full-width{
width:100%;
}
.mt1{
margin-top:1px;
}
</style>
<template>
<el-form :model="elementInfo" ref="elementInfo" >
<el-row >
<el-col span="16">
<span class="star-red">*</span>&nbsp;{{$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-if="tableShow == 2">
<el-col span="22" class="mt1">
<el-form-item prop="expression" :rules="{ required: true, message:$t('validate.required'), trigger: 'blur' }"><!--expression和metric的验证只能有一个不能同时存在:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
<el-input size="mini" @input="changeExpression" class="full-width" ref="metricExpression" 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">
&nbsp;&nbsp;<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="tableShow == 1">
<el-col span="22" >
<el-form-item :label-width="80" prop="metric" :rules="{ required: true, message: $t('validate.required'), trigger: 'blur' }"><!--:rules="{ required: true, type: 'string', message: '', trigger: 'change' }"-->
<el-select ref="metricSelect" class="full-width" filterable 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">
&nbsp;&nbsp;<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 && 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
filterable
@change="changeTag"
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" >
&nbsp;&nbsp;<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: {
// (最后整体保存添加的图标的时候执行)保存, chartdata点击确认后保存本身数据并返回给chartdata
saveTarget() {
this.$refs.elementInfo.validate((valid) => {
if (valid) {
this.$emit('on-add-target-success', this.elementInfo);
}
});
},
// 删除该选项,第一步,传递要删除的参数
deleteTarget() {
//alert('metric第一步删除的指针之后回调box的第一个步'+this.pointer);
this.$emit('on-delete-target', this.pointer);
},
// 第二步,on-delete-target回调保存数据
subSave() {
//alert('metric第二步bus.chartAddInfo.metricTarget,指针'+JSON.stringify(bus.chartAddInfo.metricTarget)+'==pointer'+this.pointer);
bus.chartAddInfo.metricTarget[this.pointer] = this.elementInfo;
//alert('metric第二步保存信息到bus'+JSON.stringify(this.elementInfo))
this.$emit('sub-save-ok');
},
// 第三步,将数据重新赋值,sub-save-ok回调
setSubdata(index) {
//alert('metric第三步bus='+JSON.stringify(bus.chartAddInfo.metricTarget));
//alert('metric第三步bus('+this.pointer+')='+JSON.stringify(bus.chartAddInfo.metricTarget[this.pointer]));
this.elementInfo = bus.chartAddInfo.metricTarget[this.pointer];
//alert('metric第三步index'+index+',如果index是当前pointer('+this.pointer+')则继续把bus中的值赋值给当前metric')
//alert('metric第三步elementInfo='+JSON.stringify(this.elementInfo));
if(this.elementInfo.type==='expert'){
//alert('metric第三步expert');
// 当该项expression为空时重置一下
if (!this.elementInfo.expression && this.$refs.metricExpression) {
this.$refs.metricExpression.reset();
}
this.clickTabelShow(2,'expert');
}else {
// alert('metric第三步normal');
// 当该项metric为空时重置一下
if (!this.elementInfo.metric && this.$refs.metricSelect) {
this.$refs.metricSelect.reset();
}
this.clickTabelShow(1,'normal');
}
},
// 选择metric
selectMetric() {
if (this.elementInfo.metric) {//选择了metric则设置tagList否则设置为空
this.getSuggestTags(this.elementInfo.metric);
} else {
this.elementInfo.tagList = [];
}
this.$emit('on-change-condition');
},
// 获取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;
},
changeTag(){
this.$emit('on-change-condition');
},
changeExpression(v){
this.$emit('on-change-condition');
},
// 编辑已有图表状态时,先填充数据
setMdata(data) {
//alert('metricSetData'+JSON.stringify(data));
this.setDataFlag = true;
this.target = Object.assign({}, data);
//this.pointer =
if(this.target.type==='expert'){
this.tableShow = 2;
this.elementInfo.type = this.target.type;
this.elementInfo.expression = this.target.expression;
}else {
this.tableShow = 1;
this.elementInfo.type = this.target.type;
//解析expression=>标签列表值列表index对应设置selectedTagList及tagList里的isSelect为true
var expression = this.target.expression;
//alert('expression=='+expression);
if(expression.indexOf('{')>-1){
this.elementInfo.metric = expression.substring(0,expression.indexOf('{'));
}else {
this.elementInfo.metric = expression;
}
//alert(this.elementInfo.metric);
//this.selectMetric(); // 获取tag
this.$get('metric/labelName?metric='+this.elementInfo.metric).then(response => {
this.elementInfo.selectedTagList = [];
this.elementInfo.tagList = [];
if (response.code === 200) {
//this.elementInfo.tagList = response.data.list;
if(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);
});
}
if(expression.indexOf('{')>-1){
let labValArrStr = expression.substring(expression.indexOf('{')+1,expression.indexOf('}'));
this.stringToTags(labValArrStr);
}
}else {
this.elementInfo.tagList = [];
}
});
}
},
//字符串格式化为对象metric{a='1',b=~'2|3|4'}===>a='1',b=~'2|3|4'
stringToTags(str) {
let labArr = str.split(',');
labArr.forEach((item, index) => {//b=~'1|2|3'
let labName = item.substring(0,item.indexOf('='));
this.elementInfo.tagList.every((tagItem,index) => {
if(tagItem.name===labName){
tagItem.isSelect = true;
return false;
}else {
return true;
}
});
//查询metricLabel名称对应的LabelValue
this.$get('metric/labelVal?metric='+this.elementInfo.metric+"&name="+labName).then(response => {
const tagObj = {
name:labName,//选中的metricLabel名称
list:[],//metricLabel名称对应的LabelValue
value:[]//最终选择的值
};
let labVal = item.substring(item.indexOf('=')+1,item.length);
if(labVal.indexOf('~')!=-1){
labVal = labVal.substring(2,labVal.length-1);
let valArr = labVal.split('|');
valArr.forEach((labItem, labIndex) => {
tagObj.value.push(labItem);
});
}else {
labVal = labVal.substring(1,labVal.length-1);
tagObj.value.push(labVal);
}
if (response.code === 200) {
if(response.data.list){
response.data.list.forEach((resItem) => {
tagObj.list.push(resItem.value)
});
this.elementInfo.selectedTagList.push(tagObj);
}else{
response.data.forEach((resItem) => {
tagObj.list.push(resItem.value)
});
this.elementInfo.selectedTagList.push(tagObj);
}
}else {
this.elementInfo.selectedTagList.push(tagObj);
}
});
});
},
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;
}
this.$emit('on-change-condition');
},
//删除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);
this.$emit('on-change-condition');
},
clickTabelShow(val,type){
this.tableShow = val;
this.elementInfo.type = type;
if(val===2){
this.elementInfo.metric='';
}
if(val===1){
this.elementInfo.expression='';
}
this.$emit('on-change-condition');
}
},
computed: {
metricShowList() {
const obj = {
arr: [...this.metricList],
text: '',
};
return obj;
},
},
mounted() {
bus.$on('clear_history', () => {
this.clearHistory();
});
},
};
</script>

View File

@@ -0,0 +1,51 @@
.metric-dashboard {
width: 100%;
height:100%;
// padding: 6px 10px 6px;
.box-content {
//position: relative;
width:calc(100% - 13px);
}
.table-list {
margin-top:30px;
overflow-y:auto;
height: 100%;
}
.row-width{
//width:calc(100% - 0px);
}
.chartBox {
width: 100%;
float: left;
}
.chartBoxMul {
width: 100%;
float: left;
}
.common-detail-top-title {
font-size: 16px;
color: #5697f1;
cursor: pointer;
}
.chart-das {
height: 98%;
.border-area {
// border: 1px solid #d6d6d6;
}
}
.metric-title{
border-left: 3px solid #e07700;
font-weight: bold;
padding-left:5px;
}
}
/*
<style scoped>
.metricPreview {
height: 100%;
width:100%;
}
</style>
*/

View File

@@ -1,10 +1,8 @@
<style scoped> <style lang="scss">
.metricPreview { @import './metricPreview.scss';
height: 100%;
}
</style> </style>
<template> <template>
<div class="metricPreview"> <div class="main-container metric-dashboard">
<div class="content-left"> <div class="content-left">
<div class="sidebar-title">{{$t('dashboard.title')}}</div> <div class="sidebar-title">{{$t('dashboard.title')}}</div>
<div class="sidebar-info"> <div class="sidebar-info">
@@ -14,17 +12,169 @@
</div> </div>
<div class="content-right"> <div class="content-right">
<div class="top-tools" >
<div class="float-left metric-title" >指标预览
</div>
<div class="float-right mr-10">
<el-date-picker size="small" ref="calendar"
format="yyyy/MM/dd HH:mm"
@change="dateChange"
v-model="searchTime"
type="datetimerange"
:picker-options="pickerOptions"
:range-separator="$t('dashboard.panel.to')"
:start-placeholder="$t('dashboard.panel.startTime')"
:end-placeholder="$t('dashboard.panel.endTime')"
align="right">
</el-date-picker>
</div>
</div>
<div class="table-list" >
<div class="box-content" >
<el-row :gutter="20" class="row-width" >
<el-col :span="12" >
<div >{{$t('dashboard.panel.chartForm.metric')}}</div>
<metric-set ref="metricSet" @on-view-chart="getChartParam"></metric-set>
</el-col>
<el-col :span="12" >
<el-row class="border-area" v-show="chartCount === 'single'">
<div class="chartBox">
<line-chart-block
:show-setting="false"
ref="editChartSingle"
@on-refresh-data="refreshChart"
@on-remove-chart-block="removeChart"
@on-edit-chart-block="editData"
></line-chart-block>
</div>
</el-row>
<el-row class="border-area" v-show="chartCount === 'multiple'">
<div class="chartBoxMul" v-for="(item, index) in lineList">
<line-chart-block
:show-setting="false"
ref="editChartMultiple"
@on-refresh-data="refreshChart"
@on-remove-chart-block="removeChart"
@on-edit-chart-block="editData"
></line-chart-block>
</div>
</el-row>
</el-col>
</el-row>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import bus from '../../../libs/bus';
import metricSet from './metricSet';
import lineChartBlock from '../../charts/line-chart-block';
export default { export default {
name: "metricPreview", name: "metricPreview",
components: {
lineChartBlock,
metricSet
},
data() { data() {
return { return {
filter: {
start_time: '',
end_time: '',
},
total: 0, // 总数
selectedData: null, // 选中数据
searchName: '', // 搜索名称
showTagModal: false,
tagSet: null, // 根据你metric获取的tags信息
metricInfo: {}, // 保存参数信息
chartCount: 'single', // multiple
lineList: [], // 线列表
searchTime:[new Date().setHours(new Date().getHours()-1),new Date()],
//searchTime:[new Date().setMinutes(new Date().getMinutes()-1),new Date()],
pickerOptions: {
shortcuts: [
{
text: this.$t("dashboard.panel.recOne"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setHours(start.getHours() - 1);
picker.$emit('pick', [start, end]);
}
},{
text: this.$t("dashboard.panel.recFour"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setHours(start.getHours() - 4);
picker.$emit('pick', [start, end]);
}
}, {
text: this.$t("dashboard.panel.recOneDay"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 1);
picker.$emit('pick', [start, end]);
}
}, {
text: this.$t("dashboard.panel.yesterday"),
onClick(picker) {
const start = new Date();
const end = new Date();
start.setDate(start.getDate() - 1);
start.setHours(0);
start.setMinutes(0);
start.setSeconds(0);
end.setDate(end.getDate() - 1);
end.setHours(23);
end.setMinutes(59);
end.setSeconds(59);
picker.$emit('pick', [start, end]);
}
},{
text: this.$t("dashboard.panel.recSevenDay"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 7);
picker.$emit('pick', [start, end]);
}
}, {
text: this.$t("dashboard.panel.recOneMonth"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 30);
picker.$emit('pick', [start, end]);
}
}, {
text: this.$t("dashboard.panel.curMonth"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setDate(1);
start.setHours(0);
start.setMinutes(0);
picker.$emit('pick', [start, end]);
}
},{
text: this.$t("dashboard.panel.lastMonth"),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setDate(1);
start.setMonth(start.getMonth() - 1);
end.setDate(0);
start.setStart();
end.setEnd();
picker.$emit('pick', [start, end]);
}
}]
},
} }
}, },
methods: { methods: {
@@ -37,10 +187,204 @@ export default {
t: +new Date() t: +new Date()
} }
}); });
} },
// 获取默认时间
getInit() {
const end = new Date();
const start = new Date();
start.setHours(start.getHours() - 1);
start.setSeconds(0);
end.setSeconds(59);
this.filter.start_time = bus.timeFormate(start, 'yyyy-MM-dd hh:mm:ss');
this.filter.end_time = bus.timeFormate(end, 'yyyy-MM-dd hh:mm:ss');
},
dateChange(time) {
this.filter.start_time = `${time[0]}:00`;
this.filter.end_time = `${time[1]}:59`;
this.getChartParam(this.metricInfo, this.chartCount);
if (this.chartCount === 'single') {
this.$refs.editChartSingle.changeDate(this.searchTime);
} else if (this.chartCount === 'multiple') {
if (this.lineList.length > 0) {
this.$refs.editChartMultiple.forEach((item) => {
item.changeDate(this.searchTime);
});
}
}
},
// 获取图表信息
getChartParam(params, chartCount) {
this.chartCount = chartCount;
this.metricInfo = params;
if (this.chartCount === 'single') {
this.$refs.editChartSingle.showLoad();
} else if (this.chartCount === 'multiple') {
if (this.lineList.length > 0) {
this.$refs.editChartMultiple.forEach((item) => {
item.showLoad();
});
}
}
const startTime = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss');
const endTime = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss');
this.filter.start_time = startTime;
this.filter.end_time = endTime;
let step = bus.getStep(startTime,endTime);
this.$get('/prom/api/v1/query_range?query='+params.elements[0].expression+"&start="+startTime+"&end="+endTime+'&step='+step).then(response => {
this.$refs.metricSet.loading = false;
if (response.status === 'success') {
if (response.data.result) {
this.lineList = response.data.result;
// 如果只有一组数据,强制显示一条
if (response.data.result.length === 1) {
this.chartCount = 'single';
}
}
this.getChartData(response, params);
}else {
this.chartCount = 'single';
// 图表不可保存
this.$refs.metricSet.saveDisabled = true;
this.$refs.metricSet.isSave = false;
this.chartCount = 'single';
if(response.msg){
this.$message.error(response.msg);
}else {
this.$message.error(response);
}
this.$nextTick(() => {
this.$refs.editChartSingle.setData(params, [], 0, this.filter);
});
}
});
},
// 获取一个图表具体数据
getChartData(response, params) {
const chartItem = Object.assign({}, params);
chartItem.title = params.metric || '预览图';
const series = [];
const legend = [];
// 一个图表
response.data.result.forEach((queryItem) => {
const seriesItem = {
theData: {
name: '',
symbol:'none', //去掉点
smooth:true, //曲线变平滑
data: [],
type:chartItem.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 += `${tag}="${queryItem.metric[tag]}",`;
}
});
if(host.endsWith(',')){host = host.substr(0,host.length-1);}
host +="}";
legend.push(host);
// 图表中每条线的名字,去掉最后的逗号与空格
seriesItem.theData.name = host;
seriesItem.metric_name = seriesItem.theData.name;
// 将秒改为毫秒
seriesItem.theData.data = queryItem.values.map(dpsItem =>
[dpsItem[0] * 1000, dpsItem[1]]);
series.push(seriesItem.theData);
} else {/*
const errorMsg = `metric ${params.metric} 和tags ${params.tags} 组合下无数据`;
this.$message.error({
duration: 15,
content: errorMsg,
closable: true,
});
*/
}
});
// 将获取的数据运用于创建多个图表备用
this.$refs.metricSet.setSeries(response.data.result.values);//???
if (this.chartCount === 'single') {
this.setSize(chartItem.span, 0);
const filterParams = Object.assign({}, this.filter);
filterParams.chartCount = 'single';
this.$nextTick(() => {
this.$refs.editChartSingle.setData(chartItem, series, 0, filterParams,legend);
});
} else if (this.chartCount === 'multiple') {
const filterParams = Object.assign({}, this.filter);
filterParams.chartCount = 'multiple';
this.setSize(chartItem.span, 'all');
this.$nextTick(() => {
series.forEach((serieData, index) => {
// 设置每个图表名称
const chartInfoParams = Object.assign({}, chartItem);
chartInfoParams.title = serieData.name;
this.$refs.editChartMultiple[index]
.setData(chartInfoParams, [serieData], 0, filterParams,[legend[index]]);
});
});
}
},
// 设置图表的宽度
setSize(size, index) {
this.$nextTick(() => {
const chartBox = document.getElementsByClassName('chartBox');
if (index === 0) {
chartBox[index].style.width = `${(size / 12) * 100}%`;
} else {
const chartBoxMul = document.getElementsByClassName('chartBoxMul');
Array.prototype.slice.call(chartBoxMul).forEach((item) => {
const obj = item;
obj.style.width = `${(size / 12) * 100}%`;
});
}
});
},
refreshChart() {},
removeChart() {},
editData() {},
/*
// 查看指标确认
viewConfirm() {
this.showTagModal = false;
},
// 取消查看
viewCancel() {
this.showTagModal = false;
this.selectedData = {};
},
// 查看详情
viewDetail(item) {
this.selectedData = item;
},
// eslint-disable-next-line
search: _.debounce(function() { // 输入框筛选
}, 300),
// 刷新
reload() {
this.getChartParam(this.metricInfo, this.chartCount);
},
// 滚动条复位
refresh_scroll() {
window.scrollTo(0, 0);
},
*/
}, },
mounted: function() { mounted: function() {
this.getInit();
}, },
watch: { watch: {

View File

@@ -0,0 +1,59 @@
.metric-set {
/*搜索栏样式*/
.search-data {
width: 100%;
padding: 5px 15px;
box-sizing: border-box;
overflow: hidden;
background: #f5f7f9;
}
.search-data-title {
margin-left: 10px;
}
.metric-form {
padding-top: 20px;
box-sizing: border-box;
// height: 69%;
// max-height: 400px;
// overflow: auto;
}
.operate-area {
border-top: 1px solid #dfe7f2;
border-bottom: 1px solid #dfe7f2;
padding: 5px 0;
.operate-area-title {
padding: 5px 0;
font-size: 14px;
font-weight: bold;
}
}
.element-item {
padding-top: 20px;
border-top: 1px dashed #dfe7f2;
}
.ivu-form-item .ivu-form-item {
margin-bottom: 24px;
}
.host-form-item {
.ivu-form-item-content {
line-height: 1;
}
}
.pt-5{
padding-top:5px;
}
.pb-5{
padding-bottom: 5px;
}
.mt-10{
margin-top:10px;
}
.nx-btn-disabled{
cursor:not-allowed;
}
}
// .setdata {
// height: 100%;
// position: relative;
// }

View File

@@ -0,0 +1,498 @@
<style lang="scss">
@import 'metricSet';
</style>
<template>
<div class="metric-set" >
<div class="metric-form">
<el-row class="element-item" >
<chart-metric ref="chartTag"
:metric-list="metricList"
@on-add-target-success="getTarget"
@on-change-condition="getCondition"
></chart-metric>
</el-row>
<el-row class="pt-5 pb-5">
<button type="primary" @click="preview" :disabled="loading" class="nz-btn nz-btn-size-normal nz-btn-style-normal ">预览图表</button>
<button :disabled="saveDisabled || loading" type="primary" @click="preview('change')"
:class="{'nz-btn nx-btn-disabled' : (saveDisabled || loading), 'nz-btn nz-btn-size-normal nz-btn-style-normal' : !(saveDisabled || loading)}"
>{{chartSwitchWord}}</button>
<!-- <Button type="primary" @click="addTarget">添加指标</Button> -->
</el-row>
<el-row class="operate-area">
<div class="operate-area-title">保存图表到看板:</div>
<button :disabled="saveDisabled" type="primary" @click="createNew"
:class="{'nz-btn nx-btn-disabled' : saveDisabled , 'nz-btn nz-btn-size-normal nz-btn-style-normal' : !saveDisabled}"
>创建看板</button>
<button :disabled="saveDisabled" type="primary" @click="selectPanel"
:class="{'nz-btn nx-btn-disabled' : saveDisabled , 'nz-btn nz-btn-size-normal nz-btn-style-normal' : !saveDisabled}"
>选择已有看板</button>
</el-row>
<el-row class="mt-10">
<el-checkbox :disabled="saveDisabled" v-model="isSave">options</el-checkbox>
</el-row>
<el-form :model="chartInfo" ref="chartInfo" v-show="isSave">
<el-row>
<div class="common-float-left">
<el-form-item :label-width="80" label="图宽" prop="span"
:rules="{ required: true, type: 'number', message: '请选择图表宽度', trigger: 'change' }">
<el-select style="width:130px;" size="small" v-model="chartInfo.span" transfer>
<el-option v-for="item in spanList" :key="item" :label="'span-' + item" :value="item">
span-{{item}}
</el-option>
</el-select>
</el-form-item>
</div>
<div class="common-float-left">
<el-form-item :label-width="80" label="图高" prop="height"
:rules="{ required: true, type: 'number', message: '请输入图表高度', trigger: 'change' }">
<el-input-number :min="400" size="small" v-model="chartInfo.height" style="width: 130px" placeholder="请输入图表高度"></el-input-number>px
</el-form-item>
</div>
</el-row>
<el-row>
<div class="common-float-left">
<el-form-item :label-width="80" label="类型" prop="type"
:rules="{ required: true, message: '请选择图表类型', trigger: 'change' }">
<el-select transfer style="width:130px;" v-model="chartInfo.type" size="small">
<el-option v-for="item in typeList"
:key="item.id"
:label="item.name"
:value="item.id">
{{item.name}}
</el-option>
</el-select>
</el-form-item>
</div>
</el-row>
</el-form>
</div>
<metric-pre-panel-box :chartInfo="chartInfo" :elementTarget="elementTarget" :seriesData="seriesData" :chartCount="chartCount" @reload="reload" ref="panelBox"></metric-pre-panel-box>
</div>
</template>
<script>
import axios from 'axios';
import bus from '../../../libs/bus';
import chartMetric from "./chartMetricForPreview";
import metricPrePanelBox from "../../common/rightBox/metricPrePanelBox";
export default {
name: 'metricSet',
props: {
},
components: {
'chart-metric':chartMetric,
'metric-pre-panel-box':metricPrePanelBox
},
data() {
return {
total: 0,
loading: false, // 加载中
chartModal: false, // 展示弹出框
// 新建信息
chartInfo: {
title: '', // 名称
span: 12, // 宽度
height: 400, // 高度
type: 'line', // 类型
},
chartId: 0,
elements: [1], // 指标部分 tarNum
elementTarget: {}, // 获取chartTag数据后保存
// 宽度列表
spanList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
typeList: [
{
id:"line",
name:this.$t("dashboard.panel.chartForm.typeVal.line.label")
},
{
id:"bar",
name:this.$t("dashboard.panel.chartForm.typeVal.bar.label")
}
],
//productId: 0,
panelId: 0,
metricList: [], // metric列表
isSave: false, // 是否要保存
saveDisabled: true, // 不可保存
createModal: false, // 创建看板panel
panelList: [],
// 创建看板信息
chartSaveInfo: {
title: '', // 图表名称
name: '', // panel名称
panelId: '', // 选择以后panelId
}, // 保存看板
isCreatePanel: false,
createModalTitle: '',
chartCount: 'single', // multiple多图,single单图
metricInfo: {}, // 保存从chartTag中获取的seriesData
createChartList: [], // 创建多个图表的图表信息
seriesData: [], // 查询图表后生成的series信息,用于生成多个图表时提供图表title
panelItem: {}, // 保存看板信息
};
},
computed: {
chartSwitchWord() {
const word = this.chartCount === 'single' ? '多图展示' : '单图展示';
return word;
},
},
watch: {},
methods: {
// 父级调用保存series,作为多个图表生成title
setSeries(data) {
this.seriesData = data;
},
// 默认打开
initOpen() {
this.getSuggestMetric();
},
// chart-tag中条件更改时,触发不能创建
getCondition() {
this.saveDisabled = true;
},
reload(){
this.saveDisabled = true;
this.elementTarget = {};
},
// 创建看板
createNew() {
this.$refs.panelBox.show(true);
//this.panel = {id: '', name: ''};
this.$refs.panelBox.setTitle(this.$t("dashboard.panel.createPanelTitle"));
this.$refs.panelBox.setIsCreatePanel(true);
this.$refs.panelBox.setChartTitle(this.metricInfo.metric);
},
// 选择看板
selectPanel() {
this.$refs.panelBox.show(true);
this.$refs.panelBox.setTitle(this.$t("dashboard.panel.selectPanelTitle"));
this.$refs.panelBox.setIsCreatePanel(false);
this.$refs.panelBox.setChartTitle(this.metricInfo.metric);
this.$refs.panelBox.getTableData();
},
// 取消创建modal
cancel() {
this.panelList = [];
this.chartSaveInfo.panelId = ''; // 选择panel
this.chartSaveInfo.name = ''; // panel名称创建
if (this.$refs.chartSaveInfo) {
this.$refs.chartSaveInfo.resetFields();
}
if (this.$refs.panelSelect) {
this.$refs.panelSelect.reset();
}
this.createModal = false;
},
// 重置已选择的看板
initSaveInfo() {
this.chartSaveInfo.name = ''; // panel名称创建
this.chartSaveInfo.panelId = ''; // 选择panel
},
close() {
this.elementTarget = {}; // 初始化清空参数
this.initInfo();
this.chartModal = false;
},
// 初始化信息
initInfo() {
this.chartInfo.title = '';
this.chartInfo.type = 1;
this.chartInfo.span = 12;
this.chartInfo.height = 400;
this.elements = [1];
this.elementTarget = {};
},
// 生成预览图表
preview(params) {
if (params === 'change') {
this.chartCount = this.chartCount === 'single' ? 'multiple' : 'single';
}
this.elementTarget = {}; // 初始化清空参数
// 触发每个tag组件内部进行校验
this.$refs.chartTag.saveTarget(); // => getTartget
// 保存图表时进行参数认证
if (this.isSave) {
this.$refs.chartInfo.validate();
}
},
// 获取每个tag组件内部校验后数据,点击生成图表时触发
getTarget(target) {
// 由于条件限制每次其实只有一个target
this.elementTarget = JSON.parse(JSON.stringify(target));
this.$refs.chartInfo.validate((valid) => {
if (valid) {
this.loading = true;
// 保存图表到看板
const params = Object.assign({}, this.chartInfo);
//params.tags = this.tagsToString(this.elementTarget.tagList);
const elements = [];
//params.elementTargetType = this.elementTarget.type;
if(this.elementTarget.type==='normal'){
const metricStr = bus.tagsToString(this.elementTarget.metric,this.elementTarget.selectedTagList);
//params.expression = metricStr;
elements.push({
expression: metricStr,
type: this.elementTarget.type,
});
params.metric = this.elementTarget.metric;
}else if(this.elementTarget.type==='expert'){
//params.expression = this.elementTarget.expression;
elements.push({
expression: this.elementTarget.expression,
type: this.elementTarget.type,
});
params.metric = this.elementTarget.expression;
}
params.elements = elements;
//params.expression
this.metricInfo = params;
this.saveDisabled = false; // 查询成功以后,可以进行保存操作
// 将参数返回,进行查询
this.$emit('on-view-chart', params, this.chartCount);
}
});
},
// 确认创建图表
createConfirm() {
this.$refs.chartInfo.validate((valid) => {
if (valid) { // 先判断参数正确与否
this.$refs.chartSaveInfo.validate((flag) => {
if (flag) { // 再判断弹出窗口内内容是否正确
if (this.isCreatePanel) { // 创建看板
this.addPanels();
} else { // 选择看板
this.panelId = this.chartSaveInfo.panelId;
this.getOptions();
}
}
});
}
});
},
// 创建看板接口
addPanels() {
addPanels({
productId: this.productId,
name: this.chartSaveInfo.name,
}).then((res) => {
if (res.status === 200 && res.data.code === 200) {
this.panelId = res.data.panel.id;
this.panelItem = res.data.panel;
this.getOptions();
} else {
this.$Message.warning('创建看板失败');
}
});
},
// 选择看板
panelSelect(id) {
if (id) {
const panel = this.panelList.find(p => p.id === id);
if (panel) {
this.panelItem = panel;
}
}
},
// 生成图表的时候,使用该函数,汇总创建图表参数
getOptions() {
this.$refs.chartInfo.validate((valid) => {
const params = {
productId: this.productId,
panelId: this.panelId,
// title: this.chartSaveInfo.title,
type: this.chartInfo.type,
span: this.chartInfo.span,
height: this.chartInfo.height,
};
// 多图模式
if (this.chartCount === 'multiple') {
this.createChartList = [];
this.seriesData.forEach((queryItem) => {
const soleParam = Object.assign({}, params);
const elements = [];
let tagStr = ''; // tag字符串
let host = `${queryItem.metric}, `; // 名称
const tagsArr = Object.keys(queryItem.tags);
// 根据图表展示时获取的tag进行组装名称以及tag字符串
tagsArr.forEach((tag) => {
if (tag !== 'uuid') {
host += `${tag}=${queryItem.tags[tag]}, `;
if (tagStr) {
tagStr += `,${tag}=${queryItem.tags[tag]}`;
} else {
tagStr += `${tag}=${queryItem.tags[tag]}`;
}
}
});
// 图表中每条线的名字,去掉最后的逗号与空格
host = host.substring(0, host.length - 2);
soleParam.title = host;
elements.push({
metric: this.elementTarget.metric,
tags: tagStr,
});
soleParam.elements = elements;
if (valid) {
this.addMultipleChart(soleParam);
}
});
} else if (this.chartCount === 'single') {
params.title = this.chartSaveInfo.title;
const elements = [];
const tagStr = this.tagsToString(this.elementTarget.tagList);
elements.push({
metric: this.elementTarget.metric,
tags: tagStr,
});
params.elements = elements;
if (valid) {
this.addCharts(params);
}
}
});
},
// 新建单个图表single
addCharts(params) {
addCharts(params).then((res) => {
if (res.status === 200) {
if (res.data.code === 200) {
this.$Message.success('添加成功');
this.createModal = false;
this.elementTarget = {};
this.saveDisabled = true; // 创建成功后,不能再次创建
// this.initInfo();
this.$Modal.confirm({
title: '创建成功',
content: `是否前去看板:${this.panelItem.name},查看创建的图表`,
onOk: () => {
this.$router.push({
path: `/console/panel/detail/${this.panelId}/${this.productId}`,
query: {
product: this.$route.query.product,
panel: this.panelItem.name,
},
});
},
onCancel: () => {
this.cancel();
},
});
} else {
this.$Message.error(`创建失败:${res.data.message || res.statusText}`);
}
}
});
},
// 创建多个图表
addMultipleChart(params) {
this.createChartList.push(params);
if (this.seriesData.length === this.createChartList.length) {
const api = [];
this.createChartList.forEach((item) => {
api.push(addCharts(item));
});
axios.all(api).then((res) => {
let successFlag = true;
res.forEach((item) => {
if (item.data.code !== 200) {
successFlag = false;
}
});
if (successFlag) {
this.saveDisabled = true; // 创建成功后,不能再次创建
this.$Message.success('添加成功');
this.createModal = false;
this.elementTarget = {};
this.createChartList = [];
this.$Modal.confirm({
title: '创建成功',
content: `是否前去看板:${this.panelItem.name},查看创建的图表`,
onOk: () => {
this.$router.push({
path: `/console/panel/detail/${this.panelId}/${this.productId}`,
query: {
product: this.$route.query.product,
panel: this.panelItem.name,
},
});
},
});
} else {
this.$Message.error('添加失败');
}
});
}
},
// 增加指标,tarNum
addTarget() {
this.elements.push(1);
},
// 获取metric列表
getSuggestMetric(id) {
this.$get('metric', {pageNo: 1, pageSize: -1}).then(response => {
if (response.code === 200) {
this.metricList = response.data.list;
}else {
this.metricList = [];
}
})
},
// 获取文本宽度
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);
return width;
},
// 创建打开
createData(panelId) {
this.panelId = panelId;
this.elementTarget = {}; // 初始化清空参数
this.initInfo(); // 初始化图表信息
this.chartModal = true;
this.initOpen(); // 获取metric, productId数据
this.elements = [1];
},
/*
tagsToString(arr) {
let str = '';
arr.forEach((item, index) => {
if (index === 0) {
str += item.value.indexOf('*') > -1 ? `${item.name}=*` : `${item.name}=${item.value.join('|')}`;
} else {
str += item.value.indexOf('*') > -1 ? `,${item.name}=*` : `,${item.name}=${item.value.join('|')}`;
}
});
return str;
},
*/
},
mounted() {
this.initOpen();
/*
bus.$on('on-shrink-change', () => {
this.preview();
});
*/
},
beforeDestroy() {
/*
bus.$off('on-shrink-change');
*/
},
};
</script>

View File

@@ -293,7 +293,7 @@
}, },
pageObj: { pageObj: {
pageNo: 1, pageNo: 1,
pageSize: 9999,//此处获取所有数据,所以设置一个较大的值 pageSize: -1,//此处获取所有数据,所以设置一个较大的值
total:0 total:0
}, },
chartsData: [],//中间部分图表相关数据 chartsData: [],//中间部分图表相关数据
@@ -461,7 +461,7 @@
}); });
}, },
getTableData: function() { getTableData: function() {
this.$get('panel').then(response => { this.$get('panel?pageNo=1&pageSize=-1').then(response => {
if (response.code === 200) { if (response.code === 200) {
this.panelData = response.data.list; this.panelData = response.data.list;
if( response.data.list.length>0 ){ if( response.data.list.length>0 ){

View File

@@ -118,6 +118,38 @@ export default new Vue({
}); });
return fm; return fm;
}, },
// 格式化tag为字符串表达式
tagsToString(metric,arr) {
let str = metric;
let sepStr = '';
arr.forEach((item, index) => {
if (index === 0) {
str +="{"
if(item.value.length===1){
str += `${item.name}='${item.value.join('|')}'`;
sepStr = ',';
}else if(item.value.length>1){
str += `${item.name}=~'${item.value.join('|')}'`;
sepStr = ',';
}
} else {
if(item.value.length===1){
str += sepStr+`${item.name}='${item.value.join('|')}'`;
sepStr = ',';
}else if(item.value.length>1){
str += sepStr+`${item.name}=~'${item.value.join('|')}'`;
sepStr = ',';
}
}
});
if(str.indexOf('{')>-1){
str +="}";
}
if(str.endsWith('{}')){
str = str.substring(0,str.indexOf('{'));
}
return str;
},
getStep(startTime,endTime){ getStep(startTime,endTime){
const start = new Date(startTime); const start = new Date(startTime);
const end = new Date(endTime); const end = new Date(endTime);