Skip to content

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'
headerPageHeader 的配置项FPageHeaderProps——
footer-fixed是否固定底部操作栏boolean—false
tabs-config标签页配置PageLayoutTabsConfig——
active-tab当前激活的标签 key,支持 v-model:active-tabstring | number—首个标签

Events ​

事件名说明回调参数
update:active-tab激活标签变更时触发(name: string | number)

Slots ​

插槽名说明参数
default页面主体内容—
tab-对应 key 标签的内容,优先级高于 component{ tab: PageLayoutTabItem }
other-contentbody 与 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 圆角、浅色边框和阴影
  • 固定底部时会自动添加阴影效果