index.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <template>
  2. <div class="h-100 position-relative">
  3. <div class="dashboard-card-wrap">
  4. <div class="dashboard-card-header">
  5. <div class="dashboard-card-header-left">
  6. {{ form.fd.name }}<a-icon class="ml-2" type="loading" v-if="loading" />
  7. <span v-if="isResDeny" class="ml-2"><a-icon class="warning-color mr-1" type="warning" />{{ $t('common.permission.403') }}</span>
  8. </div>
  9. <div class="dashboard-card-header-right">
  10. <slot name="actions" :handle-edit="handleEdit" />
  11. <router-link v-if="!edit" to="/vminstance" class="ml-2">
  12. <icon type="arrow-right" style="font-size:18px" />
  13. </router-link>
  14. </div>
  15. </div>
  16. <div class="dashboard-card-body flex-column justify-content-center">
  17. <template v-if="chartOptions.series[0].data.length">
  18. <div class="flex-fill position-relative">
  19. <div class="dashboard-fco-wrap">
  20. <e-chart :options="chartOptions" style="height: 100%; width: 100%;" autoresize />
  21. </div>
  22. </div>
  23. </template>
  24. <template v-else>
  25. <a-empty />
  26. </template>
  27. </div>
  28. </div>
  29. <base-drawer :visible.sync="visible" :title="$t('dashboard.text_5')" @ok="handleSubmit">
  30. <a-form
  31. hideRequiredMark
  32. :form="form.fc"
  33. v-bind="formItemLayout">
  34. <a-form-item :label="$t('dashboard.text_6')">
  35. <a-input v-decorator="decorators.name" />
  36. </a-form-item>
  37. <a-form-item :label="$t('dashboard.near')">
  38. <a-select v-decorator="decorators.from">
  39. <a-select-option :value="7">{{$t('dashboard.vm_count_near', [7])}}</a-select-option>
  40. <a-select-option :value="14">{{$t('dashboard.vm_count_near', [14])}}</a-select-option>
  41. <a-select-option :value="21">{{$t('dashboard.vm_count_near', [21])}}</a-select-option>
  42. <a-select-option :value="30">{{$t('dashboard.vm_count_near', [30])}}</a-select-option>
  43. </a-select>
  44. </a-form-item>
  45. </a-form>
  46. </base-drawer>
  47. </div>
  48. </template>
  49. <script>
  50. import * as R from 'ramda'
  51. import { mapGetters } from 'vuex'
  52. import { load } from '@Dashboard/utils/cache'
  53. import { chartColors } from '@/constants'
  54. import BaseDrawer from '@Dashboard/components/BaseDrawer'
  55. import { resolveValueChangeField } from '@/utils/common/ant'
  56. import { getSignature } from '@/utils/crypto'
  57. import { getRequestT } from '@/utils/utils'
  58. import { hasPermission } from '@/utils/auth'
  59. export default {
  60. name: 'VmHistoryCount',
  61. components: {
  62. BaseDrawer,
  63. },
  64. props: {
  65. options: {
  66. type: Object,
  67. required: true,
  68. },
  69. params: Object,
  70. edit: Boolean,
  71. dataRangeParams: {
  72. type: Object,
  73. },
  74. },
  75. data () {
  76. const initialNameValue = (this.params && this.params.name) || this.$t('dashboard.vm_history_count')
  77. const initialFromValue = (this.params && this.params.from) || 7
  78. return {
  79. data: [],
  80. visible: false,
  81. loading: false,
  82. seriesData: [],
  83. form: {
  84. fc: this.$form.createForm(this, {
  85. onValuesChange: (props, values) => {
  86. const newField = resolveValueChangeField(values)
  87. R.forEachObjIndexed((item, key) => {
  88. this.$set(this.form.fd, key, item)
  89. }, newField)
  90. },
  91. }),
  92. fd: {
  93. name: initialNameValue,
  94. from: initialFromValue,
  95. },
  96. },
  97. decorators: {
  98. name: [
  99. 'name',
  100. {
  101. initialValue: initialNameValue,
  102. rules: [
  103. { required: true, message: this.$t('dashboard.text_8') },
  104. ],
  105. },
  106. ],
  107. from: [
  108. 'from',
  109. {
  110. initialValue: initialFromValue,
  111. },
  112. ],
  113. },
  114. formItemLayout: {
  115. wrapperCol: {
  116. span: 18,
  117. },
  118. labelCol: {
  119. span: 6,
  120. },
  121. },
  122. chartOptions: {
  123. tooltip: {
  124. show: true,
  125. trigger: 'item',
  126. confine: true,
  127. position: (point, params, dom, rect, size) => {
  128. const number = this.$t('dashboard.stage', [params.value])
  129. const series = `<div style="color: #616161;">${params.marker} <span>${params.name}</span>: <span>${number}</span></div>`
  130. const wrapper = `<div class="chart-tooltip-wrapper">
  131. <div class="lines-wrapper">${series}</div>
  132. </div>`
  133. dom.style.border = 'none'
  134. dom.style.backgroundColor = 'transparent'
  135. dom.innerHTML = wrapper
  136. },
  137. },
  138. grid: {
  139. left: 50,
  140. bottom: 30,
  141. top: 30,
  142. right: 20,
  143. width: 'auto',
  144. },
  145. xAxis: {
  146. type: 'category',
  147. data: [],
  148. },
  149. yAxis: {
  150. type: 'value',
  151. },
  152. series: [
  153. {
  154. type: 'bar',
  155. data: [],
  156. },
  157. ],
  158. color: chartColors,
  159. },
  160. }
  161. },
  162. computed: {
  163. ...mapGetters(['scope', 'isAdminMode', 'isDomainMode', 'isProjectMode']),
  164. queryMode () {
  165. if (this.isAdminMode && (this.dataRangeParams?.scope !== 'domain' && this.dataRangeParams?.scope !== 'project')) {
  166. return 'all.servers'
  167. } else if (this.isDomainMode || (this.isAdminMode && this.dataRangeParams?.scope === 'domain')) {
  168. return 'domain.servers'
  169. } else if (this.isProjectMode || (this.isAdminMode && this.dataRangeParams?.scope === 'project') || (this.isDomainMode && this.dataRangeParams?.scope === 'project')) {
  170. return 'project.servers'
  171. } else {
  172. return 'all.servers'
  173. }
  174. },
  175. isResDeny () {
  176. return !hasPermission({ key: 'bill_analysises_list' })
  177. },
  178. },
  179. watch: {
  180. 'form.fd' (val) {
  181. this.fetchData()
  182. for (const key in this.decorators) {
  183. let config = this.decorators[key][1] || {}
  184. config = {
  185. ...config,
  186. initialValue: val[key],
  187. }
  188. this.decorators[key][1] = config
  189. }
  190. },
  191. 'dataRangeParams.scope': {
  192. handler (val) {
  193. this.fetchData()
  194. },
  195. immediate: true,
  196. },
  197. 'dataRangeParams.domain': {
  198. handler (val) {
  199. this.fetchData()
  200. },
  201. immediate: true,
  202. },
  203. 'dataRangeParams.project': {
  204. handler (val) {
  205. this.fetchData()
  206. },
  207. immediate: true,
  208. },
  209. },
  210. created () {
  211. const values = { ...this.form.fd }
  212. this.$emit('update', this.options.i, values)
  213. this.fetchData()
  214. },
  215. methods: {
  216. refresh () {
  217. return this.fetchData()
  218. },
  219. async fetchData () {
  220. this.loading = true
  221. try {
  222. const requestData = this.genQueryData()
  223. requestData.signature = getSignature(requestData)
  224. const params = {
  225. $t: getRequestT(),
  226. ignoreErrorStatusCode: [403],
  227. }
  228. const data = await load({
  229. res: 'unifiedmonitors',
  230. actionArgs: {
  231. url: '/v1/unifiedmonitors/query',
  232. method: 'POST',
  233. params,
  234. data: requestData,
  235. },
  236. useManager: false,
  237. resPath: 'data.series[0]',
  238. })
  239. const { points = [] } = data || {}
  240. const yData = []
  241. const xData = []
  242. points.map(item => {
  243. yData.push(item[0])
  244. xData.push(this.$moment(item[1]).format('MM-DD'))
  245. })
  246. this.chartOptions.xAxis.data = xData
  247. this.chartOptions.series[0].data = yData
  248. } finally {
  249. this.loading = false
  250. }
  251. },
  252. genQueryData () {
  253. const ret = {
  254. metric_query: [
  255. {
  256. model: {
  257. database: 'telegraf',
  258. measurement: 'usage',
  259. select: [
  260. [
  261. {
  262. type: 'field',
  263. params: [this.queryMode],
  264. },
  265. {
  266. type: 'last',
  267. },
  268. ],
  269. ],
  270. },
  271. },
  272. ],
  273. scope: this.scope,
  274. show_meta: true,
  275. from: `${this.form.fd.from * 24}h`,
  276. interval: '24h',
  277. unit: true,
  278. }
  279. const tags = []
  280. if (this.isAdminMode) {
  281. if (this.dataRangeParams?.scope === 'domain' && this.dataRangeParams?.domain) {
  282. tags.push({
  283. key: 'domain_id',
  284. operator: '=',
  285. value: this.dataRangeParams?.domain,
  286. })
  287. }
  288. if (this.dataRangeParams?.scope === 'project' && this.dataRangeParams?.project) {
  289. tags.push({
  290. key: 'tenant_id',
  291. operator: '=',
  292. value: this.dataRangeParams?.project,
  293. })
  294. }
  295. }
  296. if (this.isDomainMode) {
  297. if (this.dataRangeParams?.scope === 'project' && this.dataRangeParams?.project) {
  298. tags.push({
  299. key: 'tenant_id',
  300. operator: '=',
  301. value: this.dataRangeParams?.project,
  302. })
  303. }
  304. }
  305. if (tags.length) {
  306. ret.metric_query[0].model.tags = tags
  307. }
  308. return ret
  309. },
  310. handleEdit () {
  311. this.visible = true
  312. },
  313. async handleSubmit () {
  314. try {
  315. const values = await this.form.fc.validateFields()
  316. this.form.fd = values
  317. const updateValues = { ...values }
  318. this.$emit('update', this.options.i, updateValues)
  319. this.visible = false
  320. } catch (error) {
  321. throw error
  322. }
  323. },
  324. },
  325. }
  326. </script>