| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- <template>
- <a-card :title="title" size="small" class="monitor-form" :class="{ 'hideBody': !panelShow }">
- <div slot="extra">
- <a-icon type="delete" v-if="showDelete" @click="remove" class="mr-2 remove-icon" />
- <a-icon :type="panelShow ? 'up' : 'down'" @click="toggle" />
- </div>
- <a-form
- v-show="panelShow"
- v-bind="formItemLayout"
- :form="form.fc">
- <a-form-item :label="$t('monitor.monitor_metric')" class="mb-0">
- <metric
- :form="form"
- :decorators="decorators"
- :res_type_measurements="res_type_measurements"
- :res_types="res_types"
- :loading="metricLoading"
- @metricChange="getMetricInfo"
- @metricClear="resetChart" />
- </a-form-item>
- <a-form-item :label="$t('monitor.monitor_filters')">
- <filters
- :form="form"
- ref="filtersRef"
- :decorators="decorators.filters"
- :init-filters="initFilters"
- @remove="$nextTick(toParams)"
- :loading="metricInfoLoading"
- :metricInfo="metricInfo"
- @tagValuesChange="tagValuesChange" />
- </a-form-item>
- <a-form-item :label="$t('monitor.monitor_group')">
- <base-select
- v-decorator="decorators.group_by"
- :options="groupbyOpts"
- @change="groupbyChange"
- class="w-100"
- :select-props="{ mode: 'multiple', placeholder: $t('monitor.text_114'), allowClear: true }" />
- </a-form-item>
- <a-form-item :label="$t('monitor.monitor_function')">
- <base-select
- v-decorator="decorators.function"
- :options="functionOpts"
- class="w-100"
- :select-props="{ placeholder: $t('monitor.text_115'), allowClear: allowClearGroupFunction }" />
- </a-form-item>
- <a-form-item :label="$t('monitor.monitor_result_function')">
- <base-select
- v-decorator="decorators.result_function"
- :options="resultFunctionOpts"
- class="w-100"
- :select-props="{ placeholder: $t('monitor.text_115'), allowClear: true }" />
- </a-form-item>
- <a-form-item v-if="form.fd.result_function === 'percentile'" :label="$t('monitor.monitor_percentile')">
- <a-input-number :min="1" :max="99" v-decorator="decorators.percentile" placeholder="1~99" />
- </a-form-item>
- <a-form-item :label="$t('common.name')" v-if="!queryOnly">
- <a-input v-decorator="decorators.name" :placeholder="$t('common.placeholder')" />
- </a-form-item>
- </a-form>
- </a-card>
- </template>
- <script>
- import _ from 'lodash'
- import * as R from 'ramda'
- import Metric from '@Monitor/sections/Metric'
- import Filters from '@Monitor/sections/Filters'
- import { metric_zh } from '@Monitor/constants'
- import { resolveValueChangeField } from '@/utils/common/ant'
- import { uuid, getRequestT } from '@/utils/utils'
- export default {
- name: 'ExplorerForm',
- components: {
- Metric,
- Filters,
- },
- props: {
- panel: {
- type: Object,
- default: () => ({}),
- },
- queryOnly: {
- type: Boolean,
- default: true,
- },
- formItemLayout: {
- type: Object,
- default: () => ({
- wrapperCol: {
- span: 21,
- },
- labelCol: {
- span: 3,
- },
- }),
- },
- showDelete: {
- type: Boolean,
- default: false,
- },
- defaultPanelShow: {
- type: Boolean,
- },
- timeRangeParams: {
- type: Object,
- default: () => ({}),
- },
- extraParams: {
- type: Object,
- default: () => ({}),
- },
- },
- data () {
- const initialValue = {}
- const initFilters = []
- const initTagOperators = {}
- if (this.panel && ((this.panel.common_alert_metric_details && this.panel.common_alert_metric_details.length > 0) || (this.panel?.setting && this.panel.setting.conditions))) {
- const f = this.panel?.common_alert_metric_details?.[0] || this.panel?.setting?.conditions?.[0]?.query?.model
- const q = _.get(this.panel, 'settings.conditions[0].query.model') || this.panel?.setting?.conditions?.[0]?.query?.model
- const r = _.get(this.panel, 'settings.conditions[0].query.result_reducer') || this.panel?.setting?.conditions?.[0]?.query?.result_reducer
- initialValue.name = this.panel.name || this.panel.panel_name || ''
- initialValue.res_type = f.res_type || this.panel?.common_alert_metric_details?.[0]?.res_type
- initialValue.metric_key = f.measurement || this.panel?.common_alert_metric_details?.[0]?.measurement
- initialValue.metric_value = f.field
- initialValue.tags = {}
- if (q) {
- if (q.group_by) {
- const _g = q.group_by.filter((g) => { return g.type === 'tag' && g.params && g.params[0] })
- if (_g.length) {
- initialValue.group_by = _g.map(item => {
- return item.params[0]
- })
- }
- }
- if (q.tags) {
- q.tags.map((tag) => {
- const key = uuid()
- initialValue.tags[key] = tag
- initFilters.push({ key: key, tagValueOpts: [] })
- initTagOperators[key] = tag.operator
- })
- }
- initialValue.function = ''
- if (q.select && q.select.length > 0 && q.select[0].length > 1) {
- const s = q.select[0]
- const _t = s[s.length - 1].type
- if (_t) initialValue.function = _t.toUpperCase()
- }
- }
- if (r) {
- initialValue.result_function = r.type
- if (r.params && r.params.length) {
- if (r.params[0] === 50) {
- initialValue.result_function = 'p50'
- } else if (r.params[0] === 95) {
- initialValue.result_function = 'p95'
- } else {
- initialValue.percentile = r.params[0]
- }
- }
- }
- }
- const getkey = (index, key, defaultValue) => {
- if (initialValue.tags && initialValue.tags[index] && initialValue.tags[index][key]) {
- if (key === 'value') {
- const v = initialValue.tags[index][key]
- if (v.startsWith('/^') && v.endsWith('$/')) {
- const values = v.replace('/^', '').replace('$/', '').split('|').map(item => {
- let k = item
- if (k.startsWith('^')) k = k.replace('^', '')
- if (k.endsWith('$')) k = k.replace('$', '')
- return k
- })
- return values
- } else {
- return [v]
- }
- }
- if (key === 'operator') {
- const v = initialValue.tags[index][key]
- if (v === '=') return '=~'
- if (v === '!=') return '!~'
- }
- return initialValue.tags[index][key]
- } else {
- return defaultValue
- }
- }
- const decorators = {
- metric_res_type: [
- 'metric_res_type',
- {
- initialValue: initialValue.res_type,
- rules: [
- { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- metric_key: [
- 'metric_key',
- {
- initialValue: initialValue.metric_key,
- rules: [
- { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- metric_value: [
- 'metric_value',
- {
- initialValue: initialValue.metric_value,
- rules: [
- { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- filters: {
- tagCondition: i => [
- `tagConditions[${i}]`,
- {
- initialValue: getkey(i, 'condition', 'AND'),
- rules: [
- { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- tagKey: i => [
- `tagKeys[${i}]`,
- {
- initialValue: getkey(i, 'key', ''),
- rules: [
- // { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- tagOperator: i => [
- `tagOperators[${i}]`,
- {
- initialValue: getkey(i, 'operator', '=~'),
- rules: [
- { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- tagValue: i => [
- `tagValues[${i}]`,
- {
- initialValue: getkey(i, 'value', []),
- rules: [
- // { required: true, message: this.$t('common.select') },
- ],
- },
- ],
- },
- group_by: [
- 'group_by',
- {
- initialValue: initialValue.group_by || [],
- },
- ],
- function: [
- 'function',
- {
- initialValue: initialValue.function,
- },
- ],
- result_function: [
- 'result_function',
- {
- initialValue: initialValue.result_function,
- },
- ],
- percentile: [
- 'percentile',
- {
- initialValue: initialValue.percentile,
- rules: [{ required: true, message: this.$t('common.tips.input', [this.$t('monitor.monitor_percentile')]) }],
- },
- ],
- }
- if (!this.queryOnly) {
- decorators.name = [
- 'name',
- {
- initialValue: initialValue.name || '',
- rules: [
- { required: true, message: `${this.$t('common.placeholder')}${this.$t('common.name')}` },
- ],
- },
- ]
- }
- return {
- prevName: initialValue.name,
- form: {
- fc: this.$form.createForm(this, {
- onValuesChange: this.onValuesChange,
- }),
- fd: {
- result_function: initialValue.result_function,
- tagOperators: initTagOperators,
- },
- },
- decorators: decorators,
- initFilters: initFilters,
- groupbyOpts: [],
- functionOpts: [],
- resultFunctionOpts: [
- {
- key: 'p50',
- label: 'P50',
- },
- {
- key: 'p95',
- label: 'P95',
- },
- {
- key: 'min',
- label: 'MIN',
- },
- {
- key: 'max',
- label: 'MAX',
- },
- {
- key: 'avg',
- label: 'MEAN',
- },
- {
- key: 'sum',
- label: 'SUM',
- },
- {
- key: 'count',
- label: 'COUNT',
- },
- {
- key: 'percentile',
- label: 'PERCENTILE',
- },
- ],
- metricKeyOpts: [],
- metricInfo: {},
- metricKeyItem: {},
- mertricItem: {},
- panelShow: this.defaultPanelShow,
- oldParams: {},
- oldResParams: {},
- metricLoading: false,
- metricInfoLoading: false,
- res_type_measurements: {},
- res_types: [],
- allowClearGroupFunction: true,
- }
- },
- computed: {
- title () {
- if (!this.panelShow && this.form.fd.metric_key) {
- return this.getTitle()
- }
- return this.$t('monitor.monitor_fill_filters')
- },
- },
- watch: {
- defaultPanelShow (val) {
- this.panelShow = val
- },
- timeRangeParams () {
- this.getMeasurement()
- },
- },
- created () {
- this.getMeasurement()
- },
- methods: {
- getTitle () {
- let padding = ' '
- if (this.$store.getters.setting.language === 'zh-CN') {
- padding = ''
- }
- let label = this.metricKeyItem && this.metricKeyItem.metric_res_type ? this.$t(`dictionary.${this.metricKeyItem.metric_res_type}`) + padding : ''
- label += this.metricKeyItem && this.metricKeyItem.label ? this.metricKeyItem.label : '-'
- const metricLabel = _.get(this.mertricItem, 'description.display_name')
- const metricName = _.get(this.mertricItem, 'description.name')
- if (metricLabel) {
- label += `(${metric_zh[metricLabel] ? metric_zh[metricLabel] + ' ' + metricName : metricLabel + ' ' + metricName})`
- }
- return label
- },
- resetChart () {
- this.$emit('resetChart')
- this.$refs.filtersRef.reset()
- },
- remove () {
- this.$emit('remove')
- },
- groupbyChange (a) {
- if (!a || a.length === 0) {
- this.form.fc.setFieldsValue({
- [this.decorators.function[0]]: '',
- })
- this.allowClearGroupFunction = true
- } else {
- this.allowClearGroupFunction = false
- if (this.functionOpts && this.functionOpts.length) {
- const mean = this.functionOpts.find(val => val.key.toLowerCase() === 'mean')
- this.form.fc.setFieldsValue({
- [this.decorators.function[0]]: mean.key,
- })
- }
- }
- },
- onValuesChange (props, values) {
- const newField = resolveValueChangeField(values)
- R.forEachObjIndexed((item, key) => {
- if (!['tagValues'].includes(key)) {
- if (R.is(Object, this.form.fd[key]) && R.is(Object, item)) {
- this.$set(this.form.fd, key, { ...this.form.fd[key], ...item })
- } else {
- this.$set(this.form.fd, key, item)
- }
- }
- }, newField)
- const changedKeys = Object.keys(values)
- if (changedKeys.length === 1 && changedKeys[0] === 'name') {
- this.$emit('nameChange', this.form.fd.name)
- return
- }
- if (changedKeys.length === 1 && changedKeys[0] === 'tagValues') {
- return
- }
- this.$nextTick(this.toParams)
- if ((values.hasOwnProperty('metric_key') && !values.metric_key) || (values.hasOwnProperty('metric_value') && !values.metric_value)) {
- this.resetChart()
- }
- },
- tagValuesChange (item) {
- this.$nextTick(this.toParams)
- const values = this.form.fc.getFieldsValue()
- if ((values.hasOwnProperty('metric_key') && !values.metric_key) || (values.hasOwnProperty('metric_value') && !values.metric_value)) {
- this.resetChart()
- }
- },
- async getMeasurement () {
- try {
- this.metricLoading = true
- const params = { scope: this.$store.getters.scope, ...this.timeRangeParams, ...this.extraParams }
- const { data: { res_type_measurements, res_types } } = await new this.$Manager('unifiedmonitors', 'v1').get({ id: 'measurements', params })
- this.res_type_measurements = res_type_measurements
- this.res_types = res_types
- this.metricLoading = false
- } catch (error) {
- this.metricLoading = false
- throw error
- }
- },
- async getMetricInfo ({ metricKey, mertric, mertricItem, metricKeyItem }) {
- try {
- this.metricKeyItem = metricKeyItem || {}
- this.mertricItem = mertricItem || {}
- const title = this.getTitle()
- if (this.prevName === '' || this.form.fd.name === this.prevName) {
- this.form.fc.setFieldsValue({ name: title })
- this.prevName = title
- }
- this.$emit('mertricItemChange', { ...mertricItem, title: title, metricKeyItem })
- const params = {
- $t: getRequestT(),
- database: metricKeyItem && metricKeyItem.database ? metricKeyItem.database : 'telegraf',
- measurement: metricKey,
- field: mertric,
- scope: this.$store.getters.scope,
- ...this.timeRangeParams,
- ...this.extraParams,
- }
- this.metricInfoLoading = true
- const { data } = await new this.$Manager('unifiedmonitors', 'v1').get({ id: 'metric-measurement', params })
- this.metricInfo = data
- if (R.is(Array, this.metricInfo.tag_key)) {
- this.groupbyOpts = this.metricInfo.tag_key.map(v => ({ key: v, label: v }))
- }
- const Aggregations = _.get(this.metricInfo, 'func.field_opt_value.Aggregations')
- if (R.is(Array, Aggregations)) {
- this.functionOpts = Aggregations.map(v => ({ key: v, label: this.$te(`monitor.func.${v}`) ? this.$t(`monitor.func.${v}`) : v }))
- }
- const { group_by: groupBy, function: func } = this.form.fc.getFieldsValue([this.decorators.group_by[0], this.decorators.function[0]])
- const resetFields = {}
- const resetGroupBy = groupBy.filter(key => (this.metricInfo?.tag_key || []).includes(key))
- resetFields[this.decorators.group_by[0]] = resetGroupBy
- if (!~(Aggregations || []).indexOf(func)) {
- resetFields[this.decorators.function[0]] = undefined
- }
- this.form.fc.setFieldsValue(resetFields)
- this.metricInfoLoading = false
- } catch (error) {
- this.metricInfo.tag_key = []
- this.metricInfo.func = {}
- this.metricInfoLoading = false
- throw error
- }
- },
- toggle () {
- this.panelShow = !this.panelShow
- },
- toParams (ignoreEmit) {
- const fd = this.form.fc.getFieldsValue()
- const params = {
- database: this.metricKeyItem && this.metricKeyItem.database ? this.metricKeyItem.database : 'telegraf',
- }
- const resParams = {}
- const tags = []
- if (!this.queryOnly) {
- let formErr
- this.form.fc.validateFieldsAndScroll({ scroll: { alignWithTop: true, offsetTop: 100 } }, (err, values) => { if (err) { formErr = err } })
- if (formErr) return
- params.name = fd.name
- }
- if (fd.metric_key) params.measurement = fd.metric_key
- if (fd.metric_value) params.select = [[{ type: 'field', params: [fd.metric_value] }]]
- if (R.is(Object, fd.tagValues)) {
- R.forEachObjIndexed((value, key) => {
- let val = value
- if ((fd.tagOperators[key] === '=~' || fd.tagOperators[key] === '!~') && val && val.length) {
- val = `/${val.map(v => `^${v}$`).join('|')}/`
- } else {
- val = R.is(Array, val) ? (val.length ? val[0] : '') : val
- }
- if (val) {
- const tag = {
- key: fd.tagKeys[key],
- value: val,
- operator: fd.tagOperators[key],
- }
- if (fd.tagConditions && fd.tagConditions[key]) {
- tag.condition = fd.tagConditions[key]
- }
- tags.push(tag)
- }
- }, fd.tagValues)
- }
- if (tags.length) {
- params.tags = tags
- }
- if ((R.is(Array, fd.group_by) && fd.group_by.length)) {
- params.group_by = fd.group_by.map(group_by => {
- return { type: 'tag', params: [group_by] }
- })
- }
- if (fd.result_function) {
- resParams.type = fd.result_function
- if (fd.result_function === 'percentile') {
- if (fd.percentile) {
- resParams.params = [Math.max(1, Math.min(fd.percentile, 99))]
- } else {
- delete resParams.params
- delete resParams.type
- }
- } else if (fd.result_function === 'p50') {
- resParams.type = 'percentile'
- resParams.params = [50]
- } else if (fd.result_function === 'p95') {
- resParams.type = 'percentile'
- resParams.params = [95]
- }
- }
- if (!fd.metric_key || !fd.metric_value) {
- this.oldParams = params
- return params
- }
- if (R.is(String, fd.function) && fd.function.length) {
- params.select[0].push({ type: fd.function.toLowerCase(), params: [] })
- }
- if (R.equals(this.oldParams, params) && R.equals(this.oldResParams, resParams)) return params
- this.$emit('paramsChange', params, resParams)
- this.oldResParams = resParams
- this.oldParams = params // 记录为上一次 params
- },
- },
- }
- </script>
- <style lang="less" scoped>
- @import '../../../../src/styles/less/theme';
- .monitor-form {
- &.hideBody ::v-deep .ant-card-body {
- padding: 0 !important;
- }
- .remove-icon {
- transition: color 0.1s ease-in;
- &:hover {
- color: @error-color;
- }
- }
- // 让 label 和 wrapper 基于父元素宽度,而不是固定的 span
- ::v-deep .ant-form-item {
- display: flex;
- flex-wrap: nowrap;
- align-items: flex-start;
- }
- ::v-deep .ant-form-item-label,
- ::v-deep .ant-form-item-label > label {
- width: 150px !important;
- flex: 0 0 150px !important;
- padding-right: 8px;
- }
- // // 覆盖 Ant Design 的栅格类
- ::v-deep .ant-col {
- width: auto !important;
- max-width: none !important;
- }
- ::v-deep .ant-form-item-control-wrapper {
- flex: 1 1 auto;
- width: auto !important;
- min-width: 0;
- max-width: 100%;
- }
- ::v-deep .ant-form-item-control {
- width: 100%;
- }
- }
- </style>
|