index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <template>
  2. <div>
  3. <page-header :title="this.panelId ? $t('monitor.dashboard.dialog.project.update') : $t('monitor.dashboard.dialog.project.create')" />
  4. <page-body needMarginBottom v-if="initFinished">
  5. <a-row>
  6. <a-col :md="{ span: 24 }" :lg="{ span: 22 }" :xl="{ span: 16 }" :xxl="{ span: 11 }" class="mb-5">
  7. <monitor-forms
  8. :panel="panel"
  9. :queryOnly="false"
  10. :multiQuery="false"
  11. :timeRangeParams="timeRangeParams"
  12. :extraParams="extraParams"
  13. @nameChange="nameChange"
  14. @refresh="refresh"
  15. @remove="remove"
  16. @resetChart="resetChart"
  17. @mertricItemChange="mertricItemChange" />
  18. </a-col>
  19. <a-col class="line mb-5"
  20. :md="{ span: 24 }"
  21. :lg="{ span: 22 }"
  22. :xl="{ span: 16 }"
  23. :xxl="{ span: 12, offset: 1 }">
  24. <monitor-header
  25. class="mb-4"
  26. :time.sync="time"
  27. :timeGroup.sync="timeGroup"
  28. :showTimegroup="true"
  29. :showGroupFunc="true"
  30. :customTime.sync="customTime"
  31. :showCustomTimeText="time==='custom'"
  32. customTimeUseTimeStamp
  33. @refresh="fetchAllData" />
  34. <div v-for="(item, i) in seriesList" :key="i">
  35. <monitor-line
  36. :ref="`monitorLine${i}`"
  37. class="mb-3"
  38. :pager="seriesListPager[i]"
  39. :loading="loadingList[i]"
  40. :description="seriesDescription[i]"
  41. :metricInfo="metricList[i][0]"
  42. :series="item"
  43. :reducedResult="resultList[i]"
  44. :reducedResultOrder="resultOrderList[i]"
  45. showTableExport
  46. @pageChange="pageChange"
  47. @chartInstance="setChartInstance"
  48. @reducedResultOrderChange="(order) => reducedResultOrderChange(i, order)"
  49. @exportTable="(total) => exportTable(i, total)" />
  50. </div>
  51. <a-card v-if="!seriesList.length && loadingList[0]"
  52. class="explorer-monitor-line d-flex align-items-center justify-content-center">
  53. <loader :loading="true" />
  54. </a-card>
  55. </a-col>
  56. </a-row>
  57. </page-body>
  58. <page-footer>
  59. <div slot="right">
  60. <a-button class="mr-3" type="primary"
  61. :loading="loading"
  62. :disabled="metricList.length === 0"
  63. @click="handleConfirm">
  64. {{ $t('common.save') }}
  65. </a-button>
  66. <a-button @click="goback">{{ $t('common.cancel') }}</a-button>
  67. </div>
  68. </page-footer>
  69. </div>
  70. </template>
  71. <script>
  72. import get from 'lodash/get'
  73. import echarts from 'echarts'
  74. import MonitorForms from '@Monitor/sections/ExplorerForm'
  75. import MonitorLine from '@Monitor/sections/MonitorLine'
  76. // import { MONITOR_MAX_POINTERS } from '@Monitor/constants'
  77. import MonitorHeader from '@/sections/Monitor/Header'
  78. import { getRequestT, uuid } from '@/utils/utils'
  79. import { getSignature } from '@/utils/crypto'
  80. import MonitorTimeMixin from '@/mixins/monitorTime'
  81. export default {
  82. name: 'MonitorDashboardChartCreate',
  83. components: {
  84. MonitorForms,
  85. MonitorLine,
  86. MonitorHeader,
  87. },
  88. mixins: [MonitorTimeMixin],
  89. data () {
  90. const extraParams = {}
  91. if (this.$route.params.scope) {
  92. extraParams.scope = this.$route.params.scope
  93. }
  94. if (this.$route.params.domain_id) {
  95. extraParams.domain_id = this.$route.params.domain_id
  96. }
  97. if (this.$route.params.project_id) {
  98. extraParams.project_id = this.$route.params.project_id
  99. }
  100. return {
  101. initFinished: !this.$route.params.id,
  102. panelId: this.$route.params.id || '',
  103. panel: {},
  104. time: '1h',
  105. // groupFunc: 'mean',
  106. timeGroup: '1m',
  107. customTime: null,
  108. metricList: [],
  109. seriesList: [],
  110. resultList: [],
  111. resultOrderList: [],
  112. seriesListPager: [],
  113. chartInstanceList: [], // e-chart 实例
  114. loadingList: [],
  115. seriesDescription: [],
  116. extraParams: extraParams,
  117. get,
  118. tablePageSize: 10,
  119. }
  120. },
  121. computed: {
  122. timeFormatStr () {
  123. return this.timeOpts[this.time].timeFormat
  124. },
  125. timeRangeParams () {
  126. const params = {}
  127. if (this.time === 'custom') { // 自定义时间
  128. if (this.customTime && this.customTime.from && this.customTime.to) {
  129. params.from = this.customTime.from
  130. params.to = this.customTime.to
  131. }
  132. } else {
  133. params.from = this.time
  134. }
  135. return params
  136. },
  137. },
  138. watch: {
  139. timeGroup () {
  140. this.fetchAllData()
  141. },
  142. time () {
  143. this.fetchAllData()
  144. },
  145. customTime () {
  146. this.fetchAllData()
  147. },
  148. // groupFunc (val) {
  149. // this.fetchAllData()
  150. // },
  151. },
  152. // created () {
  153. // if (this.panelId) {
  154. // this.fetchPanel()
  155. // }
  156. // },
  157. mounted () {
  158. if (this.panelId) {
  159. this.fetchPanel()
  160. }
  161. },
  162. methods: {
  163. initTablePageSize (size) {
  164. this.tablePageSize = size
  165. },
  166. fetchPanel () {
  167. this.loading = true
  168. try {
  169. const params = {
  170. details: true,
  171. ...this.extraParams,
  172. }
  173. new this.$Manager('alertpanels', 'v1').get({ id: this.panelId, params }).then((res) => {
  174. this.loading = false
  175. this.panel = Object.assign({}, this.panel, res.data)
  176. const { time, timeGroup, customTime, groupFunc } = this.$route.params
  177. this.time = time
  178. this.timeGroup = timeGroup
  179. this.customTime = customTime
  180. this.groupFunc = groupFunc
  181. this.initFinished = true
  182. })
  183. } catch (error) {
  184. throw error
  185. } finally {
  186. this.loading = false
  187. }
  188. },
  189. remove (i) {
  190. this.metricList.splice(i, 1)
  191. this.chartInstanceList.splice(i, 1)
  192. this.seriesList.splice(i, 1)
  193. this.resultList.splice(i, 1)
  194. this.resultOrderList.splice(i, 1)
  195. this.loadingList.splice(i, 1)
  196. },
  197. setChartInstance (val, i) {
  198. this.chartInstanceList.push(val)
  199. echarts.connect(this.chartInstanceList)
  200. },
  201. resetChart (i) {
  202. if (this.seriesList && this.seriesList.length && this.seriesList[i]) {
  203. this.$set(this.seriesList, i, [])
  204. this.$set(this.resultList, i, [])
  205. this.$set(this.resultOrderList, i, [])
  206. this.$set(this.metricList, i, [])
  207. this.$set(this.seriesDescription[i], 'title', '')
  208. }
  209. },
  210. mertricItemChange (item, i) {
  211. const t = +this.time.replace(/\D+/, '')
  212. const existBalance = this.seriesDescription.find(val => val.id === 'balance')
  213. if (!existBalance && item.id === 'balance' && ~this.time.indexOf('h') && t < 3) { // 时间都是转换成h了,这里仅需要对比h即可
  214. this.time = '72h'
  215. this.$message.warning(this.$t('common_562', [item.label]))
  216. }
  217. this.$set(this.seriesDescription, i, item)
  218. },
  219. async fetchAllData () {
  220. const jobs = []
  221. this.loadingList = []
  222. for (let i = 0; i < this.metricList.length; i++) {
  223. const metric_query = this.metricList[i]
  224. // groupFunc 为该时间间隔内如何聚合数据,与原图表group_by不一样
  225. // metric_query.forEach((query, index) => {
  226. // const { model = {} } = query
  227. // const { select = [] } = model
  228. // if (select.length) {
  229. // select.forEach(s => {
  230. // const index = s.findIndex(item => ['mean', 'min', 'max', this.groupFunc].includes(item.type))
  231. // if (index !== -1) {
  232. // s[index].type = this.groupFunc
  233. // } else {
  234. // s.push({ type: this.groupFunc, params: [] })
  235. // }
  236. // })
  237. // } else {
  238. // select.push([{ type: this.groupFunc, params: [] }])
  239. // metric_query[index].model.select = select
  240. // }
  241. // })
  242. this.loadingList.push(true)
  243. jobs.push(this.fetchData(metric_query, this.tablePageSize, 0))
  244. }
  245. try {
  246. const res = await Promise.all(jobs)
  247. this.seriesList = res.map(val => get(val, 'series') || [])
  248. this.resultList = res.map(val => get(val, 'reduced_result') || [])
  249. this.resultOrderList = res.map(val => '')
  250. this.seriesListPager = res.map((val, index) => ({ seriesIndex: index, total: get(val, 'series_total') || 0, page: 1, limit: this.tablePageSize }))
  251. this.loadingList = this.loadingList.map(v => false)
  252. this.saveMonitorConfig()
  253. } catch (error) {
  254. this.loadingList = this.loadingList.map(v => false)
  255. throw error
  256. }
  257. },
  258. reducedResultOrderChange (i, order) {
  259. this.resultOrderList[i] = order
  260. this.metricList[i][0].result_reducer_order = order
  261. this._refresh(i, this.seriesListPager[i].limit, 0, true)
  262. },
  263. async _refresh (i, limit, offset, ignoreOrder) {
  264. try {
  265. this.$set(this.loadingList, i, true)
  266. const { series = [], reduced_result = [], series_total = 0 } = await this.fetchData(this.metricList[i], limit, offset)
  267. this.$set(this.seriesList, i, series)
  268. this.$set(this.resultList, i, reduced_result)
  269. if (!ignoreOrder) {
  270. this.$set(this.resultOrderList, i, '')
  271. }
  272. this.$set(this.seriesListPager, i, { seriesIndex: i, total: series_total, page: 1 + offset / limit, limit: limit })
  273. this.loadingList[i] = false
  274. } catch (error) {
  275. this.$set(this.seriesList, i, [])
  276. this.$set(this.resultList, i, [])
  277. if (!ignoreOrder) {
  278. this.$set(this.resultOrderList, i, '')
  279. }
  280. this.$set(this.loadingList, i, false)
  281. throw error
  282. }
  283. },
  284. async refresh (params, resParams, i) { // 将多个查询 分开调用
  285. const val = { model: params }
  286. if (resParams.type) {
  287. val.result_reducer = resParams
  288. }
  289. const metric_query = [val]
  290. this.$set(this.metricList, i, metric_query)
  291. await this._refresh(i, this.tablePageSize, 0)
  292. },
  293. async pageChange (pager) {
  294. await this._refresh(pager.seriesIndex, pager.limit, (pager.page - 1) * pager.limit)
  295. this.saveMonitorConfig({ tablePageSize: pager.limit })
  296. },
  297. async fetchData (metric_query, limit, offset) {
  298. try {
  299. const data = {
  300. metric_query,
  301. interval: this.timeGroup,
  302. scope: this.$store.getters.scope,
  303. slimit: limit,
  304. soffset: offset,
  305. ...this.timeRangeParams,
  306. ...this.extraParams,
  307. }
  308. if (!data.metric_query || !data.metric_query.length || !data.from) return
  309. // groupFunc 为该时间间隔内如何聚合数据,与原图表group_by不一样
  310. // data.metric_query.forEach((query, index) => {
  311. // const { model = {} } = query
  312. // const { select = [] } = model
  313. // if (select.length) {
  314. // select.forEach(s => {
  315. // const index = s.findIndex(item => ['mean', 'min', 'max', this.groupFunc].includes(item.type))
  316. // if (index !== -1) {
  317. // s[index].type = this.groupFunc
  318. // } else {
  319. // s.push({ type: this.groupFunc, params: [] })
  320. // }
  321. // })
  322. // } else {
  323. // select.push([{ type: this.groupFunc, params: [] }])
  324. // data.metric_query[index].model.select = select
  325. // }
  326. // })
  327. data.signature = getSignature(data)
  328. const { data: resdata } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: getRequestT() } })
  329. return resdata
  330. } catch (error) {
  331. throw error
  332. }
  333. },
  334. goback () {
  335. this.$router.push({ path: '/monitor-dashboard', query: { dashboard_id: this.$route.params.dashboard } })
  336. },
  337. async handleConfirm () {
  338. this.loading = true
  339. const mq = this.metricList[0]
  340. const name = mq[0].model.name
  341. delete mq[0].model.name
  342. try {
  343. const data = {
  344. name: name || uuid(32),
  345. dashboard_id: this.$route.params.dashboard,
  346. metric_query: mq,
  347. interval: this.timeGroup,
  348. scope: this.$store.getters.scope,
  349. ...this.timeRangeParams,
  350. }
  351. if (!data.metric_query || !data.metric_query.length || !data.from || !data.dashboard_id) return
  352. if (this.panelId) {
  353. // update
  354. await new this.$Manager('alertpanels', 'v1').update({ id: this.panelId, data })
  355. } else {
  356. await new this.$Manager('alertpanels', 'v1').create({ data })
  357. }
  358. this.loading = false
  359. this.goback()
  360. } catch (error) {
  361. throw error
  362. } finally {
  363. this.loading = false
  364. }
  365. },
  366. nameChange (name, index) {
  367. if (this.metricList[index] && this.metricList[index][0] && this.metricList[index][0].model) {
  368. this.$set(this.metricList[index][0].model, 'name', name)
  369. }
  370. },
  371. async exportTable (index, total) {
  372. try {
  373. const { series = [], reduced_result = [], series_total = 0 } = await this.fetchData(this.metricList[index], total, 0)
  374. if (this.$refs[`monitorLine${index}`] && this.$refs[`monitorLine${index}`][0] && this.$refs[`monitorLine${index}`][0].exportFullData) {
  375. this.$refs[`monitorLine${index}`][0].exportFullData(series, reduced_result, series_total)
  376. }
  377. } catch (error) {
  378. throw error
  379. }
  380. },
  381. },
  382. }
  383. </script>