List.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <template>
  2. <div>
  3. <monitor-header
  4. v-if="isTemplate && isTemplateEdit"
  5. :time.sync="time"
  6. :showCustomTime="false"
  7. :showGroupFunc="false"
  8. :showTimegroup="false"
  9. @refresh="refresh" />
  10. <page-list
  11. :list="list"
  12. :columns="templateListColumns || columns"
  13. :single-actions="singleActions"
  14. :group-actions="groupActions"
  15. :export-data-options="exportDataOptions"
  16. :showGroupActions="showGroupActions"
  17. :showSearchbox="showSearchbox"
  18. :show-single-actions="!isTemplate"
  19. :show-page="!isTemplate">
  20. <template v-slot:group-actions-append>
  21. <monitor-header
  22. class-name="ml-2"
  23. :time.sync="time"
  24. :customTime.sync="customTime"
  25. :showGroupFunc="false"
  26. :showTimegroup="false"
  27. :show-sync="false"
  28. customTimeUseTimeStamp />
  29. </template>
  30. </page-list>
  31. </div>
  32. </template>
  33. <script>
  34. import * as R from 'ramda'
  35. import { levelMaps } from '@Monitor/constants'
  36. import WindowsMixin from '@/mixins/windows'
  37. import ListMixin from '@/mixins/list'
  38. import BrandIcon from '@/sections/BrandIcon'
  39. import { getNameFilter, getDescriptionFilter } from '@/utils/common/tableFilter'
  40. import { getTimeTableColumn, getStatusTableColumn, getNameDescriptionTableColumn, getCopyWithContentTableColumn } from '@/utils/common/tableColumn'
  41. import { strategyColumn, levelColumn, getStrategyInfo } from '@Monitor/views/commonalert/utils'
  42. import GlobalSearchMixin from '@/mixins/globalSearch'
  43. import ResTemplateListMixin from '@/mixins/resTemplateList'
  44. import MonitorHeader from '@/sections/Monitor/Header'
  45. import { isCE } from '@/utils/utils'
  46. import ColumnsMixin from '../mixins/columns'
  47. import SingleAction from '../mixins/singleActions'
  48. export default {
  49. name: 'AlertResourceList',
  50. components: {
  51. MonitorHeader,
  52. },
  53. mixins: [WindowsMixin, ListMixin, GlobalSearchMixin, ColumnsMixin, SingleAction, ResTemplateListMixin],
  54. props: {
  55. id: String,
  56. getParams: {
  57. type: Object,
  58. default: () => ({}),
  59. },
  60. data: {
  61. type: Object,
  62. required: false,
  63. },
  64. resType: String,
  65. listId: String,
  66. hiddenColumns: {
  67. type: Array,
  68. default: () => [],
  69. },
  70. templateParams: {
  71. type: Object,
  72. default: () => ({}),
  73. },
  74. },
  75. data () {
  76. let resource = 'monitorresourcealertees'
  77. if (isCE() || this.$store.getters.isSysCE || (!(this.$store.getters.workflow?.enabledKeys || []).includes('alert-event') && !(this.$store.getters.workflow?.enabledKeys || []).includes('alert-ticket'))) {
  78. resource = 'monitorresourcealerts'
  79. }
  80. return {
  81. list: this.$list.createList(this, this.listOptions(resource)),
  82. resTypeItems: [],
  83. time: this.templateParams.time || '168h',
  84. customTime: null,
  85. }
  86. },
  87. computed: {
  88. columns () {
  89. let columns = this.listColumns()
  90. if (this.hiddenColumns.length) {
  91. columns = columns.filter(item => {
  92. return !this.hiddenColumns.some(item2 => item2 === item.field)
  93. })
  94. }
  95. return columns
  96. },
  97. exportDataOptions () {
  98. return {
  99. downloadType: 'local',
  100. title: this.$t('monitor.text_17'),
  101. items: this.columns,
  102. hiddenFields: ['ip', 'value_str', 'brand', 'alert_details'],
  103. fixedItems: [
  104. { key: 'data.tags.ip', label: 'IP' },
  105. { key: 'data.value_str', label: this.$t('monitor.text_16') },
  106. { key: 'data.tags.brand', label: this.$t('compute.text_176') },
  107. { key: 'data.alert_details', label: this.$t('monitor.condition') },
  108. ],
  109. }
  110. },
  111. },
  112. watch: {
  113. time (val) {
  114. this.list.fetchData()
  115. },
  116. customTime (val) {
  117. this.list.fetchData()
  118. },
  119. resTypeItems (val) {
  120. this.$nextTick(() => {
  121. this.list.filterOptions = this.filters()
  122. })
  123. },
  124. },
  125. created () {
  126. this.allAlertManager = new this.$Manager('alertrecords', 'v1')
  127. this.list.fetchData()
  128. this.initResType()
  129. },
  130. methods: {
  131. refresh () {
  132. this.list.fetchData()
  133. },
  134. filters () {
  135. const options = {
  136. name: getNameFilter({ field: 'name', label: this.$t('monitor.text_99') }),
  137. description: getDescriptionFilter(),
  138. level: {
  139. label: this.$t('monitor.level'),
  140. dropdown: true,
  141. items: Object.values(levelMaps),
  142. },
  143. send_state: {
  144. label: this.$t('common.sendState'),
  145. dropdown: true,
  146. filter: true,
  147. items: [
  148. { key: 'ok', label: this.$t('status.alertSendState.ok') },
  149. { key: 'silent', label: this.$t('status.alertSendState.silent') },
  150. { key: 'shield', label: this.$t('status.alertSendState.shield') },
  151. ],
  152. formatter: (val) => {
  153. return `send_state.equals(${val})`
  154. },
  155. },
  156. res_type: {
  157. label: this.$t('monitor.text_97'),
  158. dropdown: true,
  159. items: this.resTypeItems,
  160. },
  161. res_name: {
  162. field: 'res_name',
  163. label: this.$t('common_151'),
  164. },
  165. ip: { label: 'IP' },
  166. // created_at: getTimeRangeFilter({ label: this.$t('monitor.text_14'), field: 'trigger_time' }),
  167. }
  168. for (const key of Object.keys(options)) {
  169. if (this.hiddenColumns.some(item => item === key)) {
  170. delete options[key]
  171. }
  172. }
  173. return options
  174. },
  175. listOptions (resource) {
  176. return {
  177. ctx: this,
  178. id: this.id || this.listId,
  179. idKey: 'row_id',
  180. resource: resource,
  181. apiVersion: 'v1',
  182. getParams: this.getParam,
  183. genParamsCb: (params) => { return Object.assign({}, params, { details: true }) },
  184. filter: this.resType ? { res_type: [this.resType] } : {},
  185. filterOptions: this.filters(),
  186. hiddenColumns: ['alert_rule'],
  187. isTemplate: this.isTemplate,
  188. templateLimit: this.templateLimit,
  189. }
  190. },
  191. listColumns () {
  192. return [
  193. getNameDescriptionTableColumn({
  194. edit: false,
  195. showDesc: false,
  196. editDesc: false,
  197. title: this.$t('common_151'),
  198. hideField: true,
  199. field: 'res_name',
  200. onManager: this.onManager,
  201. slotCallback: row => {
  202. return (
  203. <side-page-trigger onTrigger={() => this.handleOpenSidepage(row)}>{ row.res_name }</side-page-trigger>
  204. )
  205. },
  206. }),
  207. getTimeTableColumn({ field: 'trigger_time', title: this.$t('monitor.text_14') }),
  208. getStatusTableColumn({ statusModule: 'alertrecord', field: 'alert_state' }),
  209. {
  210. field: 'res_type',
  211. title: this.$t('monitor.text_97'),
  212. minWidth: 80,
  213. formatter: ({ row }) => {
  214. let rule = row.alert_rule
  215. if (R.is(Array, rule)) {
  216. rule = row.alert_rule[0]
  217. }
  218. if (rule && rule.res_type) {
  219. if (this.$te(`dictionary.${rule.res_type}`)) {
  220. return this.$t(`dictionary.${rule.res_type}`)
  221. }
  222. }
  223. return '-'
  224. },
  225. },
  226. strategyColumn('alert_rule'),
  227. {
  228. field: 'alert_details',
  229. title: this.$t('monitor.condition'),
  230. formatter: ({ row }) => {
  231. const { strategy } = getStrategyInfo(row.data.alert_details || (row.alert_rule && row.alert_rule.length && row.alert_rule[0]))
  232. return strategy
  233. },
  234. },
  235. levelColumn(),
  236. getCopyWithContentTableColumn({
  237. field: 'ip',
  238. title: 'IP',
  239. hideField: true,
  240. message: row => row.data?.tags?.ip || '-',
  241. formatter: ({ row }) => {
  242. return row.data?.tags?.ip || ''
  243. },
  244. slotCallback: (row) => {
  245. return row.data?.tags?.ip || '-'
  246. },
  247. }),
  248. {
  249. field: 'alert_name',
  250. title: this.$t('monitor.text_99'),
  251. formatter: ({ row }) => row.alert_name || '-',
  252. },
  253. {
  254. field: 'alert_count',
  255. title: this.$t('monitor.alert_count'),
  256. minWidth: 100,
  257. slots: {
  258. default: ({ row }) => {
  259. return row.alert_count || 0
  260. },
  261. },
  262. },
  263. {
  264. field: 'brand',
  265. title: this.$t('compute.text_176'),
  266. slots: {
  267. default: ({ row }, h) => {
  268. let brand = R.path(['data', 'tags', 'brand'], row)
  269. if (!brand) return [<data-loading />]
  270. if (brand === 'kvm') brand = 'OneCloud'
  271. return [
  272. <BrandIcon name={brand} />,
  273. ]
  274. },
  275. },
  276. formatter: ({ row }) => {
  277. let brand = R.path(['data', 'tags', 'brand'], row)
  278. if (!brand) return ''
  279. if (brand === 'kvm') brand = 'OneCloud'
  280. return brand
  281. },
  282. },
  283. {
  284. field: 'value_str',
  285. title: this.$t('monitor.text_16'),
  286. align: 'right',
  287. formatter: ({ row }) => row.data ? row.data.value_str : '-',
  288. },
  289. {
  290. field: 'send_state',
  291. title: this.$t('common.sendState'),
  292. formatter: ({ row }) => this.$t(`status.alertSendState.${row.send_state}`),
  293. },
  294. ]
  295. },
  296. getParam () {
  297. const ret = {
  298. ...(R.is(Function, this.getParams) ? this.getParams() : this.getParams),
  299. details: true,
  300. alerting: true,
  301. }
  302. if (this.time) {
  303. let timeFilter = ''
  304. if (this.time.includes('h')) {
  305. ret.start_time = this.$moment().utc().subtract(this.time.replace('h', ''), 'hours').format('YYYY-MM-DD HH:mm:ss')
  306. ret.end_time = this.$moment().utc().format('YYYY-MM-DD HH:mm:ss')
  307. } else if (this.time === 'last_month') {
  308. ret.start_time = this.$moment().utc().subtract(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss')
  309. ret.end_time = this.$moment().utc().subtract(1, 'month').endOf('month').format('YYYY-MM-DD HH:mm:ss')
  310. } else if (this.time === 'custom') {
  311. ret.start_time = this.$moment(this.customTime.from).utc().format('YYYY-MM-DD HH:mm:ss')
  312. ret.end_time = this.$moment(this.customTime.to).utc().format('YYYY-MM-DD HH:mm:ss')
  313. }
  314. timeFilter = `trigger_time.between("${ret.start_time}", "${ret.end_time}")`
  315. if (ret.start_time && ret.end_time) {
  316. if (ret.filter) {
  317. if (R.is(Array, ret.filter)) {
  318. ret.filter.push(timeFilter)
  319. } else {
  320. ret.filter = [ret.filter, timeFilter]
  321. }
  322. } else {
  323. ret.filter = [timeFilter]
  324. }
  325. }
  326. }
  327. if (this.isTemplate && this.templateParams?.topN) {
  328. ret.top = this.templateParams?.topN
  329. }
  330. return ret
  331. },
  332. handleOpenSidepage (row) {
  333. const { tags = {} } = row.data || {}
  334. const data = { ...tags }
  335. data.id = row.monitor_resource_id
  336. data.ip = data.ip || data.vm_ip
  337. this.sidePageTriggerHandle(this, 'AlertResourceSidePage', {
  338. id: row.res_id,
  339. resource: () => {
  340. return {
  341. data,
  342. }
  343. },
  344. getParams: this.getParam,
  345. alert_id: row.alert_id,
  346. })
  347. },
  348. initResType () {
  349. this.allAlertManager.get({
  350. id: 'distinct-field',
  351. params: {
  352. scope: this.$store.getters.scope,
  353. extra_field: 'res_type',
  354. details: true,
  355. },
  356. }).then(res => {
  357. const { res_type = [] } = res.data || {}
  358. this.resTypeItems = res_type.map(item => {
  359. let label = item
  360. if (this.$te(`dictionary.${item}`)) {
  361. label = this.$t(`dictionary.${item}`)
  362. }
  363. return {
  364. key: item,
  365. label,
  366. }
  367. })
  368. })
  369. },
  370. },
  371. }
  372. </script>