大屏项目开发技术选型(适配,布局,代码组织)

290 阅读1分钟

缩放方案

最终选的是zoom缩放方案,参考这篇文章避坑,使用Hooks实现

juejin.cn/post/713341…

JS内容:

import { onBeforeUnmount, onMounted, ref } from 'vue';
import { DESIGN_HEIGHT, DESIGN_WIDTH } from '@/views/DataScreen/config/ScreenDisignSize';

export const useScreenAutoSize = () => {
  const screenWidth = ref(window.innerWidth);
  const screenHeight = ref(window.innerHeight);

  onMounted(async () => {
    window.addEventListener('resize', handleResize);

    // 初始计算一次缩放比例
    calculateZoomLevel();
  });

  onBeforeUnmount(() => {
    window.removeEventListener('resize', handleResize);
  });

  // 监听窗口大小变化事件
  function handleResize() {
    console.log('handleResize', innerContainerStyle.value);

    screenWidth.value = window.innerWidth;
    screenHeight.value = window.innerHeight;
    calculateZoomLevel();
  }

  const innerContainerStyle = ref({
    width: `${DESIGN_WIDTH}px` // 中间容器的宽度固定为设计稿宽度
  });

  // 计算缩放比例
  function calculateZoomLevel() {
    const widthScale = screenWidth.value / DESIGN_WIDTH;
    const heightScale = screenHeight.value / DESIGN_HEIGHT;
    innerContainerStyle.value.zoom = Math.min(widthScale, heightScale);
  }

  return {
    innerContainerStyle
  };
};

模版内容:

<template>
  <!--  outer-container 用来实现页面内容的水平和垂直居中 -->
  <div class="outer-container">
    <!--    inner-container 用来实现宽高等比例缩放 -->
    <div class="inner-container" :style="innerContainerStyle">
      <div class="content">
        <!-- 在这里放置你的大屏内容 -->


<div class="body">
  <a-row style="height: 100%" gutter="2">
    <a-col :span="6">
      <div class="left-content-box">
       
      </div>
    </a-col>
    <a-col :span="12">
      <!--              screen-detail-modal这个ID是ScreenDetailModalComponent组件中通过teleport挂载用 -->
      <div id="screen-detail-modal"></div>
    </a-col>
    <a-col :span="6">
      <div class="right-content-box">
      
      </div>
    </a-col>
  </a-row>
</div>

       
      </div>
    </div>
  </div>
</template>

CSS内容:

.outer-container {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
  height: 100vh; /* 占满整个视口高度 */
  background-color: rgba(0, 16, 22, 1); /* 页面两侧留白的背景色 */

  .inner-container {
    position: relative;
    width: 2000px; /* 中间容器的宽度固定为设计稿宽度 */
    height: 1119px; /* 中间容器的高度固定为设计稿高度 */
    background-color: rgba(0, 16, 22, 1);

    .content {
      background-image: url('@/assets/data-screen/模型 背景.png');
      height: 100%;
      width: 100%;

      .body {
        margin: 16px 16px 0 16px;
        height: 1000px;
        display: grid;

        #screen-detail-modal {
          height: 100%;
          width: 100%;
        }

        .right-content-box {
          height: 100%;
          display: grid;
          grid-template-rows: 2fr 1fr 1fr;
        }

        .left-content-box {
          height: 100%;
          display: grid;
          grid-template-rows: 1fr 1fr 1fr 1fr;
        }
      }
    }
  }
}

布局方案

因为大屏整体是左侧+中间Modal+右侧的布局,又大多数都是等分的块盒,使用场景对浏览器兼容性要求不高,且之前没有尝试过grid布局,所以选用grid布局。一用惊为天人- . -

juejin.cn/post/685457…

参考这一篇就够了。顺便找到了这样一张图:

image.png

记录一个样式难点:

有两行元素,高度不定,我想实现这两行自适应。用grid布局试了很久才成功。

grid-template-rows: repeat(auto-fit, minmax(200px, min-content));

Vue3挂载到页面任意节点的支持

由于左侧和右侧点击之后都会出现包含不同内容的中间Modal,所以考虑把Modal挂载到布局的中间元素,而不是通过定位实现,看起来比较优雅。查了API,使用vue3的teleport

中间的盒子加上ID,方便挂载: <div id="screen-detail-modal"></div>

之后实现自定义组件:

view部分:

<template>
  <teleport to="#screen-detail-modal" v-if="visibleProps">
    <div class="screen-detail-modal-component-root">
      <div class="shadow" @click="closeModal()"></div>

      <span @click="closeModal()">关闭</span>

      <div class="screen-detail-modal-body">
        <div class="screen-detail-modal-content">
          <slot></slot>
        </div>
      </div>
    </div>
  </teleport>
</template>

<script>
import { toRefs } from 'vue';

export default {
  name: 'ScreenDetailModalComponent',
  props: {
    visible: {
      type: Boolean,
      required: false
    }
  },
  setup(props, { emit }) {
    const newProps = toRefs(props);

    return {
      closeModal: () => {
        emit('update:visible', false);
      },
      visibleProps: newProps.visible
    };
  }
};
</script>

<style lang="scss" scoped src="./index.scss"></style>

CSS部分:

.screen-detail-modal-component-root {
  padding: 24px 48px;
  height: 100%;

  .shadow {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: black;
    z-index: 3;
    opacity: 0.3;
  }

  .screen-detail-modal-body {
    background-image: url('@/assets/data-screen/mid-bar-body@2x.png');
    background-size: 100% 100%;
    z-index: 3;
    height: 100%;
    position: relative;
    background-color: rgb(0, 16, 22);

    .screen-detail-modal-content {
      position: absolute;
      height: 100%;
      width: 100%;
      padding: 12px 24px;
    }
  }
}

使用:

这里有一个坑,要挂载的元素分为根元素外面/里面,如果在里面,需要保证要挂载的元素先加载,所以这里要注意一下引用方式,采用组件异步引用。

还有个坑,因为挂载到了其他元素,所以CSS的写法,要先选择中间元素的选择器,之后再在子层级写内容,不能按照代码的结构去写CSS。

View:

<ScreenDetailModalComponent v-model:visible="visible">

</ScreenDetailModalComponent>


const ScreenDetailModalComponent = defineAsyncComponent(() =>
  import('@/views/DataScreen/components/ScreenDetailModalComponent/index.vue')
);

Hooks:

export default () => {
  const visible = ref(false);
  const detailData = ref({});

  const showDetailModal = () => {
    visible.value = true;
    //加载详情弹窗数据

    console.log('showDetailModal');
  };

  return {
    showDetailModal,
    visible,
    detailData
  };
};

CSS:

.screen-detail-modal-content {
  .elderly-info-detail-content {
    display: grid;
    grid-template-rows: 2fr 3fr 4fr;
    grid-row-gap: 48px;
    height: 100%;
  }
}


动态设置style的背景图片属性

view:

<div class="screen-card-component-root" :style="backgroundImageStyle">

JS:

const backgroundImageStyle = computed(() => {
  return {
    backgroundImage: `url(${require('你的图片地址')})`
  };
});

return {
backgroundImageStyle
}

单独设置URL或者require是不生效的。

require效果等同于new URL(url, import .meta .url).href

代码组织结构

最后!代码组织架构如下,个人觉得非常清晰,可能这就是React后遗症吧

image.png

其他收获:

  1. 不要沉迷细节优化,要先开发完大模块
  2. 多看文档,少走弯路