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

DetachedPanel 浮动面板

可拖拽的浮动面板,Teleport 到 body。配合 TearOffTab / DockLayout 的 detach 动作使用。

基础用法

按住头部即可拖动;resizable 启用浏览器原生 resize。Tauri / Electron 场景里把它转成独立 window 时只需在 tear-off 事件里 spawn 一个新 BrowserWindow / WebView。

背景 视口
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfDetachedPanel, CfButton } from '@chufix-design/vue';
const open = ref(false);
const host = ref<HTMLElement | null>(null);
</script>
<template>
  <div ref="host" class="demo-floating-host demo-floating-host--panel">
    <div class="demo-row">
      <CfButton @click="open = true">弹出浮动面板</CfButton>
    </div>
    <CfDetachedPanel
      v-model:open="open"
      title="Inspector"
      :to="host ?? 'body'"
      :x="24"
      :y="64"
      :width="320"
      :height="220"
    >
      <div style="font-size: 13px; color: var(--fg-2); line-height: 1.6;">
        拖动顶部把手可以移动面板。<br>
        外部 Tauri/Electron 应用可在 tear-off 事件里把它转换成独立窗口。
      </div>
    </CfDetachedPanel>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfDetachedPanel, CfButton } from '@chufix-design/vue';
const open = ref(false);
const host = ref<HTMLElement | null>(null);
</script>
<template>
  <div ref="host" class="demo-floating-host demo-floating-host--panel">
    <div class="demo-row">
      <CfButton @click="open = true">弹出浮动面板</CfButton>
    </div>
    <CfDetachedPanel
      v-model:open="open"
      title="Inspector"
      :to="host ?? 'body'"
      :x="24"
      :y="64"
      :width="320"
      :height="220"
    >
      <div style="font-size: 13px; color: var(--fg-2); line-height: 1.6;">
        拖动顶部把手可以移动面板。<br>
        外部 Tauri/Electron 应用可在 tear-off 事件里把它转换成独立窗口。
      </div>
    </CfDetachedPanel>
  </div>
</template>
import { useState } from 'react';
import { CfButton, CfDetachedPanel } from '@chufix-design/react';

export default function Demo() {
  const [open, setOpen] = useState(false);
  const [host, setHost] = useState<HTMLElement | null>(null);
  return (
    <>
      <div ref="host" className="demo-floating-host demo-floating-host--panel">
          <div className="demo-row">
            <CfButton onClick={() => setOpen(true)}>弹出浮动面板</CfButton>
          </div>
          <CfDetachedPanel open={open} onOpenChange={setOpen} title="Inspector" to={host ?? 'body'} x={24} y={64} width={320} height={220} >
            <div style={{ fontSize: 13, color: "var(--fg-2)", lineHeight: 1.6 }}>
              拖动顶部把手可以移动面板。<br>
              外部 Tauri/Electron 应用可在 tear-off 事件里把它转换成独立窗口。
            </div>
          </CfDetachedPanel>
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfButton, CfDetachedPanel } from '@chufix-design/react';

export default function Demo() {
  const [open, setOpen] = useState(false);
  const [host, setHost] = useState<HTMLElement | null>(null);
  return (
    <>
      <div ref="host" className="demo-floating-host demo-floating-host--panel">
          <div className="demo-row">
            <CfButton onClick={() => setOpen(true)}>弹出浮动面板</CfButton>
          </div>
          <CfDetachedPanel open={open} onOpenChange={setOpen} title="Inspector" to={host ?? 'body'} x={24} y={64} width={320} height={220} >
            <div style={{ fontSize: 13, color: "var(--fg-2)", lineHeight: 1.6 }}>
              拖动顶部把手可以移动面板。<br>
              外部 Tauri/Electron 应用可在 tear-off 事件里把它转换成独立窗口。
            </div>
          </CfDetachedPanel>
        </div>
    </>
  );
}

多面板共存

可同时打开多个 DetachedPanel;通过 zIndex 显式分层。resizable=true 启用浏览器原生 resize 句柄。

背景 视口
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfDetachedPanel, CfButton } from '@chufix-design/vue';
const a = ref(false);
const b = ref(false);
const host = ref<HTMLElement | null>(null);
</script>
<template>
  <div ref="host" class="demo-floating-host demo-floating-host--panel demo-floating-host--tall">
    <div class="demo-row">
      <CfButton @click="a = true">面板 A</CfButton>
      <CfButton variant="secondary" @click="b = true">面板 B(resizable)</CfButton>
    </div>
    <CfDetachedPanel
      v-model:open="a"
      title="Inspector A"
      :to="host ?? 'body'"
      :x="24"
      :y="64"
      :width="280"
      :height="180"
      :z-index="1500"
    >
      <div style="font-size: 12px; color: var(--fg-2);">头部拖动可移动整个面板。</div>
    </CfDetachedPanel>
    <CfDetachedPanel
      v-model:open="b"
      title="Inspector B"
      :to="host ?? 'body'"
      :x="336"
      :y="104"
      :width="320"
      :height="220"
      resizable
      :z-index="1501"
    >
      <div style="font-size: 12px; color: var(--fg-2);">右下角可拖拽改变大小(resizable)。</div>
    </CfDetachedPanel>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfDetachedPanel, CfButton } from '@chufix-design/vue';
const a = ref(false);
const b = ref(false);
const host = ref<HTMLElement | null>(null);
</script>
<template>
  <div ref="host" class="demo-floating-host demo-floating-host--panel demo-floating-host--tall">
    <div class="demo-row">
      <CfButton @click="a = true">面板 A</CfButton>
      <CfButton variant="secondary" @click="b = true">面板 B(resizable)</CfButton>
    </div>
    <CfDetachedPanel
      v-model:open="a"
      title="Inspector A"
      :to="host ?? 'body'"
      :x="24"
      :y="64"
      :width="280"
      :height="180"
      :z-index="1500"
    >
      <div style="font-size: 12px; color: var(--fg-2);">头部拖动可移动整个面板。</div>
    </CfDetachedPanel>
    <CfDetachedPanel
      v-model:open="b"
      title="Inspector B"
      :to="host ?? 'body'"
      :x="336"
      :y="104"
      :width="320"
      :height="220"
      resizable
      :z-index="1501"
    >
      <div style="font-size: 12px; color: var(--fg-2);">右下角可拖拽改变大小(resizable)。</div>
    </CfDetachedPanel>
  </div>
</template>
import { useState } from 'react';
import { CfDetachedPanel } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState(false);
  const [a, setA] = useState(false);
  const [b, setB] = useState(false);
  const [b, setB] = useState(false);
  return (
    <>
      <CfDetachedPanel open={a} onOpenChange={setA} width={280} />
      <CfDetachedPanel open={b} onOpenChange={setB} resizable />
    </>
  );
}
import { useState } from 'react';
import { CfDetachedPanel } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState(false);
  const [a, setA] = useState(false);
  const [b, setB] = useState(false);
  const [b, setB] = useState(false);
  return (
    <>
      <CfDetachedPanel open={a} onOpenChange={setA} width={280} />
      <CfDetachedPanel open={b} onOpenChange={setB} resizable />
    </>
  );
}

API

属性类型默认值说明
open / v-model:openbooleanfalse
titlestring头部标题
x / ynumber自动初始位置
width / heightnumber | string360 / 240
resizablebooleanfalse启用原生 resize 句柄
closablebooleantrue
tostring | HTMLElement'body'Teleport 目标;传入局部容器时面板改为容器内定位
zIndexnumber

事件:update:open / move(x, y)

反馈与讨论

DetachedPanel 浮动面板 的讨论

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