From d4346b670e2ee396c209d1c0fdb73fbb99245e57 Mon Sep 17 00:00:00 2001 From: SonyLeo <746591437@qq.com> Date: Mon, 29 Jun 2026 19:43:30 -0700 Subject: [PATCH 01/12] feat(layout): add various layout demos including resizable aside, floating panels, and scrollable main content --- docs/.vitepress/themeConfig.ts | 1 + docs/demos/layout/aside-collapse-effect.vue | 191 ++++++++++++ docs/demos/layout/aside-modes.vue | 143 +++++++++ docs/demos/layout/aside-resizable.vue | 106 +++++++ docs/demos/layout/aside-slot-props.vue | 197 +++++++++++++ docs/demos/layout/basic.vue | 61 ++++ docs/demos/layout/floating-panels.vue | 170 +++++++++++ docs/demos/layout/floating.vue | 113 +++++++ docs/demos/layout/main-scroll-bubble.vue | 61 ++++ docs/demos/layout/main-scroll-div.vue | 56 ++++ docs/demos/layout/main-scroll.vue | 92 ++++++ docs/src/components/layout.md | 308 ++++++++++++++++++++ 12 files changed, 1499 insertions(+) create mode 100644 docs/demos/layout/aside-collapse-effect.vue create mode 100644 docs/demos/layout/aside-modes.vue create mode 100644 docs/demos/layout/aside-resizable.vue create mode 100644 docs/demos/layout/aside-slot-props.vue create mode 100644 docs/demos/layout/basic.vue create mode 100644 docs/demos/layout/floating-panels.vue create mode 100644 docs/demos/layout/floating.vue create mode 100644 docs/demos/layout/main-scroll-bubble.vue create mode 100644 docs/demos/layout/main-scroll-div.vue create mode 100644 docs/demos/layout/main-scroll.vue create mode 100644 docs/src/components/layout.md diff --git a/docs/.vitepress/themeConfig.ts b/docs/.vitepress/themeConfig.ts index 552102715..c60a727f0 100644 --- a/docs/.vitepress/themeConfig.ts +++ b/docs/.vitepress/themeConfig.ts @@ -15,6 +15,7 @@ const sharedSidebarItems = [ base: '/components/', items: [ { text: 'Container 容器', link: 'container' }, + { text: 'Layout 布局', link: 'layout' }, { text: 'Bubble 气泡', link: 'bubble' }, { text: 'Sender 消息输入框', link: 'sender' }, { text: 'Prompts 提示集', link: 'prompts' }, diff --git a/docs/demos/layout/aside-collapse-effect.vue b/docs/demos/layout/aside-collapse-effect.vue new file mode 100644 index 000000000..28d3b902e --- /dev/null +++ b/docs/demos/layout/aside-collapse-effect.vue @@ -0,0 +1,191 @@ + + + + + + + diff --git a/docs/demos/layout/aside-modes.vue b/docs/demos/layout/aside-modes.vue new file mode 100644 index 000000000..78b9e411d --- /dev/null +++ b/docs/demos/layout/aside-modes.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/docs/demos/layout/aside-resizable.vue b/docs/demos/layout/aside-resizable.vue new file mode 100644 index 000000000..6311094ab --- /dev/null +++ b/docs/demos/layout/aside-resizable.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/docs/demos/layout/aside-slot-props.vue b/docs/demos/layout/aside-slot-props.vue new file mode 100644 index 000000000..5051291d7 --- /dev/null +++ b/docs/demos/layout/aside-slot-props.vue @@ -0,0 +1,197 @@ + + + + + + + diff --git a/docs/demos/layout/basic.vue b/docs/demos/layout/basic.vue new file mode 100644 index 000000000..2b4af0607 --- /dev/null +++ b/docs/demos/layout/basic.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/docs/demos/layout/floating-panels.vue b/docs/demos/layout/floating-panels.vue new file mode 100644 index 000000000..26d4bbb78 --- /dev/null +++ b/docs/demos/layout/floating-panels.vue @@ -0,0 +1,170 @@ + + + + + + + diff --git a/docs/demos/layout/floating.vue b/docs/demos/layout/floating.vue new file mode 100644 index 000000000..e85bfff6e --- /dev/null +++ b/docs/demos/layout/floating.vue @@ -0,0 +1,113 @@ + + + + + + + diff --git a/docs/demos/layout/main-scroll-bubble.vue b/docs/demos/layout/main-scroll-bubble.vue new file mode 100644 index 000000000..f0219e49c --- /dev/null +++ b/docs/demos/layout/main-scroll-bubble.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/docs/demos/layout/main-scroll-div.vue b/docs/demos/layout/main-scroll-div.vue new file mode 100644 index 000000000..2698c7ebf --- /dev/null +++ b/docs/demos/layout/main-scroll-div.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/docs/demos/layout/main-scroll.vue b/docs/demos/layout/main-scroll.vue new file mode 100644 index 000000000..9df127122 --- /dev/null +++ b/docs/demos/layout/main-scroll.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/docs/src/components/layout.md b/docs/src/components/layout.md new file mode 100644 index 000000000..79434c3e3 --- /dev/null +++ b/docs/src/components/layout.md @@ -0,0 +1,308 @@ +--- +outline: [1, 3] +--- + +# Layout 布局 + +`Layout` 用来组织带有头部、主内容区、底部和左右侧栏的页面。 + +它覆盖三类核心场景: + +- 标准页面骨架 +- 可收起的左右侧栏 +- 可拖动、可缩放的浮层布局 + +## 基础布局 + +`Layout` 用于定义页面结构,区域中的具体内容可按需配置。 + + + +## 侧栏 + +### 展示形态 + +用 `leftAside` / `rightAside` 配置侧栏。 + +- `dock`:占据页面空间 +- `drawer`:覆盖在内容上方 + +`drawer` 的宽度优先通过 `--tr-layout-drawer-width` 控制,未设置时回退到侧栏展开宽度。 + + + +### 收起行为 + +`collapsedWidth` 控制收起后还保留多少宽度,`collapseEffect` 控制收起时的动画效果。 + +- `collapsedWidth > 0`:收起后保留一条窄栏 +- `collapsedWidth = 0`:收起后完全隐藏 +- `overlay`:内容留在原位 +- `slide`:内容跟着一起移动 + + + +### 状态控制 + +用 `leftAside` / `rightAside` 控制开关和宽度。 + +侧栏内容通过 `left-aside` / `right-aside` 提供。 + +受控用法下,状态变化后需要同步回写。侧栏内部切换可使用 `Layout.AsideToggle`,默认插槽提供 `{ isOpen }`。 + + + +### 宽度调整 + +`resizable` 可以开启 `dock` 侧栏的拖拽改宽,宽度范围由 `minExpandedWidth` 和 `maxExpandedWidth` 控制。 + + + +## 主区滚动 + +`Layout.ProxyScrollbar` 用来显示主区滚动条,`scrollTarget` 指向实际滚动的内容区域。 + +> 适合消息区居中的对话页:内容可以居中,滚动条仍固定在主区右侧,视觉更整齐。 + +- `scrollTarget` 传实际滚动元素,或对应组件实例的 `ref` +- 该元素需自行设置尺寸、`box-sizing` 和滚动样式 +- `Layout.ProxyScrollbar` 会自动隐藏该元素的原生滚动条 + +```css +.scroll-host { + width: 100%; + height: 100%; + box-sizing: border-box; + overflow: auto; +} +``` + + + +## 浮层 + +适合临时面板或悬浮工作区,只在 `mode="floating"` 时生效。 + +### 基本用法 + +`defaultFloatingState` 设置初始位置和大小,`floatingOptions` 设置拖动、缩放和尺寸范围。 + +- `defaultFloatingState`:只设置初始值 +- `floatingState`:由外部控制位置和大小 + +传 `floatingState` 时,需要在 `update:floatingState` 中同步最新值。 + + + +### 浮层工作区 + +浮层里同样可以放入侧栏、头部和主区。常见用法是把左右两侧都做成按需展开的 drawer。 + + + +## Props + +### Layout {#layout-props} + +| 属性名 | 说明 | 类型 | 默认值 | +| ------ | ---- | ---- | ------ | +| `mode` | 布局模式;`normal` 参与普通布局,`floating` 会脱离普通布局,不占原来的位置空间 | `'normal' \| 'floating'` | `'normal'` | +| `leftAside` | 左侧栏配置 | [`LayoutAsideProps`](#layout-aside-props) | `-` | +| `rightAside` | 右侧栏配置 | [`LayoutAsideProps`](#layout-aside-props) | `-` | +| `floatingState` | 受控浮层状态,需配合 `update:floatingState` 回写 | [`LayoutFloatingState`](#layout-floating-state) | `-` | +| `defaultFloatingState` | 非受控浮层初始状态,仅首次挂载读取一次 | [`LayoutFloatingState`](#layout-floating-state) | `-` | +| `floatingOptions` | 浮层拖拽、缩放和尺寸约束配置 | [`LayoutFloatingOptions`](#layout-floating-options) | `-` | + +### Layout.ProxyScrollbar {#layout-proxy-scrollbar-props} + +| 属性名 | 说明 | 类型 | 默认值 | +| ------ | ---- | ---- | ------ | +| `scrollTarget` | 真实滚动容器的元素,或对应组件实例的 ref | [`LayoutScrollTarget`](#layout-scroll-target) | `-` | + +### Layout.AsideToggle {#layout-aside-toggle-props} + +| 属性名 | 说明 | 类型 | 默认值 | +| ------ | ---- | ---- | ------ | +| `side` | 控制的侧栏位置 | `'left' \| 'right'` | `-` | + +它是一个现成的开关按钮,只能在 `Layout` 内部使用,通常放在 `left-aside` / `right-aside` 插槽中。 + +## Slots + +### Layout {#layout-slots} + +| 插槽名 | 说明 | 作用域参数 | +| ------ | ---- | ---------- | +| `left-aside` | 左侧栏内容 | `-` | +| `header` | 顶部区域 | `-` | +| `main` | 主区内容 | `-` | +| `footer` | 底部区域 | `-` | +| `right-aside` | 右侧栏内容 | `-` | + +### Layout.AsideToggle + +| 插槽名 | 说明 | 作用域参数 | +| ------ | ---- | ---------- | +| `default` | 自定义切换按钮内容 | `{ isOpen: boolean }` | + +## Events + +### Layout {#layout-layout-events} + +| 事件名 | 说明 | 回调参数 | +| ------ | ---- | -------- | +| `update:floatingState` | 浮层位置或尺寸变化 | (value: [`LayoutFloatingState`](#layout-floating-state)) | +| `aside-open-change` | 侧栏开关变化 | (detail: [`LayoutAsideOpenDetail`](#layout-aside-open-detail)) | +| `left-aside-open-change` | 左侧栏开关变化 | (detail: [`LayoutAsideOpenValue`](#layout-aside-open-value)) | +| `right-aside-open-change` | 右侧栏开关变化 | (detail: [`LayoutAsideOpenValue`](#layout-aside-open-value)) | +| `aside-resize-start` | 开始调整侧栏宽度 | (detail: [`LayoutAsideResizeDetail`](#layout-aside-resize-detail)) | +| `aside-resize` | 调整侧栏宽度时持续触发 | (detail: [`LayoutAsideResizeDetail`](#layout-aside-resize-detail)) | +| `aside-resize-end` | 结束调整侧栏宽度 | (detail: [`LayoutAsideResizeDetail`](#layout-aside-resize-detail)) | +| `left-aside-resize-start` | 开始调整左侧栏宽度 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `left-aside-resize` | 调整左侧栏宽度时持续触发 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `left-aside-resize-end` | 结束调整左侧栏宽度 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `right-aside-resize-start` | 开始调整右侧栏宽度 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `right-aside-resize` | 调整右侧栏宽度时持续触发 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `right-aside-resize-end` | 结束调整右侧栏宽度 | (detail: [`LayoutAsideResizeValue`](#layout-aside-resize-value)) | +| `floating-drag-start` | 开始拖动浮层 | (detail: [`LayoutFloatingDragDetail`](#layout-floating-drag-detail)) | +| `floating-drag` | 拖动浮层时持续触发 | (detail: [`LayoutFloatingDragDetail`](#layout-floating-drag-detail)) | +| `floating-drag-end` | 结束拖动浮层 | (detail: [`LayoutFloatingDragDetail`](#layout-floating-drag-detail)) | +| `floating-resize-start` | 开始调整浮层尺寸 | (detail: [`LayoutFloatingResizeDetail`](#layout-floating-resize-detail)) | +| `floating-resize` | 调整浮层尺寸时持续触发 | (detail: [`LayoutFloatingResizeDetail`](#layout-floating-resize-detail)) | +| `floating-resize-end` | 结束调整浮层尺寸 | (detail: [`LayoutFloatingResizeDetail`](#layout-floating-resize-detail)) | + +## Types {#types} + +### LayoutAsideProps {#layout-aside-props} + +| 字段 | 说明 | 类型 | 默认值 | +| ---- | ---- | ---- | ------ | +| `mode` | 侧栏模式 | `'dock' \| 'drawer'` | `'dock'` | +| `open` | 受控开关状态 | `boolean` | `-` | +| `defaultOpen` | 非受控初始开关状态 | `boolean` | `left: true` / `right: false` | +| `expandedWidth` | 受控展开宽度;`drawer` 未设置 `--tr-layout-drawer-width` 时会回退使用该宽度 | `number` | `-` | +| `defaultExpandedWidth` | 非受控初始展开宽度;`drawer` 宽度回退值 | `number` | `left: 300` / `right: 320` | +| `minExpandedWidth` | 最小展开宽度边界 | `number` | `left: 200` / `right: 240` | +| `maxExpandedWidth` | 最大展开宽度边界 | `number` | `left: 560` / `right: 640` | +| `collapsedWidth` | 收起后保留的窄栏宽度,仅 `dock` 生效 | `number` | `0` | +| `collapseEffect` | `dock` 收起到窄栏时的内容动画 | `'overlay' \| 'slide'` | `'overlay'` | +| `resizable` | 是否允许拖拽改宽,仅 `dock` 生效 | `boolean` | `false` | + +### LayoutAsideOpenDetail {#layout-aside-open-detail} + +| 字段 | 说明 | 类型 | +| ---- | ---- | ---- | +| `side` | 当前侧栏位置 | `'left' \| 'right'` | +| `open` | 当前是否展开 | `boolean` | + +### LayoutAsideOpenValue {#layout-aside-open-value} + +| 字段 | 说明 | 类型 | +| ---- | ---- | ---- | +| `open` | 当前是否展开 | `boolean` | + +### LayoutAsideResizeDetail {#layout-aside-resize-detail} + +| 字段 | 说明 | 类型 | +| ---- | ---- | ---- | +| `side` | 当前侧栏位置 | `'left' \| 'right'` | +| `expandedWidth` | 当前侧栏宽度 | `number` | + +### LayoutAsideResizeValue {#layout-aside-resize-value} + +| 字段 | 说明 | 类型 | +| ---- | ---- | ---- | +| `expandedWidth` | 当前侧栏宽度 | `number` | + +### LayoutScrollTarget {#layout-scroll-target} + +`HTMLElement | Pick | null | undefined` + +### LayoutFloatingState {#layout-floating-state} + +| 字段 | 说明 | 类型 | 默认值 | +| ---- | ---- | ---- | ------ | +| `placement` | 浮层位置 | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' \| 'center'` | `'center'` | +| `offsetX` | 横向偏移;`placement` 为 `center` 时不生效 | `number` | `24` | +| `offsetY` | 纵向偏移;`placement` 为 `center` 时不生效 | `number` | `24` | +| `width` | 浮层宽度;非受控时表示初始值,受控时表示当前值 | `number` | `420` | +| `height` | 浮层高度;非受控时表示初始值,受控时表示当前值 | `number` | `560` | + +### LayoutFloatingOptions {#layout-floating-options} + +| 字段 | 说明 | 类型 | 默认值 | +| ---- | ---- | ---- | ------ | +| `draggable` | 是否允许拖动浮层 | `boolean` | `true` | +| `resizable` | 是否允许通过浮层边缘手柄调整尺寸 | `boolean` | `false` | +| `minWidth` | 最小宽度 | `number` | `320` | +| `maxWidth` | 最大宽度 | `number` | `视口宽度` | +| `minHeight` | 最小高度 | `number` | `240` | +| `maxHeight` | 最大高度 | `number` | `视口高度` | + +### LayoutFloatingDragDetail {#layout-floating-drag-detail} + +与 [`LayoutFloatingState`](#layout-floating-state) 一致。 + +### LayoutFloatingResizeDetail {#layout-floating-resize-detail} + +在 [`LayoutFloatingState`](#layout-floating-state) 基础上增加以下字段: + +| 字段 | 说明 | 类型 | +| ---- | ---- | ---- | +| `handle` | 当前拖动的边或角 | `'s' \| 'e' \| 'w' \| 'ne' \| 'nw' \| 'se' \| 'sw'` | + +## CSS 变量 + +### 布局基础 {#layout-css-basics} + +| 变量名 | 说明 | +| ------ | ---- | +| `--tr-layout-height` | 布局高度 | +| `--tr-layout-bg` | 容器背景 | +| `--tr-layout-left-aside-bg` | 左侧栏背景 | +| `--tr-layout-right-aside-bg` | 右侧栏背景 | +| `--tr-layout-header-bg` | 顶部背景 | +| `--tr-layout-main-bg` | 主区背景 | +| `--tr-layout-footer-bg` | 底部背景 | +| `--tr-layout-divider-color` | 分隔线颜色 | +| `--tr-layout-overlay-bg` | drawer 遮罩颜色 | +| `--tr-layout-panel-shadow` | drawer 阴影 | +| `--tr-layout-floating-radius` | 浮层圆角 | +| `--tr-layout-floating-shadow` | 浮层阴影 | +| `--tr-layout-floating-z-index` | 浮层层级 | + +### 内容与交互 {#layout-css-content} + +| 变量名 | 说明 | +| ------ | ---- | +| `--tr-layout-main-min-width` | 主区最小宽度 | +| `--tr-layout-drawer-width` | drawer 展示宽度 | +| `--tr-layout-main-scrollbar-width` | 滚动条宽度 | +| `--tr-layout-main-scrollbar-thumb-bg` | 滚动条滑块颜色 | +| `--tr-layout-main-scrollbar-thumb-bg-hover` | 滑块悬停颜色 | +| `--tr-layout-main-scrollbar-thumb-bg-active` | 滑块激活颜色 | From 6f9aac558bb95160efdfd6d3610a902bbe08a515 Mon Sep 17 00:00:00 2001 From: SonyLeo <746591437@qq.com> Date: Mon, 29 Jun 2026 21:26:04 -0700 Subject: [PATCH 02/12] fix: review suggestions --- docs/demos/layout/aside-modes.vue | 6 +++--- docs/demos/layout/aside-resizable.vue | 4 ++-- docs/demos/layout/aside-slot-props.vue | 22 ++++------------------ docs/demos/layout/floating-panels.vue | 6 +++--- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/docs/demos/layout/aside-modes.vue b/docs/demos/layout/aside-modes.vue index 78b9e411d..960c7a1e7 100644 --- a/docs/demos/layout/aside-modes.vue +++ b/docs/demos/layout/aside-modes.vue @@ -1,16 +1,16 @@ diff --git a/docs/demos/layout/aside-resizable.vue b/docs/demos/layout/aside-resizable.vue index 6311094ab..06c8c38d9 100644 --- a/docs/demos/layout/aside-resizable.vue +++ b/docs/demos/layout/aside-resizable.vue @@ -1,7 +1,7 @@ diff --git a/docs/demos/layout/aside-slot-props.vue b/docs/demos/layout/aside-slot-props.vue index 5051291d7..6658a71b4 100644 --- a/docs/demos/layout/aside-slot-props.vue +++ b/docs/demos/layout/aside-slot-props.vue @@ -1,7 +1,7 @@ @@ -84,14 +84,6 @@ function updateRightAsideOpen(detail: LayoutAsideOpenDetail) { - -
- - - -