<template> <div v-loading="loading" class="bs-remote-preview" element-loading-text="远程组件加载中..." > <div class="remote-preview-inner-wrap"> <component :is="remoteComponent" :config="config" /> </div> </div> </template> <script> import remoteVueLoader from 'remote-vue-loader' import * as _echarts from 'echarts' import * as g2Plot from '@antv/g2plot' import { getBizComponentInfo } from 'data-room-ui/js/api/bigScreenApi' import innerRemoteComponents, { getRemoteComponents } from 'data-room-ui/RemoteComponents/remoteComponentsList' export default { name: 'BsComponentPreview', props: { vueContent: { type: String, default: '' }, settingContent: { type: String, default: '' } }, computed: { config: { get () { // eslint-disable-next-line prefer-const let option = {} // eslint-disable-next-line prefer-const let setting = [] // eslint-disable-next-line prefer-const, no-unused-vars let title = '' // eslint-disable-next-line prefer-const, no-unused-vars let data = [] // eslint-disable-next-line prefer-const, no-unused-vars let optionHandler = '' const g2Plots = g2Plot const echarts = _echarts // eslint-disable-next-line prefer-const let settingContent = this.settingContentInner?.replaceAll('const ', '') // 去掉 export default及后面代码 settingContent = settingContent?.replace(/export default[\s\S]*/, '') eval(settingContent) return { title, option, setting, echarts, g2Plots, optionHandler } }, set (val) {} } }, watch: { settingContentInner () { this.getRemoteComponent() }, vueContentInner () { this.getRemoteComponent() }, vueContent (newVal) { this.vueContentInner = newVal }, settingContent (newVal) { this.settingContentInner = newVal } }, data () { return { loading: false, remoteComponent: null, vueContentInner: this.vueContent, settingContentInner: this.settingContent?.replaceAll('const ', '') } }, created () { this.viewComponent() }, methods: { async viewComponent () { // 如果有编码,则获取组件信息 if (this.$route.query?.code) { const data = await getBizComponentInfo(this.$route.query?.code) this.vueContentInner = data.vueContent this.settingContentInner = data.settingContent this.config = this.dataFormatting(this.config) this.remoteComponent = remoteVueLoader('data:text/plain,' + encodeURIComponent(this.vueContentInner)) this.loading = false } // 如果有组件的dirName,则获取系统组件信息 if (this.$route.query?.dirName) { const dirName = this.$route.query?.dirName const remoteComponentList = [...innerRemoteComponents, ...getRemoteComponents()] const config = remoteComponentList?.find(item => item.customize.vueSysComponentDirName === dirName) this.config.option = config?.option this.config.title = config?.title const vueFile = config.customize?.vueFile this.remoteComponent = vueFile this.loading = false } }, // 尝试渲染远程文件或远程字符串 getRemoteComponent () { this.loading = true this.config = this.dataFormatting(this.config, { success: false }) this.remoteComponent = remoteVueLoader('data:text/plain,' + encodeURIComponent(this.vueContentInner)) this.loading = false }, /** * 组件的配置 * @returns {Promise<unknown>} */ // 将config.setting的配置转化为option里的配置,这里之所以将转化的方法提出来,是因为在改变维度指标和样式的时候都需要转化 transformSettingToOption (config, type) { let option = null config.setting.forEach(set => { if (set.optionField) { const optionField = set.optionField.split('.') option = config.option optionField.forEach((field, index) => { if (index === optionField.length - 1) { // 数据配置时,必须有值才更新 if ((set.tabName === type && type === 'data' && set.value) || (set.tabName === type && type === 'custom')) { option[field] = set.value } } else { option = option[field] } }) } }) config.option = { ...config.option, ...option } return config }, dataFormatting (config, data) { // 数据返回成功则赋值 if (data?.success) { data = data.data config = this.transformSettingToOption(config, 'data') // 获取到后端返回的数据,有则赋值 // const option = config.option // const setting = config.setting if (config.dataHandler) { try { // 此处函数处理data eval(config.dataHandler) } catch (e) { console.error(e) } } config.option.data = data } else { // 数据返回失败则赋前端的模拟数据 config.option.data = this.plotList?.find(plot => plot.name === config.name)?.option?.data || config.option.data } return config }, // 组件的样式改变,返回改变后的config changeStyle (config) { config = { ...this.config, ...config } config = this.transformSettingToOption(config, 'custom') // 这里定义了option和setting是为了保证在执行eval时,optionHandler、dataHandler里面可能会用到, // const option = config.option // const setting = config.setting if (this.config.optionHandler) { try { // 此处函数处理config eval(this.config.optionHandler) } catch (e) { console.error(e) } } if (this.chart) { this.chart.update(config.option) } this.changeChartConfig(config) return config } } } </script> <style lang="scss" scoped> .bs-remote-preview { position: absolute; min-height: 100%; min-width: 100%; overflow: hidden; box-sizing: border-box; .remote-preview-inner-wrap { position: absolute; height: calc(100% - 40px); width: 100%; overflow: auto; padding: 5px 20px; background-color: var(--bs-background-1); } } </style>