merge: 删除无用文件

This commit is contained in:
chenjinsong
2021-11-29 16:36:43 +08:00
parent f39d41adab
commit 339e437a6e
5 changed files with 0 additions and 2193 deletions

View File

@@ -1,624 +0,0 @@
<template>
<div class="full-width-height">
<nz-bottom-data-lis
:layout="[]"
:tabs="tabs"
:targetTab="targetTab"
@changeTab="changeTab"
class="full-width-height"
>
<template v-slot:title><span :title="obj.name">{{obj.name}}</span></template>
<template v-slot>
<div class="panel">
<!--图表-->
<div class="table-list" id="tableList">
<div ref="dashboardScrollbar" id="dashboardScrollbar" style="height: 100%; overflow: auto;">
<div class="box-content">
<chart-list :nowTimeType='nowTimeType' :additional-info="obj" :detail="detail" :draggable="draggable" :from="from" :is-model="from == $CONSTANTS.fromRoute.model" @on-edit-chart="editChart"
@on-refresh-time="refreshTime" @on-remove-chart="delChart" ref="chartList" :panel-lock="panelLock"></chart-list>
</div>
</div>
</div>
<button :class="{'to-top-is-hover': tableHover}" @click="toTop(scrollbarWrap)" class="to-top" style="bottom: 0;" v-show="showTopBtn"><i class="nz-icon nz-icon-top"></i></button>
<transition name="right-box">
<chart-box :chart="chart" :from="from" :panel-data="panelData" :panel-data-list="panelDataList" :show-panel="showPanel" @close="closeRightBox" @delete-chart="delChart" @on-create-success="createSuccess" @on-delete-success="delChartOk" ref="addChartModal" v-if="rightBox.show"></chart-box>
</transition>
</div>
</template>
</nz-bottom-data-lis>
</div>
</template>
<script>
import ChartBox from '../../../page/dashboard/chartBox'
import ChartList from '../../../charts/chart-list'
import bus from '../../../../libs/bus'
import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
export default {
name: 'chartTempPreview',
mixins: [subDataListMixin],
props: {
from: String,
obj: Object,
draggable: { type: Boolean, default: true },
detail: Object
},
data () {
return {
panelLock: true,
showTopBtn: false, // top按钮
visible: false,
rightBox: { // 面板弹出框相关
show: false
},
tableHover: false,
searchTime: bus.getTimezontDateRange(),
intervalTimer: null,
interval: 0,
panel: { // 新增panel
id: '',
name: ''
},
chart: {},
blankChart: {
id: '',
title: '',
type: 'line',
span: 12,
height: '400',
unit: 2,
param: {
url: '',
threshold: ''
},
elements: [{ expression: '', legend: '', type: 'expert', id: '' }],
panel: '',
sync: 0
},
pageObj: {
pageNo: 1,
pageSize: -1, // 此处获取所有数据,所以设置一个较大的值
total: 0
},
chartsData: [], // 中间部分图表相关数据
panelData: [],
panelDataList: [], // chartTemp列表
searchMsg: { // 给搜索框子组件传递的信息
zheze_none: true,
searchLabelList: [
]
},
searchLabel: {}, // 搜索参数
// ---图表相关参数--start
dataList: [], // 数据列表
// searchName: '', // 搜索名称
filter: { // 过滤条件
// productId: 0,
panelId: 0,
start_time: '',
end_time: '',
searchName: ''
},
showPanel: {
name: '',
type: this.from,
id: ''
},
// removeModal: false, // 删除弹出
// deleteObj: {}, // 删除对象
// ---图表相关参数--end
scrollbarWrap: null,
nowTimeType: {}
}
},
components: {
'chart-box': ChartBox,
'chart-list': ChartList,
nzBottomDataList
},
methods: {
// 刷新
refresh () {
this.getTableData(this.obj.id)
},
refreshTime (st, et) {
const startTime = bus.timeFormate(st, 'yyyy-MM-dd hh:mm')
const endTime = bus.timeFormate(et, 'yyyy-MM-dd hh:mm')
this.searchTime = [startTime, endTime]
},
panelReloadForDel () {
this.getTableData(this.obj.id)
},
/* 图表相关操作--start */
addChart () {
this.chart = this.newChart()
this.rightBox.show = true
},
newChart () {
return JSON.parse(JSON.stringify(this.blankChart))
},
// 切换tab
changeTab (tab) {
this.$emit('changeTab', tab)
},
closeRightBox (refresh) {
this.rightBox.show = false
if (refresh) {
this.refresh()
}
},
// 编辑图表信息,打开编辑弹窗
editChart (data) {
if (!data.param) {
data.param = { url: '', threshold: '' }
}
this.chart = JSON.parse(JSON.stringify(data))
this.rightBox.show = true
},
// 移除图表:弹出确认框询问
delChart (data, from) {
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
type: 'warning'
}).then(() => {})
},
delChartOk () {
this.getData(this.filter)
},
// 图表创建成功回调panel页面进行图表的刷新
createSuccess (msg, data, params) {
this.getData(this.filter)
},
// 获取数据,用在子页面
getData (params) {
if (params.start_time === '' || params.end_time === '') {
const now = bus.getTimezontDateRange()
const endTimeTmp = bus.timeFormate(now[1].getTime(), 'yyyy-MM-dd hh:mm:ss')
const startTimeTmp = bus.timeFormate(now[0].getTime(), 'yyyy-MM-dd hh:mm:ss')
params.start_time = startTimeTmp
params.end_time = endTimeTmp
params.from = this.from
}
if (this.$refs.chartList) {
this.$refs.chartList.initData(params)
}
},
/* 图表相关操作--end */
/* 时间条件查询--start */
// 选择日期变化
dateChange (val) {
// this.searchTime = [...val];
const nowTimeType = this.$refs.pickTime.$refs.timePicker.nowTimeType
this.nowTimeType = this.$refs.pickTime.$refs.timePicker.nowTimeType
this.setSearchTime(nowTimeType.type, nowTimeType.value)
this.filter.start_time = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss')
this.filter.end_time = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss')
this.filter.panelId = this.showPanel.id
this.getData(this.filter)
},
/* 时间条件查询--end */
setSearchTime (type, val) { // 设置searchTime
if (type === 'minute') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setMinutes(new Date(bus.computeTimezone(new Date().getTime())).getMinutes() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'm')
} else if (type === 'hour') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setHours(new Date(bus.computeTimezone(new Date().getTime())).getHours() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'h')
} else if (type === 'date') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setDate(new Date(bus.computeTimezone(new Date().getTime())).getDate() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'd')
}
this.$refs.pickTime.$refs.timePicker.searchTime = this.searchTime
},
// 公用操作
getTableData (linkId) {
if (this.from == this.$CONSTANTS.fromRoute.rule || this.from == this.$CONSTANTS.fromRoute.endpoint) {
this.getData(this.filter)
} else {
this.panelData = [this.obj]
if (this.panelData.length > 0) {
this.showPanel.id = this.filter.panelId = this.panelData[0].id
this.getData(this.filter)
}
}
},
// 定期刷新
selectInterval (val) {
this.visible = false
clearInterval(this.intervalTimer)
if (val) {
this.interval = val
const start = new Date(this.searchTime[1])
const now = new Date()
const interval = Math.floor((now.getTime() - start.getTime()) / 1000) // 计算当前结束时间到现在的间隔(秒)
if (interval >= 60) { // 如果结束时间到现在超过1分钟
this.getIntervalData(interval)
}
this.intervalTimer = setInterval(() => {
this.getIntervalData(this.interval)
}, val * 1000)
}
},
getIntervalData (interval) { // interval:结束时间到现在的秒数
const start = new Date(this.searchTime[0])
const end = new Date(this.searchTime[1])
start.setSeconds(start.getSeconds() + interval)
end.setSeconds(end.getSeconds() + interval)
const startTime = bus.timeFormate(start, 'yyyy-MM-dd hh:mm')
const endTime = bus.timeFormate(end, 'yyyy-MM-dd hh:mm')
this.searchTime = [startTime, endTime]
// 刷新数据
this.dateChange()
},
pageNo (val) {
this.pageObj.pageNo = val
this.getTableData(this.obj.id)
},
pageSize (val) {
this.pageObj.pageSize = val
this.getTableData(this.obj.id)
},
search: function () {
if (this.$refs.chartList) {
this.$refs.chartList.searchCharts(this.filter.searchName)
}
},
// 滚动事件触发下拉加载
onScroll () {
const _self = this
this.scrollbarWrap.addEventListener('scroll', bus.debounce(function () {
_self.showTopBtn = _self.scrollbarWrap.scrollTop > 50
_self.$refs.chartList.loadChartData(_self.scrollbarWrap.scrollTop)
}, 300))
},
focusInput: function () {
let classVal = document.getElementById('queryPanel').parentElement.getAttribute('class')
classVal = classVal.replace('query-input-inactive', 'query-input-active')
document.getElementById('queryPanel').parentElement.setAttribute('class', classVal)
this.$refs.queryPanel.focus()
},
blurInput: function () {
if (!this.filter.searchName || this.filter.searchName == '') {
setTimeout(function () {
let classVal = document.getElementById('queryPanel').parentElement.getAttribute('class')
classVal = classVal.replace('query-input-active', 'query-input-inactive')
document.getElementById('queryPanel').parentElement.setAttribute('class', classVal)
}, 100)
}
},
clearInput: function () {
this.$refs.queryPanel.focus()
},
syncChart: function () {
if (this.from == this.$CONSTANTS.fromRoute.asset || this.from == this.$CONSTANTS.fromRoute.model) {
this.$confirm(this.$t('tip.syncTip'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
type: 'warning'
}).then(() => {
const param = {
modelId: this.from == this.$CONSTANTS.fromRoute.model ? this.obj.id : null,
assetId: this.from == this.$CONSTANTS.fromRoute.asset ? this.obj.id : null
}
this.$put('/model/syncChart', param).then(response => {
if (response.code == 200) {
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.syncSuccess') })
if (this.from == this.$CONSTANTS.fromRoute.asset) {
this.refresh()
}
} else {
console.error(response.msg)
this.$message.error(response.msg)
}
})
})
}
},
toTop (wrap) {
let currentTop = wrap.scrollTop
const interval = currentTop / 10
const intervalFunc = setInterval(function () { // 花200ms分10次回到顶部模拟动画效果
if (currentTop === 0) {
clearInterval(intervalFunc)
} else {
currentTop = (currentTop - interval) < interval * 0.5 ? 0 : currentTop - interval
wrap.scrollTop = currentTop
}
}, 20)
},
tableListEnter () {
this.tableHover = true
},
tableListLeave () {
this.tableHover = false
}
},
mounted: function () {
this.scrollbarWrap = this.$refs.dashboardScrollbar
this.onScroll()
document.querySelector('#tableList').addEventListener('mouseenter', this.tableListEnter)
document.querySelector('#tableList').addEventListener('mouseleave', this.tableListLeave)
},
watch: {
'filter.searchName': function (n, o) {
const temp = this
setTimeout(function () {
temp.search()
}, 1000)
},
obj: {
immediate: true,
handler (n, o) {
setTimeout(() => {
if (n && n.id) {
this.getTableData(n.id)
}
}, 500)
}
}
},
beforeDestroy () {
document.querySelector('#tableList').removeEventListener('mouseenter', this.tableListEnter)
document.querySelector('#tableList').removeEventListener('mouseleave', this.tableListLeave)
if (this.scrollbarWrap) {
this.scrollbarWrap.removeEventListener('scroll', bus.debounce)
};
}
}
</script>
<style scoped lang="scss">
.panel {
height: 100%;
}
.panel .el-table {
border-radius: 5px;
}
.panel-list-width {
width:240px;
}
.panel-dropdown-title {
line-height:24px;
padding-left:5px;
margin-left:10px;
margin-top: 3px;
text-align:left;
border-radius:4px;
width:120px;
height:24px;
border:solid 1px #d8dce1;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.panel-list-title {
min-height:24px;
width:100px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.panel-list-item {
width:190px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.content-right-option {
cursor: pointer;
display: inline-block;
margin-right: 6px;
}
.content-right-option .nz-icon-delete {
color: #F98D9A;
}
.content-right-option .nz-icon-delete:hover {
color: #D96D7A;
}
.content-right-option .nz-icon-view {
color: #60BEFF;
}
.content-right-option .nz-icon-view:hover {
color: #409EFF;
}
/* begin-chart list*/
.table-list {
margin-top: 6px;
overflow-y: auto;
height: calc(100% - 56px);
}
.box-content {
position: relative;
}
/* end-chart list*/
/* begin--Panel-自定义可编辑的el-select下拉框样式*/
.panel-dropdown-btn {
display: inline-block;
margin-left: 7px;
float: right;
color: #60BEFF;
font-size: 13px
}
.panel-dropdown-btn:hover {
color: #409EFF;
}
.panel-dropdown-btn-create {
display: inline-block;
float: left;
font-size: 13px;
color: #F98D9A;
width: 100%;
}
.panel-dropdown-btn-create:hover {
color: #D96D7A;
}
.panel-dropdown-btn-delete {
color: #F98D9A;
font-size: 13px
}
.panel-dropdown-btn-delete:hover {
color: #D96D7A;
}
.panel-dropdown-error-message {
color: #F98D9A;
}
/* end--Panel-自定义可编辑的el-select下拉框样式*/
.panel-select-width {
width: 150px;
}
.panel-refresh-interval {
margin-right: 5px;
float: right;
}
.panel-refresh-interval-select {
width: 95px;
}
.panel-calendar {
float: right;
margin-right: 1px;
}
.top-tools {
button {
background: $btn-light-background-color;
outline: none;
border: 1px solid #ccc;
}
button:hover {
background: $btn-light-background-color-hover;
}
}
.nz-dashboard-dropdown {
height: 300px;
overflow-y: auto;
li {
/*padding: 0 20px !important;*/
padding-left:20px !important;
padding-right:0px !important;
width:240px;
white-space:nowrap;
overflow-x:hidden;
text-overflow:ellipsis;
}
}
.nz-dashboard-dropdown-bg {
background: $global-text-color-active;
color: #fff;
}
.el-dropdown-link {
cursor: pointer;
font-weight: bold;
}
.refresh {
display: flex;
background: #fff;
border-radius: 4px;
align-items: center;
justify-content: center;
margin: 0 10px;
border: 1px solid #ccc;
background: $btn-light-background-color;
span {
display: inline-block;
padding: 1px 8px;
}
}
.popover_ul li {
padding: 10px 3px;
cursor: pointer;
}
.popover_ul li:hover {
background: $dropdown-hover-background-color !important;
color: $global-text-color-active !important;
}
.nz-dashboard-refresh {
border-right: 1px solid #ccc;
color: #F0BF84;
}
.nz-dashboard-picker {
}
.full-width-height{
width: 100%;
height: 100%;
}
</style>
<style lang="scss">
.nz-dashboard-dropdown {
z-index: 3001 !important;
}
.panel .top-tools input {
background-color: $content-right-background-color;
}
.panel .top-tools .el-input__inner {
background-color: $content-right-background-color;
}
.panel-calendar .el-range-editor--mini.el-input__inner {
height: 25px !important;
border-color: #d8d8d8;
}
.panel-calendar .el-range-editor--mini .el-range__close-icon {
line-height: 18px;
}
.panel-calendar .el-range-editor--mini .el-range__icon {
display: none;
}
.panel-calendar .el-range-editor--mini .el-range-separator {
line-height: 17px;
}
.panel-calendar .el-date-editor--datetimerange.el-input, .panel-calendar .el-date-editor--datetimerange.el-input__inner {
padding-right: 0;
vertical-align: top;
}
.nz-dashboard-dropdown .nz-icon-edit {
font-size: 12px;
}
</style>

View File

@@ -1,10 +0,0 @@
diff a/nezha-fronted/src/components/common/rightBox/setting/globalizationBox.vue b/nezha-fronted/src/components/common/rightBox/setting/globalizationBox.vue (rejected hunks)
@@ -145,7 +145,7 @@
if (response.code === 200) {
this.languageList = response.data
if (!this.editGlobalization.id) {
- this.languageList[0].value = 'en'
+ this.languageList[1].value = 'en'
}
} else {
this.$message.error(response.msg)

View File

@@ -1,125 +0,0 @@
<template>
<div>
<el-dialog class="nz-dialog" :title="$t('overall.changePin')" :visible.sync="visible" @open="dialogOpened" :modal-append-to-body='false' @closed="dialogClosed" width="600px" :show-close="false" >
<el-form :model="user" label-position = "top" label-width="150px" :rules="rules" ref="changePinForm" size="mini">
<el-form-item :label="$t('config.user.account')" prop="username" v-show="curUser != sysUser">
<el-input type="text" autocomplete="false" v-model="user.username" disabled id="change-pin-username"></el-input>
</el-form-item>
<el-form-item :label="$t('config.user.oldPin')" prop="pin">
<el-input type="password" autocomplete="false" :show-password="true" v-model="user.pin" maxlength="20" :placeholder="$t('config.user.inputOldPin')" id="change-pin-pin"></el-input>
</el-form-item>
<el-form-item :label="$t('config.user.newPin')" prop="newPin">
<el-input type="password" autocomplete="false" :show-password="true" v-model="user.newPin" maxlength="20" :placeholder="$t('config.user.inputNewPin')" id="change-pin-newPin"></el-input>
</el-form-item>
<el-form-item :label="$t('config.user.confirmPin')" prop="confirmPin">
<el-input type="password" autocomplete="false" :show-password="true" v-model="user.confirmPin" maxlength="20" :placeholder="$t('config.user.inputConfirmPin')" id="change-pin-confirmPin"></el-input>
</el-form-item>
</el-form>
<!--底部按钮-->
<template slot="footer">
<div class="right-box-bottom-btns" >
<button @click="close" id="change-pin-esc"
class="nz-btn nz-btn-size-normal-new nz-btn-style-light-new">
<span>{{$t('overall.cancel')}}</span>
</button>
<button @click="changePin" id="change-pin-save"
class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'changePin',
props: {
curUser: { type: String },
showDialog: { type: Boolean, default: false }
},
data () {
const temp = this
const validatePass = (rule, value, callback) => {
if (value && value != '') {
callback()
} else {
callback(new Error(temp.$t('config.user.invalidPin')))
}
}
const validateConfirmPass = (rule, value, callback) => {
if (value && value != '' && value == temp.user.newPin) {
callback()
} else {
callback(new Error(temp.$t('config.user.confirmPinErr')))
}
}
return {
user: {
username: '',
pin: '',
newPin: '',
confirmPin: ''
},
sysUser: localStorage.getItem('nz-username'),
rules: {
pin: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }],
newPin: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }, { validator: validatePass, trigger: 'blur' }],
confirmPin: [{ required: true, message: this.$t('config.user.reinputPin'), trigger: 'blur' }, { validator: validateConfirmPass, trigger: 'blur' }]
},
visible: false
}
},
created () {
this.user.username = this.curUser && this.curUser != '' ? this.curUser : localStorage.getItem('nz-username')
},
methods: {
dialogOpened: function () {
if (this.$refs.changePinForm) {
this.$refs.changePinForm.resetFields()
}
},
dialogClosed: function () {
this.$emit('dialogClosed')
},
close: function () {
this.visible = false
},
changePin: function () {
this.$refs.changePinForm.validate((valid) => {
if (valid) {
const paramObj = {
pin: this.user.pin,
newPin: this.user.newPin
}
this.$get('/sys/user/pin?oldPin=' + paramObj.pin + '&newPin=' + paramObj.newPin).then(response => {
if (response && response.code == 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.close()
} else {
this.$message.error(response.msg)
}
})
}
})
}
},
watch: {
showDialog: function (n, o) {
this.visible = n
}
}
}
</script>
<style >
.el-dialog__footer {
margin-top: 30px;
padding: 10px 20px 20px;
text-align: right;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
</style>

View File

@@ -1,975 +0,0 @@
<template>
<div v-clickoutside="clickout" :class="{'metric-editor-rb': styleType === 2}" class="metric-editor">
<div :id="id" ref="editor" :style="{height: styleType === 2 ? 'auto' : ''}" class="editor-core"></div>
<span class="nz-input-append editor-clear" style="display: none" @click="clearContent"><i class="nz-icon nz-icon-circle-close"></i></span>
<div class="metric-editor-popper" :style="{left:popperPos.left+'px'}" v-show="false">
<div class="metric-popper-main" v-show="showType">
<div ref="scroll" style="height: 100%;width:100%; overflow: auto;">
<div v-for="(key, index) in orders" :key="index">
<div v-html="key" class="popper-group" v-show="showSuggestions[key]"></div>
<div v-for="(item) in showSuggestions[key]" :key="item.insertText" :type="key" :value="item.insertText" class="popper-item" @mouseout="itemOut(item)" @mouseover="itemHover(item)" v-html="item.label" @click.stop="handleItemClick(key,item,$event)"></div>
</div>
</div>
</div>
<div class="metric-popper-desc" v-if="showType&&Object.keys(showSuggestions).length>0&&showDescription && detailItem && detailItem.documentation">
<div><b>{{detailItem.insertText}}</b></div>
<hr/>
<div>{{detailItem.documentation}}</div>
</div>
</div>
</div>
</template>
<script>
import 'quill/dist/quill.snow.css'
import Quill from 'quill'
import suggestions from './suggestions'
export default {
name: 'editor',
props: {
value: String,
metricList: { type: Array },
styleType: Number,
historyParam: Object
},
model: {
prop: 'value',
event: 'expression-change'
},
components: {
},
data () {
return {
id: 'editor-' + this.guid(),
cursorIndex: 0,
pivotalCursorIndex: 0, // 用于记录输入特殊符号后当前光标的位置,方便选择后覆盖
newExpressionIndex: 0,
editRange: { start: 0, end: 0 },
noStyleSuggestions: {}, // 存储最原始的item数据
orders: ['history', 'operators', 'functions', 'metrics', 'labels', 'values', 'range'],
showSuggestions: {}, // 存储显示的item数据包含mark样式
storedSuggestions: [], // 存储每个item的dom
toggleSelectIndex: -1,
content: '',
popperPos: { left: '20px' },
options: {
theme: 'snow',
modules: {
toolbar: null
},
placeholder: this.$t('dashboard.metricPreview.inputTip')
},
labelValues: {},
tempStoreMetric: {},
showType: false,
showDescription: false,
detailItem: {},
formatTimer: null,
userChangeTimer: null,
filterTimer: null
}
},
created () {
this.queryMetrics()
},
methods: {
userChange (char, operation, newContent, oldContent) {
this.changeSuggestions('type')
this.dealSpecilChar(char, operation)
if (this.pivotalCursorIndex !== 0) {
// console.log('pivotalCursorIndex',this.pivotalCursorIndex,'cursorIndex',this.cursorIndex)
newContent = newContent.substring(this.pivotalCursorIndex, this.cursorIndex)
}
clearTimeout(this.filterTimer)
this.filterTimer = setTimeout(() => {
this.filterItems(newContent)
}, 200)
},
dealSpecilChar (char, operation) { // 控制括号的成对添加和删除
// console.log('specil char',char)
if (/^[\{\(\[\,]$/g.test(char)) {
if (operation === 'insert') {
if (char === '{' || char === ',') {
if (char === '{') {
this.addDoubleChar('}')
}
this.changeSuggestions('label')
} else if (char === '(') {
this.addDoubleChar(')')
this.queryTypeInfos()
} else if (char === '[') {
this.addDoubleChar(']')
this.changeSuggestions('range')
}
} else {
this.delDoubleChar(char)
}
} else if (/^[(=|!=|=~|!~)\"]/g.test(char)) {
if (operation === 'insert') {
if (char === '"') {
this.addDoubleChar('"')
}
this.changeSuggestions('values')
} else {
if (char === '"') {
this.delDoubleChar(char)
}
}
}
},
addDoubleChar (char) {
this.insertTextAtIndex(char, this.cursorIndex)
this.quill.setSelection(this.cursorIndex)
},
delDoubleChar (leftChar) {
const char = this.content.charAt(this.cursorIndex)
const temp = leftChar + char
if (/(\{\})|(\(\))|(\[\])|(\"\")/.test(temp)) {
this.quill.deleteText(this.cursorIndex, 1, 'api')
}
let tempIndex = this.pivotalCursorIndex
let tempChar = this.content.charAt(tempIndex - 1)
// 向前查找,找到边界
while (!/[\{\(\[,= ]/.test(tempChar) && tempIndex > 0) {
tempIndex--
tempChar = this.content.charAt(tempIndex)
}
// console.log('tempIndex =====',tempIndex)
this.pivotalCursorIndex = tempIndex === 0 ? -1 : tempIndex + 1
// console.log('after del double char',this.pivotalCursorIndex)
},
changeSuggestions (type) {
if (type === 'label') {
this.queryLabels()
} else if (type === 'values') {
this.queryValues()
} else if (type === 'type') {
this.packageTypeInfo()
} else if (type === 'range') {
this.noStyleSuggestions = { range: suggestions.getRateRange() }
}
},
packageTypeInfo () {
const functionReg = /[a-zA-Z_]\w*?\(.*?\)/g
if (functionReg.test(this.content)) {
functionReg.lastIndex = 0
const funcMatch = functionReg.exec(this.content)
const funcContent = funcMatch[0]
const funcReg = /\(.*\)/g
const match = funcReg.exec(funcContent)
const funcBody = match[0].substring(1, match[0].length - 1)
this.matchMetric(funcBody, funcMatch.index + match.index)
} else {
this.matchMetric()
}
},
formatContent () {
const punctuationReg = /[\{\}\(\)\[\]]/g
this.formatText(punctuationReg, { color: '#ff7941' })
const labelKeyReg = /[a-z_]\w*(?=\s*(=|!=|=~|!~))/g
this.formatText(labelKeyReg, { color: '#3274d9' })
const labelValueReg = /"(?:\\.|[^\\"])*"/g
this.formatText(labelValueReg, { color: '#3eb15b' })
const metricReg = /([A-Za-z:][\w:]*)\b(?![\(\]{=!",])/g
this.formatText(metricReg, { color: '#52545c' })
// let rangeReg=/(?<=\[)\b\d+[smdwy]\b(?=\])/ig
// this.formatText(rangeReg,{'color':'#9954bb'})
const rangeReg = /\[\b(\d+[smdwy])\b\]/ig
if (rangeReg.test(this.content)) {
const matchs = this.globalMatch(rangeReg, this.content)
// console.log('matchs',matchs)
matchs.forEach(item => {
this.quill.formatText(item.index + 1, item[1].length, { color: '#9954bb' }, 'silent')
})
}
},
formatText (pattern, style) {
if (pattern.test(this.content)) {
const matchs = this.globalMatch(pattern, this.content)
matchs.forEach(item => {
this.quill.formatText(item.index, item[0].length, style, 'silent')
})
}
},
matchMetric () {
const metricReg = /[a-zA-Z_]\w*?\b\{.*?\}/g
if (metricReg.test(this.content)) {
const match = this.globalMatch(metricReg, this.content)
if (match && match.length > 0) {
let isInnerExpression = false
let editExpression = null
match.forEach(item => {
const content = item[0]
const index = item.index
const length = content.length
if (index < this.cursorIndex && index + length > this.cursorIndex) { // 判断是否在一个完整子表达式中
isInnerExpression = true
editExpression = item
}
})
// console.log('match',match,'isInnerExpress',isInnerExpression)
if (isInnerExpression && editExpression) {
const expression = editExpression[0]
const labelValuesReg = /\{((\w*?(=|!=|=~|!~).*?,{0,1})+?)\}/
if (labelValuesReg.test(expression)) {
const match = labelValuesReg.exec(expression)
if (editExpression.index + match.index > this.cursorIndex) {
this.queryTypeInfos()
}
}
} else {
let tempIndex = this.cursorIndex
let tempChar = this.content.charAt(tempIndex - 1)
// 向前查找,找到边界
while (tempIndex > 0 && /[a-zA-Z_]/.test(tempChar)) {
tempIndex--
tempChar = this.content.charAt(tempIndex)
}
this.queryTypeInfos()
}
}
} else {
// console.log('mo match metric')
this.queryTypeInfos()
}
},
queryTypeInfos () {
this.noStyleSuggestions = {}
if (this.historyParam && this.historyParam.useHistory) {
const username = localStorage.getItem('nz-username')
const historyJson = localStorage.getItem(this.historyParam.key)
if (historyJson && historyJson !== 'undefined') {
const historyObj = JSON.parse(historyJson)
let history = historyObj[username]
history = history.filter(item => {
return item.time + item.expire >= new Date().getTime()
})
this.$set(this.noStyleSuggestions, 'history', history.slice(0, 10))
}
}
this.$set(this.noStyleSuggestions, 'metrics', this.tempStoreMetric)
// this.$set(this.noStyleSuggestions,'operators',suggestions.getOperators())
this.$set(this.noStyleSuggestions, 'functions', suggestions.getFunctions())
},
queryValues () {
const labelValuesReg = /\{((\w*?(=|!=|=~|!~).*?,{0,1})+?)\}/
if (labelValuesReg.test(this.content)) {
const match = labelValuesReg.exec(this.content)
const labelValues = match[0]
const index = match.index
if (labelValues) {
let tempCounter = this.cursorIndex
let equalIndex = -1
let boundaryIndex = -1
while (tempCounter >= index) {
const char = this.content.charAt(tempCounter)
if (/(=|!)/.test(char)) {
equalIndex = tempCounter
}
if (char === ',' || char === '{') {
boundaryIndex = tempCounter + 1
break
}
tempCounter--
}
const label = this.content.substring(boundaryIndex, equalIndex)
// console.log('label',this.content.substring(boundaryIndex,equalIndex))
const values = this.labelValues.get(label)
this.noStyleSuggestions = { values: values }
}
}
},
queryLabels () {
const labels = /\{.*\}/
if (labels.test(this.content)) {
const match = labels.exec(this.content)
const index = match.index
const matchContent = match[0]
if (this.cursorIndex < index || this.cursorIndex > index + matchContent.length) {
return
}
} else {
this.queryTypeInfos()
return
}
const metricReg = /([a-zA-Z_]\w*)\b\{.*?\}/g
let editMetricExpression = ''
const match = this.globalMatch(metricReg, this.content)
if (match && match.length > 0) {
for (let i = 0; i < match.length; i++) {
const temp = match[i]
if (temp.index < this.cursorIndex && temp.index + temp[0].length > this.cursorIndex) {
editMetricExpression = temp[0]
break
}
}
}
// console.log('query metric',editMetricExpression)
const metric = /([a-zA-Z_]\w*)\b(?=\{)/.exec(editMetricExpression)[0]
const timeRange = this.getDefaultTimeRange()
this.$get('/prom/api/v1/series?match[]={__name__="' + metric + '"}&start=' + timeRange[0] + '&end=' + timeRange[1]).then(response => {
if (response.status === 'success') {
const result = new Map()
response.data.forEach(item => {
for (const key in item) {
const value = item[key]
let values = result.get(key)
if (values) {
values.add(value)
} else {
values = new Set()
values.add(value)
}
result.set(key, values)
}
})
for (const key of result.keys()) {
let values = result.get(key)
values = Array.from(values).map(item => {
return { label: item, insertText: item }
})
result.set(key, values)
}
this.labelValues = result
const labels = Array.from(this.labelValues.keys()).map(item => {
return { label: item, insertText: item }
})
this.noStyleSuggestions = { labels: labels }
// console.log('change labels',this.showSuggestions)
} else {
this.noStyleSuggestions = {}
}
})
},
getDefaultTimeRange () {
const end = new Date()
const start = new Date()
start.setHours(new Date().getHours() - 1)
return [(start.getTime() / 1000).toFixed(0), (end.getTime() / 1000).toFixed(0)]
},
handleItemClick (type, item) {
// console.log(type,item,event)
if (type === 'metrics') {
this.handleMetricClick(item)
} else if (type === 'labels') {
this.handleLabelClick(item)
} else if (type === 'values') {
this.handleValueClick(item)
} else if (type === 'operators') {
this.handleOperatorClick(item)
} else if (type === 'functions') {
this.handleFunctionClick(item)
} else if (type === 'range') {
this.handleRangeClick(item)
} else if (type === 'history') {
this.handleHistoryClick(item)
}
},
handleHistoryClick (item) {
this.setContent(item.insertText)
this.showType = false
this.quill.blur()
this.$nextTick(() => {
this.$emit('on-blur')
})
},
handleRangeClick (item) {
this.quill.setSelection(this.pivotalCursorIndex, this.cursorIndex - this.pivotalCursorIndex, 'api')
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
this.insertTextAtIndex(item.insertText, this.pivotalCursorIndex)
this.showType = false
},
handleOperatorClick (item) {
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
this.$nextTick(() => {
this.insertTextAtIndex(item.insertText, this.pivotalCursorIndex, 'user')
this.quill.setSelection(this.pivotalCursorIndex + item.insertText.length)
this.storeCursor()
})
this.showType = false
},
handleFunctionClick (item) {
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
this.$nextTick(() => {
this.insertTextAtIndex(item.insertText, this.pivotalCursorIndex, 'user')
this.quill.setSelection(this.pivotalCursorIndex + item.insertText.length)
this.storeCursor()
})
this.showType = false
},
handleMetricClick (item) {
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
this.$nextTick(() => {
this.insertTextAtIndex(item.insertText, this.pivotalCursorIndex, 'user')
this.quill.setSelection(this.pivotalCursorIndex + item.insertText.length)
this.storeCursor()
})
this.showType = false
},
handleLabelClick (item) {
this.quill.setSelection(this.pivotalCursorIndex, this.cursorIndex - this.pivotalCursorIndex, 'api')
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
this.insertTextAtIndex(item.insertText + '', this.pivotalCursorIndex)
this.storeCursor().then(() => {
this.insertTextAtIndex('=', null, 'user')
this.quill.setSelection(this.cursorIndex + 1)
this.changeSuggestions('value')
})
this.showType = true
},
handleValueClick (item) {
const preChar = this.content.substring(this.pivotalCursorIndex - 1, this.pivotalCursorIndex)
this.quill.setSelection(this.pivotalCursorIndex, this.cursorIndex - this.pivotalCursorIndex, 'api')
this.deleteTextInRange(this.pivotalCursorIndex, this.cursorIndex)
if (preChar === '=') {
this.insertTextAtIndex('"' + item.insertText + '"', this.pivotalCursorIndex)
} else {
this.insertTextAtIndex(item.insertText, this.pivotalCursorIndex)
}
this.showType = false
},
toggleSelect (event) { // 上下键选择
if (event.code === 'ArrowDown' || event.keyCode === 40) {
if (this.toggleSelectIndex !== -1) {
const oldSuggestion = this.storedSuggestions[this.toggleSelectIndex]
oldSuggestion.classList = 'popper-item'
}
const suggestion = this.storedSuggestions[this.toggleSelectIndex >= this.storedSuggestions.length - 1 ? this.storedSuggestions.length - 1 : ++this.toggleSelectIndex]
const classList = suggestion.classList
suggestion.classList = classList + ' toggle-bg'
this.moveScroll()
} else if (event.code === 'ArrowUp' || event.keyCode === 38) {
if (this.toggleSelectIndex >= 1) {
const oldSuggestion = this.storedSuggestions[this.toggleSelectIndex]
oldSuggestion.classList = 'popper-item'
const suggestion = this.storedSuggestions[this.toggleSelectIndex <= 0 ? 0 : --this.toggleSelectIndex]
const classList = suggestion.classList
suggestion.classList = classList + ' toggle-bg'
this.moveScroll()
}
}
const item = this.findItemByToggleIndex()
if (item && item.documentation) {
this.showDescription = true
this.detailItem = this.deepClone(item)
} else {
this.showDescription = false
this.detailItem = {}
}
},
findItemByToggleIndex () {
const selectedItem = this.storedSuggestions[this.toggleSelectIndex]
if (selectedItem) {
const insertText = selectedItem.getAttribute('value')
const keys = Object.keys(this.noStyleSuggestions)
for (let i = 0; i < keys.length; i++) {
const items = this.noStyleSuggestions[keys[i]]
const item = items.find(t => {
return t.insertText === insertText
})
if (item) {
return item
}
}
}
return null
},
pickItem () { // Tab键 获取选择
const selectedItem = this.storedSuggestions[this.toggleSelectIndex]
if (selectedItem) {
const type = selectedItem.getAttribute('type')
const insertText = selectedItem.getAttribute('value')
if (type === 'metrics') {
this.handleMetricClick({ insertText: insertText })
} else if (type === 'labels') {
this.handleLabelClick({ insertText: insertText })
} else if (type === 'values') {
this.handleValueClick({ insertText: insertText })
} else if (type === 'operators') {
this.handleOperatorClick({ insertText: insertText })
} else if (type === 'range') {
this.handleRangeClick({ insertText: insertText })
} else if (type === 'functions') {
this.handleFunctionClick({ insertText: insertText })
} else if (type === 'history') {
this.handleHistoryClick({ insertText: insertText })
}
}
},
filterItems (input) { // 过滤下拉选显示
const suggestions = this.deepClone(this.noStyleSuggestions)
new Promise(resolve => {
const result = {}
this.orders.forEach(key => {
const typeValues = suggestions[key]
result[key] = []
typeValues && typeValues.forEach(item => {
const index = item.insertText.toLowerCase().indexOf(input.toLowerCase())
// console.log('index',index,'input',input)
if (index !== -1) {
// console.log('item',item.label,'input',input)
// item.label=item.label.replace(input,`<mark>${input}</mark>`);
const label = item.label
const pre = label.substring(0, index)
const middle = label.substring(index, index + input.length)
const suf = label.substring(index + input.length, label.length)
item.label = `${pre}<mark>${middle}</mark>${suf}`
result[key].push(item)
}
})
if (result[key].length < 1) {
delete result[key]
}
})
this.showSuggestions = result
resolve()
}).then(() => {
this.showType = Object.keys(this.showSuggestions).length > 0
})
/* let scroll=this.$refs.scroll.wrap;
scroll.scrollTop=0; */
},
storeSuggestions () {
this.$nextTick(() => {
const suggestions = this.$el.querySelectorAll('.popper-item')
this.storedSuggestions = [...suggestions]
// console.log('storedSuggestions->',suggestions)
})
},
setContent (value) {
this.clearContent()
this.quill.setText(value)
},
clearContent () {
this.quill.deleteText(0, this.quill.getLength(), 'api')
this.cursorIndex = 0
this.pivotalCursorIndex = 0
},
insertTextAtIndex (content, index, source = 'api') {
this.quill.insertText(index || this.cursorIndex, content, source)
},
deleteTextInRange (from, to) {
return this.quill.deleteText(from, to - from, 'api')
},
setCursor (offset) {
this.$nextTick(() => {
if (!this.quill.hasFocus) {
this.quill.focus()
}
this.quill.setSelection(this.cursorIndex + offset, this.cursorIndex + offset, 'api')
this.storeCursor()
})
},
storeCursor (char) {
const $temp = this
return new Promise(function (resolve) {
$temp.$nextTick(() => {
const range = $temp.quill.getSelection()
// console.log('range',range)
if (range && range.length === 0) {
$temp.cursorIndex = range.index
// console.log('current cursor-->',$temp.cursorIndex)
if (/^[\{\(\[,= "]$/g.test(char)) {
$temp.pivotalCursorIndex = $temp.cursorIndex
// console.log('store pivotalCursorIndex',$temp.pivotalCursorIndex)
}
resolve()
}
})
})
},
moveCursorToEnd () {
setTimeout(() => {
this.$nextTick(() => {
const offset = this.quill.getLength()
this.setCursor(offset)
}, 100)
})
},
moveScroll () {
/* let scroll=this.$refs.scroll.wrap;
//console.log('scroll',scroll)
if(scroll){
let curScroll=scroll.scrollTop;//滚动条当前位置
let scrollLen=scroll.scrollHeight;//滚动条总长
let mainDom=this.$el.querySelector('.metric-popper-main');
let windowHeight=mainDom.clientHeight;
let highlightItem=this.storedSuggestions[this.toggleSelectIndex];
let itemHeight=highlightItem.clientHeight;
let itemPos=highlightItem.offsetTop; //高亮item距离父div顶部的距离
// console.log('curScroll',curScroll,'scrollLen',scrollLen,'windowHeight',windowHeight,'itemPos',itemPos,'itemHeight',itemHeight)
if(itemPos + itemHeight*2 > windowHeight){ //提前两个位置
scroll.scrollTop=itemPos-windowHeight+itemHeight*2
}else{
let interval = windowHeight/10;
let intervalFunc = setInterval(function(){ //花200ms分10次回到顶部模拟动画效果
if (curScroll == 0) {
clearInterval(intervalFunc);
} else {
curScroll = (curScroll - interval) < interval*0.5 ? 0 : curScroll - interval;
scroll.scrollTop = curScroll;
}
}, 20);
}
} */
},
clickout () {
if (this.quill && this.quill.hasFocus()) {
this.$emit('on-blur')
}
this.showType = false
},
itemHover (item) {
if (item && item.documentation) {
this.showDescription = true
this.detailItem = this.deepClone(item)
}
},
itemOut () {
if (this.toggleSelectIndex !== -1) {
const tempItem = this.findItemByToggleIndex()
if (tempItem && tempItem.documentation) {
this.showDescription = true
this.detailItem = this.deepClone(tempItem)
} else {
this.showDescription = false
this.detailItem = {}
}
} else {
this.showDescription = false
this.detailItem = {}
}
},
getContent (from, to) {
let content = this.quill.getText(from, to)
content = content.substring(0, content.length - 1)
return content
},
getPosition () {
this.$nextTick(() => {
this.popperPos = this.quill.getBounds(this.cursorIndex)
// console.log('position:',this.popperPos)
})
},
guid () {
function S4 () {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
}
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
},
registerKeydown () {
const $temp = this
$temp.$el.addEventListener('keydown', function (event) {
// console.log('keydown',event)
$temp.toggleSelect(event)
})
this.quill.keyboard.addBinding({
key: 'DOWN'
}, function () {
return false
})
this.quill.keyboard.addBinding({
key: 'UP'
}, function () {
return false
})
// this.quill.keyboard.addBinding({ key:'backspace'}, function(range,content) {
// console.log('quill tab binding',range,content)
// return false;
// });
},
deepClone (source) {
return JSON.parse(JSON.stringify(source))
},
globalMatch (pattern, content) {
const result = []
let temp
pattern.lastIndex = 0
while ((temp = pattern.exec(content)) != null) {
result.push(temp)
}
return result
},
queryMetrics () {
if (this.metricList && this.metricList.length > 0) {
this.tempStoreMetric = this.deepClone(this.metricList)
this.queryTypeInfos()
} else {
this.$get('/prom/api/v1/label/__name__/values').then(response => {
if (response.status === 'success') {
const metrics = response.data.map(item => {
return { label: item, insertText: item }
})
this.tempStoreMetric = this.deepClone(metrics)
this.queryTypeInfos()
}
})
}
}
},
computed: {
},
mounted () {
const $temp = this
const bindings = { // quill默认的键只能通过这种方式覆盖
tab: {
key: 9,
handler () {
$temp.pickItem()
}
},
enter: {
key: 13,
handler () {
if (!$temp.showType) {
$temp.showType = false
$temp.quill.blur()
$temp.$emit('on-enter')
return false
} else {
$temp.pickItem()
}
}
}
}
this.options.modules.keyboard = {}
this.options.modules.keyboard.bindings = bindings
this.quill = new Quill(this.$refs.editor, this.options)
$temp.$el.addEventListener('paste', function (even) {
/* let text='';
setTimeout(()=>{
text=$temp.getContent();
},100)//此值必须小于userChangeTimer的值
setTimeout(()=>{
$temp.setContent(text);
$temp.quill.setSelection($temp.cursorIndex+text.length);
},200)//此值必须大于userChangeTimer的值 */
})
this.quill.clipboard.addmatchers(Node.ELEMENT_NODE, (node, delta) => {
const ops = []
delta.ops.forEach(op => {
if (op.insert && typeof op.insert === 'string') {
ops.push({
insert: op.insert
})
}
})
delta.ops = ops
return delta
})
this.quill.on('text-change', function (delta, oldDelta, source) {
if (source !== 'silent') {
// console.log('delta',delta,'oldDelta',oldDelta,'source',source)
let char = ''; let operation = ''
let oldContent = ''
oldDelta.ops.forEach((item, index) => {
if (index !== oldDelta.ops.length - 1) {
oldContent += item.insert
}
})
if (delta.ops.length === 1) {
if (delta.ops[0].insert) {
char = delta.ops[0].insert
operation = 'insert'
} else if (delta.ops[0].delete) {
char = oldContent
operation = 'delete'
} else {
console.error('unknow operation')
}
} else if (delta.ops.length === 2) {
if (delta.ops[1].insert) {
char = delta.ops[1].insert
operation = 'insert'
} else if (delta.ops[1].delete) {
char = oldContent.substring(delta.ops[0].retain, delta.ops[0].retain + delta.ops[1].delete)
operation = 'delete'
} else {
console.error('unknow operation')
}
}
const promise = $temp.storeCursor(char)
if (source === 'user') {
// console.log(delta,oldDelta,oldContent)
// clearTimeout($temp.userChangeTimer)
// $temp.userChangeTimer=setTimeout(()=>{
const newContent = $temp.getContent()
promise.then(() => { // 存储完光标后再进行后面的操作
$temp.userChange(char, operation, newContent, oldContent)
})
// },100)
}
$temp.content = $temp.getContent()
// console.log('current content-->',$temp.content)
$temp.getPosition()
/* clearTimeout($temp.formatTimer)
$temp.formatTimer=setTimeout($temp.formatContent,100) */
$temp.formatContent()
}
})
this.registerKeydown()
},
watch: {
content (n) {
this.$emit('expression-change', n)
if (!n) {
this.cursorIndex = 0
this.pivotalCursorIndex = 0
}
},
showSuggestions: {
immediate: true,
deep: true,
handler () {
this.toggleSelectIndex = -1
this.storeSuggestions()
}
}
},
beforeDestroy () {
this.quill.off('text-change')
}
}
</script>
<style scoped lang="scss">
.metric-editor{
position: relative;
height: 36px;
}
.metric-editor-popper{
position: absolute;
height: 600px;
padding: 10px;
margin-top: 5px;
overflow: hidden;
z-index:2001;
}
.metric-editor-popper .metric-popper-main{
display: inline-block;
box-sizing: border-box;
border: 1px solid lightgrey;
height: 100%;
/*max-width: 66%;*/
min-width: 200px;
float: left;
border-radius: 3px;
box-shadow: 0 5px 10px 0 #dde4ed;
color: #52545c;
padding: 5px;
background-color: white;
overflow-x: hidden;
}
.metric-editor-popper .metric-popper-desc{
display: inline-block;
box-sizing: border-box;
border-top: 1px solid lightgrey;
border-bottom: 1px solid lightgrey;
border-right: 1px solid lightgrey;
max-width: 44%;
height: 100%;
border-radius: 3px;
box-shadow: 0 5px 10px 0 #dde4ed;
color: #52545c;
padding: 5px;
background-color: white;
}
.metric-editor-popper .popper-item{
height: 20px;
line-height: 20px;
font-size: 12px !important;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
text-overflow: ellipsis;
white-space: nowrap;
padding: 8px 8px 8px 16px;
transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) 0s, border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) 0s, background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) 0s, padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1) 0s;
}
.metric-editor-popper .popper-group{
height: 20px;
line-height: 20px;
font-size: 16px !important;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
text-overflow: ellipsis;
white-space: nowrap;
padding: 8px 8px 8px 8px;
border-bottom: 1px solid #dcdcdc;
}
.toggle-bg{
background-color:rgb(233, 237, 242) ;
}
.editor-core:hover + .editor-clear{
display: inline-block !important;
}
.editor-clear {
padding-top: 8px;
}
.editor-clear:hover{
display: inline-block !important;
}
</style>
<style lang="scss">
mark{
color: rgb(255, 133, 27);
border-bottom: 1px solid rgb(255, 133, 27);
background: inherit;
padding: 0;
}
.metric-editor .ql-editor{
padding:7px 10px 10px 10px !important;
overflow: hidden;
}
.metric-editor.metric-editor-rb {
min-height: 32px;
border-radius: 4px;
height: auto;
border: 1px solid #DCDFE6;
box-sizing: border-box;
.editor-clear {
padding-top: 4px;
}
.ql-container.ql-snow {
border: none;
.ql-editor {
padding: 5px 0 !important;
>p {
padding: 0 26px 0 15px;
font-size: 12px;
color: rgb(96, 98, 102);
}
}
}
}
</style>

View File

@@ -1,459 +0,0 @@
<style lang="scss">
@import './metricPreview.scss';
</style>
<template>
<div class="main-container metric-dashboard">
<left-menu >
<div slot="content-left" class="slot-content">
<div class="sidebar-title">{{$t('dashboard.title')}}</div>
<div class="sidebar-info">
<div class="sidebar-info-item " @click="jumpTo('overview')">{{$t('dashboard.overview.title')}}</div>
<div class="sidebar-info-item sidebar-info-top" @click="jumpTo('panel')">{{$t('dashboard.panel.title')}}</div>
<div class="sidebar-info-item sidebar-info-item-active">{{$t('dashboard.metricPreview.title')}}</div>
<!-- <div class="sidebar-info-item "@click="jumpTo('explore')">{{$t('dashboard.metricPreview.title')}}</div>-->
</div>
</div>
<div slot="content-right" class="slot-content">
<!--<div class="top-tools">
<div class="float-right mr-10">
<my-date-picker size="mini" 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">
</my-date-picker>
</div>
</div>-->
<div class="table-list" >
<div class="box-content" >
<el-row :gutter="20" class="row-width" style="height: calc(100% - 65px);">
<el-col :span="10" >
<metric-set :panelData="panelData" ref="metricSet" @on-view-chart="getChartParam" @reloadPanel="getPanelData" ></metric-set>
</el-col>
<el-col :span="0.5" ><div>&nbsp;</div></el-col>
<el-col :span="13" class="chart-preview-area" >
<my-date-picker size="mini" 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')"
style="position:absolute; right: 10px"
align="right">
</my-date-picker>
<el-row class="border-area" v-show="chartCount === 'single'" style="margin-top: 40px;">
<div class="chartBox">
<line-chart-block
:show-setting="false"
:isExplore="true"
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 style="height: 100%; overflow: auto;">
<div v-for="item in lineList" :key="item" class="chartBoxMul">
<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>
</div>
</el-row>
</el-col>
</el-row>
</div>
</div>
</div>
</left-menu>
</div>
</template>
<script>
import bus from '../../../libs/bus'
import metricSet from './metricSet'
import lineChartBlock from '../../charts/line-chart-block'
export default {
name: 'metricPreview',
components: {
lineChartBlock,
metricSet
},
data () {
return {
filter: {
start_time: '',
end_time: ''
},
total: 0, // 总数
selectedData: null, // 选中数据
searchName: '', // 搜索名称
showTagModal: false,
tagSet: null, // 根据你metric获取的tags信息
metricInfo: {}, // 保存参数信息
chartCount: 'single', // multiple
lineList: [], // 线列表
panelData: [], // panel列表
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: {
jumpTo (data, id) {
// this.$store.state.assetData.moduleData = data;
// this.$store.state.assetData.selectedData = id;
bus.$emit('menu-change', data)
this.$store.state.showPanel.id = 0
this.$store.state.showPanel.name = ''
this.$router.push({
path: '/' + data,
query: {
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')
this.$refs.editChartSingle.setDivFirstShow(true)
},
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)
})
}
}
},
getPanelData () { // 获取panel数据
this.$get('panel?pageNo=1&pageSize=-1').then(response => {
if (response.code === 200) {
this.panelData = response.data.list
}
})
},
// 获取图表信息
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
const step = bus.getStep(startTime, endTime)
const query = params.elements[0].expression
this.$get('/prom/api/v1/query_range?query=' + encodeURIComponent(query) + '&start=' + this.$stringTimeParseToUnix(startTime) + '&end=' + this.$stringTimeParseToUnix(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'
}
if (response.data.result.length === 0) {
this.$refs.editChartSingle.setData(params, [], 0, this.filter, [])
}
}
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 if (response.error) {
this.$message.error(response.error)
} 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: 0.2, // 曲线变平滑
data: [],
type: chartItem.type ? chartItem.type : 'line',
// visible: true,
// threshold: null,
chartTitle: '',
lineStyle: {
width: 1,
opacity: 0.9
}
},
metric_name: ''
}
// 图表中每条线的名字,后半部分
// let host = `${queryItem.metric.__name__}`;//up,
let host = ''// up,
let charName = ''
if (queryItem.metric.__name__) {
host = `${queryItem.metric.__name__}`// up,
charName = `${queryItem.metric.__name__}`
}
// let charName = `${queryItem.metric.__name__}`;
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"]] ]
// 判断是否有数据, && tagsArr.length > 0
if (dpsArr.length > 0) {
if (queryItem.metric.__name__) {
host += '{'
}
tagsArr.forEach((tag, i) => {
if (tag !== '__name__') {
host += `${tag}="${queryItem.metric[tag]}",`
}
if (tag === 'asset') {
const labVal = `${queryItem.metric[tag]}`
if (labVal !== '') {
charName += `(${queryItem.metric[tag]})`
}
}
})
if (host.endsWith(',')) { host = host.substr(0, host.length - 1) }
if (queryItem.metric.__name__) {
host += '}'
}
if (!host || host === '') {
host = params.metric
}
const alias = params.metric
legend.push({ name: host, alias: alias })
// 图表中每条线的名字,去掉最后的逗号与空格
seriesItem.theData.name = host
seriesItem.theData.chartTitle = charName
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, series)
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.chartTitle
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 () {
this.getInit()
this.getPanelData()
},
watch: {
}
}
</script>