Appearance
加载器集成
本节介绍如何使用 HFLVLoader 渲染通过 HFLVEditor3 生成的页面配置。
关于缩放:当实际尺寸超过设计尺寸,且画布不是默认的“不缩放”设置时,才会进行缩放并重新计算组件尺寸。
基本用法
vue
<template>
<div ref="appContainerRef" class="app-container" :style="containerStyle">
<VEPage :canvas-style="canvasStyle" :component-data="componentData"></VEPage>
<DialogContainer></DialogContainer>
</div>
</template>其中用到了 HFLVLoader 的两个核心组件:
- VEPage:页面渲染组件
- DialogContainer:弹出框容器组件,用于展示运行时配置的弹出框
核心配置
1. 容器设置
| 配置项 | 说明 |
|---|---|
| 尺寸适配 | 容器需要明确宽高,加载器会根据容器尺寸自动缩放画布 |
| 样式隔离 | 推荐使用 .app-container 包裹,避免样式污染 |
2. 画布渲染
| 配置项 | 说明 |
|---|---|
canvas-style | 画布基础样式(尺寸、背景等) |
component-data | 配置组件数组 |
passToParentSwitch | 是否将背景传递给父容器(与画布设置联动) |
js
// 自动计算适配尺寸
import { reCalcCanvasSize } from 'hflvloader';
const { canvasStyleData, componentData } = reCalcCanvasSize(
containerWidth,
containerHeight,
originalCanvasStyle,
originalComponentData
);高级集成
背景配置处理
js
// 根据 passToParentSwitch 处理背景
if (canvasStyle.passToParentSwitch) {
containerStyle.backgroundColor = canvasStyle.backgroundColor;
containerStyle.backgroundImage = `url(${canvasStyle.backgroundImage})`;
} else {
clearBackground();
}容器参数
通过provide提供一个名为VE-ContainerParam的容器参数对象
js
provide('VE-ContainerParam', containerParam);自定义函数提供
js
// 提供自定义函数,在组件的自定义函数调用中使用 helper.customFunction("test",1,2)
provide('veCustomFunctions', {
test: (a, b) => a + b
});TIP
尺寸适配最佳实践
- 监听容器尺寸变化
- 使用
reCalcCanvasSize重新计算渲染参数 - 特别注意:当设计尺寸大于容器时,应以设计尺寸为基准
动态规则数据
此配置项非必设,适用于需要统一维护规则的业务场景。
在 hflvloader 提供的 Store 中,内置了一个动态规则对象,用于存储 Key/Value 结构的规则数据。你可以通过 setRuleData 函数写入规则,业务组件则可根据 Key 获取对应的规则配置,实现自身的差异化逻辑。
典型应用场景:根据数据的时效性动态显示不同颜色值,此时可通过统一的规则对象进行维护,避免在多处硬编码判断逻辑。
js
import {useDataCacheStore} from "hflvloader";
const dataCacheStore = useDataCacheStore();
// 设置规则数据
dataCacheStore.setRuleData(setKey, _rule);完整示例
vue
<template>
<div ref="appContainerRef" class="app-container" :style="containerStyle">
<VEPage :canvas-style="canvasStyle" :component-data="componentData"></VEPage>
<DialogContainer></DialogContainer>
</div>
</template>
<script setup name="PageRenderer">
import {ref, computed, provide, onMounted, onUnmounted, nextTick, onBeforeMount} from 'vue';
import useAppStore from "@/store/modules/app.js";
import {reCalcCanvasSize} from "hflvloader";
import {useRoute} from "vue-router";
import {isNotEmpty} from "@/api/common/util/commutil.js";
import {dynamicRulesLoader} from "@/views/page/DynamicRulesLoader.js";
import {qryPageConfigDataWidthCache} from "@/custom-component/common/Page.js";
const appStore = useAppStore();
const width = ref(1200);
const height = ref(800);
let backgroundColor = null;
const appContainerRef = ref(null);
const appMainSize = computed(() => appStore.appMainSize)
const canvasStyle = ref({});
const componentData = ref([]);
let oldCanvasStyle = {}; // 原始数据
let oldComponentData = []; // 原始数据
const backgroundInfo = ref({
color: null,
image: null,
repeat: null,
size: null,
position: null,
diyStyles: {}
})
const route = useRoute();
const props = defineProps({
pageId: {
type: String,
default: '',
require: true
},
containerParam: {
type: Object,
default: () => ({})
}
})
const containerParam = computed(() => {
return props.containerParam || {};
})
const updateDimensions = () => {
const appMain = document.querySelector('.app-main');
if (appMain) {
width.value = appMain.offsetWidth;
height.value = appMain.offsetHeight;
}
};
const clearBackground = () => {
Object.assign(backgroundInfo.value, {
color: 'transparent',
image: null,
repeat: null,
size: null,
position: null,
diyStyles: {}
})
}
const containerStyle = computed(() => {
let _style = {
width: `${width.value}px`,
height: `${height.value}px`,
'background-color': backgroundInfo.value.color
}
if (backgroundInfo.value.image) {
_style['background-image'] = 'url(' + backgroundInfo.value.image + ')'
}
if (backgroundInfo.value.repeat) {
_style['background-repeat'] = backgroundInfo.value.repeat;
}
if (backgroundInfo.value.size) {
_style['background-size'] = backgroundInfo.value.size;
}
if (backgroundInfo.value.position) {
_style['background-position'] = backgroundInfo.value.position;
}
let _diyStyle = {};
if (backgroundInfo.value.diyStyles && Object.keys(backgroundInfo.value.diyStyles).length > 0) {
_diyStyle = backgroundInfo.value.diyStyles || {};
}
let style = {..._style, ..._diyStyle};
if (isNotEmpty(style.background) && !isNotEmpty(style['background-color'])) {
delete style['background-color'];
}
// 没有设置背景色时
if (!(isNotEmpty(style.background) || isNotEmpty(style['background-image']) || isNotEmpty(style['background-color']))) {
style['background-color'] = 'transparent !important';
}
return style;
});
watch(appMainSize, (newVal, oldVal) => {
nextTick(() => {
console.log(newVal.width + ' ' + newVal.height);
updateDimensions();
reCalcSizeData(); // 重新计算尺寸数据
})
})
const reCalcSizeData = () => {
// 如果设计尺寸比较大 就以设计尺寸为准
if (oldCanvasStyle.width > width.value) {
width.value = oldCanvasStyle.width;
}
if (oldCanvasStyle.height > height.value) {
height.value = oldCanvasStyle.height;
}
let _calcResult = reCalcCanvasSize(width.value, height.value, oldCanvasStyle, oldComponentData);
if (_calcResult) {
let _canvasStyleData = _calcResult.canvasStyleData || {};
if (oldCanvasStyle.passToParentSwitch) {
_canvasStyleData.backgroundColor = null;
_canvasStyleData.backgroundImage = null;
_canvasStyleData.backgroundSize = null;
_canvasStyleData.backgroundPosition = null;
_canvasStyleData.diyStyles = {};
}
canvasStyle.value = _canvasStyleData;
componentData.value = _calcResult.componentData || [];
} else {
canvasStyle.value = {};
componentData.value = [];
}
}
onBeforeMount(() => {
})
onMounted(() => {
loadPage();
nextTick(() => {
updateDimensions();
reCalcSizeData(); // 重新计算尺寸数据
});
dynamicRulesLoader.load();
});
const loadPage = () => {
if (isNotEmpty(props.pageId)) {
let qryParam = {};
qryParam.id = props.pageId;
qryPageConfigDataWidthCache(qryParam).then(function (res2) {
if (res2 && res2.data && res2.data.pageData) {
let pageData = JSON.parse(res2.data.pageData);
if (pageData) {
oldCanvasStyle = pageData.canvasStyle || {};
oldComponentData = pageData.componentData || [];
} else {
oldCanvasStyle = {};
oldComponentData = [];
}
if (oldCanvasStyle.passToParentSwitch) {
backgroundInfo.value.color = oldCanvasStyle.backgroundColor;
backgroundInfo.value.image = oldCanvasStyle.backgroundImage;
backgroundInfo.value.size = oldCanvasStyle.backgroundSize;
backgroundInfo.value.position = oldCanvasStyle.backgroundPosition;
backgroundInfo.value.diyStyles = oldCanvasStyle.diyStyles;
} else {
clearBackground();
}
reCalcSizeData(); // 重新计算尺寸数据
}
});
}
}
watch(() => props.pageId, (val) => {
if (val) {
loadPage();
}
}, {immediate: true})
onUnmounted(() => {
});
provide('VE-ContainerParam', containerParam);
provide('veCustomFunctions',{
test:(a,b)=>{
return a+b;
}
});
</script>
<style lang="scss" scoped>
.app-container {
padding: 0px;
display: flex;
align-items: center;
margin: auto;
overflow: auto;
.canvas {
position: relative;
margin: auto;
}
}
</style>