Skip to content

巧妙地设计

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; 这个属性做了以下几件事情:

  1. 定义了三列:这个属性定义了网格容器将有三个列轨道(column tracks)。
  2. 指定列宽
    • 第一列的宽度被设置为 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>