Skip to content

加载器集成

本节介绍如何使用 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

尺寸适配最佳实践

  1. 监听容器尺寸变化
  2. 使用 reCalcCanvasSize 重新计算渲染参数
  3. 特别注意:当设计尺寸大于容器时,应以设计尺寸为基准

动态规则数据

此配置项非必设,适用于需要统一维护规则的业务场景。

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>

基于 VitePress 构建