index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <template>
  2. <div class="overview-index">
  3. <a-row v-if="scope !=='project' && !isTemplate">
  4. <overview-nav :items="navs" @change="changeNav" />
  5. </a-row>
  6. <a-row v-if="!isTemplate">
  7. <div class="monitor-overview-chart mb-2">
  8. <div class="title-wrapper">
  9. <div class="title">
  10. {{ $t('monitor.dashboard.overview.title') }}
  11. <help-tooltip name="monitorDashboardOverviewTips" />
  12. </div>
  13. </div>
  14. <summary-cards :scope="curNav.scope" :scopeId="curNav.id" style="padding-top: 1em;" />
  15. </div>
  16. </a-row>
  17. <a-row>
  18. <a-col :span="8">
  19. <div class="monitor-overview-chart mb-2">
  20. <div class="title-wrapper">
  21. <div class="title">{{ $t('monitor.overview_alert_sum') }}</div>
  22. </div>
  23. <overview-ring
  24. yAxisFormat="0"
  25. chartHeigth="299px"
  26. :chartData="ringChart.chartData"
  27. :chartEvents="ringChartEvent()"
  28. :title="$t('monitor.overview_alert_sum_pie')"
  29. :subtitle="ringChart.subtitle"
  30. :loading="ringChart.loading"
  31. :exportExcelColumns="exportExcelColumns"
  32. :exportName="$t('monitor.overview_alert_sum')" />
  33. </div>
  34. </a-col>
  35. <a-col :span="16">
  36. <div class="monitor-overview-chart mb-2">
  37. <div class="title-wrapper">
  38. <div class="title">{{ $t('monitor.overview_alert_trend') }}</div>
  39. </div>
  40. <overview-line
  41. yAxisFormat="0"
  42. chartHeigth="299px"
  43. :isHistogram="true"
  44. :chartData="lineChart.chartData"
  45. :chartSetting="lineChart.chartSetting"
  46. :loading="lineChart.loading" />
  47. </div>
  48. </a-col>
  49. </a-row>
  50. <a-row v-if="!isTemplate">
  51. <overview-card :scope="curNav.scope" :extraParams="extraParams" @changeNav="updateNavs" />
  52. </a-row>
  53. </div>
  54. </template>
  55. <script>
  56. import OverviewRing from '@Monitor/components/MonitorCard/sections/chart/ring'
  57. import OverviewLine from '@Monitor/components/MonitorCard/sections/chart/line'
  58. import OverviewCard from '@Monitor/components/MonitorCard/OverviewCard'
  59. import OverviewNav from '@Monitor/components/MonitorCard/sections/nav'
  60. import { getSignature } from '@/utils/crypto'
  61. import SummaryCards from './SummaryCards'
  62. export default {
  63. name: 'OverviewIndex',
  64. components: {
  65. SummaryCards,
  66. OverviewRing,
  67. OverviewLine,
  68. OverviewCard,
  69. OverviewNav,
  70. },
  71. props: {
  72. isTemplate: {
  73. type: Boolean,
  74. default: false,
  75. },
  76. isTemplateEdit: {
  77. type: Boolean,
  78. default: false,
  79. },
  80. templateParams: {
  81. type: Object,
  82. default: () => ({}),
  83. },
  84. scopeParams: {
  85. type: Object,
  86. default: () => ({}),
  87. },
  88. },
  89. data () {
  90. const scope = this.$store.getters.scope
  91. const u = this.$store.getters.userInfo
  92. const navs = []
  93. if (scope === 'system') {
  94. navs.push({ id: 'system', location: this.$t('cloudenv.text_457'), title: this.$t('cloudenv.text_457'), scope })
  95. } else if (scope === 'domain') {
  96. navs.push({ id: u.projectDomainId, location: this.$t('dictionary.domain'), title: u.projectDomain, scope })
  97. } else if (scope === 'project') {
  98. navs.push({ id: u.projectId, location: this.$t('dictionary.project'), title: u.projectName, scope })
  99. }
  100. return {
  101. scope: scope,
  102. navs: navs,
  103. curNav: navs[0],
  104. ringChart: { loading: true },
  105. lineChart: {
  106. loading: true,
  107. chartSetting: {},
  108. chartData: { rows: [], columns: [] },
  109. },
  110. loading: false,
  111. exportExcelColumns: {
  112. [this.$t('common_151')]: {
  113. field: 'name',
  114. },
  115. [this.$t('monitor.text_98')]: {
  116. field: 'count',
  117. },
  118. },
  119. }
  120. },
  121. computed: {
  122. scopeLevel () {
  123. return Math.max(['project', 'domain', 'system'].indexOf(this.curNav.scope) + 1, 0)
  124. },
  125. extraParams () {
  126. const ret = {}
  127. if (this.curNav.scope === 'domain') ret.domain_id = this.curNav.id
  128. if (this.curNav.scope === 'project') ret.project_id = this.curNav.id
  129. return ret
  130. },
  131. },
  132. watch: {
  133. 'curNav.scope' () {
  134. this.$nextTick(() => {
  135. this.fetchAllCharts()
  136. })
  137. },
  138. },
  139. created () {
  140. this.fetchAllCharts()
  141. },
  142. methods: {
  143. updateNavs (row) {
  144. if (!row || !row.tags) {
  145. console.log(`toNav ${row}`)
  146. return
  147. }
  148. const tags = row.tags
  149. console.log(tags)
  150. // system -> domain
  151. if (this.scopeLevel > 2 && tags.domain_id) {
  152. this.navs = this.navs.slice(0, 1)
  153. this.navs.push({ id: tags.domain_id, location: this.$t('dictionary.domain'), name: tags.project_domain, scope: 'domain' })
  154. }
  155. // domain -> project
  156. if (this.scopeLevel > 1 && tags.tenant_id) {
  157. const end = this.navs[0].scope === 'system' ? 2 : 1
  158. this.navs = this.navs.slice(0, end)
  159. this.navs.push({ id: tags.tenant_id, location: this.$t('dictionary.project'), name: tags.tenant, scope: 'project' })
  160. }
  161. this.changeNav(this.navs[this.navs.length - 1])
  162. },
  163. changeNav: function (e) {
  164. this.curNav = e
  165. },
  166. ringChartEvent: function () {
  167. const self = this
  168. return {
  169. click: function (e) {
  170. self.toHistory(e)
  171. },
  172. }
  173. },
  174. toHistory: function (e) {
  175. const matchs = this.ringChart.chartData.rows.filter(row => { return row.name === e.name && row.count === e.value })
  176. if (matchs.length > 0) {
  177. this.$router.push({ path: '/alertrecord', query: { res_type: matchs[0].raw_name } })
  178. }
  179. },
  180. async fetchAllCharts () {
  181. await this.fetchPieChartData()
  182. // 近30日告警趋势图
  183. await this.fetchTabChartData()
  184. },
  185. commonParams () {
  186. const extendParams = {
  187. scope: this.curNav.scope,
  188. }
  189. Object.assign(extendParams, this.extraParams)
  190. return extendParams
  191. },
  192. chartQueryData () {
  193. const extendParams = this.commonParams()
  194. return {
  195. from: '720h',
  196. interval: '24h',
  197. metric_query: [
  198. {
  199. model: {
  200. database: 'monitor',
  201. measurement: 'alert_record_history',
  202. select: [
  203. [{ params: ['res_num'], type: 'field' }, { type: 'sum' }],
  204. ],
  205. group_by: [{
  206. type: 'tag',
  207. params: ['res_type'],
  208. }],
  209. tags: [],
  210. },
  211. },
  212. ],
  213. unit: true,
  214. ...extendParams,
  215. }
  216. },
  217. tabChartData (rawDatas) {
  218. const chartData = {
  219. columns: [],
  220. rows: [],
  221. }
  222. if (rawDatas && rawDatas.length > 0) {
  223. const name = this.$t('common_648')
  224. chartData.columns = [name]
  225. const _temp = {}
  226. rawDatas.map((item) => {
  227. const points = item.points
  228. if (!item.points) {
  229. return
  230. }
  231. let raw_name = item.raw_name
  232. if (raw_name === '{res_type=host}') raw_name = 'host'
  233. if (raw_name === '{res_type=agent}') raw_name = 'agent'
  234. const columnName = item.raw_name ? (this.$te(`dictionary.${raw_name}`) ? this.$t(`dictionary.${raw_name}`) : raw_name) : this.$t('monitor.overview_alert.undefined')
  235. chartData.columns.push(columnName)
  236. let series = points.map((item) => {
  237. return { [name]: item[1], value: item[0] }
  238. })
  239. series = series.sort((a, b) => {
  240. return a[name] - b[name]
  241. })
  242. for (const i in series) {
  243. const d = new Date(series[i][name])
  244. const rn = `${d.getMonth() + 1}/${d.getDate()}`
  245. if (_temp.hasOwnProperty(rn)) {
  246. _temp[rn][columnName] = series[i].value
  247. } else {
  248. _temp[rn] = { [name]: rn }
  249. _temp[rn][columnName] = series[i].value
  250. }
  251. }
  252. })
  253. // base data
  254. const initData = {}
  255. chartData.columns.slice(1).map((item) => { initData[item] = 0 })
  256. // fill data
  257. const rows = []
  258. const now = new Date()
  259. for (let i = 30; i > 0; i--) {
  260. const cur = new Date(now - i * 24 * 60 * 60 * 1000)
  261. const rn = `${cur.getMonth() + 1}/${cur.getDate()}`
  262. if (_temp.hasOwnProperty(rn)) {
  263. rows.push(Object.assign({}, initData, _temp[rn]))
  264. } else {
  265. rows.push(Object.assign({}, { [name]: rn }, initData))
  266. }
  267. }
  268. chartData.rows = rows
  269. }
  270. return chartData
  271. },
  272. async fetchTabChartData (measurement, field) {
  273. try {
  274. this.lineChart.loading = true
  275. this.lineChart.chartData = {
  276. columns: [],
  277. rows: [],
  278. }
  279. var data = this.chartQueryData()
  280. data.signature = getSignature(data)
  281. if (this.isTemplate) {
  282. if (this.scopeParams.scope) {
  283. data.scope = this.scopeParams.scope
  284. }
  285. // if (this.scopeParams.project_id) {
  286. // data.metric_query[0].model.tags.push({
  287. // key: 'project_id',
  288. // value: this.scopeParams.project_id,
  289. // operator: '=',
  290. // })
  291. // }
  292. if (this.scopeParams.domain_id) {
  293. data.metric_query[0].model.tags.push({
  294. key: 'domain_id',
  295. value: this.scopeParams.domain_id,
  296. operator: '=',
  297. })
  298. }
  299. }
  300. const { data: { series = [] } } = await new this.$Manager('unifiedmonitors', 'v1').performAction({ id: 'query', action: '', data, params: { $t: new Date().getSeconds() } })
  301. const self = this
  302. this.$nextTick(_ => {
  303. this.lineChart.rawDatas = series
  304. this.lineChart.chartData = self.tabChartData(series)
  305. this.lineChart.chartSetting.stack = { alerts: this.lineChart.chartData.columns.slice(1) }
  306. this.lineChart.loading = false
  307. })
  308. } catch (error) {
  309. this.lineChart.loading = false
  310. throw error
  311. } finally {
  312. this.lineChart.loading = false
  313. }
  314. },
  315. async fetchPieChartData () {
  316. try {
  317. this.ringChart.loading = true
  318. this.ringChart.chartData = {
  319. columns: [],
  320. rows: [],
  321. }
  322. const params = this.commonParams()
  323. if (this.isTemplate) {
  324. if (this.scopeParams.scope) {
  325. params.scope = this.scopeParams.scope
  326. }
  327. if (this.scopeParams.project_id) {
  328. params.scope = 'domain'
  329. params.project_id = this.scopeParams.project_id
  330. }
  331. if (this.scopeParams.domain_id) {
  332. params.scope = 'domain'
  333. params.domain_id = this.scopeParams.domain_id
  334. }
  335. }
  336. const { data: series = { } } = await new this.$Manager('alertrecords', 'v1').get({ id: 'total-alert', params: params })
  337. this.$nextTick(_ => {
  338. const chartData = {
  339. columns: ['name', 'count', 'raw_name'],
  340. rows: [],
  341. }
  342. let count = 0
  343. if (Object.keys(series).length > 0) {
  344. for (const item in series) {
  345. count += series[item]
  346. chartData.rows.push({ raw_name: item, name: this.$t(`dictionary.${item}`), count: series[item] })
  347. }
  348. } else {
  349. chartData.rows.push({ raw_name: '', name: '', count: 0 })
  350. }
  351. this.ringChart.subtitle = String(count)
  352. this.ringChart.chartData = chartData
  353. this.ringChart.loading = false
  354. })
  355. } catch (error) {
  356. this.ringChart.loading = false
  357. throw error
  358. }
  359. },
  360. },
  361. }
  362. </script>
  363. <style lang="less" scoped>
  364. @import '../../../../../src/styles/less/theme';
  365. .monitor-overview-chart {
  366. border: 1px solid #F1F1F1;
  367. padding: 6px 24px 12px 6px;
  368. margin-left: 6px;
  369. margin-right: 6px;
  370. .header {
  371. font-weight: 500;
  372. .title-wrapper {
  373. .subtitle {
  374. font-size: 12px;
  375. color: #ccc;
  376. }
  377. }
  378. }
  379. }
  380. </style>