布局系统概述
介绍
布局系统是 RuoYi-Plus-UniApp 移动端应用的核心架构组件之一,基于 @uni-helper/vite-plugin-uni-layouts 插件实现,提供了灵活、强大且易于扩展的页面布局管理能力。通过布局系统,开发者可以统一管理应用中所有页面的布局结构,实现全局组件的复用、主题配置的集中管理以及页面样式的标准化配置。
布局系统采用了分层架构设计,将应用的布局层与页面内容层完全分离,使得每个页面可以专注于业务逻辑的实现,而无需关心通用的布局结构和全局组件。系统默认提供了一套完整的布局模板,包含主题配置、全局反馈组件(Toast、Notify、MessageBox)、授权组件等核心功能,同时支持开发者根据业务需求创建自定义布局模板。
核心特性:
- 插件化架构 - 基于 UniLayouts 插件实现,与 UniApp 生态无缝集成,提供开箱即用的布局管理能力
- 灵活配置 - 支持在 pages.json 和页面文件的 route 块中配置布局,支持全局配置和页面级配置
- 主题系统集成 - 内置主题配置提供者(ConfigProvider),通过 useTheme 组合式函数实现全局主题管理
- 全局组件复用 - 统一管理 Toast、Notify、MessageBox 等全局反馈组件,避免在每个页面重复引入
- 授权流程管理 - 集成 AuthModal 组件,支持头像、昵称、手机号等用户信息授权流程
- Route 块配置 - 支持在页面文件中通过
<route>块配置布局类型、页面样式、元数据等信息 - 多平台支持 - 完整支持 H5、微信小程序、App 等多个平台的特定配置和样式定制
- 开发体验优化 - 提供详细的配置注释和完整的 TypeScript 类型支持,提升开发效率
架构设计
整体架构
布局系统采用三层架构设计:
┌─────────────────────────────────────────┐
│ Vite Plugin Layer │
│ (UniLayouts 插件 - 构建时处理) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Layout Template Layer │
│ (布局模板 - 运行时容器) │
│ - default.vue (默认布局) │
│ - custom.vue (自定义布局) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Page Content Layer │
│ (页面内容 - 业务逻辑) │
└─────────────────────────────────────────┘插件层(Vite Plugin Layer)
插件层负责在构建时处理布局配置,将 pages.json 中的 layout 字段和页面文件中的 <route> 块配置解析并注入到最终的页面组件中。
插件配置:
// vite/plugins/index.ts
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
export default async ({ command, mode, env }) => {
const vitePlugins: any[] = []
// UniLayouts 插件配置
vitePlugins.push(UniLayouts())
// ... 其他插件
return vitePlugins
}工作流程:
- 解析配置 - 读取 pages.json 中的
layout字段和页面文件中的<route>块 - 模板匹配 - 根据
layout值查找对应的布局模板文件(如layouts/default.vue) - 组件包装 - 将页面组件包装到布局模板的插槽中
- 路由注册 - 将处理后的组件注册到 UniApp 路由系统
布局模板层(Layout Template Layer)
布局模板层定义了应用的整体布局结构,是所有页面的容器。每个布局模板都是一个 Vue 组件,通过插槽(slot)渲染具体的页面内容。
默认布局结构:
<!-- layouts/default.vue -->
<template>
<wd-config-provider :theme-vars="themeVars">
<!-- 页面内容插槽 -->
<slot />
<!-- 全局反馈组件 -->
<wd-toast />
<wd-notify />
<wd-message-box />
<!-- 授权组件 -->
<AuthModal />
</wd-config-provider>
</template>
<script lang="ts" setup>
const { themeVars } = useTheme({
// 主题变量覆盖
})
</script>布局模板职责:
- 主题配置 - 通过
wd-config-provider组件提供全局主题变量 - 全局组件 - 渲染 Toast、Notify、MessageBox 等全局反馈组件
- 业务组件 - 渲染 AuthModal 等业务相关的全局组件
- 内容插槽 - 提供
<slot />插槽渲染具体页面内容
页面内容层(Page Content Layer)
页面内容层是具体的业务页面组件,专注于业务逻辑的实现,无需关心布局结构和全局组件。
页面配置方式:
方式一:在 pages.json 中配置
{
"pages": [
{
"path": "pages/index/index",
"type": "home",
"layout": "default"
}
]
}方式二:在页面文件中使用 route 块配置
<template>
<view class="page">
<!-- 页面内容 -->
</view>
</template>
<route lang="json5">
{
layout: 'default',
type: 'page',
style: {
navigationStyle: 'custom'
}
}
</route>布局类型
Default 布局
默认布局是应用中最常用的布局模板,提供了完整的主题配置和全局组件支持。适用于大部分业务页面。
布局组成:
- ConfigProvider - 主题配置提供者
- Toast - 轻提示组件
- Notify - 消息通知组件
- MessageBox - 确认弹窗组件
- AuthModal - 授权弹窗组件
使用场景:
- 普通业务页面
- 列表页面
- 详情页面
- 表单页面
- 需要全局反馈组件的页面
配置示例:
{
"path": "pages/user/profile",
"type": "page",
"layout": "default"
}Custom 布局
自定义布局允许开发者根据特定业务需求创建专属的布局模板。适用于需要特殊布局结构的页面。
创建自定义布局:
<!-- layouts/custom.vue -->
<template>
<view class="custom-layout">
<!-- 自定义导航栏 -->
<view class="custom-navbar">
<slot name="navbar" />
</view>
<!-- 主内容区域 -->
<view class="custom-content">
<slot />
</view>
<!-- 自定义底部栏 -->
<view class="custom-footer">
<slot name="footer" />
</view>
</view>
</template>使用自定义布局:
<template>
<template #navbar>
<view>自定义导航栏内容</view>
</template>
<view>页面主内容</view>
<template #footer>
<view>自定义底部内容</view>
</template>
</template>
<route lang="json5">
{
layout: 'custom'
}
</route>核心组件
ConfigProvider 主题配置
wd-config-provider 组件是 WD UI 组件库的全局配置提供者,用于统一管理主题变量、深色模式等全局配置。
主要功能:
- 主题变量定制 - 支持覆盖组件库默认的主题变量
- 深色模式 - 支持 light/dark 两种主题模式
- 全局样式 - 为所有子组件提供统一的样式配置
基本用法:
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
</wd-config-provider>
</template>
<script lang="ts" setup>
import { useTheme } from '@/composables/useTheme'
const { themeVars } = useTheme({
colorTheme: '#409EFF',
colorSuccess: '#52C41A',
colorWarning: '#FFBA00',
colorDanger: '#F56C6C'
})
</script>主题变量类型:
interface ConfigProviderThemeVars {
// 基础色彩
colorTheme?: string // 主题色
colorSuccess?: string // 成功色
colorWarning?: string // 警告色
colorDanger?: string // 危险色
// Loading 组件
loadingSize?: string // Loading 大小
// MessageBox 组件
messageBoxTitleColor?: string // 标题颜色
messageBoxContentColor?: string // 内容颜色
// Notify 组件
notifyPadding?: string // 内边距
notifyFontSize?: string // 字体大小
// 导航栏
navbarTitleFontSize?: string // 标题字体大小
navbarTitleFontWeight?: string // 标题字体粗细
// ... 更多变量
}Toast 轻提示
wd-toast 组件用于显示轻量级的提示信息,支持成功、失败、警告、加载等多种状态。
使用方式:
import { useToast } from '@/wd'
const toast = useToast()
// 成功提示
toast.success('操作成功')
// 失败提示
toast.error('操作失败')
// 警告提示
toast.warning('请注意')
// 普通提示
toast.info('提示信息')
// 加载提示
toast.loading('加载中...')
// 关闭提示
toast.close()特点:
- 自动关闭(默认 2000ms)
- 支持自定义持续时间
- 支持图标和纯文本模式
- 支持多种提示类型
Notify 消息通知
wd-notify 组件用于显示顶部通知栏消息,适用于需要用户注意但不打断操作的场景。
使用方式:
import { useNotify } from '@/wd'
const notify = useNotify()
// 成功通知
notify.success('操作成功')
// 失败通知
notify.error('操作失败')
// 警告通知
notify.warning('请注意')
// 普通通知
notify.info('通知消息')特点:
- 从顶部滑入
- 支持自动关闭
- 支持多种通知类型
- 不阻断用户操作
MessageBox 确认弹窗
wd-message-box 组件用于显示需要用户确认的重要操作弹窗。
使用方式:
import { useMessageBox } from '@/wd'
const messageBox = useMessageBox()
// 确认对话框
messageBox.confirm('确定要删除这条数据吗?')
.then(() => {
console.log('用户点击了确定')
})
.catch(() => {
console.log('用户点击了取消')
})
// 警告对话框
messageBox.alert('这是一条警告信息')
.then(() => {
console.log('用户确认了警告')
})特点:
- 支持确认和取消操作
- 支持自定义标题和内容
- 支持 Promise 回调
- 阻断用户操作,强制用户做出选择
AuthModal 授权弹窗
AuthModal 组件是项目自定义的授权组件,用于获取用户的头像、昵称、手机号等信息。
主要功能:
- 头像授权 - 支持用户选择和上传头像
- 昵称授权 - 支持用户设置昵称
- 手机号授权 - 支持微信小程序手机号快捷授权
- 灵活控制 - 支持控制是否需要手机号授权
组件实现:
<template>
<wd-popup
v-model="userStore.authModalVisible"
position="bottom"
closable
>
<view class="p-4">
<!-- 标题 -->
<wd-text text="授权" size="38" />
<!-- 表单区域 -->
<wd-form ref="formRef" :model="form">
<!-- 头像选择 -->
<wd-cell title="头像">
<wd-button
type="icon"
:icon="form.avatar || 'camera'"
open-type="chooseAvatar"
@chooseavatar="chooseavatar"
/>
</wd-cell>
<!-- 昵称输入 -->
<wd-input
v-model="form.nickName"
label="昵称"
type="nickname"
/>
</wd-form>
<!-- 操作按钮 -->
<view class="mt-8">
<wd-button @click="reject">残忍拒绝</wd-button>
<wd-button type="success" @click="agree">立即授权</wd-button>
</view>
</view>
</wd-popup>
</template>
<script setup lang="ts">
const userStore = useUserStore()
const toast = useToast()
const form = ref({
avatar: '',
nickName: ''
})
// 选择头像
const chooseavatar = (detail) => {
// 上传头像逻辑
upload.fastUpload(detail.avatarUrl, {
onSuccess(res) {
form.value.avatar = res.url
}
})
}
// 同意授权
const agree = async () => {
const [err] = await updateUserProfile({
avatar: form.value.avatar,
nickName: form.value.nickName
})
if (!err) {
userStore.updateUserInfo(form.value)
userStore.authModalVisible = false
toast.success('授权成功!')
}
}
// 拒绝授权
const reject = () => {
userStore.authModalVisible = false
toast.warning('你拒绝了授权')
}
</script>使用场景:
- 用户首次登录
- 用户信息不完整时
- 需要更新用户信息时
- 特定功能需要授权时
控制授权弹窗:
// 显示授权弹窗
userStore.authModalVisible = true
// 设置是否需要手机号授权
userStore.requirePhoneAuth = true主题系统
useTheme 组合式函数
useTheme 是项目提供的主题管理组合式函数,用于统一管理应用的主题配置。
核心功能:
- 默认主题 - 提供组件库的默认主题配置
- 全局覆盖 - 支持全局主题变量覆盖
- 局部定制 - 支持页面级主题变量定制
- 响应式更新 - 主题变量变化时自动更新所有组件
基本用法:
import { useTheme } from '@/composables/useTheme'
// 使用默认主题
const { themeVars } = useTheme()
// 局部定制主题
const { themeVars } = useTheme({
colorTheme: '#409EFF',
colorSuccess: '#52C41A'
})
// 全局主题管理
const { setGlobalTheme, resetGlobalTheme, getCurrentTheme } = useTheme()
// 设置全局主题
setGlobalTheme({
colorTheme: '#ff4757'
})
// 重置全局主题
resetGlobalTheme()
// 获取当前主题
const currentTheme = getCurrentTheme()实现原理:
// composables/useTheme.ts
import type { ConfigProviderThemeVars } from '@/wd'
// 全局主题覆盖状态
const globalThemeOverrides = ref<Partial<ConfigProviderThemeVars>>({})
// 默认主题配置
const DEFAULT_THEME: ConfigProviderThemeVars = {
colorTheme: '#409EFF',
colorSuccess: '#52C41A',
colorWarning: '#FFBA00',
colorDanger: '#F56C6C',
loadingSize: '40rpx',
// ... 更多默认配置
}
export const useTheme = (localOverrides?: Partial<ConfigProviderThemeVars>) => {
// 按优先级合并配置:默认主题 < 全局覆盖 < 局部覆盖
const themeVars = computed<ConfigProviderThemeVars>(() => ({
...DEFAULT_THEME,
...globalThemeOverrides.value,
...localOverrides
}))
const setGlobalTheme = (overrides: Partial<ConfigProviderThemeVars>) => {
globalThemeOverrides.value = {
...globalThemeOverrides.value,
...overrides
}
}
const resetGlobalTheme = () => {
globalThemeOverrides.value = {}
}
const getCurrentTheme = () => themeVars.value
return {
themeVars,
setGlobalTheme,
resetGlobalTheme,
getCurrentTheme
}
}主题优先级:
局部覆盖 (最高) > 全局覆盖 > 默认主题 (最低)使用示例:
<!-- 页面级主题定制 -->
<template>
<wd-config-provider :theme-vars="themeVars">
<view class="page">
<wd-button type="primary">主题按钮</wd-button>
</view>
</wd-config-provider>
</template>
<script lang="ts" setup>
const { themeVars } = useTheme({
// 仅在此页面生效的主题变量
colorTheme: '#ff6b6b',
navbarTitleFontSize: '36rpx'
})
</script>// 全局主题切换
import { useTheme } from '@/composables/useTheme'
const { setGlobalTheme } = useTheme()
// 切换到暗黑主题
const switchToDarkTheme = () => {
setGlobalTheme({
colorTheme: '#2d2d2d',
colorSuccess: '#4ade80',
colorWarning: '#fbbf24',
colorDanger: '#ef4444'
})
}
// 切换到亮色主题
const switchToLightTheme = () => {
setGlobalTheme({
colorTheme: '#409EFF',
colorSuccess: '#52C41A',
colorWarning: '#FFBA00',
colorDanger: '#F56C6C'
})
}页面配置
pages.json 配置
在 pages.json 中配置页面的布局类型、页面类型等基本信息。
基础配置:
{
"pages": [
{
"path": "pages/index/index",
"type": "home",
"layout": "default"
},
{
"path": "pages/user/profile",
"type": "page",
"layout": "default"
}
]
}配置字段说明:
path- 页面路径(必填)type- 页面类型:page(普通页面)、home(首页)、tabbar(标签页)layout- 布局类型:default(默认布局)、custom(自定义布局)等
全局样式配置:
{
"globalStyle": {
"navigationBarTitleText": "应用标题",
"navigationStyle": "custom",
"mp-alipay": {
"transparentTitle": "always",
"titlePenetrate": "YES"
},
"h5": {
"titleNView": false
},
"app-plus": {
"bounce": "none"
}
}
}Route 块配置
在页面文件中使用 <route> 块可以更灵活地配置页面布局和样式。
基础配置:
<template>
<view class="page">页面内容</view>
</template>
<route lang="json5">
{
// 布局类型
layout: 'default',
// 页面类型
type: 'page',
// 页面样式配置
style: {
navigationStyle: 'custom',
navigationBarTitleText: '页面标题'
}
}
</route>完整配置示例:
<route lang="json5">
{
// ===== UniLayouts 插件专用 =====
layout: 'default', // 指定使用的布局模板
// ===== 页面类型 =====
type: 'page', // 'page' | 'home' | 'tabbar'
// ===== 页面样式配置 =====
style: {
// 导航栏配置
navigationStyle: 'default', // 'default' | 'custom'
navigationBarTitleText: '页面标题',
navigationBarBackgroundColor: '#000000',
navigationBarTextStyle: 'white', // 'black' | 'white'
navigationBarShadow: {
colorType: 1 // 0-无阴影,1-有阴影
},
// 窗口配置
backgroundColor: '#ffffff',
backgroundTextStyle: 'dark', // 'dark' | 'light'
backgroundColorTop: '#ffffff',
backgroundColorBottom: '#ffffff',
// 下拉刷新
enablePullDownRefresh: false,
onReachBottomDistance: 50,
// 其他配置
disableScroll: false,
titlePenetrate: 'NO', // 'YES' | 'NO'
// App 平台特有
'app-plus': {
bounce: 'vertical', // 'none' | 'vertical' | 'horizontal'
scrollIndicator: 'none', // 'none' | 'default'
animationType: 'slide-in-right',
animationDuration: 300,
titleNView: {
buttons: [
{
text: '按钮',
fontSize: '16px',
color: '#333333',
float: 'right'
}
]
}
},
// H5 平台特有
h5: {
pullToRefresh: {
color: '#2bd009'
},
titleNView: {
backgroundColor: '#f7f7f7',
buttons: [
{
text: '分享',
type: 'share'
}
]
}
},
// 小程序平台特有
'mp-weixin': {
shareElement: 'element-id' // 页面间共享元素
}
},
// ===== 自定义数据 =====
meta: {
auth: true, // 是否需要登录
title: '自定义标题',
description: '页面描述',
roles: ['admin', 'user'], // 权限控制
keepAlive: true, // 是否缓存页面
transition: 'slide-left' // 页面切换动画
}
}
</route>配置优先级:
页面 route 块配置 > pages.json 页面配置 > globalStyle 全局配置导航栏集成
自定义导航栏
项目默认使用自定义导航栏,通过 wd-navbar 组件实现。
基础用法:
<template>
<view class="page">
<wd-navbar title="页面标题" />
<view class="content">页面内容</view>
</view>
</template>导航栏配置:
<template>
<wd-navbar
title="页面标题"
:fixed="true"
:safe-area-inset-top="true"
left-arrow
@click-left="handleBack"
>
<!-- 左侧插槽 -->
<template #left>
<wd-icon name="arrow-left" />
</template>
<!-- 右侧插槽 -->
<template #right>
<wd-icon name="more" />
</template>
</wd-navbar>
</template>
<script lang="ts" setup>
const handleBack = () => {
uni.navigateBack()
}
</script>导航栏样式定制:
<template>
<wd-navbar
title="页面标题"
:custom-style="{
backgroundColor: '#409EFF',
color: '#ffffff'
}"
/>
</template>胶囊导航栏
针对微信小程序等平台,项目提供了 wd-navbar-capsule 组件,用于实现原生胶囊按钮的导航栏布局。
基础用法:
<template>
<wd-navbar-capsule title="页面标题" />
</template>特点:
- 自动适配微信小程序胶囊按钮位置
- 支持安全区域适配
- 支持自定义标题和背景色
底部标签栏集成
Tabbar 组件
底部标签栏使用 wd-tabbar 组件实现,支持图标、文字、徽标等多种形式。
基础用法:
<template>
<view class="tabbar-page">
<view class="content">页面内容</view>
<wd-tabbar v-model="active">
<wd-tabbar-item
v-for="item in tabbarItems"
:key="item.value"
:icon="item.icon"
:title="item.title"
:value="item.value"
@click="handleTabClick(item)"
/>
</wd-tabbar>
</view>
</template>
<script lang="ts" setup>
const active = ref('home')
const tabbarItems = ref([
{ value: 'home', title: '首页', icon: 'home' },
{ value: 'menu', title: '菜单', icon: 'menu' },
{ value: 'my', title: '我的', icon: 'user' }
])
const handleTabClick = (item) => {
uni.switchTab({
url: `/pages/${item.value}/${item.value}`
})
}
</script>项目中的 Tabbar 实现:
项目将 Tabbar 的每个标签页拆分为独立的 Vue 组件,便于维护和管理。
src/components/tabbar/
├── Home.vue # 首页标签内容
├── Menu.vue # 菜单标签内容
└── My.vue # 我的标签内容Home.vue 示例:
<template>
<view class="min-h-[100vh]">
<wd-navbar title="首页" />
<!-- 轮播图 -->
<wd-swiper :list="swiperList" />
<!-- 金刚区 -->
<wd-row :gutter="16">
<wd-col v-for="item in menuList" :key="item.title" :span="6">
<view class="menu-item" @click="handleMenuClick(item)">
<wd-icon :name="item.icon" :color="item.color" />
<wd-text :text="item.title" />
</view>
</wd-col>
</wd-row>
<!-- 商品列表 -->
<wd-paging :fetch="pageGoods">
<template #item="{ item }">
<wd-card>
<!-- 商品卡片内容 -->
</wd-card>
</template>
</wd-paging>
</view>
</template>
<script lang="ts" setup>
const swiperList = ref<string[]>([])
const menuList = ref([
{ title: '外卖', icon: 'goods', color: '#ff6b6b' },
{ title: '超市', icon: 'cart', color: '#4ecdc4' },
{ title: '水果', icon: 'apple', color: '#45b7d1' },
{ title: '药店', icon: 'bag-fill', color: '#96ceb4' }
])
</script>布局与路由
布局切换
不同的页面可以使用不同的布局模板,通过配置 layout 字段实现布局切换。
场景一:普通页面使用默认布局
{
"path": "pages/user/profile",
"layout": "default"
}场景二:登录页使用自定义布局
{
"path": "pages/auth/login",
"layout": "custom"
}场景三:页面内动态切换布局
虽然 UniLayouts 不支持运行时动态切换布局,但可以通过条件渲染实现类似效果:
<template>
<component :is="currentLayout">
<view class="content">页面内容</view>
</component>
</template>
<script lang="ts" setup>
import DefaultLayout from '@/layouts/default.vue'
import CustomLayout from '@/layouts/custom.vue'
const layoutType = ref('default')
const currentLayout = computed(() => {
return layoutType.value === 'default' ? DefaultLayout : CustomLayout
})
</script>分包配置
对于大型应用,可以使用分包功能将不同模块的页面分离,每个分包可以使用不同的布局。
分包配置:
{
"subPackages": [
{
"root": "pages-sub/admin",
"pages": [
{
"path": "user/user",
"type": "page",
"layout": "default"
}
]
},
{
"root": "pages-sub/customer",
"pages": [
{
"path": "order/list",
"type": "page",
"layout": "custom"
}
]
}
]
}分包布局优化:
为不同分包创建专属的布局模板,减少不必要的全局组件加载:
layouts/
├── default.vue # 主包默认布局
├── admin.vue # 管理分包布局
└── customer.vue # 客户分包布局API 文档
UniLayouts 配置
插件配置接口:
interface UniLayoutsOptions {
// 布局文件目录,默认为 'src/layouts'
layoutsDir?: string
// 默认布局,当页面未指定布局时使用
defaultLayout?: string
// 是否排除特定页面
exclude?: string[]
}使用示例:
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
vitePlugins.push(
UniLayouts({
layoutsDir: 'src/layouts',
defaultLayout: 'default',
exclude: ['pages/auth/login']
})
)Route 块配置接口
Route 块类型定义:
interface RouteConfig {
// 布局类型
layout?: string
// 页面类型
type?: 'page' | 'home' | 'tabbar'
// 页面样式配置
style?: PageStyle
// 自定义元数据
meta?: Record<string, any>
// 预加载规则
preloadRule?: PreloadRule
// 窗口表现
window?: WindowConfig
// 平台特定配置
'mp-alipay'?: Record<string, any>
'mp-baidu'?: Record<string, any>
'mp-toutiao'?: Record<string, any>
}
interface PageStyle {
// 导航栏配置
navigationStyle?: 'default' | 'custom'
navigationBarTitleText?: string
navigationBarBackgroundColor?: string
navigationBarTextStyle?: 'black' | 'white'
// 窗口配置
backgroundColor?: string
backgroundTextStyle?: 'dark' | 'light'
enablePullDownRefresh?: boolean
onReachBottomDistance?: number
// 平台特定样式
'app-plus'?: AppPlusStyle
h5?: H5Style
'mp-weixin'?: MpWeixinStyle
}useTheme API
函数签名:
function useTheme(
localOverrides?: Partial<ConfigProviderThemeVars>
): UseThemeReturn
interface UseThemeReturn {
// 响应式主题配置变量
themeVars: ComputedRef<ConfigProviderThemeVars>
// 设置全局主题覆盖
setGlobalTheme: (overrides: Partial<ConfigProviderThemeVars>) => void
// 重置全局主题覆盖
resetGlobalTheme: () => void
// 获取当前完整主题配置
getCurrentTheme: () => ConfigProviderThemeVars
}使用示例:
import { useTheme } from '@/composables/useTheme'
// 基础使用
const { themeVars } = useTheme()
// 局部定制
const { themeVars } = useTheme({
colorTheme: '#ff4757',
loadingSize: '48rpx'
})
// 全局主题管理
const { setGlobalTheme, resetGlobalTheme, getCurrentTheme } = useTheme()
// 设置全局主题
setGlobalTheme({
colorTheme: '#2d2d2d'
})
// 获取当前主题
const theme = getCurrentTheme()
console.log('当前主题配置:', theme)
// 重置主题
resetGlobalTheme()最佳实践
1. 合理选择布局模板
根据页面的实际需求选择合适的布局模板,避免加载不必要的全局组件。
推荐做法:
{
"pages": [
{
"path": "pages/index/index",
"layout": "default" // 普通页面使用默认布局
},
{
"path": "pages/auth/login",
"layout": "custom" // 登录页使用简洁的自定义布局
}
]
}不推荐做法:
{
"pages": [
{
"path": "pages/auth/login",
"layout": "default" // 登录页不需要 Toast、Notify 等全局组件
}
]
}2. 主题配置分层管理
将主题配置分为全局配置和局部配置,实现灵活的主题管理。
全局主题配置:
// composables/useAppTheme.ts
import { useTheme } from '@/composables/useTheme'
export const useAppTheme = () => {
const { setGlobalTheme } = useTheme()
// 亮色主题
const lightTheme = {
colorTheme: '#409EFF',
colorSuccess: '#52C41A',
colorWarning: '#FFBA00',
colorDanger: '#F56C6C'
}
// 暗黑主题
const darkTheme = {
colorTheme: '#2d2d2d',
colorSuccess: '#4ade80',
colorWarning: '#fbbf24',
colorDanger: '#ef4444'
}
const switchTheme = (theme: 'light' | 'dark') => {
setGlobalTheme(theme === 'light' ? lightTheme : darkTheme)
}
return { switchTheme }
}页面级主题定制:
<template>
<wd-config-provider :theme-vars="themeVars">
<view class="special-page">
<!-- 使用特殊主题色的页面 -->
</view>
</wd-config-provider>
</template>
<script lang="ts" setup>
const { themeVars } = useTheme({
colorTheme: '#ff6b6b' // 仅在此页面生效
})
</script>3. 优化全局组件使用
合理使用全局组件,避免在每个页面重复引入。
推荐做法:
<!-- layouts/default.vue -->
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<!-- 全局组件统一在布局中引入 -->
<wd-toast />
<wd-notify />
<wd-message-box />
</wd-config-provider>
</template><!-- pages/xxx/xxx.vue -->
<template>
<view class="page">
<!-- 直接使用全局组件的方法 -->
<wd-button @click="showToast">显示提示</wd-button>
</view>
</template>
<script lang="ts" setup>
import { useToast } from '@/wd'
const toast = useToast()
const showToast = () => {
toast.success('操作成功') // 无需在页面引入组件
}
</script>不推荐做法:
<!-- 每个页面都引入全局组件 -->
<template>
<view class="page">
<view class="content">内容</view>
<wd-toast /> <!-- ❌ 不推荐 -->
<wd-notify /> <!-- ❌ 不推荐 -->
</view>
</template>4. Route 块配置规范
使用 Route 块配置时,保持配置的一致性和可读性。
推荐做法:
<template>
<view class="page">页面内容</view>
</template>
<route lang="json5">
{
// 分组注释,提高可读性
// ===== 布局配置 =====
layout: 'default',
type: 'page',
// ===== 导航栏配置 =====
style: {
navigationStyle: 'custom',
navigationBarTitleText: '页面标题',
navigationBarBackgroundColor: '#ffffff'
},
// ===== 权限配置 =====
meta: {
auth: true,
roles: ['admin']
}
}
</route>不推荐做法:
<route lang="json5">
{
layout: 'default',
type: 'page',
style: { navigationStyle: 'custom', navigationBarTitleText: '页面标题', navigationBarBackgroundColor: '#ffffff' },
meta: { auth: true, roles: ['admin'] }
}
</route>5. 自定义布局的命名规范
创建自定义布局时,使用清晰的命名,便于维护和理解。
推荐命名:
layouts/
├── default.vue # 默认布局(包含所有全局组件)
├── simple.vue # 简洁布局(仅包含主题配置)
├── admin.vue # 管理后台布局
├── customer.vue # 客户端布局
└── auth.vue # 认证页面布局不推荐命名:
layouts/
├── default.vue
├── layout1.vue # ❌ 命名不明确
├── layout2.vue # ❌ 命名不明确
└── custom.vue # ❌ 过于通用6. 分包布局优化
为不同分包使用专属布局,减少主包体积。
推荐做法:
{
"subPackages": [
{
"root": "pages-sub/admin",
"pages": [
{
"path": "user/list",
"layout": "admin" // 使用管理后台专属布局
}
]
}
]
}<!-- layouts/admin.vue -->
<template>
<wd-config-provider :theme-vars="adminThemeVars">
<slot />
<!-- 仅包含管理后台需要的组件 -->
</wd-config-provider>
</template>常见问题
1. 布局不生效
问题描述:
配置了 layout 字段,但页面仍然没有使用指定的布局模板。
可能原因:
- UniLayouts 插件未正确配置
- 布局文件路径错误
- 布局文件命名错误
- 缓存未清除
解决方案:
检查插件配置:
// vite/plugins/index.ts
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
vitePlugins.push(UniLayouts()) // 确保插件已添加检查布局文件:
src/layouts/
├── default.vue # ✅ 正确:文件名小写,.vue 后缀
├── custom.vue # ✅ 正确
└── Default.vue # ❌ 错误:首字母大写清除缓存并重新构建:
# 删除缓存目录
rm -rf .vite
rm -rf node_modules/.vite
# 重新安装依赖
pnpm install
# 重新构建
pnpm dev:h52. 主题变量不生效
问题描述:
配置了主题变量,但组件样式没有改变。
可能原因:
theme-vars属性绑定错误- 主题变量名称错误
- ConfigProvider 组件未包裹目标组件
- 组件不支持该主题变量
解决方案:
检查 ConfigProvider 包裹:
<!-- ✅ 正确 -->
<template>
<wd-config-provider :theme-vars="themeVars">
<wd-button type="primary">按钮</wd-button>
</wd-config-provider>
</template>
<!-- ❌ 错误:Button 不在 ConfigProvider 内 -->
<template>
<view>
<wd-config-provider :theme-vars="themeVars" />
<wd-button type="primary">按钮</wd-button>
</view>
</template>检查主题变量名称:
// ✅ 正确
const { themeVars } = useTheme({
colorTheme: '#409EFF',
colorSuccess: '#52C41A'
})
// ❌ 错误:变量名错误
const { themeVars } = useTheme({
primaryColor: '#409EFF', // 应该是 colorTheme
successColor: '#52C41A' // 应该是 colorSuccess
})检查组件支持的主题变量:
查阅组件文档,确认组件支持的主题变量列表。不是所有组件都支持所有主题变量。
3. 全局组件方法调用失败
问题描述:
调用 toast.success() 等方法时报错或无效果。
可能原因:
- 全局组件未在布局中引入
- 组件方法导入错误
- 组件未正确初始化
解决方案:
确保布局中引入了全局组件:
<!-- layouts/default.vue -->
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<!-- 必须引入这些全局组件 -->
<wd-toast />
<wd-notify />
<wd-message-box />
</wd-config-provider>
</template>正确导入组件方法:
// ✅ 正确
import { useToast, useNotify, useMessageBox } from '@/wd'
const toast = useToast()
const notify = useNotify()
const messageBox = useMessageBox()
toast.success('成功')
notify.info('通知')
// ❌ 错误
import { Toast } from '@/wd' // 不存在的导出
Toast.success('成功')4. Route 块配置不生效
问题描述:
在页面文件中配置了 <route> 块,但配置没有生效。
可能原因:
- route 块语法错误
- route 块位置错误
- UniPages 插件配置问题
- JSON5 格式错误
解决方案:
检查 route 块语法:
<!-- ✅ 正确 -->
<template>
<view>内容</view>
</template>
<route lang="json5">
{
layout: 'default',
type: 'page'
}
</route>
<script setup>
// 脚本内容
</script>
<!-- ❌ 错误:route 块必须在 script 之前 -->
<template>
<view>内容</view>
</template>
<script setup>
// 脚本内容
</script>
<route lang="json5">
{
layout: 'default'
}
</route>检查 JSON5 格式:
<!-- ✅ 正确 -->
<route lang="json5">
{
layout: 'default', // JSON5 支持单引号和尾随逗号
type: 'page',
}
</route>
<!-- ❌ 错误:JSON 不支持尾随逗号 -->
<route lang="json">
{
"layout": "default",
"type": "page",
}
</route>确保 UniPages 插件配置正确:
// vite/plugins/index.ts
import createUniPages from './uni-pages'
vitePlugins.push(createUniPages(mode)) // 确保 UniPages 插件在 UniLayouts 之前
vitePlugins.push(UniLayouts())5. 授权弹窗不显示
问题描述:
设置了 userStore.authModalVisible = true,但授权弹窗没有显示。
可能原因:
- AuthModal 组件未在布局中引入
- userStore 状态未正确初始化
- 弹窗被其他元素遮挡
- Popup 组件配置问题
解决方案:
确保 AuthModal 在布局中引入:
<!-- layouts/default.vue -->
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<wd-toast />
<wd-notify />
<wd-message-box />
<!-- 必须引入 AuthModal -->
<AuthModal />
</wd-config-provider>
</template>
<script setup>
import AuthModal from '@/components/auth/AuthModal.vue'
</script>检查 userStore 状态:
// 使用前确保 store 已初始化
const userStore = useUserStore()
// 显示授权弹窗
userStore.authModalVisible = true
// 检查状态是否正确设置
console.log('授权弹窗状态:', userStore.authModalVisible)检查 Popup 组件配置:
<!-- AuthModal.vue -->
<template>
<wd-popup
v-model="userStore.authModalVisible"
position="bottom"
closable
:z-index="9999" <!-- 确保 z-index 足够高 -->
>
<!-- 弹窗内容 -->
</wd-popup>
</template>6. 页面样式配置冲突
问题描述:
同时在 pages.json 和 route 块中配置了页面样式,但实际生效的配置不符合预期。
可能原因:
配置优先级不清楚,导致配置被覆盖。
解决方案:
理解配置优先级:
页面 route 块配置(最高)> pages.json 页面配置 > globalStyle 全局配置(最低)推荐只在一处配置:
<!-- 推荐:统一使用 route 块配置 -->
<template>
<view class="page">内容</view>
</template>
<route lang="json5">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '页面标题'
}
}
</route>// 或统一使用 pages.json 配置
{
"pages": [
{
"path": "pages/index/index",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "页面标题"
}
}
]
}避免重复配置:
<!-- ❌ 不推荐:同时在两处配置 -->
<route lang="json5">
{
style: {
navigationBarTitleText: '标题A' // 这个会生效
}
}
</route>{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "标题B" // 这个会被覆盖
}
}
]
}