开发预览 更新于 2026-05-10

DockLayout 多面板布局

递归 split + tabbed pane 的桌面应用布局壳。v1 声明式布局,v2 加入拖拽重排。

基础用法

布局以一棵 DockGroup 树声明:每个 group 要么有 children(继续切分)要么有 panels(这是一个 tabbed 区域)。 内容通过 named slot panel-{id}(Vue)/ slots["panel-{id}"](React)提供。

背景 视口
  • 📁 src
  • 📄 orders.ts
  • 📄 login.ts
// orders.ts
export const fetchOrders = () => …
$ pnpm dev
[vite] dev server running on :5173
src/App.vue
<script setup lang="ts">
import { CfDockLayout } from '@chufix-design/vue';
import type { DockGroup } from '@chufix-design/vue';

const layout: DockGroup = {
  id: 'root',
  orientation: 'horizontal',
  children: [
    {
      id: 'left',
      orientation: 'vertical',
      panels: [
        { id: 'tree', title: 'Explorer', closable: false },
      ],
      size: '240px',
    },
    {
      id: 'main',
      orientation: 'vertical',
      children: [
        {
          id: 'editor',
          orientation: 'horizontal',
          panels: [
            { id: 'orders', title: 'orders.ts', closable: true, detachable: true },
            { id: 'login', title: 'login.ts', closable: true, detachable: true },
          ],
        },
        {
          id: 'bottom',
          orientation: 'horizontal',
          panels: [
            { id: 'terminal', title: 'Terminal', closable: false },
            { id: 'output', title: 'Output', closable: false },
          ],
          size: '160px',
        },
      ],
    },
  ],
};
</script>
<template>
  <div style="height: 360px; border: 1px solid var(--line-1); border-radius: var(--r-6); overflow: hidden;">
    <CfDockLayout :layout="layout">
      <template #panel-tree>
        <ul style="padding: 12px 16px; margin: 0; list-style: none; font-size: 12px; color: var(--fg-2);">
          <li>📁 src</li>
          <li style="padding-left: 14px;">📄 orders.ts</li>
          <li style="padding-left: 14px;">📄 login.ts</li>
        </ul>
      </template>
      <template #panel-orders>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">// orders.ts
export const fetchOrders = () =&gt; …</pre>
      </template>
      <template #panel-login>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">// login.ts
export const login = () =&gt; …</pre>
      </template>
      <template #panel-terminal>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">$ pnpm dev
[vite] dev server running on :5173</pre>
      </template>
      <template #panel-output>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-2);">[14:32:01] Build succeeded</pre>
      </template>
    </CfDockLayout>
  </div>
</template>
<script setup>
import { CfDockLayout } from '@chufix-design/vue';

const layout= {
  id: 'root',
  orientation: 'horizontal',
  children: [
    {
      id: 'left',
      orientation: 'vertical',
      panels: [
        { id: 'tree', title: 'Explorer', closable: false },
      ],
      size: '240px',
    },
    {
      id: 'main',
      orientation: 'vertical',
      children: [
        {
          id: 'editor',
          orientation: 'horizontal',
          panels: [
            { id: 'orders', title: 'orders.ts', closable, detachable: true },
            { id: 'login', title: 'login.ts', closable, detachable: true },
          ],
        },
        {
          id: 'bottom',
          orientation: 'horizontal',
          panels: [
            { id: 'terminal', title: 'Terminal', closable: false },
            { id: 'output', title: 'Output', closable: false },
          ],
          size: '160px',
        },
      ],
    },
  ],
};
</script>
<template>
  <div style="height: 360px; border: 1px solid var(--line-1); border-radius: var(--r-6); overflow: hidden;">
    <CfDockLayout :layout="layout">
      <template #panel-tree>
        <ul style="padding: 12px 16px; margin: 0; list-style: none; font-size: 12px; color: var(--fg-2);">
          <li>📁 src</li>
          <li style="padding-left: 14px;">📄 orders.ts</li>
          <li style="padding-left: 14px;">📄 login.ts</li>
        </ul>
      </template>
      <template #panel-orders>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">// orders.ts
export const fetchOrders = () =&gt; …</pre>
      </template>
      <template #panel-login>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">// login.ts
export const login = () =&gt; …</pre>
      </template>
      <template #panel-terminal>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-1);">$ pnpm dev
[vite] dev server running on :5173</pre>
      </template>
      <template #panel-output>
        <pre style="margin: 0; padding: 12px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-2);">[14:32:01] Build succeeded</pre>
      </template>
    </CfDockLayout>
  </div>
</template>
import { CfDockLayout } from '@chufix-design/react';

export default function Demo() {
  const layout: DockGroup = {
    id: 'root',
    orientation: 'horizontal',
    children: [
      {
        id: 'left',
        orientation: 'vertical',
        panels: [
          { id: 'tree', title: 'Explorer', closable: false },
        ],
        size: '240px',
      },
      {
        id: 'main',
        orientation: 'vertical',
        children: [
          {
            id: 'editor',
            orientation: 'horizontal',
            panels: [
              { id: 'orders', title: 'orders.ts', closable: true, detachable: true },
              { id: 'login', title: 'login.ts', closable: true, detachable: true },
            ],
          },
          {
            id: 'bottom',
            orientation: 'horizontal',
            panels: [
              { id: 'terminal', title: 'Terminal', closable: false },
              { id: 'output', title: 'Output', closable: false },
            ],
            size: '160px',
          },
        ],
      },
    ],
  };
  return (
    <>
      <div style={{ height: 360, border: "1px solid var(--line-1)", borderRadius: "var(--r-6)", overflow: "hidden" }}>
          <CfDockLayout layout={layout}>
              <ul style={{ padding: "12px 16px", margin: 0, listStyle: "none", fontSize: 12, color: "var(--fg-2)" }}>
                <li>📁 src</li>
                <li style={{ paddingLeft: 14 }}>📄 orders.ts</li>
                <li style={{ paddingLeft: 14 }}>📄 login.ts</li>
              </ul>
    </>
  );
}
import { CfDockLayout } from '@chufix-design/react';

export default function Demo() {
  const layout= {
    id: 'root',
    orientation: 'horizontal',
    children: [
      {
        id: 'left',
        orientation: 'vertical',
        panels: [
          { id: 'tree', title: 'Explorer', closable: false },
        ],
        size: '240px',
      },
      {
        id: 'main',
        orientation: 'vertical',
        children: [
          {
            id: 'editor',
            orientation: 'horizontal',
            panels: [
              { id: 'orders', title: 'orders.ts', closable, detachable: true },
              { id: 'login', title: 'login.ts', closable, detachable: true },
            ],
          },
          {
            id: 'bottom',
            orientation: 'horizontal',
            panels: [
              { id: 'terminal', title: 'Terminal', closable: false },
              { id: 'output', title: 'Output', closable: false },
            ],
            size: '160px',
          },
        ],
      },
    ],
  };
  return (
    <>
      <div style={{ height: 360, border: "1px solid var(--line-1)", borderRadius: "var(--r-6)", overflow: "hidden" }}>
          <CfDockLayout layout={layout}>
              <ul style={{ padding: "12px 16px", margin: 0, listStyle: "none", fontSize: 12, color: "var(--fg-2)" }}>
                <li>📁 src</li>
                <li style={{ paddingLeft: 14 }}>📄 orders.ts</li>
                <li style={{ paddingLeft: 14 }}>📄 login.ts</li>
              </ul>
    </>
  );
}

最简布局(无嵌套)

一个 group + 多个 panels = 普通 tabbed 视图,等价于 CfTabs;当只有 tab 切换需求时直接用 CfTabs 更轻。

背景 视口
预览区(这是一个仅 tab 切换的简化布局)
src/App.vue
<script setup lang="ts">
import { CfDockLayout } from '@chufix-design/vue';
import type { DockGroup } from '@chufix-design/vue';
const layout: DockGroup = {
  id: 'root',
  orientation: 'vertical',
  panels: [
    { id: 'preview', title: '预览', closable: false },
    { id: 'console', title: 'Console', closable: false },
  ],
};
</script>
<template>
  <div style="height: 240px; border: 1px solid var(--line-1); border-radius: var(--r-6); overflow: hidden;">
    <CfDockLayout :layout="layout">
      <template #panel-preview>
        <div style="padding: 16px; color: var(--fg-2);">预览区(这是一个仅 tab 切换的简化布局)</div>
      </template>
      <template #panel-console>
        <pre style="margin: 0; padding: 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-2);">$ ready
[14:32] /v1/orders → 200 OK · 288ms</pre>
      </template>
    </CfDockLayout>
  </div>
</template>
<script setup>
import { CfDockLayout } from '@chufix-design/vue';
const layout= {
  id: 'root',
  orientation: 'vertical',
  panels: [
    { id: 'preview', title: '预览', closable: false },
    { id: 'console', title: 'Console', closable: false },
  ],
};
</script>
<template>
  <div style="height: 240px; border: 1px solid var(--line-1); border-radius: var(--r-6); overflow: hidden;">
    <CfDockLayout :layout="layout">
      <template #panel-preview>
        <div style="padding: 16px; color: var(--fg-2);">预览区(这是一个仅 tab 切换的简化布局)</div>
      </template>
      <template #panel-console>
        <pre style="margin: 0; padding: 16px; font-family: var(--font-mono); font-size: 12px; color: var(--fg-2);">$ ready
[14:32] /v1/orders → 200 OK · 288ms</pre>
      </template>
    </CfDockLayout>
  </div>
</template>
import { CfDockLayout } from '@chufix-design/react';

export default function Demo() {
  const layout: DockGroup = {
    id: 'root',
    orientation: 'vertical',
    panels: [
      { id: 'preview', title: '预览', closable: false },
      { id: 'console', title: 'Console', closable: false },
    ],
  };
  return (
    <>
      <div style={{ height: 240, border: "1px solid var(--line-1)", borderRadius: "var(--r-6)", overflow: "hidden" }}>
          <CfDockLayout layout={layout}>
              <div style={{ padding: 16, color: "var(--fg-2)" }}>预览区(这是一个仅 tab 切换的简化布局)</div>
    </>
  );
}
import { CfDockLayout } from '@chufix-design/react';

export default function Demo() {
  const layout= {
    id: 'root',
    orientation: 'vertical',
    panels: [
      { id: 'preview', title: '预览', closable: false },
      { id: 'console', title: 'Console', closable: false },
    ],
  };
  return (
    <>
      <div style={{ height: 240, border: "1px solid var(--line-1)", borderRadius: "var(--r-6)", overflow: "hidden" }}>
          <CfDockLayout layout={layout}>
              <div style={{ padding: 16, color: "var(--fg-2)" }}>预览区(这是一个仅 tab 切换的简化布局)</div>
    </>
  );
}

API

属性类型默认值说明
layoutDockGroup布局描述树
activeRecord<groupId, panelId>{}各 group 的活动 tab id

DockGroup{ id, orientation: 'horizontal' \| 'vertical', children?: DockGroup[], panels?: DockPanel[], size? }

DockPanel{ id, title, contentKey?, closable?, detachable?, size?, collapsed? }

事件:active-change(groupId, panelId) / panel-close(...) / panel-detach(...)

v1 限制:splitter resize 与 drag-to-rearrange 暂未支持,将在 v2 加入。

反馈与讨论

DockLayout 多面板布局 的讨论

0
0 / 600
正在加载评论...