Warden layout
1. 项目说明
使用本框架前假设您具备一定的React、TypeScript及Webpack构建开发经验,并且对antd组件库umi框架有一定的了解。
2. 技术特性
- 多个登录页布局样式
- 多个主架页主题布局交叉切换
- 布局预留玻璃特效
- 结合分隔菜和Logo大小属性切换可演变更多的主题样式
- 支持全局动态国际化
- 已封装echarts图表组件化(React)
- 集成后端SSE消息推送
- 菜单支持多种权限集成方式
- 菜单图标支持选中模式
- 预留自定义皮肤方案
3. 开发手册
3.1. 安装方式
$ yarn add warden-layout
/** - or - */
$ npm add warden-layout
// package.json
"dependencies": {
"antd": "^5.21.0",
"umi": "^4.3.36",
...
}
// config.tsx
import { defineConfig } from "umi"
export default defineConfig({
plugins: [
'@umijs/plugins/dist/initial-state',
'@umijs/plugins/dist/model',
'@umijs/plugins/dist/access',
'@umijs/plugins/dist/locale'
],
initialState: {},
model: {},
access: {},
mfsu: false,
})
// src/layouts/index.tsx
import WardenLayout from 'WardenLayout'
export default() => {
return (
<WardenLayout />
)
}// src/layouts/company.tsx
import WardenLayout from 'WardenLayout'
export default() => {
return (
<WardenLayout />
)
}
// src/routes.tsx
export default [
{ path:"/", redirect:"/main", name:"Home", },
{ path:"/docs", component: "docs", name:"Docs" },
{ path:"/main", component:"@/layouts/company", name:"Main",
routes:[...]
}
]
3.2.布局配置
// src/layouts/company.tsx
import WardenLayout, { Warden } from 'WardenLayout'
export default() => {
const layoutConfig:Warden.IConfig = {
"theme": "dark", // 黑暗模式
"systemTheme": true, // 跟随系统主题模式
"layoutType": "headMenu", // 布局类型
"primaryColor": "#417ffb", // 主题颜色
"compact": false, // 紧凑模式
"leftMenuInline": true,
"backgroundBlur": true,
"leftEmptyHidden": true,
"menuSplit": true,
"brandLogo": "svg@/images/logo.svg",
"brandTitle": "app.main.brand.title",
"rootItemMenuGroup": false,
...
}
return (
<WardenLayout config={layoutConfig} />
)
}- theme
Theme - 明暗模式 - systemTheme
boolean - theme是否跟随系统 - layoutType
LayoutType - 布局类型 - primaryColor
string - 主题颜色 - menuSkin
string - 主题皮肤 - menuTransparent
boolean - 菜单背景透明 - backgroundBlur
boolean - 菜单背景模糊 - containerTransparent
boolean - 容器组件透明 - menuBackgroundStyle
MenuBackgroundStyle - 菜单背景独立样式 - menuIconSize
number - 菜单图标大小 - compact
boolean - 紧凑模式 - hideBorder
boolean - 隐藏线条 - menuSplit
boolean - 分隔菜单 - leftEmptyHidden
boolean - 左侧菜单为空隐藏 - subItemMenuTransparent
boolean - 子菜单背景透明 - leftMenuInline
boolean - 左侧菜单内联模式 - rootItemMenuGroup
boolean - 左侧菜单一级分组 - hideFooter
boolean - 全局隐藏页脚 - hideBreadcrumb
boolean - 全局隐藏面包屑 - hideTitleBar
boolean - 全局隐藏标题栏 - brandLogo
string - 品牌Logo - brandTitle
string - 品牌标题 - localeEnabled
boolean - 启用国际化(布局) - logoNavigateRoute
string - 点击logo导航路由 - avatarNavigateRoute
string - 形象导航路由 - avatarReplaceBrand
boolean - 形象替换品牌Logo - menuIconVariant
boolean | string[] - 菜单图标启用激活状态
// src/pages/welcome.tsx
import { useConfigContext } from 'WardenLayout'
import { Button } from 'antd'
export default() => {
const {config,setConfig} = useConfigContext()
return (
<div><Button onClick={()=>{setConfig({...config, primaryColor:"#ff6600"})}}>Change color</Button></div>
)
}3.3.布局属性
// Full screen switch icon, it receives an array, and the exit icon is placed in front of it
const layoutConfig:Warden.IConfig = { ... }
const screenIcons:JSX.Element[] = [
<SvgIcon src="/svg/menu/main-screen-in.svg" style={ {width:"16px",height:"16px"}} />,
<SvgIcon src="/svg/menu/main-screen-out.svg" style={ {width:"16px",height:"16px"}} />
]
return (
<WardenLayout
config={layoutConfig}
logoPopover={<CustomLogoPopoverPanel />}
toolbarUserPanel={<CustomToolbarUserPanel />}
toolbarButtons={[
<GithubButton key="githubButton" />,
<CustomBellButton key="bellButton" />,
<CustomThemeButton key="themeButton" />]}
screenIcons={screenIcons}
footer={<FooterPanel />}
/>
)- footer
JSX.Element - 全局容器页脚 - toolbarButtons
JSX.Element[] - 工具栏按钮 - avatarPopover
JSX.Element - 用户形象弹出面版 - logoPopover
JSX.Element - Logo悬停弹出面版 - leftExpandPanel
JSX.Element - 左侧栏底部扩展面版 - screenIcons
JSX.Element[] - 全屏切换图标 - wardenMenuData
IMenuData[] - 菜单数据
3.4. 菜单和路由
// config.tsx
import { defineConfig }from 'umi'
import routes from './routes'
export default defineConfig({
initialState: {},
model: {},
...,
routes: routes
})
➰菜单图标(icon):默认使用集成的
// routes.tsx
export default [
{ path:"/", icon:"CaretUpOutlined" ... },
{ path:"/docs", icon: "umi@local:doc" ... },
{ path:"/docs", icon: "img@/images/cb.png" ... },
{ path:"/main", icon: "svg@https://xxx.com/xxx.svg" ... },
]
menuIconVariant如果设为true表示启用默认给图标名后缀加上“Filled”和“Filled”字段进行切换选中状态,要确保图库和路径中有这些图标存在,否则显示异常,如果是自定义的后缀,可以配置一个2个长度的数组:['-unchecked','checked']
// src/layout/company.tsx
import WardenLayout, { Warden } from 'WardenLayout'
export default() => {
const layoutConfig:Warden.IConfig = {
"menuIconVariant": ['-unchecked','-checked'],
...
}
return (
<WardenLayout config={layoutConfig} />
)
}
// routes.tsx
export default [
{ path:"/main/discover/welcome", badge:"12" ... },
{ path:"/main/discover/introduce", badge:{ position:"left" } ... },
{ path:"/main/discover/message", tag:"New" ... },
]
菜单扩展项的动态更新需要用到消息总线,warden-layout已封装好了监听和抽象,注意这里没有用到钩子,而且是全局的消息事件,需要先用IExtraBadgeProps 或IExtraTagProps 构建消息体,然后利用原生CustomEvent 发送消息。
// src/pages/demo.tsx
import { IExtraBadgeProps,IExtraTagProps,IMenuMessageEvent } from 'WardenLayout'
import { Button } from 'antd'
const sendMenuMessage=(e:IMenuMessageEvent)=>{
const sender = new CustomEvent("menu-message",{detail:e})
window.dispatchEvent(sender)
}
export default() => {
// 向指定菜单发送随机汽泡信息
const randomMessage = () => {
const newCount = Math.floor(Math.random() * 100);
const badgeData:IExtraBadgeProps = {
filterKey:"path",
filterValue:"/main/discover/message",
data:{
count:newCount,
position:"left",
dot:false,
color:"red",
}
}
sendMenuMessage({id:"badge",data:badgeData})
}
// 向指定菜单发送标签信息
const setMenuTag = (data:any) => {
const tagData:IExtraTagProps = {
filterKey:"path",
filterValue:"/main/discover/introduce",
data
}
sendMenuMessage({id:"tag",data:tagData})
}
return (
<div>
<Button onClick={()=>{ randomMessage () }}>Send random badge</Button>
<Button onClick={()=>{ setMenuTag ({color:"green",text:"Success"}) }}>Send tag data</Button>
</div>
)
}
// routes.tsx
export default [
{ path:"/main/discover/welcome", access:"canRead" ... }, // umi的access鉴权方式
{ path:"/main/discover/introduce", authorities:[ "user:read","user:write" ] ... } // warden-layout 简洁方式
]
/** src/access.js
* umi的鉴权方式需要额外配置这里
*/
export default function (initialState) {
const { currentUser } = initialState;
return {
canRead:()=>{
return ["user:read","user:auery"].some(item=>currentUser.authorities.includes(item));
}
}
}
3.5. 环境变量
warden-layout自身有两个环境变量比较重要,ENABLE_SETTING 和ENABLE_CONFIG_STORAGE ,ENABLE_SETTING:用于开启抽屉配置组件,默认是关闭的,这个功能一般只是在开发环境下使用,但如果想在其它环境下使用配置开启即可(不推荐)。ENABLE_CONFIG_STORAGE:是开启配置实时存储功能,默认是开启的,如果你想固定配好的布局效果,请关闭它。
// config.tsx
import { defineConfig } from "umi"
export default defineConfig({
...
define: {
"process.env.ENABLE_SETTING": process.env.ENABLE_SETTING,
"process.env.ENABLE_CONFIG_STORAGE": process.env.ENABLE_CONFIG_STORAGE,
"process.env.API_URI": process.env.API_URI
}
})
// .env
ENABLE_SETTING = false
ENABLE_CONFIG_STORAGE = true
API_URI = https://api.warden.vip
3.6. 自定义皮肤
// src/commponents/AppSkin.tsx
import { Warden } from 'WardenLayout'
const defaultSkins:Warden.IMenuSkin[] = [
{
theme:"light",
primaryColor:"#358cf1",
name:"blueSky",
label:"blueSky",
backgroundImage:"/images/skins/blue-sky-bg.png",
icon:"/images/skins/blue-sky-small.png"
},{
theme:"light",
primaryColor:"#ff677b",
name:"pinkRomantic",
label:"pinkRomantic",
backgroundImage:"/images/skins/pink-romantic-bg.jpg",
icon:"/images/skins/pink-romantic-small.jpg"
}
...
]
// app.tsx
import { Warden } from 'WardenLayout'
import { defaultSkins } from './components/AppSkin'
export async function getInitialState():Promise<{
skins?:Warden.IMenuSkin[];
currentUser?:Warden.IUser;
getUserInfo?:() => Promise<Warden.IUser | undefined>;
}> {
const getUserInfo = async() => {
// 获取用户信息
const userInfo = {
...
}
return userInfo
}
return {
getUserInfo,
skins:defaultSkins, // 自定义皮肤挂载
currentUser:await getUserInfo()
}
}
// src/layout/company.tsx
import WardenLayout, { Warden } from 'WardenLayout'
export default() => {
const layoutConfig:Warden.IConfig = {
"menuSkin": "blueSky"
...
}
return (
<WardenLayout config={layoutConfig} />
)
}
// src/pages/welcome.tsx
import { useConfigContext } from 'WardenLayout'
import { Button } from 'antd'
export default() => {
const {config,setConfig} = useConfigContext()
return (
<div><Button onClick={()=>{setConfig({...config, menuSkin:"blueSky"})}}>Change color</Button></div>
)
}
- name
string - 皮肤唯一名称 - primaryColor
string - 主题颜色 - label
string - 皮肤显示名称(自动按skin.{name}.label进行国际化) - theme
Theme - 明暗主题 - layoutType
LayoutType - 布局类型 - menuBackgroundStyle
MenuBackgroundStyle - 菜单背景样式 - backgroundImage
string - 主体背景图片(与内容互斥) - icon
string - 皮肤缩略图 - content
JSX.Element - 主体背景内容(与图片互斥)
3.7.容器组件
// src/pages/welcome.tsx
import { Container } from 'WardenLayout'
import { Button } from 'antd'
export default() => {
return (
<Container mode="panel">
<div>
<Button>Change color</Button>
</div>
</Container>
)
}
- mode
ContainerMode - 容器模式 - hideTitle
boolean - 隐藏标题 - hideBreadcrumb
string - 隐藏面包屑导航 - hideFooter
Theme - 隐藏页脚 - transparent
LayoutType - 容器透明 - bordered
boolean - 是否带边框 - menuByBackground
boolean - 容器背景随菜单特效 - stretch
ContainerStretch - 容器高度伸缩模式
3.8. 所有类型参数
- : 明暗主题类型
- light
string - 明亮模式 - dark
string - 黑暗模式
- : 布局类型
- headMenu
string - 主菜单在顶部 - leftMenu
string - 主菜单在左侧
- : 主菜单背景样式
- normal
string - 默认随主题和皮肤 - black
string - 接近于黑色 - primary
string - 采用主题色
- : 容器模式
- none
string - 无内外边距 - box
string - 有20px外边距,背景透明 - panel
string - 内外都有20边距,有背景色
- : 容器高度拉伸模式(只在容器为panel模式下有效)
- none
string - 内容高度 - auto
string - 外边距会铺满但是透明的 - full
string - 带背景完全铺满
- : 菜单图标类型
- ant
string - ant-design/icons图标库 - umi
string - umi图库组件 - svg
string - svg图片 - img
string - png位图
4. 版本更新
- ➕ 增加皮肤加载效果
- 🔨 优化菜单popup状态下badge显示问题
- 🔨 修正全屏模式下切换路由产生的线节问题
- 💔 移除warden菜单图标
- 🔨 修正菜单国际化的问题
- 🔨 修正左侧品牌logo与用户头像切换的问题
- 🍹 优化菜单avatar弹出面版dark模式
- 🔨 修正皮肤切回纯色模式的显示bug
- ➕ 增加全局公共弹窗插槽
- ➕ 增加皮肤列表加载效果
- ➕ 增加强制菜单黑色背景
- ➕ 增加容器组件增加stretch高度填充功能
- 🍹 优化皮肤切换加载效果
- 🔨 修正菜单badge消息通知
- 🔨 修正菜单badge和tag折叠后的显示问题