NEZ-3213 feat: rightBox metrics支持

This commit is contained in:
zhangyu
2023-10-11 15:44:41 +08:00
parent 6bc310b48a
commit b63d43cb34
20 changed files with 626 additions and 214 deletions

View File

@@ -61,13 +61,29 @@ const baseConfig = {
resolve('node_modules/@interactjs'),
resolve('node_modules/vue-grid-layout'),
resolve('node_modules/pl-table/package'),
resolve('node_modules/uplot'),
resolve('node_modules/uplot')
],
exclude: '/node_modules/',
options: {
presets: ['@babel/preset-env', '@vue/babel-preset-jsx']
}
},
{
test: /\.ts$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
'ts-loader'
],
include: [
resolve('node_modules/monaco-editor')
],
exclude: /node_modules/
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
@@ -173,6 +189,6 @@ if (arg === 'html' || devStart === 'dev:html') {
})
}
module.exports = {
...baseConfig,
...baseConfig
// transpileDependencies: ['uplot']
}

View File

@@ -123,6 +123,7 @@
@import './page/dashboard/explore/exploreHistory.scss';
@import './page/dashboard/explore/logTab.scss';
@import './page/dashboard/explore/promqlInput.scss';
@import './page/dashboard/explore/queryPrompt.scss';
@import './page/dashboard/overview/chart.scss';
@import './page/dashboard/overview/overview2.scss';
@import './page/dashboard/chartBox.scss';

View File

@@ -32,10 +32,72 @@
width: 200px;
}
}
.explore-history-box-content-header {
background: #F9F9F9;
display: flex;
height: 32px;
.query-prompt-content-text {
font-weight: 600;
line-height: 22px;
}
}
.query-prompt-content-text {
box-sizing: border-box;
padding: 5px 0 5px 10px;
font-size: 14px;
color: #666666;
}
.query-prompt-name {
width: 52%;
display: flex;
.query-prompt-content-title{
max-width: 100%;
display: inline-block;
}
.query-prompt-content-icon{
margin-left: 10px;
display: none;
}
}
.query-prompt-name:hover {
.query-prompt-content-title{
max-width: calc(100% - 24px);
}
.query-prompt-content-icon{
display: inline-block;
}
.query-prompt-content-icon:hover{
color: $--color-primary;
cursor: pointer;
}
}
.query-prompt-type {
width: 12%;
}
.query-prompt-remark {
width: 36%;
padding-right: 5px;
display: flex;
align-items: center;
.query-prompt-content-title{
display: inline-block;
width: calc(100% - 24px);
flex: 1;
}
.query-prompt-content-icon{
margin-left: 10px;
}
}
.explore-history-box-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
position: relative;
.in-bottom {
bottom: 0;
position: absolute;
}
}
.explore-history-item {
height: 32px;

View File

@@ -35,6 +35,12 @@
}
}
.nz-icon-alert-list {
cursor: pointer;
}
.nz-icon-alert-list:hover{
color: $--color-primary;
}
}
.input-box .append-msg {
@@ -180,8 +186,11 @@
.lines-content {
//height: auto !important;
//width: 100% !important;
//position: unset !important;
top: 5px !important;
//position: relative !important;
////top: 5px !important;
//.view-lines {
// width: 100% !important;
//}
}
}
.topo-page {
@@ -201,7 +210,7 @@
}
.input-box-log {
flex: unset;
width: calc(100% - 292px);
width: calc(100% - 190px);
}
}
}

View File

@@ -0,0 +1,26 @@
.query-prompt-box {
width: calc(100% - 10px);
height: 400px;
background: $--background-color-empty;
box-shadow: 0px 0px 21px 4px $--dropdown-menu-box-shadow-color;
border-radius: 2px;
position: absolute;
bottom: -6px;
left: 5px;
transform: translateY(100%);
z-index: 11;
max-height: 400px;
.explore-history-box-header {
padding-left: 20px;
}
div.explore-history-item {
padding: 0;
}
.query-prompt-metric{
display: flex;
flex-direction: column;
height: calc(100% - 45px);
box-sizing: border-box;
padding: 10px 0 10px 10px
}
}

View File

@@ -301,9 +301,7 @@ export default {
unit = 'm'
const noTime = this.momentStrToTimestamp(start + ' 00:00:00', 'YYYY-MM-DD HH:mm:ss')
end = this.momentStrToTimestamp(this.momentSetMonthDate(noTime, 1)) - 1000 // 当月1号 00:00:00 减1s 则是上月最后一天 23:59:59
console.log(end, '1')
start = this.momentSetMonthDate(end, 1, 'YYYY-MM-DD') + ' 00:00:00'
console.log(start, '1')
start = this.momentTz(this.momentStrToTimestamp(start, 'YYYY-MM-DD HH:mm:ss'))
end = this.momentTz(end)
}

View File

@@ -915,7 +915,6 @@ export default {
this.$refs.alertRuleForm.validate((blooen, object) => {
Object.keys(object).forEach(item => {
const keyArr = item.split('.')
console.log(keyArr)
if (keyArr.length >= 4) {
this.editAlertRule[keyArr[0]][keyArr[1]][keyArr[2]].error = true
}

View File

@@ -35,13 +35,16 @@
:key="expressionsShow[index-1].id"
>
<div class="chart-title chart-title-config">
<span class="chart-title-content el-form-item" :class="{
'is-error' : expressionsShow[index-1].error,
'hide-input': expressionsShow[index-1].hideInput
}">
<span
class="chart-title-content el-form-item"
:class="{
'is-error' : expressionsShow[index-1].error,
'hide-input': expressionsShow[index-1].hideInput
}"
>
<i class="nz-icon nz-icon-arrow-down" :class="expressionsShow[index-1].show?'':'is-active'" @click.stop="showExpression(index)"></i>
<el-input
style="width: 120px"
style="width: 90px"
@mousedown.stop
v-model="expressionName[index-1]"
size="small"
@@ -50,7 +53,10 @@
@focus.stop="showInput(index-1,false)"
@blur="showInput(index-1,true)"
/>
<div v-if="expressionsShow[index-1].error" class="el-form-item__error" style="top: 10px;left: 164px"> {{expressionsShow[index-1].error}}</div>
<div v-if="expressionsShow[index-1].error" class="el-form-item__error" style="top: 30px;left: 25px">{{expressionsShow[index-1].error}}</div>
<div class="text-ellipsis" style="flex-shrink: 0;width: calc(100% - 310px)" v-if="!expressionsShow[index-1].show" :title="expressions[index - 1]">
{{expressions[index - 1]}}
</div>
</span>
<span>
<!-- 显示隐藏 -->
@@ -79,7 +85,6 @@
<el-form-item :prop="'elements.' + (index -1) + '.expression'" :rules="{ required: true, message: $t('validate.required'), trigger: 'change'}">
<promql-input
:from-father-data="true"
:metricOptionsParent="metricOptions"
:expression-list="expressions"
:id="'promqlKeys' + [index-1]"
:index="index-1"
@@ -108,7 +113,7 @@
<el-input maxlength="512" size="small" type="text" v-model="expressionsShow[index-1].legend" @change="expressionChange"></el-input>
</div>
</div>
<div style="margin-left: 10px;">
<div style="margin-left: 10px;" v-if="'metrics' === promqlType">
<div class="legend-title-new" style="width: 55px">
<span class="legend-title__span">{{$t('overall.type')}}&nbsp;</span>
<el-popover placement="top" trigger="hover" width="211" popper-class="prevent-clickoutside">
@@ -119,8 +124,8 @@
</div>
<div style="flex: 1">
<el-select size="small" v-model="expressionsShow[index-1].queryType">
<el-option :label="'range'" :value="'range'">range</el-option>
<el-option :label="'instant'" :value="'instant'">instant</el-option>
<el-option :label="'Range'" :value="'range'">Range</el-option>
<el-option :label="'Instant'" :value="'instant'">Instant</el-option>
</el-select>
</div>
</div>
@@ -129,7 +134,17 @@
<span class="legend-title__span">{{$t('overall.step')}}&nbsp;</span>
</div>
<div style="flex: 1">
<el-input-number @change="expressionChange" v-model="expressionsShow[index-1].step" size="small" :min="minStep" :precision="0" :controls="false" class="append_unit" :placeholder="$t('overall.auto')" :data-unit="$t('overall.seconds')">
<el-input-number
:disabled="expressionsShow[index-1].queryType === 'instant'"
@change="expressionChange"
v-model="expressionsShow[index-1].step"
size="small"
:min="minStep"
:precision="0"
:controls="false"
class="append_unit"
:placeholder="$t('overall.auto')"
:data-unit="$t('overall.seconds')">
</el-input-number>
</div>
</div>
@@ -1303,7 +1318,7 @@
<script>
import promqlInput from '@/components/page/dashboard/explore/promqlInput'
import nezhaColor from '@/components/common/nezhaColor'
import promqlInputMixin from '@/components/common/mixin/promqlInput'
// import promqlInputMixin from '@/components/common/mixin/promqlInput'
import publicConfig from '@/components/common/rightBox/chart/publicConfig'
import chartTypeShow from '@/components/common/rightBox/chart/chartTypeShow'
import VueTagsInput from '@johmun/vue-tags-input'
@@ -1334,7 +1349,7 @@ export default {
}
}
},
mixins: [promqlInputMixin, publicConfig, chartTypeShow],
mixins: [publicConfig, chartTypeShow],
computed: {
minStep () {
return this.nzDefaultConfig.minStep

View File

@@ -610,7 +610,6 @@ export default {
immediate: true,
handler (n) {
const obj = JSON.parse(JSON.stringify(n))
console.log(obj)
if (obj && obj.elements) {
obj.elements.forEach((item)=>{
if (!item.queryType) {

View File

@@ -1096,9 +1096,7 @@ export default {
// 时间选择器设置默认时间范围
setDefaultTimeRange () {
this.$nextTick(() => {
console.log(this.$route.query.searchTime)
if (this.$route.query.searchTime) return
console.log(213123213)
let nowTimeType = this.$lodash.cloneDeep(this.timeData[3])
const defaultTimeRange = this.$lodash.get(this.showPanel, 'param.defaultTimeRange')
if (defaultTimeRange) {

View File

@@ -0,0 +1,153 @@
import assign from 'nano-assign'
const MonacoEditor = {
name: 'MonacoEditor',
props: {
original: String,
value: {
type: String,
required: true
},
theme: {
type: String,
default: 'vs'
},
language: String,
options: Object,
amdRequire: {
type: Function
},
diffEditor: {
type: Boolean,
default: false
}
},
model: {
event: 'change'
},
watch: {
options: {
deep: true,
handler: function handler (options) {
if (this.editor) {
const editor = this.getModifiedEditor()
editor.updateOptions(options)
}
}
},
value: function value (newValue) {
if (this.editor) {
const editor = this.getModifiedEditor()
if (newValue !== editor.getValue()) {
editor.setValue(newValue)
}
}
},
original: function original (newValue) {
if (this.editor && this.diffEditor) {
const editor = this.getOriginalEditor()
if (newValue !== editor.getValue()) {
editor.setValue(newValue)
}
}
},
language: function language (newVal) {
if (this.editor) {
const editor = this.getModifiedEditor()
this.monaco.editor.setModelLanguage(editor.getModel(), newVal)
}
},
theme: function theme (newVal) {
if (this.editor) {
this.monaco.editor.setTheme(newVal)
}
}
},
mounted: function mounted () {
const _this = this
if (this.amdRequire) {
this.amdRequire(['vs/editor/editor.main'], function () {
_this.monaco = window.monaco
_this.$nextTick(function () {
_this.initMonaco(window.monaco)
})
})
} else {
// ESM format so it can't be resolved by commonjs `require` in eslint
// eslint-disable-next-line import/no-unresolved
const monaco = require('monaco-editor')
this.monaco = monaco
this.$nextTick(function () {
_this.initMonaco(monaco)
})
}
},
beforeDestroy: function beforeDestroy () {
this.editor && this.editor.dispose()
},
methods: {
initMonaco: function initMonaco (monaco) {
const _this2 = this
this.$emit('editorWillMount', this.monaco)
const options = assign({
value: this.value,
theme: this.theme,
language: this.language
}, this.options)
if (this.diffEditor) {
this.editor = monaco.editor.createDiffEditor(this.$el, options)
const originalModel = monaco.editor.createModel(this.original, this.language)
const modifiedModel = monaco.editor.createModel(this.value, this.language)
this.editor.setModel({
original: originalModel,
modified: modifiedModel
})
} else {
this.editor = monaco.editor.create(this.$el, options)
} // @event `change`
const editor = this.getModifiedEditor()
editor.onDidChangeModelContent(function (event) {
const value = editor.getValue()
if (_this2.value !== value) {
_this2.$emit('change', value, event)
}
})
this.$emit('editorDidMount', this.editor)
},
/** @deprecated */
getMonaco: function getMonaco () {
return this.editor
},
getEditor: function getEditor () {
return this.editor
},
getModifiedEditor: function getModifiedEditor () {
return this.diffEditor ? this.editor.getModifiedEditor() : this.editor
},
getOriginalEditor: function getOriginalEditor () {
return this.diffEditor ? this.editor.getOriginalEditor() : this.editor
},
focus: function focus () {
this.editor.focus()
}
},
render: function render (h) {
return h('div')
}
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.component(MonacoEditor.name, MonacoEditor)
}
export default MonacoEditor

View File

@@ -3656,7 +3656,13 @@ export default {
parsePromQL () {},
onScroll: bus.debounce(function () {
this.showTopBtn = this.scrollbarWrap.scrollTop > 50
this.hideQueryPrompt()
}, 300),
hideQueryPrompt () {
this.expressions.forEach((item, index) => {
this.$refs['promql-' + (index)][0]?.hideQueryPromptShow()
})
},
toTop (wrap) {
let currentTop = wrap.scrollTop
const interval = currentTop / 10
@@ -4426,7 +4432,6 @@ export default {
item.queryValue = this.expressions[index]
return item
})
console.log(arr, this.lastHistory)
this.lastHistory.forEach((item) => {
const findItem = arr.find(history => history.id == item.id)
if (findItem && findItem.queryValue !== item.value) {

View File

@@ -87,10 +87,10 @@ export default {
this.total = 0
this.getExploreHistory()
},
loadNextData () {
loadNextData: bus.debounce(function (val) {
this.pageObj.pageNo++
this.getExploreHistory()
},
}, 300),
changeSearchStr: bus.debounce(function (val) {
this.init()
}, 300),

View File

@@ -209,7 +209,6 @@ export default {
const self = this
const word = model.getWordAtPosition(position)
const INSERT_AS_SNIPPET_ENUM_VALUE = 4
console.log(monaco.Range.fromPositions(position))
const range = monaco.Range.fromPositions(position)
// documentation says `position` will be "adjusted" in `getOffsetAt`
// i don't know what that means, to be sure i clone it
@@ -218,9 +217,7 @@ export default {
lineNumber: position.lineNumber
}
const offset = model.getOffsetAt(positionClone)
console.log(offset)
const situation = getSituation(model.getValue(), offset)
console.log(situation)
const completionsPromise = situation != null ? Promise.resolve([]) : Promise.resolve([])
return completionsPromise.then((items) => {
// monaco by default alphabetically orders the items.
@@ -376,7 +373,6 @@ export default {
}
},
lookupFormatVariable (name, match, scopedVars, sceneObject) {
console.log(name, match, scopedVars, sceneObject)
return null
}
}

View File

@@ -26,16 +26,18 @@ export default {
padding: {
// these numbers were picked so that visually this matches the previous version
// of the query-editor the best
top: 10,
top: 5,
bottom: 5
},
wrappingStrategy: 'advanced',
renderLineHighlight: 'none',
scrollbar: {
// vertical: 'hidden',
verticalScrollbarSize: 8, // used as "padding-right"
vertical: 'hidden',
verticalScrollbarSize: 0, // used as "padding-right"
horizontal: 'hidden',
horizontalScrollbarSize: 0
},
scrollBeyondLastColumn: 0,
scrollBeyondLastLine: false,
suggest: {
showWords: true,
@@ -81,6 +83,7 @@ export default {
},
suggestFontSize: 12,
wordWrap: 'on',
wordWrapColumn: 1,
placeHolderScopedVars: {
__interval: { text: '1s', value: '1s' },
__auto: { text: '1s', value: '1s' },
@@ -113,6 +116,7 @@ export default {
if (this.monacoEditorHeight !== el[0].offsetHeight) {
this.monacoEditorHeight = el[0].offsetHeight
}
editor?.layout()
}, 100)
},
handelBeforeMount (monaco) {
@@ -120,7 +124,6 @@ export default {
const self = this
if (!window.LANGUAGE_SETUP_STARTED) {
window.LANGUAGE_SETUP_STARTED = true
// console.log(monaco)
monaco.languages.register({ id: LANG_ID })
monaco.languages.setLanguageConfiguration(LANG_ID, languageConfiguration)
monaco.languages.setMonarchTokensProvider(LANG_ID, monarchlanguage)
@@ -142,7 +145,6 @@ export default {
provideCompletionItems: (model, position) => {
const suggestions = [
...monarchlanguage.keywords.map(k => {
// console.log(k)
return {
label: k,
kind: monaco.languages.CompletionItemKind.Keyword,
@@ -156,9 +158,7 @@ export default {
}
},
nzSuggest (model, position) {
// console.log(model, position)
const suggestions = []
// console.log(parser.parse(this.code))
return suggestions
},
suggest (model, position) {
@@ -166,7 +166,6 @@ export default {
const self = this
const word = model.getWordAtPosition(position)
const INSERT_AS_SNIPPET_ENUM_VALUE = 4
console.log(monaco.Range.fromPositions(position))
const range = monaco.Range.fromPositions(position)
// documentation says `position` will be "adjusted" in `getOffsetAt`
// i don't know what that means, to be sure i clone it
@@ -175,9 +174,7 @@ export default {
lineNumber: position.lineNumber
}
const offset = model.getOffsetAt(positionClone)
console.log(offset)
const situation = getSituation(model.getValue(), offset)
console.log(situation)
const completionsPromise = situation != null ? Promise.resolve([]) : Promise.resolve([])
return completionsPromise.then((items) => {
// monaco by default alphabetically orders the items.
@@ -333,7 +330,6 @@ export default {
}
},
lookupFormatVariable (name, match, scopedVars, sceneObject) {
console.log(name, match, scopedVars, sceneObject)
return null
}
},

View File

@@ -503,16 +503,11 @@ function getErrorNode (tree, text, cursorPos) {
// to account for this situation, we "move" the cursor position back,
// so that there are no spaces between the end-of-expression and the cursor
const trimRightTextLen = text.trimEnd().length
console.log(trimRightTextLen)
const pos = trimRightTextLen < cursorPos ? trimRightTextLen : cursorPos
console.log(pos)
const cur = tree.cursorAt(pos)
console.log(cur)
do {
if (cur.from === pos && cur.to === pos) {
const { node } = cur
console.log(node)
console.log(node.type.isError)
if (node.type.isError) {
return node
}
@@ -532,15 +527,13 @@ export function getSituation (text, pos) {
}
const tree = parser.parse(text)
console.log(tree)
// if the tree contains error, it is very probable that
// our node is one of those error nodes.
// also, if there are errors, the node lezer finds us,
// might not be the best node.
// so first we check if there is an error node at the cursor position
const maybeErrorNode = getErrorNode(tree, text, pos)
console.log(maybeErrorNode)
const cur = maybeErrorNode.cursor().cursorAt(pos)
const cur = maybeErrorNode.cursor().cursorAt(pos)
const currentNode = cur.node

View File

@@ -1,76 +1,8 @@
<template>
<div class="promqlInput" :class=" errorMsg || appendMsg ? 'mb10' : ''">
<div class="query-row">
<div class="query-row" style="position: relative">
<!--explore页面的样式-->
<template v-if="styleType == 1">
<div v-if="plugins.indexOf('metric-selector') > -1">
<el-dropdown class="metric-selector">
<el-dropdown-menu style="display: none"></el-dropdown-menu>
<button
class="top-tool-btn top-tool-btn--text"
type="button"
@click="toggleDropdown"
>
{{
type === "log" ? $t("overall.logLabels") : $t("overall.metric")
}}
<i class="nz-icon nz-icon-arrow-down" style="font-size: 12px"></i>
</button>
<el-cascader-panel
v-show="dropDownVisible"
ref="metricSelector"
slot="dropdown"
v-model="cascaderValue"
v-clickoutside="closeDropdown"
v-my-loading="tempBoxShowLoading"
:options="metricOptions"
v-if="type !== 'log'"
:props="cascaderProps"
@change="metricChangeNew"
style="margin-top: 5px"
>
<template slot-scope="{ node, data }">
<div
:class="[
'nz-cascade',
data.temp && !data.child ? 'nz-cascade-temp' : '',
data.more ? 'cascader-panel-more' : '',
]"
@click="
() => {
lazyLoad(node, data);
}
"
:title="data.label"
>
<i class="nz-icon nz-icon-template2"></i>
{{ data.label }}
<i v-if="data.more" class="nz-icon nz-icon-arrow-down"></i>
</div>
</template>
</el-cascader-panel>
<el-cascader-panel
v-else
v-show="dropDownVisible"
ref="metricSelector"
slot="dropdown"
v-model="cascaderValue"
v-clickoutside="closeDropdown"
v-my-loading="tempBoxShowLoading"
:loading="loading"
:props="cascaderProps"
@change="logLabelChange"
style="margin-top: 5px"
>
<template slot-scope="{ node, data }">
<div :title="data.label" class="nz-cascade">
{{ data.label }}
</div>
</template>
</el-cascader-panel>
</el-dropdown>
</div>
<div
v-if="plugins.indexOf('metric-input') > -1"
class="input-box"
@@ -81,6 +13,7 @@
:id="pqid + 'editor'+index"
v-if="type !== 'log'"
class="not-fixed-height no-resize no-close"
style="padding-right: 24px;box-sizing: border-box"
>
</div>
<!-- <div id='editor'
@@ -101,14 +34,14 @@
<!-- ref="elInput"-->
<!-- >-->
<!-- </el-input>-->
<div>
<div class="zy" style="position: relative;overflow-x: hidden;display: block;border: 1px solid #dedede;width: 100%" v-if="type == 'log'">
<MonacoEditor
v-if="type == 'log'"
v-model="expressionList[index]"
theme="nz"
class="not-fixed-height no-resize"
ref="monacoEditor"
style="border: 1px solid #dedede;"
style="width: calc(100% - 24px)"
:style="{
height: monacoEditorHeight + 'px'
}"
@@ -126,6 +59,8 @@
<div v-if="appendMsg" class="append-msg error" style="position: absolute">
<span>{{ appendMsg }}</span>
</div>
<i class="nz-icon nz-icon-alert-list" style="position: absolute;top: 4px; right: 6px" @click="queryPromptShowChange"/>
<queryPrompt v-if="queryPromptShow" @close="queryPromptShowChange" class="no-style-class" @selectMetric="selectMetric"/>
</div>
<div class="top-tool-btn-group">
@@ -182,94 +117,9 @@
"
style="width: 100%"
>
<el-col
:class="[
plugins.indexOf('metric-selector') > -1
? 'metric-selector-title'
: 'metric-null-title',
]"
>
<el-dropdown
class="metric-selector"
style="width: 100%"
v-if="plugins.indexOf('metric-selector') > -1"
>
<el-dropdown-menu style="display: none"></el-dropdown-menu>
<span
:class="{ 'expr-title': projectRightBox }"
style="cursor: pointer;width: 100%;display: inline-block"
@click="toggleDropdown"
>{{
type === "log"
? $t("overall.logLabels")
: $t("overall.metric")
}}<i
class="nz-icon nz-icon-arrow-down"
style="
font-size: 14px;
-webkit-transform: scale(0.75);
display: inline-block;
"
></i
></span>
<el-cascader-panel
v-my-loading="tempBoxShowLoading"
v-show="dropDownVisible"
v-clickoutside="closeDropdown"
v-model="cascaderValue"
style="text-align: left; margin-top: 5px"
slot="dropdown"
ref="metricSelector"
v-if="type !== 'log'"
:props="{ emitPath: false }"
:options="metricOptions"
@change="metricChangeNew"
>
<template slot-scope="{ node, data }">
<div
:class="[
'nz-cascade',
data.temp && !data.child ? 'nz-cascade-temp' : '',
data.more ? 'cascader-panel-more' : '',
]"
@click="
() => {
lazyLoad(node, data);
}
"
:title="data.label"
>
<i class="nz-icon nz-icon-template2"></i>
{{ data.label }}
<i v-if="data.more" class="nz-icon nz-icon-arrow-down"></i>
</div>
</template>
</el-cascader-panel>
<el-cascader-panel
v-else
v-show="dropDownVisible"
ref="metricSelector"
slot="dropdown"
v-model="cascaderValue"
v-clickoutside="closeDropdown"
v-my-loading="tempBoxShowLoading"
:loading="loading"
:props="cascaderProps"
@change="logLabelChange"
>
<template slot-scope="{ node, data }">
<div :title="data.label" class="nz-cascade">
{{ data.label }}
</div>
</template>
</el-cascader-panel>
</el-dropdown>
</el-col>
<el-col
:class="
plugins.indexOf('metric-selector') > -1
? 'metric-selector-input-box'
: 'metric-null-input-box'
'metric-null-input-box'
"
:style="{ height: '100%' }"
>
@@ -283,6 +133,7 @@
:id="pqid + 'editor'+index"
v-if="type !== 'log'"
class="not-fixed-height no-resize no-close"
style="padding-right: 24px;box-sizing: border-box"
>
</div>
<!-- <div id="editor"
@@ -302,14 +153,14 @@
<!-- class="not-fixed-height no-resize"-->
<!-- ref="elInput"-->
<!-- ></el-input>-->
<div>
<div class="zy" style="position: relative;overflow-x: hidden;display: block;border: 1px solid #dedede;width: 100%" v-if="type == 'log'">
<MonacoEditor
v-if="type == 'log'"
v-model="expressionList[index]"
theme="nz"
class="not-fixed-height no-resize"
ref="monacoEditor"
style="border: 1px solid #dedede;"
style="width: calc(100% - 24px)"
:style="{
height: monacoEditorHeight + 'px'
}"
@@ -328,6 +179,8 @@
<span>{{ appendMsg }}</span>
</div>
</el-col>
<i class="nz-icon nz-icon-alert-list" style="position: absolute;top: -5px; right: 15px" @click="queryPromptShowChange"/>
<queryPrompt v-if="queryPromptShow" @close="queryPromptShowChange" class="no-style-class" @selectMetric="selectMetric"/>
</el-row>
</template>
</div>
@@ -496,15 +349,18 @@ import {
closeBracketsKeymap
} from '@codemirror/autocomplete'
import exploreHistory from '@/components/page/dashboard/explore/histoyrComponent/exploreHistory'
import MonacoEditor from 'vue-monaco'
import MonacoEditor from '@/components/page/dashboard/explore/MonacoVue/vue-monaco'
import queryPrompt from "@/components/page/dashboard/explore/queryPrompt/queryPrompt";
import logqlMixin from '@/components/page/dashboard/explore/logql/logqlMixin'
import bus from "@/libs/bus";
export default {
name: 'promqlInput',
mixins: [logqlMixin],
components: {
selectAlertSilence,
exploreHistory,
MonacoEditor
MonacoEditor,
queryPrompt
},
props: {
index: { type: Number },
@@ -548,6 +404,7 @@ export default {
data () {
return {
oldcCodeLength: '',
showPrompt: false,
newView: null,
codeMirrorValue: [],
dropDownVisible: false,
@@ -572,7 +429,12 @@ export default {
tempBoxId: {},
loading: false,
firstInit: true,
historyshow: false
historyshow: false,
queryPromptShow: false,
queryPromptPosition: {
top:0,
left:0,
}
}
},
computed: {
@@ -755,7 +617,7 @@ export default {
self.newView.dispatch(
self.newView.state.update({
effects: dynamicConfigCompartment.reconfigure(dynamicConfig),
changes: { from, to, insert: self.codeMirrorValue[self.index] }
changes: { from: 0, to, insert: self.codeMirrorValue[self.index] }
})
)
}
@@ -1238,6 +1100,51 @@ export default {
},
showHistoryBox (flag) {
this.historyshow = flag
},
queryPromptShowChange: bus.debounce(function (val) {
if (!this.queryPromptShow) {
this.setPosition()
}
this.queryPromptShow = !this.queryPromptShow
}, 100),
hideQueryPromptShow () {
this.queryPromptShow = false
},
setPosition () {
this.queryPromptPosition = {
top: 0,
left: 0
}
},
selectMetric (item) {
if (!item.id) {
this.metricChangeNew(item.name)
} else if (item.id) {
this.dropDownVisible = false
this.$get('/expression/tmpl/' + item.id).then(res => {
if (res.code === 200) {
if (!res.data.vars || !res.data.vars.length) {
this.metricChange(item.expression)
this.initCodeMirror()
return
}
res.data.vars.forEach(item => {
res.data[item] = ''
const arr = item.split('.')
const keyword = arr[0].toLowerCase()
this.getAllOptins(keyword, keyword + 'Option')
})
this.tempBox = {
...this.tempBox,
...res.data
}
setTimeout(() => {
this.tempBoxShow = true
}, 100)
}
})
}
}
},
watch: {

View File

@@ -448,7 +448,6 @@
},
debug: (value) => {
console.log(value)
}
}
}

View File

@@ -0,0 +1,206 @@
<template>
<div class="explore-history-box query-prompt-box" v-clickoutside="hideMe" v-my-loading="firstLoading">
<div class="explore-history-box-header">
<span class="header-text">{{ type === 'metric' ? $t('overall.metric') : $t('dashboard.dashboard.chartForm.typeVal.log.label')}}</span>
<el-input
v-if="type === 'metric'"
class="header-search"
size="mini"
v-model="searchStr"
@input="changeSearchStr"
:placeholder="$t('overall.search')"
suffix-icon="nz-icon nz-icon-search"
/>
</div>
<div v-if="type === 'metric'" class="query-prompt-metric">
<div class="explore-history-box-content-header">
<div class="query-prompt-name text-ellipsis query-prompt-content-text">Name</div>
<div class="query-prompt-type text-ellipsis query-prompt-content-text">Type</div>
<div class="query-prompt-remark text-ellipsis query-prompt-content-text">Description</div>
</div>
<div
class="explore-history-box-content"
v-if="metricOptions.length || firstLoading"
infinite-scroll-disabled="disabled"
v-loadMore="{
load: loadNextData,
hasMore: hasMore,
}"
>
<div v-for="(item, index) in metricOptions" :key="index" :title="item.value" class="explore-history-item" @click.stop="selectMetric(item)">
<div class="query-prompt-name query-prompt-content-text" :title="item.name">
<span class="query-prompt-content-title text-ellipsis">{{item.name}}</span>
<i class=" query-prompt-content-icon nz-icon nz-icon-override" @click.stop="copyItem(item.name)"></i>
</div>
<div class="query-prompt-type text-ellipsis query-prompt-content-text" :title="item.gname">{{item.gname}}</div>
<div class="query-prompt-remark text-ellipsis query-prompt-content-text" :title="item.remark">
<span class="query-prompt-content-title text-ellipsis">{{item.remark}}</span>
<i class=" query-prompt-content-icon nz-icon nz-icon-huiche" @click.stop="selectMetric(item)"></i>
</div>
</div>
<div v-if="pageObj.total > metricOptions.length" v-my-loading.scaleMin="loading" class="explore-history-item no-more">
{{$t('el.select.loading')}}...
</div>
<div v-else class="explore-history-item no-more" :class="metricOptions.length< 9 ? 'in-bottom': ''">
{{$t('overall.noMoreData')}}
</div>
</div>
<div v-else class="explore-history-box-content nodata">
<svg class="icon" aria-hidden="true">
<use xlink:href="#nz-icon-no-data-list"></use>
</svg>
<div class="table-no-data__title">{{$t('overall.noDataAvailable')}}</div>
</div>
</div>
</div>
</template>
<script>
import bus from '@/libs/bus'
export default {
name: 'queryPrompt',
// todo 显示位置调整
props: {
type: {
type: String,
default: 'metric'
}
},
data () {
return {
oldMetricOptions: [],
searchMetricOptions: [],
metricOptions: [],
metaData: [],
loading: false,
firstLoading: false,
searchStr: '',
hasMore: false,
pageObj: {
pageNo: 1,
pageSize: 20,
total: 0
}
}
},
mounted () {
this.init()
},
methods: {
init () {
if (this.type === 'metric') {
this.metricInit()
}
if (this.type === 'log') {
this.logInit()
}
},
metricInit () {
this.firstLoading = true
this.pageObj = {
pageNo: 1,
pageSize: 20,
total: 0
}
const promiseArr = []
this.oldMetricOptions = []
this.metricOptions = []
promiseArr.push(this.$get('/prom/api/v1/label/__name__/values'))
promiseArr.push(this.$get('/prom/api/v1/metadata'))
promiseArr.push(this.$get('/expression/tmpl?pageSize=-1'))
Promise.all(promiseArr).then(allRes => {
const values = allRes[0]
const metaData = this.metaData = allRes[1]
const template = allRes[2]
const arr = []
if (template.code === 200) { // 首先处理template
arr.push(...template.data.list)
}
if (values.code === 200 && metaData.code === 200) { // 处理 metaData 以及values为一个数组 格式与template 保持一致
arr.push(...this.calcMetaData(metaData.data, values.data))
} else if (values.code === 200) {
arr.push(...this.calcMetaData({}, values.data))
} else if (metaData.code === 200) {
arr.push(...this.calcMetaData(metaData.data, []))
}
if (values.code !== 200) {
this.$message.error(values.msg || values.error)
}
if (metaData.code !== 200) {
this.$message.error(metaData.msg || metaData.error)
}
if (template.code !== 200) {
this.$message.error(template.msg || template.error)
}
this.oldMetricOptions = this.$lodash.cloneDeep(arr)
this.searchMetricOptions = this.$lodash.cloneDeep(arr)
this.pageObj.total = this.searchMetricOptions.length
this.metricOptions = arr.slice(0, 20)
this.firstLoading = false
this.hasMore = this.pageObj.total > this.metricOptions.length
})
},
calcMetaData (metaData, values) {
const obj = {}
values.forEach(item => {
obj[item] = [{ type: '', help: '', unit: '' }]
})
const mergeObj = Object.assign(obj, metaData)
const arr = Object.keys(mergeObj).map(key => {
const item = {
id: undefined,
name: key,
gname: mergeObj[key][0].type,
expression: key,
remark: mergeObj[key][0].help,
buildIn: 0,
vars: null,
varsVal: null
}
return item
})
return arr
},
logInit () {
},
hideMe () {
this.$emit('close')
},
changeSearchStr: bus.debounce(function (val) {
this.search()
}, 300),
search () {
if (this.searchStr) {
this.searchMetricOptions = this.oldMetricOptions.filter(item => item.name.indexOf(this.searchStr) !== -1)
} else {
this.searchMetricOptions = this.$lodash.cloneDeep(this.oldMetricOptions)
}
this.metricOptions = this.searchMetricOptions.slice(0, 20)
this.pageObj.pageNo = 1
this.pageObj.total = this.searchMetricOptions.length
},
loadNextData: bus.debounce(function (val) {
this.pageObj.pageNo++
this.loadData()
}, 300),
loadData () {
const pageNo = this.pageObj.pageNo
const arr = this.searchMetricOptions.slice((pageNo - 1) * 20, pageNo * 20)
this.metricOptions.push(...arr)
this.hasMore = this.pageObj.total > this.metricOptions.length
},
selectMetric (item) {
this.$emit('selectMetric', item)
this.hideMe()
},
copyItem (value) {
this.$copyText(value).then(() => {
this.$message.success({ message: this.$t('overall.copySuccess') })
})
}
}
}
</script>

View File

@@ -0,0 +1,34 @@
{
"compilerOptions": {
// 允许js
"allowJs": true,
// 支持any
"noImplicitAny": false,
"target": "es5",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": [],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
// 导入js
"src/**/*.js",
"src/**/*.ts",
"types/*.ts",
],
"exclude": ["node_modules"]
}