You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
9.3 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div
v-loading="loading"
class="bs-remote-wrap"
element-loading-text="..."
>
<component
:is="remoteComponent"
:ref="'remoteComponent'+config.code"
:config="config"
@linkage="linkEvent"
/>
</div>
</template>
<script>
import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
import commonMixins from 'data-room-ui/js/mixins/commonMixins'
import remoteVueLoader from 'remote-vue-loader'
import * as g2Plot from '@antv/g2plot'
import * as _echarts from 'echarts'
import { mapMutations, mapState } from 'vuex'
import innerRemoteComponents, { getRemoteComponents } from 'data-room-ui/RemoteComponents/remoteComponentsList'
import { getBizComponentInfo } from 'data-room-ui/js/api/bigScreenApi'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
import cloneDeep from 'lodash/cloneDeep'
export default {
name: 'LcdpRemoteComponent',
mixins: [linkageMixins, commonMixins],
props: {
config: {
type: Object,
default: () => ({})
}
},
computed: {
...mapState('bigScreen', {
pageInfo: state => state.pageInfo,
customTheme: state => state.pageInfo.pageConfig.customTheme
}),
isPreview () {
return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
}
},
data () {
return {
loading: false,
remoteComponent: null,
componentInstance: null
}
},
watch: {
},
created () {
this.getRemoteComponent()
},
mounted () {
this.chartInit()
},
methods: {
changeData (config, filterList) {
// 调用混入中的方法
let promise = this.$options.mixins[1].methods.changeData.call(this, config, filterList)
return promise.then(res => {
config = res
// 当前组件的方法调用
let remote = this.$refs['remoteComponent'+config.code]
if (remote && remote.changeData){
remote.changeData(config, filterList)
}
return config
})
},
...mapMutations('bigScreen', ['changeChartConfig']),
// 尝试渲染远程文件或远程字符串
getRemoteComponent () {
this.loading = true
// 1. 系统组件 通过配置获取
if (this.config.customize.vueSysComponentDirName) {
const remoteComponentList = [...innerRemoteComponents, ...getRemoteComponents()]
// 获得系统组件最新的配置, 同步
const config = remoteComponentList?.find(item => item.customize.vueSysComponentDirName === this.config.customize.vueSysComponentDirName)
const vueFile = config?.customize?.vueFile
const option = config?.option
const setting = config?.setting
// 同步配置
this.synchConfig(option, setting)
this.remoteComponent = vueFile
this.loading = false
return
}
// 2. 业务组件 通过请求获取
if (this.config.customize.vueBizComponentCode) {
getBizComponentInfo(this.config.customize.vueBizComponentCode).then(data => {
const vueContent = data.vueContent
const settingContent = data.settingContent
if (!this.config?.option?.data) {
this.resolveStrSetting(settingContent)
this.config = this.dataFormatting(this.config, { success: false })
}
this.remoteComponent = remoteVueLoader('data:text/plain,' + encodeURIComponent(vueContent))
}).finally(() => {
this.loading = false
})
}
},
async chartInit () {
let config = this.config
config.g2Plots = g2Plot
config.echarts = _echarts
this.componentInstance = this.$refs['remoteComponent' + config.code]
// key和code相等说明是一进来刷新调用list接口
if (this.config.code === this.config.key || this.isPreview) {
// 改变样式
config = this.changeStyle(config) ? this.changeStyle(config) : config
// 改变数据
config = await this.changeDataByCode(config)
} else {
// 否则说明是更新这里的更新只指更新数据改变样式时是直接调取changeStyle方法因为更新数据会改变key,调用chart接口
// TODO 直接改变prop控制台会报错待优化
try {
this.config = await this.changeData(config)
} catch (e) {
console.error(e)
}
}
},
linkEvent (formData) {
this.linkage(formData)
},
/**
* 处理当前组件的字符串配置
*/
resolveStrSetting (settingContent) {
// 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 = ''
// eslint-disable-next-line prefer-const
settingContent = settingContent.replaceAll('const ', '')
// 去掉 export default及后面代码
settingContent = settingContent.replace(/export default[\s\S]*/, '')
eval(settingContent)
// this.config.option = {
// ...this.config.option,
// ...option
// }
this.config.option = {
...option,
...this.config.option
}
this.config.setting = setting
// 样式改变时更新主题配置
// this.config.theme = settingToTheme(cloneDeep(this.config), this.customTheme)
// 获取到setting后将其转化为theme
// this.config.theme = settingToTheme(this.config, this.customTheme)
return {
option,
setting,
optionHandler
}
},
/**
* 组件的配置
* @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 {
// 如果没有这个属性,则创建该属性,并赋值为空对值
if (!option[field]) {
option[field] = {}
}
option = option[field]
}
})
}
})
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, isUpdateTheme) {
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 (!isUpdateTheme) {
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
}
if (this.chart) {
this.chart.update(config.option)
}
this.changeChartConfig(config)
if (config.code === this.activeCode) {
this.changeActiveItemConfig(config)
}
// this.changeChartConfig(config)
// 在this.$refs['remoteComponent' + config.code]这个实例里判断是否存在customStyle方法如果存在则执行
const componentInstance = this.$refs['remoteComponent' + config.code]
if (componentInstance && componentInstance.$options.methods && componentInstance.$options.methods.customStyle) {
// 调用 customStyle 方法
this.$refs['remoteComponent' + config.code]?.customStyle(config)
}
return config
},
// 同步配置
synchConfig (option, setting) {
// 对比this.config.setting 和 setting进行合并数据以this.config.option对象的value为准
// TODO
}
}
}
</script>
<style lang="scss" scoped>
.bs-remote-wrap {
width: 100%;
height: 100%;
}
</style>