index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <template>
  2. <a-row>
  3. <a-col :span="isTemplate ? 24 : undefined" :md="isTemplate ? 24 : 24" :lg="isTemplate ? 24 : 22" :xl="isTemplate ? 24 : 16" :xxl="isTemplate ? 24 : 10" class="mb-5">
  4. <monitor-forms
  5. @refresh="refresh"
  6. @remove="remove"
  7. @resetChart="resetChart"
  8. :timeRangeParams="timeRangeParams"
  9. @mertricItemChange="mertricItemChange"
  10. :extraParams="extraParams"
  11. :multiQuery="!isTemplate"
  12. :panel="templateParams?.panel" />
  13. </a-col>
  14. <a-col :span="isTemplate ? 24 : undefined" class="line mb-5" :md="isTemplate ? 24 : 24" :lg="isTemplate ? 24 : 22" :xl="isTemplate ? 24 : 16" :xxl="isTemplate ? { span: 24 } : { span: 13, offset: 1 }">
  15. <monitor-header
  16. class="mb-3"
  17. :time.sync="time"
  18. :timeGroup.sync="timeGroup"
  19. :showTimegroup="true"
  20. :showGroupFunc="true"
  21. :customTime.sync="customTime"
  22. :showCustomTimeText="time==='custom'"
  23. :showCustomTime="!isTemplate"
  24. customTimeUseTimeStamp
  25. @refresh="fetchAllData" />
  26. <div v-for="(item, i) in seriesList" :key="i">
  27. <monitor-line
  28. :ref="`monitorLine${i}`"
  29. :loading="loadingList[i]"
  30. :description="seriesDescription[i]"
  31. :metricInfo="metricList[i][0]"
  32. class="mb-3"
  33. :isTemplate="isTemplate"
  34. @chartInstance="setChartInstance"
  35. :series="item"
  36. :reducedResult="resultList[i]"
  37. :timeFormatStr="timeFormatStr"
  38. :pager="seriesListPager[i]"
  39. :reducedResultOrder="resultOrderList[i]"
  40. showTableExport
  41. @pageChange="pageChange"
  42. @exportTable="(total) => exportTable(i, total)"
  43. @reducedResultOrderChange="(order) => reducedResultOrderChange(i, order)">
  44. <template #extra>
  45. <a-button class="mr-3" type="link" @click="handleSave(metricList[i], seriesDescription[i])">{{ $t('common.save') }}</a-button>
  46. </template>
  47. </monitor-line>
  48. </div>
  49. <a-card v-if="!seriesList.length && loadingList[0]" class="explorer-monitor-line d-flex align-items-center justify-content-center">
  50. <loader :loading="true" />
  51. </a-card>
  52. </a-col>
  53. </a-row>
  54. </template>
  55. <script>
  56. import get from 'lodash/get'
  57. import echarts from 'echarts'
  58. import DialogMixin from '@/mixins/dialog'
  59. import WindowsMixin from '@/mixins/windows'
  60. import MonitorForms from '@Monitor/sections/ExplorerForm'
  61. import MonitorLine from '@Monitor/sections/MonitorLine'
  62. import MonitorHeader from '@/sections/Monitor/Header'
  63. import { getRequestT } from '@/utils/utils'
  64. import { getSignature } from '@/utils/crypto'
  65. import { timeOpts } from '@/constants/monitor'
  66. import MonitorTimeMixin from '@/mixins/monitorTime'
  67. import { addMissingSeries } from '@Monitor/utils'
  68. export default {
  69. name: 'ExplorerIndex',
  70. components: {
  71. MonitorForms,
  72. MonitorLine,
  73. MonitorHeader,
  74. },
  75. mixins: [DialogMixin, WindowsMixin, MonitorTimeMixin],
  76. props: {
  77. isTemplate: {
  78. type: Boolean,
  79. default: false,
  80. },
  81. isTemplateEdit: {
  82. type: Boolean,
  83. default: false,
  84. },
  85. templateParams: {
  86. type: Object,
  87. default: () => ({}),
  88. },
  89. },
  90. data () {
  91. return {
  92. time: this.templateParams?.queryParams?.time || '1h',
  93. timeGroup: this.templateParams?.queryParams?.timeGroup || '1m',
  94. // groupFunc: 'mean',
  95. customTime: null,
  96. timeOpts,
  97. metricList: [],
  98. seriesList: [],
  99. resultList: [],
  100. resultOrderList: [],
  101. seriesListPager: [],
  102. chartInstanceList: [], // e-chart 实例
  103. loadingList: [],
  104. seriesDescription: [],
  105. get,
  106. tablePageSize: 10,
  107. }
  108. },
  109. computed: {
  110. timeFormatStr () {
  111. return this.timeOpts[this.time].timeFormat
  112. },
  113. timeRangeParams () {
  114. const params = {}
  115. if (this.time === 'custom') { // 自定义时间
  116. if (this.customTime && this.customTime.from && this.customTime.to) {
  117. params.from = this.customTime.from
  118. params.to = this.customTime.to
  119. }
  120. } else if (this.time === 'last_month') {
  121. // 计算当前时间到上个月第一天0点的小时数
  122. const now = this.$moment()
  123. const lastMonthStart = this.$moment().subtract(1, 'month').startOf('month') // 上个月第一天0点
  124. const lastMonthEnd = this.$moment().subtract(1, 'month').endOf('month') // 上个月最后一天23:59:59
  125. // from: 当前时间距离上个月1号0点多少个小时(取整)
  126. const fromHours = Math.floor(now.diff(lastMonthStart, 'hours', true))
  127. // to: 当前时间距离上个月最后一天23:59:59多少个小时(取整)
  128. const toHours = Math.floor(now.diff(lastMonthEnd, 'hours', true))
  129. params.from = `${fromHours}h`
  130. params.to = `${toHours}h`
  131. } else {
  132. params.from = this.time
  133. }
  134. return params
  135. },
  136. },
  137. watch: {
  138. timeGroup () {
  139. this.fetchAllData()
  140. },
  141. time () {
  142. this.smartFetchAllData()
  143. },
  144. customTime () {
  145. this.smartFetchAllData()
  146. },
  147. // groupFunc () {
  148. // this.fetchAllData()
  149. // },
  150. },
  151. methods: {
  152. initTablePageSize (size) {
  153. this.tablePageSize = size
  154. },
  155. smartFetchAllData () { // 根据选择的时间范围智能的赋值时间间隔进行查询
  156. this.$nextTick(this.fetchAllData)
  157. },
  158. remove (i) {
  159. this.metricList.splice(i, 1)
  160. this.chartInstanceList.splice(i, 1)
  161. this.seriesList.splice(i, 1)
  162. this.resultList.splice(i, 1)
  163. this.resultOrderList.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.resultList, i, [])
  174. this.$set(this.resultOrderList, i, '')
  175. this.$set(this.metricList, i, [])
  176. this.$set(this.seriesDescription[i], 'title', '')
  177. }
  178. },
  179. mertricItemChange (item, i) {
  180. const t = +this.time.replace(/\D+/, '')
  181. const existBalance = this.seriesDescription.find(val => val.id === 'balance')
  182. if (!this.isTemplate && !existBalance && item.id === 'balance' && ~this.time.indexOf('h') && t < 3) { // 时间都是转换成h了,这里仅需要对比h即可
  183. this.time = '72h'
  184. this.$message.warning(this.$t('common_562', [item.label]))
  185. }
  186. if (this.isTemplate && (!item.title || item.title === '-') && i === 0 && this.templateParams?.panel?.panel_name) {
  187. // 从 templateParams 中获取 common_alert_metric_details 信息,确保保存时能正确获取参数
  188. const metricDetails = this.templateParams?.panel?.common_alert_metric_details?.[0] || {}
  189. const updatedItem = {
  190. ...item,
  191. title: this.templateParams?.panel?.panel_name,
  192. // 如果 item 中缺少这些信息,从 templateParams 中补充
  193. metric_res_type: item.metric_res_type || metricDetails.res_type,
  194. metricKeyItem: item.metricKeyItem || (metricDetails.measurement ? { measurement: metricDetails.measurement } : item.metricKeyItem),
  195. key: item.key || metricDetails.field,
  196. }
  197. this.$set(this.seriesDescription, i, updatedItem)
  198. } else {
  199. this.$set(this.seriesDescription, i, item)
  200. }
  201. },
  202. async fetchAllData () {
  203. const jobs = []
  204. this.loadingList = []
  205. for (let i = 0; i < this.metricList.length; i++) {
  206. const metric_query = this.metricList[i]
  207. this.loadingList.push(true)
  208. jobs.push(this.fetchData(metric_query, this.tablePageSize, 0))
  209. }
  210. try {
  211. const moment = this.$moment()
  212. const res = await Promise.all(jobs)
  213. this.seriesList = res.map(val => addMissingSeries(get(val, 'series') || [], { ...this.timeRangeParams, interval: this.timeGroup }, moment))
  214. this.resultList = res.map(val => get(val, 'reduced_result') || [])
  215. this.resultOrderList = res.map(() => '')
  216. this.seriesListPager = res.map((val, index) => ({ seriesIndex: index, total: get(val, 'series_total') || 0, page: 1, limit: this.tablePageSize }))
  217. this.loadingList = this.loadingList.map(v => false)
  218. this.saveMonitorConfig()
  219. } catch (error) {
  220. this.loadingList = this.loadingList.map(v => false)
  221. throw error
  222. }
  223. },
  224. async _refresh (i, limit, offset, ignoreOrder) {
  225. try {
  226. this.$set(this.loadingList, i, true)
  227. const { series = [], reduced_result = [], series_total = 0 } = await this.fetchData(this.metricList[i], limit, offset)
  228. this.$set(this.seriesList, i, series)
  229. this.$set(this.resultList, i, reduced_result)
  230. if (!ignoreOrder) {
  231. this.$set(this.resultOrderList, i, '')
  232. }
  233. this.$set(this.seriesListPager, i, { seriesIndex: i, total: series_total, page: 1 + offset / limit, limit: limit })
  234. this.loadingList[i] = false
  235. } catch (error) {
  236. this.$set(this.seriesList, i, [])
  237. this.$set(this.resultList, i, [])
  238. if (!ignoreOrder) {
  239. this.$set(this.resultOrderList, i, '')
  240. }
  241. this.$set(this.loadingList, i, false)
  242. throw error
  243. }
  244. },
  245. async refresh (params, resParams, i) { // 将多个查询 分开调用
  246. const val = { model: params }
  247. if (resParams.type) {
  248. val.result_reducer = resParams
  249. }
  250. const metric_query = [val]
  251. this.$set(this.metricList, i, metric_query)
  252. await this._refresh(i, this.tablePageSize, 0)
  253. },
  254. reducedResultOrderChange (i, order) {
  255. this.resultOrderList[i] = order
  256. this.metricList[i][0].result_reducer_order = order
  257. this._refresh(i, this.seriesListPager[i].limit, 0, true)
  258. },
  259. async pageChange (pager) {
  260. await this._refresh(pager.seriesIndex, pager.limit, (pager.page - 1) * pager.limit)
  261. this.saveMonitorConfig({ tablePageSize: pager.limit })
  262. },
  263. async fetchData (metric_query, limit, offset) {
  264. try {
  265. const data = {
  266. metric_query,
  267. interval: this.timeGroup,
  268. scope: this.$store.getters.scope,
  269. slimit: limit,
  270. soffset: offset,
  271. ...this.timeRangeParams,
  272. }
  273. if (!data.metric_query || !data.metric_query.length || !data.from) return
  274. data.signature = getSignature(data)
  275. const { data: resdata } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: getRequestT() } })
  276. return resdata
  277. } catch (error) {
  278. throw error
  279. }
  280. },
  281. handleSave (mq, desc) {
  282. this.createDialog('CreateMonitorDashboardChart', {
  283. name: desc.title,
  284. metric_query: mq,
  285. timeGroup: this.timeGroup,
  286. timeRangeParams: this.timeRangeParams,
  287. })
  288. },
  289. async exportTable (index, total) {
  290. try {
  291. const { series = [], reduced_result = [], series_total = 0 } = await this.fetchData(this.metricList[index], total, 0)
  292. if (this.$refs[`monitorLine${index}`] && this.$refs[`monitorLine${index}`][0] && this.$refs[`monitorLine${index}`][0].exportFullData) {
  293. this.$refs[`monitorLine${index}`][0].exportFullData(series, reduced_result, series_total)
  294. }
  295. } catch (error) {
  296. throw error
  297. }
  298. },
  299. getTemplateParams () {
  300. const description = this.seriesDescription[0] || {}
  301. const metric = this.metricList[0]?.[0] || {}
  302. return {
  303. panel_name: description.title || description.label || '',
  304. time: this.time,
  305. timeGroup: this.timeGroup,
  306. model: metric.model || {},
  307. result_reducer: metric.result_reducer || '',
  308. common_alert_metric_details: [
  309. {
  310. res_type: description.metric_res_type || '',
  311. measurement: description.metricKeyItem?.measurement || '',
  312. field: description.key || '',
  313. },
  314. ],
  315. }
  316. },
  317. },
  318. }
  319. </script>