Appearance
BizSplitPane 分栏面板
可拖拽调宽的分栏容器:horizontal 左右 / vertical 上下分栏,拖拽分割线实时调整(px 边界 clamp、rAF 节流)、v-model:split 比例双向同步、min / max px 边界、双击分割线复位、persist-key 本地记忆宽度、ResizeObserver 容器自适应收敛、支持嵌套。「左树右表」经典布局(配合 BizTree)、列表-详情双栏(邮件 / 工单)。
vertical 模式下
left槽为上侧、right槽为下侧。
基础用法
全部商品
数码家电
服饰鞋包
食品生鲜
左右分栏 +
BizTree 组合成「左树右表」管理页布局,min 限定左栏最小宽度。<script setup lang="ts">
/** 场景:左树右表——split-pane 搭配 BizTree 的经典管理页布局 */
import { ref } from 'vue'
import { ElTable, ElTableColumn } from 'element-plus'
import { BizSplitPane, BizTree } from 'vue3-ep-biz-lib'
import type { BizTreeNodeData } from 'vue3-ep-biz-lib'
const treeData: BizTreeNodeData[] = [
{
id: 1,
label: '全部商品',
children: [
{ id: 11, label: '数码家电', isLeaf: true },
{ id: 12, label: '服饰鞋包', isLeaf: true },
{ id: 13, label: '食品生鲜', isLeaf: true },
],
},
]
const rows = ref([
{ id: 1, name: '无线降噪耳机', price: '¥899', stock: 320 },
{ id: 2, name: '智能手表 Pro', price: '¥1,299', stock: 156 },
{ id: 3, name: '机械键盘 87 键', price: '¥399', stock: 512 },
{ id: 4, name: '4K 显示器 27 寸', price: '¥2,199', stock: 87 },
])
</script>
<template>
<div style="height: 340px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane :split="0.3" :min="160">
<template #left>
<BizTree :data="treeData" default-expand-all :height="300" />
</template>
<template #right>
<ElTable :data="rows" size="small">
<ElTableColumn prop="id" label="ID" width="60" />
<ElTableColumn prop="name" label="商品" />
<ElTableColumn prop="price" label="价格" width="100" />
<ElTableColumn prop="stock" label="库存" width="80" />
</ElTable>
</template>
</BizSplitPane>
</div>
</template>
显示代码
上下分栏
本周迭代计划评审通知
产品团队
服务器资源巡检报告
运维中心
新同事入职欢迎信
人力资源
本周迭代计划评审通知
来自 产品团队 …(详情内容区域,拖动中间分割线可调整上下比例,双击复位 42%)
direction="vertical" 列表 + 详情上下布局,双击分割线复位。<script setup lang="ts">
/** 场景:邮件双栏——上下分栏(列表 + 详情),vertical 模式 */
import { ref } from 'vue'
import { BizSplitPane } from 'vue3-ep-biz-lib'
const mails = [
{ id: 1, title: '本周迭代计划评审通知', from: '产品团队' },
{ id: 2, title: '服务器资源巡检报告', from: '运维中心' },
{ id: 3, title: '新同事入职欢迎信', from: '人力资源' },
]
const current = ref(mails[0])
</script>
<template>
<div style="height: 320px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane direction="vertical" :split="0.42" :min="100">
<template #left>
<div
v-for="mail in mails"
:key="mail.id"
:style="{
padding: '8px 10px',
cursor: 'pointer',
borderRadius: '4px',
background: current.id === mail.id ? 'var(--el-color-primary-light-9)' : 'transparent',
}"
@click="current = mail"
>
<div style="font-size: 13px; font-weight: 500">{{ mail.title }}</div>
<div style="font-size: 12px; color: var(--el-text-color-secondary)">{{ mail.from }}</div>
</div>
</template>
<template #right>
<div style="padding: 10px; font-size: 13px; color: var(--el-text-color-regular)">
<h4 style="margin: 0 0 8px">{{ current.title }}</h4>
<p style="color: var(--el-text-color-secondary); line-height: 1.8">
来自 {{ current.from }} …(详情内容区域,拖动中间分割线可调整上下比例,双击复位 42%)
</p>
</div>
</template>
</BizSplitPane>
</div>
</template>
显示代码
受控与边界
左栏比例
左栏(min 120px / max 520px)
右栏自适应
滑杆与拖拽双向联动:当前 50%
v-model:split 与滑杆双向联动;min / max 为 px 边界(拖拽与受控均收敛)。<script setup lang="ts">
/** 场景:受控与边界——v-model:split 比例实时回显,min/max px 边界 */
import { ref } from 'vue'
import { ElSlider } from 'element-plus'
import { BizSplitPane } from 'vue3-ep-biz-lib'
const split = ref(0.5)
</script>
<template>
<div>
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 12px; max-width: 480px">
<span style="font-size: 13px; flex-shrink: 0">左栏比例</span>
<ElSlider v-model="split" :min="0.1" :max="0.9" :step="0.05" />
</div>
<div style="height: 240px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane v-model:split="split" :min="120" :max="520">
<template #left>
<div style="padding: 12px; font-size: 13px; color: #909399">
左栏(min 120px / max 520px)
</div>
</template>
<template #right>
<div style="padding: 12px; font-size: 13px; color: #909399">右栏自适应</div>
</template>
</BizSplitPane>
</div>
<p style="margin-top: 10px; font-size: 13px; color: #909399">
滑杆与拖拽双向联动:当前 {{ (split * 100).toFixed(0) }}%
</p>
</div>
</template>
显示代码
宽度记忆
导航区(拖动后刷新页面,宽度保持)
内容区
persist-key="biz-split-pane-demo-nav":拖拽宽度写入 localStorage,下次挂载优先恢复
persist-key 拖拽宽度写入 localStorage,刷新后恢复上次位置(优先于初始 split)。<script setup lang="ts">
/** 场景:宽度记忆——persistKey 写入 localStorage,刷新后恢复上次拖拽宽度 */
import { BizSplitPane } from 'vue3-ep-biz-lib'
</script>
<template>
<div>
<div style="height: 220px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane :split="0.4" :min="120" persist-key="biz-split-pane-demo-nav">
<template #left>
<div style="padding: 12px; font-size: 13px; color: #909399">
导航区(拖动后刷新页面,宽度保持)
</div>
</template>
<template #right>
<div style="padding: 12px; font-size: 13px; color: #909399">内容区</div>
</template>
</BizSplitPane>
</div>
<p style="margin-top: 10px; font-size: 13px; color: #909399">
persist-key="biz-split-pane-demo-nav":拖拽宽度写入 localStorage,下次挂载优先恢复
</p>
</div>
</template>
显示代码
嵌套分栏
左上:数据列表(内部再分栏)
左下:图表区
右侧详情栏(双击任意分割线可复位该栏比例)
split-pane 嵌套组合复杂布局;
resize 事件拖拽中持续外抛(含 px 与总宽)。<script setup lang="ts">
/** 场景:嵌套分栏——split-pane 嵌套(左内再上下分),resize 事件外抛 */
import { ElMessage } from 'element-plus'
import { BizSplitPane } from 'vue3-ep-biz-lib'
const onResize = (startPx: number, totalPx: number): void => {
/* 拖拽中高频触发,实际业务可做防抖;此处仅示意 */
void startPx
void totalPx
}
</script>
<template>
<div style="height: 320px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane :split="0.55" :min="180" @resize="onResize">
<template #left>
<div style="height: 100%; padding: 4px">
<BizSplitPane direction="vertical" :split="0.6" :min="90">
<template #left>
<div style="padding: 10px; font-size: 13px; color: #909399">
左上:数据列表(内部再分栏)
</div>
</template>
<template #right>
<div style="padding: 10px; font-size: 13px; color: #909399">左下:图表区</div>
</template>
</BizSplitPane>
</div>
</template>
<template #right>
<div style="padding: 10px; font-size: 13px; color: #909399">
右侧详情栏(双击任意分割线可复位该栏比例)
<br /><br />
<ElButton size="small" @click="ElMessage.info('resize 事件拖拽中持续外抛')">
说明
</ElButton>
</div>
</template>
</BizSplitPane>
</div>
</template>
显示代码
外部控制
左栏
右栏
expose
setSplit(ratio) 按比例设置、getStartPx() 读取当前左侧 px 宽度。<script setup lang="ts">
/** 场景:外部控制——setSplit / getStartPx expose,按钮驱动分栏 */
import { ref } from 'vue'
import { ElButton, ElButtonGroup } from 'element-plus'
import { BizSplitPane } from 'vue3-ep-biz-lib'
import type { BizSplitPaneInstance } from 'vue3-ep-biz-lib'
const paneRef = ref<BizSplitPaneInstance>()
const currentPx = ref(0)
</script>
<template>
<div>
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 8px">
<ElButtonGroup>
<ElButton size="small" @click="paneRef?.setSplit(0.2)">20%</ElButton>
<ElButton size="small" @click="paneRef?.setSplit(0.5)">50%</ElButton>
<ElButton size="small" @click="paneRef?.setSplit(0.8)">80%</ElButton>
</ElButtonGroup>
<ElButton size="small" @click="currentPx = paneRef?.getStartPx() ?? 0">
读取左侧宽度
</ElButton>
<span v-if="currentPx" style="font-size: 13px; color: #909399">{{ currentPx }}px</span>
</div>
<div style="height: 200px; border: 1px solid #e4e7ed; border-radius: 4px; padding: 8px">
<BizSplitPane ref="paneRef" :split="0.5" :min="100">
<template #left>
<div style="padding: 12px; font-size: 13px; color: #909399">左栏</div>
</template>
<template #right>
<div style="padding: 12px; font-size: 13px; color: #909399">右栏</div>
</template>
</BizSplitPane>
</div>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| direction | 'horizontal' | 'vertical' | 'horizontal' | 排列方向 |
| split | number | 0.5 | 左(上)侧占比 0~1(v-model:split,拖拽实时同步) |
| min | number | 80 | 左(上)侧最小尺寸(px) |
| max | number | 容器宽-40 | 左(上)侧最大尺寸(px) |
| persist-key | string | '' | 本地记忆 key(拖拽后写入 localStorage,挂载时恢复) |
| reset-on-dblclick | boolean | true | 双击分割线复位到初始 split |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:split | (value: number) | 占比变化(v-model:split) |
| resize | (startPx, totalPx) | 拖拽中持续外抛(高频,业务侧自行防抖) |
Slots
| 插槽名 | 说明 |
|---|---|
| left | 左(vertical 为上)侧面板 |
| right | 右(vertical 为下)侧面板 |
Expose
| 名称 | 类型 | 说明 |
|---|---|---|
| setSplit | (ratio: number) => void | 按比例设置左(上)侧占比 |
| getStartPx | () => number | 当前左(上)侧 px 尺寸 |
TIP
拖拽经 setPointerCapture + window pointermove 实现,快速拖出容器边界不丢事件;面板内容超出自动滚动(pane 自带 overflow: auto)。