页面内容自定义布局怎么做?2*2、3*3、1+12?

296 阅读3分钟

前言

需求:用户可以选择不同的布局切换页面内容显示风格,比如页面分割为几个区域,可以理解为div,但每个div的内容格式固定,页面效果和保安室的监控屏那样:可以一个屏幕只显示一个监控,也可以显示1个、4个、9个、10个(前面两个占屏幕一半)、13个(第一个占屏幕1/4)等等。下面是默认背景效果图:

图片.png

图片.png

图片.png

图片.png

思路

由于页面需要有默认的边框,并不是添加内容后才显示边框的,用户选择某个数据(视频)时,按顺序显示在div里。所以这里有两种方式实现这个需求:方式一:当用户选择不同的布局时,切换不同的组件,有几个布局格式就写几个组件,可维护性高一点,但内容相同,代码重复,复用性低,比如2*2布局,那对应的组件里就有4个div,2+8布局则是10个div然后对应地设置盒子边框和大小。方式二:由于内容格式相同,不同的是不同布局下div的样式,那么可以只用一个组件实现所有的布局效果:点切换布局时,动态修改div的个数以及动态绑定每个div对应的样式。 本文采用方式二实现:

主要代码

<template>
  <div class="middle">
  <!--单独对特殊的布局绑定样式-->
    <div class="layout" :class="{ one: isOne, two: isTwo }">
      <div
        v-for="item in displayAreas"
        :key="item?.id"
        class="display-div"
        :style="WHStyle"
      >
        {{ item?.name }}
        <div v-if="item">
          <button @click="removeDiv(item)">删除</button>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
//内容
const videoList = ref([])
//动态div个数,通过v-for去渲染
const displayAreas = ref([]);
//默认布局为2*2
const layout = ref("4")
//列举所有的布局:4代表2*2,10代表2+8,13代表1+12
const map = new Map([
  ["1", { width: "100%", height: "100%" }],
  ["4", { width: "50%", height: "50%" }],
  ["9", { width: "33.33%", height: "33.33%" }],
  ["13", { width: "25%", height: "25%" }],
  ["10", { width: "25%", height: "25%" }],
]);
//监听布局变化,绑定对应的样式
let WHStyle = computed(() => map.get(layout.value));
let isOne = ref(false);
let isTwo = ref(false);
//更新页面div布局、内容、样式
const updateDisplayAreas = async (newLayout, newVideoList) => {
  isOne.value = newLayout === "13";
  isTwo.value = newLayout === "10";
  //利于Array.from生成内容都为null并且元素个数可动态修改的数组
  displayAreas.value = Array.from({ length: newLayout }, () => null);
  //如果切换了布局,原来的布局内容需要做判断渲染在新布局上
  newVideoList.forEach((value, index) => {
    if (index < newLayout) {
      displayAreas.value[index] = value;
    }
  });
  await nextTick();
};
//监听布局变化
watchEffect(async () => {
  if (layout.value && videoList.value) {
    await updateDisplayAreas(layout.value, videoList.value);
  }
});
//删除某个div的内容
function removeDiv(item) {
  if (videoList.value.length >= displayAreas.value.length) {
    const diffIndex = videoList.value.findIndex(
      (item) =>
        !displayAreas.value.some((displayItem) => displayItem?.id === item.id)
    );
    //如果找到了则替换掉
    if (diffIndex !== -1) {
      displayAreas.value = displayAreas.value
        .filter((v) => v?.id !== item.id)
        .concat([videoList.value[diffIndex]]);
    }
  } else {
  //没找到就用null填充数组
    displayAreas.value = displayAreas.value
      .filter((v) => v?.id !== item.id)
      .concat([null]);
  }
}
onMounted(async () => {
  await nextTick();
  updateDisplayAreas(layout.value, videoList.value);
});
</script>
style lang="scss" scoped>
.middle {
  flex: 1;
  height: 700px;
  .videoLayout,
  one,
  two {
    height: 100%;
    .display-div {
      box-sizing: border-box;
      float: left;
      color: white;
      background-color: #033c7f;
      border: 1px solid #3a8ee6;
      &:hover {
        background-color: #044ca1;
      }
    }
  }
  .one {
    div:first-child {
      width: 50% !important;
      height: 50% !important;
    }
  }
  .two {
    div:nth-child(-n + 2) {
      width: 50% !important;
      height: 50% !important;
    }
  }
}
</style>

补充

事实上页面分为三个组件,选择数据(添加数据)-显示数据(删除数据)-切换布局、数据处理(删除数据等操作),需要考虑组件间数据共享等等,真实需求也是有点复杂的,这里只是提供一个对自定义布局修改页面的思路:1. 动态生成默认的div盒子数量但没有内容,后续再根据用户添加内容到盒子里。2. 动态绑定不同布局下div的样式。