Appearance
Layout 布局
Layout 是 AI 应用页面的通用布局组件,可用于搭建聊天页、工作台和多面板操作界面。
它提供以下能力:
- 页面骨架:统一组织头部、主区、底部与左右侧栏
- 侧栏交互:支持展开、收起、拖拽改宽和
drawer覆盖 - 浮层布局:支持定位、拖拽和缩放
- 代理滚动条:适用于内容列居中或限宽后,原生滚动条偏离主区右边界的场景
基础布局
Layout 提供 left-aside、header、main、footer 和 right-aside 五个区域插槽,用于编排页面结构。
布局模式
mode 控制 Layout 的整体形态,默认值为 normal。
normal:普通页面骨架,参与文档流布局floating:悬浮布局,脱离文档流,可用于构建悬浮工作区或拖拽窗口
侧栏
侧栏由 leftAside / rightAside 控制,类型为 LayoutAsideOptions。
侧栏内容通过 left-aside / right-aside 插槽提供。
展示形态
LayoutAsideOptions.mode 控制侧栏展示形态,默认值为 dock。
dock:占据页面空间drawer:覆盖在内容上方
drawer 的宽度优先通过 --tr-layout-drawer-width 控制,未设置时回退到侧栏展开宽度。
收起行为
collapsedWidth 控制收起后还保留多少宽度,仅 dock 模式生效;collapseEffect 控制收起时的动画效果。
collapsedWidth > 0:收起后保留一条窄栏collapsedWidth = 0:收起后完全隐藏overlay:侧栏外框保留,内容层不跟随宽度滑动slide:侧栏内容随宽度一起滑出
宽度调整
resizable 可以开启 dock 侧栏的拖拽改宽,宽度范围由 minExpandedWidth 和 maxExpandedWidth 控制。
侧栏受控
open 和 expandedWidth 是受控值,状态变化后需要通过事件同步外部状态。
defaultOpen 和 defaultExpandedWidth 只提供初始值,适合不需要外部持续控制的场景。
浮层
浮层相关配置和交互只在浮层模式(mode="floating")下生效。
defaultFloatingState:非受控初始状态,只在首次加载时读取floatingState:受控状态,由外部维护当前位置和尺寸floatingOptions:浮层行为配置,用于拖拽、缩放和尺寸约束
defaultFloatingState和floatingState不要同时传入
非受控浮层
非受控浮层通过 defaultFloatingState 设置初始位置和尺寸。
受控浮层
受控浮层以 floatingState 作为唯一状态源,组件始终按外部状态渲染。
后续通过 update:floatingState 通知外部同步。
浮层模式下的侧栏
浮层里同样可以放入侧栏、头部和主区。
代理滚动条
在布局组件中,消息列表通常作为内部滚动区域存在。但当消息列表的宽度小于外层容器宽度时,浏览器原生滚动条会出现在消息列表自身的右侧,而不是外层容器的右侧。
为了解决这个布局问题,布局组件内部使用 ProxyScrollbar 渲染代理滚动条。代理滚动条与消息列表平级,通过接收消息列表的 DOM 引用来同步真实滚动状态,并将滚动条视觉上渲染到外层容器右侧。
使用要求
- 将实际承担滚动的元素传给
scrollTarget。 - 为使
Layout.ProxyScrollbar正常生效,传给scrollTarget的滚动容器需要具备明确高度,并通过overflow: auto或overflow: scroll承担真实滚动。
scrollTarget 推荐的样式如下:
.scroll-host {
height: 100%;
overflow: auto;
}基本结构
<script setup lang="ts">
import { ref } from 'vue'
import { TrLayout } from '@opentiny/tiny-robot'
const messageListRef = ref<HTMLElement | null>(null)
</script>
<template>
<TrLayout>
<template #main>
<div ref="messageListRef" class="message-list">
<!-- messages -->
</div>
<TrLayout.ProxyScrollbar :scroll-target="messageListRef" />
</template>
</TrLayout>
</template>使用示例
注意事项
- 代理滚动条会默认给目标滚动元素添加以下样式,用于隐藏原生滚动条
.tr-layout-proxy-scrollbar-target {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}Layout.ProxyScrollbar仅用于代理滚动条显示与拖拽,不负责内容渲染或性能优化。传入ProxyScrollbar的scrollTarget必须是真正产生滚动的 DOM 元素。
侧栏开关
Layout.AsideToggle 是内置侧栏开关按钮,可以在 Layout 内部任意区域使用。
它给侧栏内容提供控制展开和收起的能力,默认插槽提供 { isOpen }。
Props
Layout
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
mode | 布局模式;normal 参与普通布局,floating 会脱离普通布局,不占原来的位置空间 | 'normal' | 'floating' | 'normal' |
leftAside | 左侧栏配置 | LayoutAsideOptions | - |
rightAside | 右侧栏配置 | LayoutAsideOptions | - |
floatingState | 受控浮层状态,需配合 update:floatingState 同步外部状态;不要和 defaultFloatingState 同时传入 | LayoutFloatingState | - |
defaultFloatingState | 非受控浮层初始状态,仅首次挂载读取一次;不要和 floatingState 同时传入 | LayoutFloatingState | - |
floatingOptions | 浮层行为配置,包括拖拽、缩放和尺寸约束;不参与状态控制 | LayoutFloatingOptions | - |
Layout.ProxyScrollbar
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
scrollTarget | 真实滚动容器的元素,或对应组件实例的 ref | LayoutScrollTarget | - |
Layout.AsideToggle
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
side | 控制的侧栏位置 | 'left' | 'right' | - |
Slots
Layout
| 插槽名 | 说明 | 作用域参数 |
|---|---|---|
left-aside | 左侧栏内容 | - |
header | 顶部区域 | - |
main | 主区内容 | - |
footer | 底部区域 | - |
right-aside | 右侧栏内容 | - |
Layout.AsideToggle
| 插槽名 | 说明 | 作用域参数 |
|---|---|---|
default | 自定义切换按钮内容 | { isOpen: boolean } |
Events
Layout
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:floatingState | 浮层位置或尺寸变化 | (value: LayoutFloatingState) |
aside-open-change | 侧栏开关变化 | (detail: LayoutAsideOpenDetail) |
left-aside-open-change | 左侧栏开关变化 | (detail: LayoutAsideOpenValue) |
right-aside-open-change | 右侧栏开关变化 | (detail: LayoutAsideOpenValue) |
aside-resize-start | 开始调整侧栏宽度 | (detail: LayoutAsideResizeDetail) |
aside-resize | 调整侧栏宽度时持续触发 | (detail: LayoutAsideResizeDetail) |
aside-resize-end | 结束调整侧栏宽度 | (detail: LayoutAsideResizeDetail) |
left-aside-resize-start | 开始调整左侧栏宽度 | (detail: LayoutAsideResizeValue) |
left-aside-resize | 调整左侧栏宽度时持续触发 | (detail: LayoutAsideResizeValue) |
left-aside-resize-end | 结束调整左侧栏宽度 | (detail: LayoutAsideResizeValue) |
right-aside-resize-start | 开始调整右侧栏宽度 | (detail: LayoutAsideResizeValue) |
right-aside-resize | 调整右侧栏宽度时持续触发 | (detail: LayoutAsideResizeValue) |
right-aside-resize-end | 结束调整右侧栏宽度 | (detail: LayoutAsideResizeValue) |
floating-drag-start | 开始拖动浮层 | (detail: LayoutFloatingDragDetail) |
floating-drag | 拖动浮层时持续触发 | (detail: LayoutFloatingDragDetail) |
floating-drag-end | 结束拖动浮层 | (detail: LayoutFloatingDragDetail) |
floating-resize-start | 开始调整浮层尺寸 | (detail: LayoutFloatingResizeDetail) |
floating-resize | 调整浮层尺寸时持续触发 | (detail: LayoutFloatingResizeDetail) |
floating-resize-end | 结束调整浮层尺寸 | (detail: LayoutFloatingResizeDetail) |
Types
LayoutAsideOptions
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
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
| 字段 | 说明 | 类型 |
|---|---|---|
side | 当前侧栏位置 | 'left' | 'right' |
open | 当前是否展开 | boolean |
LayoutAsideOpenValue
| 字段 | 说明 | 类型 |
|---|---|---|
open | 当前是否展开 | boolean |
LayoutAsideResizeDetail
| 字段 | 说明 | 类型 |
|---|---|---|
side | 当前侧栏位置 | 'left' | 'right' |
expandedWidth | 当前侧栏宽度 | number |
LayoutAsideResizeValue
| 字段 | 说明 | 类型 |
|---|---|---|
expandedWidth | 当前侧栏宽度 | number |
LayoutScrollTarget
HTMLElement | Pick<ComponentPublicInstance, '$el'> | null | undefined
LayoutFloatingState
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
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
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
draggable | 是否允许拖动浮层 | boolean | true |
resizable | 是否允许通过浮层边缘手柄调整尺寸 | boolean | false |
minWidth | 最小宽度 | number | 320 |
maxWidth | 最大宽度 | number | 视口宽度 |
minHeight | 最小高度 | number | 240 |
maxHeight | 最大高度 | number | 视口高度 |
LayoutFloatingDragDetail
与 LayoutFloatingState 一致。
LayoutFloatingResizeDetail
在 LayoutFloatingState 基础上增加以下字段:
| 字段 | 说明 | 类型 |
|---|---|---|
handle | 当前拖动的边或角 | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw' |
CSS 变量
布局基础
| 变量名 | 说明 |
|---|---|
--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 | 浮层层级 |
内容与交互
| 变量名 | 说明 |
|---|---|
--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 | 滑块激活颜色 |