index.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <div
  3. v-loading="loading"
  4. class="bs-remote-wrap"
  5. element-loading-text="远程组件加载中..."
  6. >
  7. <component
  8. :is="remoteComponent"
  9. :config="config"
  10. @linkage="linkEvent"
  11. />
  12. </div>
  13. </template>
  14. <script>
  15. import linkageMixins from 'packages/js/mixins/linkageMixins'
  16. import commonMixins from 'packages/js/mixins/commonMixins'
  17. import remoteVueLoader from 'remote-vue2-loader'
  18. import { mapMutations, mapState } from 'vuex'
  19. import _ from 'lodash'
  20. import innerRemoteComponents, { getRemoteComponents } from 'packages/RemoteComponents/remoteComponentsList'
  21. import { getBizComponentInfo } from 'packages/js/api/bigScreenApi'
  22. export default {
  23. name: 'LcdpRemoteComponent',
  24. mixins: [linkageMixins, commonMixins],
  25. props: {
  26. config: {
  27. type: Object,
  28. default: () => ({})
  29. }
  30. },
  31. computed: {
  32. ...mapState('bigScreen', {
  33. pageInfo: state => state.pageInfo,
  34. customTheme: state => state.pageInfo.pageConfig.customTheme
  35. }),
  36. isPreview () {
  37. return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
  38. }
  39. },
  40. data () {
  41. return {
  42. loading: false,
  43. remoteComponent: null
  44. }
  45. },
  46. created () {
  47. this.getRemoteComponent()
  48. },
  49. mounted () {
  50. this.chartInit()
  51. },
  52. methods: {
  53. ...mapMutations('bigScreen', ['changeChartConfig']),
  54. // 尝试渲染远程文件或远程字符串
  55. getRemoteComponent () {
  56. this.loading = true
  57. // 1. 系统组件 通过配置获取
  58. if (this.config.customize.vueSysComponentDirName) {
  59. const remoteComponentList = [...innerRemoteComponents, ...getRemoteComponents()]
  60. // 获得系统组件最新的配置, 同步
  61. const config = remoteComponentList?.find(item => item.customize.vueSysComponentDirName === this.config.customize.vueSysComponentDirName)
  62. const vueFile = config?.customize?.vueFile
  63. const option = config?.option
  64. const setting = config?.setting
  65. // 同步配置
  66. this.synchConfig(option, setting)
  67. this.remoteComponent = vueFile
  68. this.loading = false
  69. return
  70. }
  71. // 2. 业务组件 通过请求获取
  72. if (this.config.customize.vueBizComponentCode) {
  73. getBizComponentInfo(this.config.customize.vueBizComponentCode).then(data => {
  74. const vueContent = data.vueContent
  75. const settingContent = data.settingContent
  76. if (!this.config?.option?.data) {
  77. this.resolveStrSetting(settingContent)
  78. this.config = this.dataFormatting(this.config, { success: false })
  79. }
  80. this.remoteComponent = remoteVueLoader('data:text/plain,' + encodeURIComponent(vueContent))
  81. }).finally(() => {
  82. this.loading = false
  83. })
  84. }
  85. },
  86. chartInit () {
  87. // key和code相等,说明是一进来刷新,调用/chart/data/list
  88. if (this.config.code === this.config.key || this.isPreview) {
  89. // 再根据数据更新组件
  90. this.updateChart()
  91. }
  92. },
  93. linkEvent (formData) {
  94. this.linkage(formData)
  95. },
  96. /**
  97. * 处理当前组件的字符串配置
  98. */
  99. resolveStrSetting (settingContent) {
  100. // eslint-disable-next-line prefer-const
  101. let option = {}
  102. // eslint-disable-next-line prefer-const
  103. let setting = []
  104. // eslint-disable-next-line prefer-const, no-unused-vars
  105. let title = []
  106. // eslint-disable-next-line prefer-const, no-unused-vars
  107. let data = []
  108. // eslint-disable-next-line prefer-const
  109. settingContent = settingContent.replaceAll('const ', '')
  110. // 去掉 export default及后面代码
  111. settingContent = settingContent.replace(/export default[\s\S]*/, '')
  112. eval(settingContent)
  113. this.config.option = {
  114. ...this.config.option,
  115. ...option
  116. }
  117. this.config.setting = setting
  118. return {
  119. option,
  120. setting
  121. }
  122. },
  123. /**
  124. * @description: 只更新数据
  125. */
  126. updateData () {
  127. this.updateChart()
  128. },
  129. /**
  130. * 更新组件
  131. */
  132. updateChart () {
  133. if (this.isPreview) {
  134. this.changeDataByCode()
  135. } else {
  136. this.changeData(this.config)
  137. }
  138. },
  139. /**
  140. * 组件的配置
  141. * @returns {Promise<unknown>}
  142. */
  143. dataFormatting (config, data) {
  144. config = _.cloneDeep(config)
  145. // 遍历config.setting,将config.setting中的值赋值给config.option中对应的optionField
  146. config.setting.forEach(set => {
  147. if (set.optionField) {
  148. const optionField = set.optionField.split('.')
  149. let option = config.option
  150. optionField.forEach((field, index) => {
  151. if (index === optionField.length - 1) {
  152. // 数据配置时,必须有值才更新
  153. if ((set.tabName === 'data' && set.value) || set.tabName === 'custom') {
  154. option[field] = set.value
  155. }
  156. } else {
  157. option = option[field]
  158. }
  159. })
  160. }
  161. })
  162. // eslint-disable-next-line no-unused-vars
  163. const option = config.option
  164. // eslint-disable-next-line no-unused-vars
  165. const setting = config.setting
  166. if (data?.success) {
  167. data = data.data
  168. config.option = option
  169. config.option.data = data
  170. }
  171. return config
  172. },
  173. // 同步配置
  174. synchConfig (option, setting) {
  175. // 对比this.config.setting 和 setting,进行合并,数据以this.config.option对象的value为准
  176. // TODO
  177. }
  178. }
  179. }
  180. </script>
  181. <style lang="scss" scoped>
  182. .bs-remote-wrap {
  183. width: 100%;
  184. }
  185. </style>