前言
需求:用户可以选择不同的布局切换页面内容显示风格,比如页面分割为几个区域,可以理解为div
,但每个div
的内容格式固定,页面效果和保安室的监控屏那样:可以一个屏幕只显示一个监控,也可以显示1个、4个、9个、10个(前面两个占屏幕一半)、13个(第一个占屏幕1/4)等等。下面是默认背景效果图:
思路
由于页面需要有默认的边框,并不是添加内容后才显示边框的,用户选择某个数据(视频)时,按顺序显示在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
的样式。