Monitor.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <template>
  2. <base-dialog @cancel="cancelDialog" :width="1200">
  3. <div slot="header">{{ params.title }}</div>
  4. <div slot="body">
  5. <a-card class="mb-3" size="small" :title="metricData.metric">
  6. <div class="Rheader"><span>{{ $t('dashboard.text_20') }}: </span><span>{{metricData.metric}}</span></div>
  7. <div class="Rheader"><span>{{ $t('common.current_value') }}: </span><span>{{metricData.value_str}}</span></div>
  8. <monitor-header
  9. class="mt-4 mb-4"
  10. :timeOpts="timeOpts"
  11. :time.sync="time"
  12. :showTimegroup="false"
  13. :showGroupFunc="false"
  14. @refresh="fetchAllData">
  15. <template v-slot:radio-button-append>
  16. <custom-date :time.sync="time" :customTime.sync="customTime" :showCustomTimeText="time==='custom'" />
  17. </template>
  18. </monitor-header>
  19. <a-card v-if="isEmpty && isLoading" :title="seriesDescription[0]?.title" size="small">
  20. <loader :loading="true" />
  21. </a-card>
  22. <div v-for="(item, i) in seriesList" :key="i">
  23. <monitor-line :loading="loadingList[i]" :description="seriesDescription[i]" :metricInfo="metricList[i][0]" class="mb-3" @chartInstance="setChartInstance" :series="item" :timeFormatStr="timeFormatStr" :pager="seriesListPager[i]" @pageChange="pageChange" />
  24. </div>
  25. </a-card>
  26. </div>
  27. <div slot="footer">
  28. <a-button type="primary" @click="cancelDialog">{{ $t('dialog.ok') }}</a-button>
  29. </div>
  30. </base-dialog>
  31. </template>
  32. <script>
  33. import get from 'lodash/get'
  34. import echarts from 'echarts'
  35. import DialogMixin from '@/mixins/dialog'
  36. import WindowsMixin from '@/mixins/windows'
  37. import { getRequestT } from '@/utils/utils'
  38. import { getSignature } from '@/utils/crypto'
  39. import { timeOpts } from '@/constants/monitor'
  40. import MonitorLine from '@Monitor/sections/MonitorLine'
  41. import { MONITOR_MAX_POINTERS, metric_zh } from '@Monitor/constants'
  42. import CustomDate from '@/sections/CustomDate'
  43. import MonitorHeader from '@/sections/Monitor/Header'
  44. export default {
  45. name: 'ViewMonitorDialog',
  46. components: {
  47. MonitorLine,
  48. MonitorHeader,
  49. CustomDate,
  50. },
  51. mixins: [DialogMixin, WindowsMixin],
  52. data () {
  53. const metricQuery = this.params.data.map(item => {
  54. const alert_detail = item.data.alert_details
  55. const tag = item.data.tags
  56. const originTags = [{
  57. key: 'vm_id',
  58. operator: '=',
  59. value: tag.vm_id,
  60. }]
  61. if (alert_detail.measurement === 'agent_cpu') {
  62. originTags.push({
  63. key: 'cpu',
  64. operator: '=',
  65. value: 'cpu-total',
  66. })
  67. }
  68. return {
  69. model: {
  70. database: alert_detail.db,
  71. measurement: alert_detail.measurement,
  72. select: [
  73. [
  74. {
  75. type: 'field',
  76. params: [
  77. alert_detail.field,
  78. ],
  79. },
  80. ],
  81. ],
  82. tags: originTags,
  83. },
  84. }
  85. })
  86. const seriesDesc = this.params.data.map(item => {
  87. const alert_detail = item.data.alert_details || {}
  88. return {
  89. description: alert_detail.field_description,
  90. title: `${this.$t(`dictionary.${alert_detail.res_type}`)}${metric_zh[alert_detail.measurement_display_name]}(${metric_zh[alert_detail.field_description?.display_name]})`,
  91. }
  92. })
  93. return {
  94. time: '1h',
  95. timeGroup: '2m',
  96. customTime: null,
  97. timeOpts,
  98. metricList: [metricQuery],
  99. seriesList: [],
  100. seriesListPager: [],
  101. chartInstanceList: [], // e-chart 实例
  102. loadingList: [],
  103. seriesDescription: seriesDesc,
  104. get,
  105. }
  106. },
  107. computed: {
  108. isEmpty () {
  109. return this.seriesList.length === 0
  110. },
  111. isLoading () {
  112. return this.loadingList.some(v => v)
  113. },
  114. timeFormatStr () {
  115. return this.timeOpts[this.time].timeFormat
  116. },
  117. timeRangeParams () {
  118. const params = {}
  119. if (this.time === 'custom') { // 自定义时间
  120. if (this.customTime && this.customTime.from && this.customTime.to) {
  121. params.from = this.customTime.from
  122. params.to = this.customTime.to
  123. }
  124. } else {
  125. params.from = this.time
  126. }
  127. return params
  128. },
  129. curItem () {
  130. return this.params.data[0] || {}
  131. },
  132. metricData () {
  133. return this.curItem.data || {}
  134. },
  135. },
  136. watch: {
  137. time () {
  138. this.smartFetchAllData()
  139. },
  140. customTime () {
  141. this.smartFetchAllData()
  142. },
  143. },
  144. created () {
  145. this.smartFetchAllData()
  146. },
  147. methods: {
  148. smartFetchAllData () { // 根据选择的时间范围智能的赋值时间间隔进行查询
  149. let diffHour = 1
  150. const noNumberReg = /\D+/g
  151. if (this.time === 'custom') {
  152. diffHour = this.customTime.from.replace(noNumberReg, '') - this.customTime.to.replace(noNumberReg, '')
  153. } else {
  154. diffHour = this.time.replace(noNumberReg, '')
  155. }
  156. const diff = diffHour * 60 // 变分钟
  157. this.timeGroup = `${diff / MONITOR_MAX_POINTERS}m`
  158. this.$nextTick(this.fetchAllData)
  159. },
  160. remove (i) {
  161. this.metricList.splice(i, 1)
  162. this.chartInstanceList.splice(i, 1)
  163. this.seriesList.splice(i, 1)
  164. this.loadingList.splice(i, 1)
  165. },
  166. setChartInstance (val, i) {
  167. this.chartInstanceList.push(val)
  168. echarts.connect(this.chartInstanceList)
  169. },
  170. resetChart (i) {
  171. if (this.seriesList && this.seriesList.length && this.seriesList[i]) {
  172. this.$set(this.seriesList, i, [])
  173. this.$set(this.metricList, i, [])
  174. this.$set(this.seriesDescription[i], 'title', '')
  175. }
  176. },
  177. mertricItemChange (item, i) {
  178. const t = +this.time.replace(/\D+/, '')
  179. const existBalance = this.seriesDescription.find(val => val.id === 'balance')
  180. if (!existBalance && item.id === 'balance' && ~this.time.indexOf('h') && t < 3) { // 时间都是转换成h了,这里仅需要对比h即可
  181. this.time = '72h'
  182. this.$message.warning(this.$t('common_562', [item.label]))
  183. }
  184. this.$set(this.seriesDescription, i, item)
  185. },
  186. async fetchAllData () {
  187. const jobs = []
  188. this.loadingList = []
  189. for (let i = 0; i < this.metricList.length; i++) {
  190. const metric_query = this.metricList[i]
  191. this.loadingList.push(true)
  192. jobs.push(this.fetchData(metric_query, 10, 0))
  193. }
  194. try {
  195. const res = await Promise.all(jobs)
  196. this.seriesList = res.map(val => get(val, 'series') || [])
  197. this.seriesListPager = res.map((val, index) => ({ seriesIndex: index, total: get(val, 'series_total') || 0, page: 1, limit: 10 }))
  198. this.loadingList = this.loadingList.map(v => false)
  199. } catch (error) {
  200. this.loadingList = this.loadingList.map(v => false)
  201. throw error
  202. }
  203. },
  204. async _refresh (i, limit, offset) {
  205. try {
  206. this.$set(this.loadingList, i, true)
  207. const { series = [], series_total = 0 } = await this.fetchData(this.metricList[i], limit, offset)
  208. this.$set(this.seriesList, i, series)
  209. this.$set(this.seriesListPager, i, { seriesIndex: i, total: series_total, page: 1 + offset / limit, limit: limit })
  210. this.loadingList[i] = false
  211. } catch (error) {
  212. this.$set(this.seriesList, i, [])
  213. this.$set(this.loadingList, i, false)
  214. throw error
  215. }
  216. },
  217. async refresh (params, i) { // 将多个查询 分开调用
  218. const metric_query = [{ model: params }]
  219. this.$set(this.metricList, i, metric_query)
  220. await this._refresh(i, 10, 0)
  221. },
  222. async pageChange (pager) {
  223. await this._refresh(pager.seriesIndex, pager.limit, (pager.page - 1) * pager.limit)
  224. },
  225. async fetchData (metric_query, limit, offset) {
  226. try {
  227. const data = {
  228. metric_query,
  229. interval: this.timeGroup,
  230. scope: this.$store.getters.scope,
  231. slimit: limit,
  232. soffset: offset,
  233. ...this.timeRangeParams,
  234. }
  235. if (!data.metric_query || !data.metric_query.length) return
  236. data.signature = getSignature(data)
  237. const { data: resdata } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: getRequestT() } })
  238. return resdata
  239. } catch (error) {
  240. throw error
  241. }
  242. },
  243. },
  244. }
  245. </script>
  246. <style lang="scss" scoped>
  247. .Rheader {
  248. font-size: 20px;
  249. font-weight: 600;
  250. }
  251. </style>