Appearance
FPageLayout 页面布局
页面布局组件,集成 PageHeader 并提供标准的页面结构。
💡 组件命名规范:组件名主推
FPageLayout(保留兼容别名导出FinPageLayout)。
快速上手与演示
基础用法
最简单的用法,包含 header 和 body 区域。
loading
Card 模式
设置 mode="card" 启用卡片模式,body 区域会有白色背景和边框。
loading
固定底部
通过 footer-fixed 将底部操作栏固定在页面底部。
loading
标签页(插槽内容)
通过 tabs-config 声明标签页。tabs 区块渲染在 body 之下、footer 之上,与默认插槽平级且可同时展示 —— 主体表单放默认插槽,补充信息放 tabs。
标签内容通过具名插槽 tab-{key} 提供。插槽在父作用域求值,因此可以直接读写父组件的 ref,适合多个标签共享同一份表单数据的场景。
loading
标签页(组件内容)
若每个标签是各自独立、靠 props 驱动的模块,可直接在 tabs 里配置 component 与 componentProps,无需在模板中逐个写插槽。
配 v-model:active-tab 可由父级接管激活态;不配则由组件内部管理,默认激活第一个标签。
loading
完整示例
综合示例,展示所有功能:Card 模式、PageHeader 配置、固定底部操作栏。
loading
API
Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| mode | 布局模式 | 'card' | 'normal' | — | 'normal' |
| header | PageHeader 的配置项 | FPageHeaderProps | — | — |
| footer-fixed | 是否固定底部操作栏 | boolean | — | false |
| tabs-config | 标签页配置 | PageLayoutTabsConfig | — | — |
| active-tab | 当前激活的标签 key,支持 v-model:active-tab | string | number | — | 首个标签 |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:active-tab | 激活标签变更时触发 | (name: string | number) |
Slots
| 插槽名 | 说明 | 参数 |
|---|---|---|
| default | 页面主体内容 | — |
| tab- | 对应 key 标签的内容,优先级高于 component | { tab: PageLayoutTabItem } |
| other-content | body 与 tabs 之间的额外内容 | — |
| footer | 底部操作栏 | — |
类型定义
ts
import type { Component } from 'vue'
import type { TabsPropsPublic } from 'element-plus'
import type { FPageHeaderProps } from '@axewo/fin-ui'
/** 标签页激活标识(对应 ElTabPane 的 name) */
type PageLayoutTabName = string | number
/** 单个标签页配置 */
interface PageLayoutTabItem {
/** 标签页唯一标识,同时作为内容插槽名后缀(tab-{key}) */
key: PageLayoutTabName
/** 标签标题文本 */
label: string
/**
* 标签内容组件;适用于各自独立、靠 props 驱动的模块
* 若同时传入了 tab-{key} 插槽,则插槽优先
*/
component?: Component
/** 传递给 component 的 props */
componentProps?: Record<string, any>
/** 是否禁用 */
disabled?: boolean
}
/** 标签页整体配置 */
interface PageLayoutTabsConfig {
/** 标签页列表 */
tabs: PageLayoutTabItem[]
/**
* 透传给 ElTabs 的原生属性(type / tabPosition / stretch 等)
* 激活项由 PageLayout 内部管理,故排除 modelValue 与 defaultValue
*/
tabProps?: Omit<TabsPropsPublic, 'modelValue' | 'defaultValue'>
}
interface PageLayoutProps {
/** 布局模式:card 有白色背景和边框,normal 无背景 */
mode?: 'card' | 'normal'
/** PageHeader 配置 */
header?: FPageHeaderProps
/** 是否固定底部操作栏 */
footerFixed?: boolean
/**
* 标签页配置;作为独立区块渲染在 body 之下、footer 之上
* 与默认插槽平级,两者可同时存在
*/
tabsConfig?: PageLayoutTabsConfig
/** 当前激活的标签 key(配合 v-model:activeTab 实现受控) */
activeTab?: PageLayoutTabName
}最佳实践
使用场景
- 详情页:使用
normal模式展示详细信息 - 编辑页:使用
card模式 +footer-fixed提供表单编辑体验 - 列表页:通常不需要 PageLayout,直接使用 CrudTable
- 带标签的详情/编辑页:主体信息放默认插槽,条款/附件/日志等分类信息放
tabs-config
插槽还是 component?
两条路径的区别在作用域,按标签内容是否依赖父级状态来选:
- 具名插槽
tab-{key}:插槽在父作用域求值,标签内容能直接读写父组件的 ref。多个标签共享同一份表单数据时用这个。 component+componentProps:独立组件实例,拿不到父级作用域,数据全靠 props 进、事件出。标签是自洽模块时用这个,能省掉模板里逐个写插槽的重复结构。
两者可以混用 —— 同一个 tabs 数组里,部分标签配 component,部分留给插槽。同一个标签同时有插槽和 component 时插槽优先。
无论走哪条路径,tabs 数组都是必填的:label(标题文字)和顺序无法从插槽名或组件推导出来。
激活态
不传 active-tab 时由组件内部管理,默认激活第一个标签。传了则完全受控,组件只负责 emit,是否切换由父级决定 —— 因此可以拦截切换:
ts
function onActiveTabChange(key: string | number) {
if (key === 'audit' && !hasPermission.value) return // 不更新,标签不会切过去
activeTab.value = key
}若 tabs 动态变化导致当前激活的 key 不再存在,非受控模式下会自动回退到第一个标签。
样式说明
- Card 模式下 body 与 tabs 各自是一块白色卡片,带 8px 圆角、浅色边框和阴影
- 固定底部时会自动添加阴影效果