巧妙地设计
1149字约4分钟
2024-11-01
侧边栏、弹性布局
如何将一个容器分为指定几个部分,比如说一个容器视图中,A 占据 20%,B 占据 30%剩余的 C 占据。或者 A20px,b200px 剩余的 C 占据,但是有个手风琴点击后会占据 200px 的位置,需要向 C 借用。然后分为垂直、水平布局的这种讲解。按照我的例子给出多种不同的实现,目前暂时不考虑媒体查询、移动端适配之类的操作,容器视图为 container-layout,采用 scss 语法,尽量符合开闭原则,且代码简洁优雅可控制。
Flexbox 布局
(A 和 B 固定高度,C 自适应)
水平布局
.container-layout {
display: flex;
height: 100%;
.a {
flex: none;
width: 20px; /* 或者百分比 */
}
.b {
flex: none;
width: 200px; /* 或者百分比 */
}
.c {
flex: 1;
overflow: auto; /* 如果内容超出 */
}
}
垂直布局
.container-layout {
display: flex;
flex-direction: column;
height: 100%;
.a {
flex: none;
height: 20px; /* 或者百分比 */
}
.b {
flex: none;
height: 200px; /* 或者百分比 */
}
.c {
flex: 1;
overflow: auto; /* 如果内容超出 */
}
}
使用 Grid
.container-layout {
display: grid;
grid-template-columns: 20px 200px auto;
height: 100%;
.a {
grid-column: 1;
}
.b {
grid-column: 2;
}
.c {
grid-column: 3;
overflow: auto; /* 如果内容超出 */
}
}
grid-template-columns: 20px 200px auto;
这个属性做了以下几件事情:
- 定义了三列:这个属性定义了网格容器将有三个列轨道(column tracks)。
- 指定列宽:
- 第一列的宽度被设置为
20px
。 - 第二列的宽度被设置为
200px
。 - 第三列的宽度被设置为
auto
,这意味着第三列的宽度将自动调整以填充剩余空间。在 Grid 布局中,auto
通常意味着“尽可能小”,但当有可用空间时,它会扩展以填充额外的空间。
- 第一列的宽度被设置为
(A 和 B 固定高度,C 自适应)
垂直布局
.container-layout {
display: grid;
grid-template-rows: 20px 200px auto;
height: 100%;
.a {
grid-row: 1;
}
.b {
grid-row: 2;
}
.c {
grid-row: 3;
overflow: auto; /* 如果内容超出 */
}
}
手风琴效果
.container-layout {
display: flex;
flex-direction: column;
height: 100%;
.a,
.b,
.c {
transition: height 0.3s ease;
}
.a {
flex: none;
height: 20px; /* 默认高度 */
}
.b {
flex: none;
height: 200px; /* 默认高度 */
}
.c {
flex: 1;
overflow: auto; /* 如果内容超出 */
}
}
// 假设我们有一个函数来处理点击事件
function toggleAccordion(element) {
const isA = element === "a"
const accordion = isA
? document.querySelector(".a")
: document.querySelector(".b")
const other = isA
? document.querySelector(".b")
: document.querySelector(".a")
const c = document.querySelector(".c")
if (accordion.style.height === "200px") {
accordion.style.height = isA ? "20px" : "200px"
other.style.height = "200px"
c.style.flex = "1"
} else {
accordion.style.height = "200px"
c.style.flex = "0"
}
}
接口显示组件、左右
<template>
<div id="container-layout">
<div id="button-group" class="mx-12 mt-1 join d-flex justify-between">
<button class="btn btn-success rounded-2xl">Button 1</button>
<button class="btn btn-primary rounded-2xl">Button 2</button>
</div>
<div class="content-container">
<div id="code-layout" class="overflow-hidden">
<d-code-editor
class="size-full"
mode="review"
:theme="changeTheme"
v-model="Item.content"
:options="{ language: 'lua' }"
/>
</div>
<div
id="log-layout"
class="m-2 bg-gray-500 rounded-lg overflow-auto"
></div>
</div>
<div class="dropdown dropdown-top dropdown-end fixed bottom-20 right-20">
<div tabindex="0" role="button" class="btn btn-circle m-1">Click</div>
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-24 shadow"
>
<li><a>Item 1</a></li>
<li><a>Item 2</a></li>
</ul>
</div>
</div>
</template>
<script lang="ts" setup>
import { apiInfo } from "@/components/Api/help.json"
import { ref, watch } from "vue"
import { useRoute } from "vue-router"
const changeTheme = ref<boolean>(false)
let Item = ref<NoteCard>({ id: "", title: "", content: "" })
const route = useRoute()
watch(
() => route.params.id,
(newId) => {
const foundItem = apiInfo.find((element) => element.id === newId)
if (foundItem) {
Item.value = foundItem
} else {
Item.value = { id: "", title: "Not found", content: "" }
}
},
{ immediate: true }
)
</script>
<style lang="scss" scoped>
#container-layout {
display: flex;
flex-direction: column;
height: 100%;
#button-group {
height: 52px;
}
.content-container {
display: flex;
flex: 1;
overflow: hidden;
#code-layout {
flex: 1;
overflow: hidden;
}
#log-layout {
width: 300px;
/* 或者设置为一个比例,例如 30% */
max-height: 300px;
overflow: auto;
/* 如果内容超出 */
}
}
}
</style>
第二种,上下
<template>
<div id="container-layout">
<div id="button-group" class="mx-12 mt-1 join d-flex justify-between">
<button class="btn btn-success rounded-2xl">Button 1</button>
<button class="btn btn-primary rounded-2xl">Button 2</button>
</div>
<div id="code-layout" class="mx-48 mt-2 overflow-hidden">
<d-code-editor
class="size-full"
mode="review"
:theme="changeTheme"
v-model="Item.content"
:options="{ language: 'lua' }"
/>
</div>
<div id="log-layout" class="m-2 bg-gray-500 rounded-lg"></div>
<div class="dropdown dropdown-top dropdown-end fixed bottom-20 right-20">
<div tabindex="0" role="button" class="btn btn-circle m-1">Click</div>
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-24 shadow"
>
<li><a>Item 1</a></li>
<li>
<a>Item 2</a>
</li>
</ul>
</div>
</div>
</template>
<script lang="ts" setup>
import { apiInfo } from "@/components/Api/help.json"
import { ref, watch } from "vue"
import { useRoute } from "vue-router"
const changeTheme = ref<boolean>(false)
let Item = ref<NoteCard>({ id: "", title: "", content: "" })
const route = useRoute()
watch(
() => route.params.id,
(newId) => {
const foundItem = apiInfo.find((element) => element.id === newId)
if (foundItem) {
Item.value = foundItem
} else {
Item.value = { id: "", title: "Not found", content: "" }
}
},
{ immediate: true }
)
</script>
<style lang="scss" scoped>
#container-layout {
display: grid;
grid-template-rows: 52px 500px auto;
height: 100%;
#button-group {
grid-row: 1;
}
#code-layout {
grid-row: 2;
}
#log-layout {
grid-row: 3;
overflow: auto;
/* 如果内容超出 */
}
}
</style>