| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- <template>
- <overview-card-layout :card_style="`${card_stype} ${showMonitor ? '' : ''}`">
- <template #header>
- <div v-if="!readOnly">
- <a-row type="flex" style="padding: 12px;border-bottom: 1px solid #e8e8e8">
- <a-col class="d-flex" style="flex: 1 1 auto">
- <!-- 折叠 -->
- <a v-if="!isTemplate" class="font-weight-bold h-100 d-block" style="margin-right: 6px;" @click="toggleShowMonitor">
- <a-icon type="down" style="font-size: 12px;" v-if="showMonitor" />
- <a-icon type="right" style="font-size: 12px;" v-if="!showMonitor" />
- </a>
- <!-- 表格隐藏 -->
- <a-tooltip>
- <template slot="title">
- {{ $t('monitor.show_hide_legend_table') }}
- </template>
- <a class="font-weight-bold h-100 d-block" style="margin-right: 6px;" @click="toggleShowTableLegend">
- <a-icon type="line-chart" style="font-size: 14px;" v-if="showMonitor && showLegend" />
- <a-icon type="credit-card" style="font-size: 14px;" v-if="showMonitor && !showLegend" />
- <a-icon type="minus" style="font-size: 14px;" v-if="!showMonitor" />
- </a>
- </a-tooltip>
- <!-- 名称 -->
- <span>{{ panel.panel_name || (chart.metric && chart.metric.label) }}</span>
- </a-col>
- <a-col v-if="!useLocalPanels && !isTemplate" class="flex: 0 0 24px">
- <a-dropdown style="float: right" :trigger="['click']" placement="bottomRight">
- <a class="ant-dropdown-link font-weight-bold h-100 d-block action-btn" @click="e => e.preventDefault()">
- <icon type="more" style="font-size: 18px; margin-left: 9px;" />
- </a>
- <a-menu slot="overlay" @click="handleActionClick">
- <a-menu-item key="handleEdit"><a-icon type="edit" />{{$t('dashboard.text_104')}}</a-menu-item>
- <a-menu-item key="handleClone"><a-icon type="copy" />{{$t('dashboard.text_107')}}</a-menu-item>
- <a-menu-item key="handleExport"><a-icon type="download" />{{$t('table.action.export')}}</a-menu-item>
- <a-menu-item key="handleDelete"><a-icon type="delete" />{{$t('scope.text_18')}}</a-menu-item>
- </a-menu>
- </a-dropdown>
- </a-col>
- </a-row>
- </div>
- <div v-if="selectable">
- <a-radio :checked="focusPanelId == panel.panel_id" @click="(e)=>{chose_panel(panel.panel_id,panel.panel_name)}">{{panel.panel_name}}</a-radio>
- </div>
- </template>
- <monitor-line
- v-if="showMonitor"
- :isTemplate="isTemplate"
- ref="monitorLine"
- :loading="loading"
- :description="description"
- :metricInfo="metricInfo"
- :series="series"
- :reducedResult="reducedResult"
- :reducedResultOrder="reducedResultOrder"
- :pager="pager"
- :showTableLegend="showLegend"
- :monitorLineCardStyle="{border:'none'}"
- :otherCursorMovePoint="otherCursorMovePoint"
- @pageChange="pageChange"
- @chartInstance="setChartInstance"
- @exportTable="exportTable"
- @reducedResultOrderChange="reducedResultOrderChange"
- @cursorMove="cursorMove" />
- </overview-card-layout>
- </template>
- <script>
- import _ from 'lodash'
- import * as R from 'ramda'
- import WindowsMixin from '@/mixins/windows'
- import DialogMixin from '@/mixins/dialog'
- import { metric_zh, tableColumnMaps } from '@Monitor/constants'
- import { getSignature } from '@/utils/crypto'
- import { getRequestT, transformUnit } from '@/utils/utils'
- import { getNameDescriptionTableColumn } from '@/utils/common/tableColumn'
- // import { currencyUnitMap } from '@/constants/currency'
- import MonitorLine from '@Monitor/sections/MonitorLine'
- import { addMissingSeries } from '@Monitor/utils'
- import OverviewCardLayout from '../layout'
- export default {
- name: 'DashboardCard',
- components: {
- OverviewCardLayout,
- MonitorLine,
- },
- mixins: [DialogMixin, WindowsMixin],
- props: {
- // 是否作为模板组件使用
- isTemplate: {
- type: Boolean,
- default: false,
- },
- updated_at: {
- type: String,
- },
- dashboard_id: {
- type: String,
- },
- panel: {
- type: Object,
- required: true,
- default: () => {
- return {}
- },
- },
- extraParams: {
- type: Object,
- default: () => ({}),
- },
- editChart: {
- type: Function,
- required: true,
- },
- card_style: {
- type: String,
- },
- readOnly: {
- type: Boolean,
- default: () => {
- return false
- },
- },
- selectable: {
- type: Boolean,
- default: () => {
- return false
- },
- },
- focusPanelId: {
- type: String,
- default: () => {
- return ''
- },
- },
- chartHeigth: {
- type: String,
- default: '320px',
- },
- time: {
- type: String,
- },
- timeGroup: {
- type: String,
- },
- customTime: {
- type: Object,
- },
- groupFunc: {
- type: String,
- },
- tablePageSize: {
- type: Number,
- default: 10,
- },
- useLocalPanels: {
- type: Boolean,
- default: false,
- },
- cursorMove: {
- type: Function,
- },
- otherCursorMovePoint: {
- type: Array,
- default: () => {
- return [-10, -10]
- },
- },
- },
- data () {
- return {
- chart: {},
- tableLoading: false,
- resizeStatus: false,
- pager: {
- page: 1,
- limit: this.tablePageSize,
- total: 0,
- },
- series: [],
- reducedResult: {},
- reducedResultOrder: '',
- showLegend: this.isTemplate,
- showMonitor: true,
- }
- },
- computed: {
- description () {
- if (this.useLocalPanels) {
- const { selectItem, unit, metric, label } = this.panel.constants
- return {
- display_name: `${label}${metric ? `(${metric})` : ''}`,
- name: selectItem,
- unit: unit,
- }
- }
- const { common_alert_metric_details = [] } = this.panel
- return common_alert_metric_details.length ? common_alert_metric_details[0].field_description || {} : {}
- },
- metric () {
- const detail = _.get(this.panel, 'common_alert_metric_details[0]')
- if (!detail) {
- if (this.useLocalPanels) {
- const { fromItem, seleteItem, unit, metric, label } = this.panel.constants
- return {
- label: `${label}${metric ? `(${metric})` : ''}`,
- measurement: fromItem,
- field: seleteItem,
- format: unit,
- }
- }
- return { label: '', measurement: '', field: '', format: '0.00' }
- }
- const measurement = detail.measurement
- const field = detail.field
- const display_name = _.get(detail, 'field_description.display_name')
- let label = metric_zh[detail.measurement_display_name] || '-'
- if (display_name) {
- label += `(${metric_zh[display_name] ? metric_zh[display_name] : display_name})`
- }
- const unit = _.get(detail, 'field_description.unit')
- let format = '0.00'
- switch (unit) {
- case '%':
- format = '0.00 %'
- break
- case 'bps':
- format = '0.00 Bps'
- break
- case 'Bps':
- format = '0.00 b'
- break
- case 'byte':
- format = '0.00 b'
- break
- case 'RMB':
- format = '0.00'
- break
- case 'USD':
- format = '0.00'
- break
- case 'BRL':
- format = '0.00'
- break
- case 'EUR':
- format = '0.00'
- break
- case 'ms':
- format = '0.00'
- break
- case 'count':
- format = '0'
- break
- }
- return { label: label, measurement: measurement, field: field, format: format }
- },
- chartQueryData () {
- let params = {}
- if (this.useLocalPanels) {
- params = this.panel.queryData
- delete params.signature
- } else {
- const conditions = _.get(this.panel, 'setting.conditions')
- if (!conditions || conditions.length <= 0 || !conditions[0].query || !conditions[0].query.model) {
- console.log('invaild chart query condition', this.panel)
- return
- }
- const query = conditions[0].query
- const model = { ...query.model }
- if (model.group_by) {
- model.group_by = model.group_by.filter(item => {
- if ((item.type === 'time' && item.params && item.params[0] === '$interval') || (item.type === 'fill' && item.params && item.params[0] === 'none')) {
- return false
- }
- return true
- })
- }
- delete model.interval
- const metric_query = [{ model }]
- if (query.result_reducer) {
- metric_query[0].result_reducer = query.result_reducer
- }
- if (this.reducedResultOrder) {
- metric_query[0].result_reducer_order = this.reducedResultOrder
- }
- params = {
- from: query.from,
- to: query.to,
- metric_query,
- slimit: this.pager.limit,
- soffset: (this.pager.page - 1) * this.pager.limit,
- ...this.extraParams,
- }
- }
- if (this.timeGroup) {
- params.interval = this.timeGroup
- }
- if (this.time === 'custom') { // 自定义时间
- if (this.customTime && this.customTime.from && this.customTime.to) {
- params.from = this.customTime.from
- params.to = this.customTime.to
- }
- } else {
- params.from = this.time
- if (!this.isTemplate) {
- delete params.to
- }
- }
- // groupFunc 为该时间间隔内如何聚合数据,与原图表group_by不一样
- // if (this.groupFunc) {
- // const { select = [] } = model
- // if (select.length) {
- // select.forEach(s => {
- // const index = s.findIndex(item => ['mean', 'min', 'max', this.groupFunc].includes(item.type))
- // if (index !== -1) {
- // s[index].type = this.groupFunc
- // } else {
- // s.push({ type: this.groupFunc, params: [] })
- // }
- // })
- // } else {
- // select.push([{ type: this.groupFunc, params: [] }])
- // model.select = select
- // }
- // }
- return params
- },
- metricInfo () {
- const { metric_query = [] } = this.chartQueryData
- return metric_query.length ? metric_query[0] : {}
- },
- groupBy () {
- return _.get(this.metricInfo, 'model.group_by')
- },
- resultReducer () {
- return _.get(this.metricInfo, 'result_reducer')
- },
- isSelectFunction () {
- const select = _.get(this.metricInfo, 'model.select') || []
- let ret = ''
- select.map(item => {
- if (R.is(Array, item)) {
- item.map(l => {
- if (l.type === 'mean' || l.type === 'sum') {
- ret = l.type
- }
- })
- } else if (R.is(Object, item)) {
- if (item.type === 'mean' || item.type === 'sum') {
- ret = item.type
- }
- }
- })
- return ret
- },
- showTable () {
- if (!this.groupBy && !this.resultReducer && this.isSelectFunction) {
- return false
- }
- return true
- },
- chartSetting () {
- const ret = {}
- ret.tooltip = {
- trigger: 'axis',
- position: (point, params, dom, rect, size) => {
- const series = params.map((line, i) => {
- const unit = _.get(this.description, 'unit')
- const val = transformUnit(line.value[1], unit)
- const value = _.get(val, 'text') || line.value[1]
- // if (unit === 'currency') {
- // let unit = ''
- // if (currencys.length === 1) {
- // unit = currencyUnitMap[currencys[0]]?.sign || ''
- // }
- // value = unit ? `${unit} ${_.get(val, 'value' || line.value[0])}` : _.get(val, 'value' || line.value[0])
- // }
- // const color = i === this.highlight.index ? this.highlight.color : '#616161'
- let name = line.seriesName
- if (!this.showTable) {
- name = this.isSelectFunction.toUpperCase()
- } else {
- if (name.startsWith('{') && name.endsWith('}')) {
- try {
- const str = name.substring(1, name.length - 1)
- const list = str.split(',')
- const obj = {}
- list.forEach(item => {
- const [key, value] = item.split('=')
- obj[key] = value
- })
- let field = ''
- R.forEachObjIndexed((value, key) => {
- if (list.length && obj[key] && !field) {
- field = key
- }
- }, tableColumnMaps)
- name = field ? obj[field] : name
- } catch (err) { }
- }
- }
- return `<div class="d-flex align-items-center"><span>${line.marker}</span> <span class="text-truncate" style="max-width: 500px;">${name || ' '}</span>: <span>${value}</span></div>`
- }).join('')
- const wrapper = `<div>
- <div>${params[0].name}</div>
- <div class="lines-wrapper">${series}</div>
- </div>`
- dom.style.border = 'none'
- // dom.style.backgroundColor = 'transparent'
- dom.innerHTML = wrapper
- },
- }
- return ret
- },
- },
- watch: {
- updated_at: {
- deep: true,
- handler () {
- this.fetchChart()
- },
- },
- time () {
- this.fetchChart()
- },
- timeGroup () {
- this.fetchChart()
- },
- customTime () {
- this.fetchChart()
- },
- groupFunc () {
- this.fetchChart()
- },
- reducedResultOrder () {
- this.fetchChart()
- },
- },
- created () {
- this.fetchChart()
- },
- methods: {
- pageChange (pager) {
- this.pager = { ...this.pager, ...pager }
- this.$emit('pageChange', this.pager)
- this.fetchChart()
- },
- reducedResultOrderChange (order) {
- this.reducedResultOrder = order
- },
- resize () {
- this.resizeStatus = false
- this.$nextTick(() => {
- this.resizeStatus = true
- })
- },
- chose_panel (id, name) {
- this.$emit('chose_panel', { id: id, name: name })
- },
- handleActionClick ({ key }) {
- if (this[key]) this[key]()
- },
- handleDelete () {
- this.createDialog('DeleteMonitorDashboardChart', {
- data: [{ id: this.panel.panel_id, name: this.metric.label }],
- refresh: () => { this.$emit('delete') },
- columns: [getNameDescriptionTableColumn()],
- })
- },
- handleClone () {
- this.createDialog('CloneMonitorDashboardChart', {
- data: [{ id: this.panel.panel_id, name: this.metric.label, dashboard_id: this.dashboard_id }],
- panelName: this.panel.panel_name || (this.chart.metric && this.chart.metric.label),
- refresh: () => { this.$emit('delete') },
- columns: [getNameDescriptionTableColumn()],
- })
- },
- handleEdit () {
- this.editChart({ id: this.panel.panel_id })
- },
- handleExport () {
- this.$refs.monitorLine.exportTable()
- },
- toLineChartData (series) {
- const data = { columns: ['time'], rows: [] }
- const tv = {}
- series.map((s) => {
- const name = s.raw_name || s.name || ''
- const index = name.indexOf(':') + 1
- const column = index > 0 && name.slice(index).trim().length > 0 ? name.slice(index).trim() : name
- data.columns.push(column)
- s.points.map((p) => {
- const t = p[1]
- if (!tv[t]) {
- const padding = (n) => { return n > 9 ? `${n}` : '0' + n }
- const _t = new Date(t)
- const _h = padding(_t.getHours())
- const _m = padding(_t.getMinutes())
- tv[t] = { time: _t.toLocaleDateString() + ' ' + _h + ':' + _m }
- }
- tv[t][column] = p[0]
- })
- })
- // 时间排序
- const sortedKeys = Object.keys(tv).sort((a, b) => { return a - b })
- // 展开数据
- data.rows = sortedKeys.map((k) => {
- return tv[k]
- })
- return data
- },
- async fetchChart () {
- this.loading = true
- const moment = this.$moment()
- try {
- const { series, series_total = 0, reduced_result = {}, chartQueryData } = await this.fetchData()
- if (series) {
- this.series = addMissingSeries(series, chartQueryData, moment)
- this.pager = { ...this.pager, total: series_total }
- this.reducedResult = reduced_result
- }
- this.$emit('fetchDataSuccess')
- } catch (error) {
- throw error
- } finally {
- this.loading = false
- }
- },
- async fetchData (metric_query) {
- try {
- const data = _.cloneDeep(this.chartQueryData)
- if (!data) {
- this.resizeStatus = false
- return
- }
- this.resizeStatus = true
- data.signature = getSignature(this.chartQueryData)
- const { data: { series = [], series_total, reduced_result = {} } } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: getRequestT() } })
- return { series, series_total, reduced_result, chartQueryData: this.chartQueryData }
- } catch (error) {
- throw error
- }
- },
- async exportTable (total) {
- try {
- const data = { ..._.cloneDeep(this.chartQueryData), slimit: total, soffset: 0 }
- data.signature = getSignature(this.chartQueryData)
- const { data: { series = [], series_total, reduced_result = {} } } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: getRequestT() } })
- this.$refs.monitorLine.exportFullData(series, reduced_result, series_total)
- } catch (error) {
- throw error
- }
- },
- toggleShowTableLegend () {
- this.showMonitor = true
- this.showLegend = !this.showLegend
- },
- toggleShowMonitor () {
- this.showMonitor = !this.showMonitor
- },
- },
- }
- </script>
- <style scoped>
- </style>
|