index.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <template>
  2. <div class="d-flex flex-column flex-fill h-100">
  3. <template v-if="optionsLoaded">
  4. <div style="padding-left: 5px;">
  5. <dashboard-header
  6. :tabs="allOptions"
  7. :current="currentOption"
  8. :data="dashboard"
  9. :check-options-created="checkCustomOptionsCreated"
  10. :init-options="initCustomOptions"
  11. :is-default-option="isDefault"
  12. :dataRangeParams="dataRangeParams"
  13. @select="handleCurrentOptionSelect"
  14. @update-options="updateOptions"
  15. @updateDataRange="updateDataRange"
  16. @refresh="refresh" />
  17. </div>
  18. <div class="flex-fill position-relative">
  19. <div class="position-absolute" style="top: 0; left: 0; right: 0; bottom: 0;">
  20. <dashboard-content
  21. ref="content"
  22. :key="dashboardContentKey"
  23. :data="dashboard"
  24. :dataRangeParams="dataRangeParams" />
  25. </div>
  26. </div>
  27. </template>
  28. <template v-else>
  29. <div class="pt-4 pb-4 text-center"><a-spin :tip="$t('dashboard.text_117')" /></div>
  30. </template>
  31. </div>
  32. </template>
  33. <script>
  34. import * as R from 'ramda'
  35. import { mapGetters, mapState } from 'vuex'
  36. import store from '@/store'
  37. import storage from '@/utils/storage'
  38. import { addClass, removeClass, hasClass } from '@/utils/dom'
  39. import publicDefaultConfig from './config/public-default'
  40. import aiDefaultConfig from './config/ai-default'
  41. import defaultConfig from './config/default'
  42. import DashboardHeader from './components/Header'
  43. import DashboardContent from './components/Content'
  44. // option
  45. // [{ id: 'xxx', name: 'xxx', index: 2, hidden: true, type: 'default' }]
  46. export default {
  47. name: 'Dashboard',
  48. components: {
  49. DashboardHeader,
  50. DashboardContent,
  51. },
  52. data () {
  53. const isPrivate = process.env.VUE_APP_IS_PRIVATE
  54. const isAi = store.getters?.globalSetting?.value?.productVersion === 'AI'
  55. return {
  56. isPrivate,
  57. isAi,
  58. loading: false,
  59. // 面板配置是否加载完毕
  60. optionsLoaded: false,
  61. // 默认面板配置 -> [{ id: 'xxx', name: 'xxx' }]
  62. defaultOptions: isPrivate && !store.getters.isSysCE ? defaultConfig[this.$store.getters.scope].options : (isAi ? aiDefaultConfig[this.$store.getters.scope].options : publicDefaultConfig[this.$store.getters.scope].options),
  63. // 自定义面板配置
  64. customOptions: [],
  65. // 当前面板配置 -> { id: 'xxx', name: 'xxx' }
  66. currentOption: {},
  67. // 面板卡片的配置 Object, Array;
  68. dashboard: {},
  69. // 切换/重置面板数据后递增,强制重建 Content,避免 v-for 复用子组件导致卡片仍显示旧 params
  70. dashboardContentKey: 0,
  71. dataRangeParams: storage.get('__oc_dashboard_data_range__') || {
  72. scope: this.$store.getters.scope,
  73. domain: '',
  74. project: '',
  75. },
  76. }
  77. },
  78. computed: {
  79. ...mapGetters(['scope', 'globalConfig']),
  80. ...mapState('setting', {
  81. l2MenuVisible: state => state.l2MenuVisible,
  82. }),
  83. // 当前选择面板是否为默认面板
  84. isDefault () {
  85. return this.currentOption.id && this.currentOption.id === `dashboard-${this.scope}-default`
  86. },
  87. // 选择的面板信息存储到storage的key
  88. optionStorageKey () {
  89. return `__oc_dashboard_${this.scope}__`
  90. },
  91. // custom options的配置key
  92. optionsConfigKey () {
  93. return `dashboard_${this.scope}`
  94. },
  95. // 所有的面板配置
  96. allOptions () {
  97. /* const customOptions = this.customOptions.map((v) => {
  98. if (!v.name) {
  99. v.name = this.$t('dashboard.text_121')
  100. }
  101. return v
  102. }) */
  103. return R.unionWith(R.eqBy(R.prop('id')), this.customOptions, this.defaultOptions)
  104. },
  105. },
  106. watch: {
  107. l2MenuVisible (val) {
  108. this.addAppPageClass()
  109. },
  110. dataRangeParams: {
  111. handler (val) {
  112. if (val.scope) {
  113. if ((this.$store.getters.isProjectMode && (val.scope === 'domain' || val.scope === 'system')) || (this.$store.getters.isDomainMode && val.scope === 'system')) {
  114. this.dataRangeParams.scope = this.$store.getters.scope
  115. this.dataRangeParams.domain = ''
  116. this.dataRangeParams.project = ''
  117. }
  118. }
  119. },
  120. deep: true,
  121. immediate: true,
  122. },
  123. },
  124. beforeDestroy () {
  125. this.pm = null
  126. removeClass(this.$appPage, this.appPageAddedClass.join(' '))
  127. this.$appPage = null
  128. },
  129. async created () {
  130. this.pm = new this.$Manager('parameters', 'v1')
  131. try {
  132. this.customOptions = await this.getCustomOptions()
  133. } catch (error) {
  134. this.customOptions = []
  135. }
  136. // 获取以往选择的面板
  137. let selected = storage.get(this.optionStorageKey)
  138. const matched = selected && this.allOptions.find(obj => obj.id === selected.id)
  139. if (!matched) {
  140. selected = this.allOptions[0]
  141. }
  142. this.handleCurrentOptionSelect(selected)
  143. },
  144. mounted () {
  145. this.addAppPageClass()
  146. },
  147. methods: {
  148. updateDataRange (params) {
  149. this.dataRangeParams = params
  150. },
  151. addAppPageClass () {
  152. if (!this.$appPage) this.$appPage = document.getElementById('app-page')
  153. if (!this.$appPage) return
  154. const toBeAddedClass = ['h-100', 'd-flex', 'flex-column', 'mb-0']
  155. this.appPageAddedClass = []
  156. for (let i = 0, len = toBeAddedClass.length; i < len; i++) {
  157. if (!hasClass(this.$appPage, toBeAddedClass[i])) {
  158. addClass(this.$appPage, toBeAddedClass[i])
  159. this.appPageAddedClass.push(toBeAddedClass[i])
  160. }
  161. }
  162. },
  163. // 选择面板
  164. async handleCurrentOptionSelect (option) {
  165. this.currentOption = option
  166. // 按scope维度记录选择的面板信息
  167. storage.set(this.optionStorageKey, option)
  168. const dashboard = await this.getDashboard()
  169. this.dashboard = dashboard
  170. this.dashboardContentKey += 1
  171. },
  172. // 获取自定义面板配置
  173. async getCustomOptions () {
  174. this.loading = true
  175. try {
  176. const response = await this.pm.get({ id: this.optionsConfigKey })
  177. if (response.data && response.data.value) {
  178. return response.data.value
  179. }
  180. return []
  181. } catch (error) {
  182. throw error
  183. } finally {
  184. this.loading = false
  185. this.optionsLoaded = true
  186. }
  187. },
  188. // 获取面板卡片配置
  189. async getDashboard () {
  190. // 当前选择的面板id
  191. const id = this.currentOption.id
  192. this.loading = true
  193. try {
  194. const response = await this.pm.get({ id })
  195. if (response.data && response.data.value) {
  196. return response.data.value
  197. }
  198. return {}
  199. } catch (error) {
  200. if (error.isAxiosError && error.response && error.response.status === 404 && this.isDefault) {
  201. // not found system default dashboard, reinit one
  202. const shareConfig = await this.initWidgetParamter()
  203. const config = this.isPrivate && !this.$store.getters.isSysCE ? (shareConfig || defaultConfig[this.scope][id]) : (this.isAi ? aiDefaultConfig[this.scope][id] : publicDefaultConfig[this.scope][id])
  204. if (!this.globalConfig.enable_quota_check) {
  205. // remove quota widgets
  206. for (var i = 0; i < config.length; i++) {
  207. if (config[i].layout.component === 'Quota' || config[i].layout.component === 'ProjectQuota') {
  208. config.splice(i, 1)
  209. i--
  210. }
  211. }
  212. }
  213. await this.pm.create({
  214. data: {
  215. name: id,
  216. value: config,
  217. },
  218. })
  219. return config
  220. }
  221. throw error
  222. } finally {
  223. this.loading = false
  224. }
  225. },
  226. // 更新面板配置
  227. async updateOptions (newOptions) {
  228. const options = [...this.allOptions]
  229. try {
  230. this.customOptions = newOptions
  231. const response = await this.pm.update({
  232. id: this.optionsConfigKey,
  233. data: {
  234. value: newOptions,
  235. },
  236. })
  237. this.customOptions = response.data.value || []
  238. } catch (error) {
  239. this.customOptions = options
  240. throw error
  241. }
  242. },
  243. // 检查是否已经创建过控制面板的配置
  244. checkCustomOptionsCreated () {
  245. return new Promise((resolve, reject) => {
  246. this.pm.get({ id: this.optionsConfigKey }).then(() => {
  247. resolve(true)
  248. }).catch(error => {
  249. if (error.response && error.response.status === 404) {
  250. resolve(false)
  251. } else {
  252. reject(error)
  253. }
  254. })
  255. })
  256. },
  257. // 初始化控制面板配置
  258. initCustomOptions () {
  259. return this.pm.create({
  260. data: {
  261. name: this.optionsConfigKey,
  262. value: [],
  263. },
  264. })
  265. },
  266. refresh () {
  267. this.$refs.content.refresh()
  268. },
  269. async initWidgetParamter () {
  270. try {
  271. const response = await this.$store.dispatch('widgetSetting/getFetchWidgetSetting')
  272. if (response?.value && response.value[`dashboard-${this.scope}`]) {
  273. return response.value[`dashboard-${this.scope}`]
  274. }
  275. } catch (error) {
  276. console.log(error)
  277. }
  278. },
  279. },
  280. }
  281. </script>