Appearance
大屏组件开发
1. 大屏自定义渲染组件开发
// 参考案例
<template>
<div class="h-full w-full bg-cover bg-no-repeat overflow-hidden" :style="getStyle">
<div ref="barCommonRef" class="relative h-full w-full"></div>
</div>
</template>
<script setup lang="ts">
import { DesignApi } from '@/api'
import { cloneDeep, get } from 'lodash-es'
import { onBeforeMount, ref, inject, computed, onMounted, nextTick, watch, onUnmounted } from 'vue'
import { getNodeById, newFunctionHandle } from '@/components/screen-component/utils/commonUtils'
import { formatDatasetAxis, compareData, buildEventParam } from '@/components/screen-component/utils/formatDataset'
import * as echarts from 'echarts'
import 'echarts-liquidfill'
import { useResizeObserver } from '@vueuse/core'
const yAxis = ref([])
const aliasList = ref([])
const barCommonRef = ref(null)
const chartRender = ref()
const getNode = inject('getNode') as Function
const getGraph = inject('getGraph') as Function
const emits = defineEmits(['screenChange'])
const nodeData = computed(() => getNode && getNode() && getNode())
const nodeInfo = ref<Any>({})
const nodeId = ref('')
const getNodeData = () => {
nodeInfo.value = getNode().getData()
nodeId.value = getNode().id
}
useResizeObserver(barCommonRef, () => {
chartRender.value?.resize()
})
watch(
() => nodeInfo.value,
() => {
if (
!compareData(yAxis.value, nodeInfo.value?.dataOption?.yAxis) ||
!compareData(aliasList.value, nodeInfo.value?.dataOption?.aliasList)
) {
initChart()
} else {
setOption()
}
},
{
deep: true,
}
)
const renderChart = (option: Any) => {
// 渲染前处理数据
const rendChart = nodeInfo.value?.eventOption?.eventList.find((event) => event.name === 'beforeRender')
if (rendChart) {
// 处理数据逻辑
// console.log('beforeRender====>', option)
}
return option
}
const setOption = () => {
if (!chartRender.value) {
return
}
const { renderer, ...cloneOption } = nodeInfo.value.option || {}
const option = formatDatasetAxis(cloneOption, nodeInfo.value.dataOption)
const renderOption = renderChart(option)
chartRender.value.setOption({
...(renderOption || option),
})
}
const initChart = () => {
if (chartRender.value) {
chartRender.value.dispose()
}
if (!barCommonRef.value) {
return
}
const { renderer, ...cloneOption } = nodeInfo.value.option || {}
yAxis.value = cloneDeep(nodeInfo.value.dataOption.yAxis)
aliasList.value = cloneDeep(nodeInfo.value.dataOption.aliasList)
const eventList = nodeInfo.value?.eventOption?.eventList || []
const chart = echarts.init(barCommonRef.value, null, renderer)
const option = formatDatasetAxis(cloneOption, nodeInfo.value.dataOption)
const renderOption = renderChart(option)
chart.setOption(
{
...(renderOption || option),
},
true
)
eventList
?.filter((e) => e.name !== 'beforeRender')
.forEach((event) => {
chart.on(event.name, (params) => {
updateNodeData(event, params)
})
})
// 数据联动逻辑处理
if (
nodeInfo.value?.dataOption?.linkageDataList?.length > 0 ||
nodeInfo.value?.option?.toScreen?.name ||
nodeInfo.value.option?.toScreen?.name
) {
chart.on('click', (params) => {
const event = buildEventParam(nodeInfo.value?.dataOption?.linkageDataList)
updateNodeData(event, params)
})
}
chartRender.value = chart
}
// 背景颜色
const getStyle = computed(() => {
let str = ''
// 背景色
if (nodeInfo.value.option.renderBackgroundColor) {
str += `background-color: ${nodeInfo.value.option.renderBackgroundColor};`
}
// 背景图片
if (nodeInfo.value.option?.renderBackgroundUrl) {
str += `background-image: url('/api/magus-screen/screen/file/download?path=${nodeInfo.value.option.renderBackgroundUrl}');`
}
if (nodeInfo.value.option?.renderOpacity) {
str += `opacity: ${nodeInfo.value.option.renderOpacity};`
}
return str
})
const size = ref({})
const sizeChange = () => {
size.value = getNode().getSize()
}
const readonly = computed(() => nodeInfo.value.readonly)
const setDataOption = (nodeData: Any, params: Any, assign: Any) => {
const dataOption = cloneDeep(nodeData?.dataOption)
let flag = false
const dataSearchItem = dataOption.dataSetSearch.find((item) => item.name === assign.searchName)
// 最后设置的值
const setValue = get(params, assign.key)
if (dataSearchItem && dataSearchItem.value !== setValue) {
dataSearchItem.value = setValue
flag = true
}
return { dataOption, flag }
}
const successCallBack = (data) => {
console.log('successCallBack', data)
}
const errorCallBack = (e) => {
console.log('errorCallBack', e)
}
const toScreenInfo = (params: AnyObject) => {
const toScreen = nodeInfo.value?.option?.toScreen || {}
const data = toScreen?.data?.map((item) => {
return {
...item,
field: get(params, item.field),
}
})
emits('screenChange', { ...toScreen, data })
}
// 处理事件
const updateNodeData = ({ event }, params) => {
if (readonly.value) return
toScreenInfo(params)
event.forEach((eve: Any) => {
// 变更数据
if (eve.type === 'assign') {
if (Array.isArray(eve.assign)) {
eve?.assign.forEach((item) => {
const node = getNodeById(getGraph(), item.dataId as string)
if (node) {
const nodeData = node.getData()
const { dataOption, flag } = setDataOption(nodeData, params, item)
if (flag) {
node.updateData({ ...nodeData, dataOption })
}
}
})
} else {
const node = getNodeById(getGraph(), eve?.assign?.dataId as string)
if (node) {
const nodeData = node.getData()
const { dataOption, flag } = setDataOption(nodeData, params, eve.assign)
if (flag) {
node.updateData({ ...nodeData, dataOption })
}
}
}
} else {
const res = {}
newFunctionHandle(params, res, eve.script, true, successCallBack, errorCallBack)
}
})
}
onMounted(() => {
nodeData.value.on('change:data', ({ current }) => {
getNodeData()
})
nodeData.value.on('change:size', () => {
sizeChange()
})
initChart()
})
onBeforeMount(() => {
getNodeData()
sizeChange()
})
</script>
- 所有从节点数据中进行数据绑定请使用计算属性
// 例如案例中的样式
const getStyle = computed(() => {
let str = ''
// 背景色
if (nodeInfo.value.option.renderBackgroundColor) {
str += `background-color: ${nodeInfo.value.option.renderBackgroundColor};`
}
// 背景图片
if (nodeInfo.value.option?.renderBackgroundUrl) {
str += `background-image: url('/api/magus-screen/screen/file/download?path=${nodeInfo.value.option.renderBackgroundUrl}');`
}
if (nodeInfo.value.option?.renderOpacity) {
str += `opacity: ${nodeInfo.value.option.renderOpacity};`
}
return str
})
- 获取画布实例
const getGraph = inject('getGraph') as Function
const graph = getGraph()
- 获取节点实例与实例数据
const getNode = inject('getNode') as Function
const node = getNode() // 节点实例
const nodeData = node.getData()
// 如果要使用内置数据集的数据,请取 nodeData.dataOption.data
const data = computed(() => nodeData?.dataOption?.data || [])
- 触发大屏跳转的方法并携带参数
emits('screenChange', {
name: '', // 跳转的大屏名称
data: [{
filed: '', // 传过去的值
target: [['id', 'key'], ['id', 'key']], // 批量需要修改的组件节点的id和对应查询参数(data.dataOption.dataSetSearch)的key
}]
})
2. 大屏自定义渲染组件属性面板开发
- 获取画布实例
const graph = useGraphInstance()
- 获取节点实例与实例数据
const node = useCurrentNode()
const data = node.value?.getData()
// 如果要使用内置数据集的数据,data的结构只有有这几项
data: {
dataOption: {
type: '', //进行数据集查询的类型标识 sql,api,lot
data: [], // 数据存储位置
dataSetSearch: [{name: '', defaultValue: ''}], //数据查询参数
}
}
- 父节点传递的数据集列表
const props = defineProps<{ dataSourceList: Array}>()
3.打包文件配置
import propComp from './config.vue'
import webCom from './bar.vue'
import {
option,
defaultSize,
dataOption,
eventOption,
} from '@/components/screen-component/Charts/Bars/BarCommon/config'
const comData = {
option,
defaultSize,
dataOption,
eventOption,
}
export { webCom, comData, propComp }
// webCom 打包文件抛出来的渲染组件
// propComp 打包文件抛出来的属性面板组件 (当所有的组件都在组件内部完成是可没有)
// comData 组件的默认值