Skip to content

Commit 327c095

Browse files
committed
docs: finish docs
1 parent ac65e91 commit 327c095

File tree

12 files changed

+144
-84
lines changed

12 files changed

+144
-84
lines changed

docs/.vuepress/public/locale.gif

736 KB
Loading

docs/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
home: true
3-
heroText: umi 中台下总成
3+
heroText: umi 中台助手
44
tagline: 快速构建一个中台系统所需的基础元素
55
actionText: 新手上路 →
66
actionLink: /guide/
@@ -45,7 +45,7 @@ footer: MIT Licensed | Copyright © 2020-present Howard.Zuo
4545

4646
## 快速开始
4747

48-
安装 umi 下总成
48+
安装 umi 中台助手
4949

5050
```bash
5151
yarn global add yo generator-umi

docs/guide/data.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,24 @@
11
# 数据
2+
3+
数据管理使用详情,参考[plugin-model](https://umijs.org/zh-CN/plugins/plugin-model)
4+
5+
核心思路就是利用 `hooks` 来做 model,是有别于 `redux` 的另一种数据管理手段。
6+
7+
我们依旧以登录页面为例讲解。
8+
9+
`src/pages/o/login/index.tsx`
10+
11+
```typescript
12+
// 业务逻辑被提取到了 src/models/useLoginModel.ts 中
13+
// 本组件只用到了 initBackground
14+
const { initBackground } = useModel('useLoginModel', m => pick(m, 'initBackground'))
15+
16+
// 然后组件挂载后,执行该方法,我们就得到了一个酷炫的背景
17+
useEffect(() => {
18+
initBackground()
19+
}, [initBackground])
20+
```
21+
22+
至于这种新的风格,基于 `hooks` 的 model 如何编写? 那真的简单的一批,就是纯纯的 自定义 `hooks`
23+
24+
打开 `src/models/useLoginModel.ts` 看看就知道了

docs/guide/layout.md

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,48 @@
33
脚手架扩展了 `umi` 的布局内容,分两个方面:
44

55
1. 提供了一些默认的布局(`BLANK``PRO_LAYOUT`),可供选择
6-
2. 提供了布局选择器,在无需调整 `src/layouts/index.tsx` 的前提下,通过在 `src/layouts/options` 下按规则编写新的布局,然后在 `src/layouts/options/index.tsx` 将其注册到对应的路由形态的选择器列表里,就能在页面里使用了
6+
2. 提供了布局选择器,在无需调整布局入口 `src/layouts/index.tsx` 的前提下,通过在 `src/layouts/options` 下按规则编写新的布局,然后在 `src/layouts/options/index.tsx` 将其注册到对应的路由形态的选择器列表里,就能在页面里使用了
77

88
> 关于 路由形态,不清楚的朋友 [来来来](/guide/route.md)
99
10+
## 内置布局介绍
11+
12+
### BLANK
13+
14+
即:空布局,也是登录页面 `src/pages/o/login/index.tsx` 使用的布局。可以看到 登录页面里有指定其使用的布局为 `BLANK`,如下:
15+
16+
```typescript
17+
Login.layout = 'BLANK'
18+
```
19+
20+
### PRO_LAYOUT
21+
22+
即:ant-design pro 使用的由导航、侧边栏、内容区域 组成的布局。由于该布局导航里的用户信息,侧边栏的菜单都依赖用户数据,所以该布局只能被 AuthRequiredPage 使用。也是分析(`src/pages/dashboard/analysis/index.tsx`)、监控页面(`src/pages/dashboard/monitor/index.tsx`) 使用的布局。可以看到 这两个页面里有指定其使用的布局为 `PRO_LAYOUT`,如下:
23+
24+
```typescript
25+
Monitor.layout = 'PRO_LAYOUT'
26+
```
27+
1028
## 布局处理器列表
1129

1230
请看 `src/layouts/options/index.tsx`,找到如下代码:
1331

1432
```typescript
1533
// 路由形态为 OpenPage 时的布局处理器列表
16-
export const OPEN_LAYOUTS = [BlankResolver]
34+
const OPEN_LAYOUTS = [BlankResolver]
1735
// 路由形态为 AuthRequiredPage 时的布局处理器列表
18-
export const AUTH_REQUIRED_LAYOUTS = [ProLayoutResolver, BlankResolver]
36+
const AUTH_REQUIRED_LAYOUTS = [ProLayoutResolver, BlankResolver]
1937
```
2038

21-
路由选择器执行逻辑为,先在布局入口 `src/layouts/index.tsx` 进行路由形态判定,然后根据形态在对应的布局处理器列表进行依次判定(从左至右),第一个匹配到的布局处理器,会被拿来对路由进行最终渲染。
39+
路由处理器执行逻辑为,先在布局入口 `src/layouts/index.tsx` 进行路由形态判定,然后根据形态在对应的布局处理器列表进行依次判定(从左至右),第一个匹配到的布局处理器,会被拿来对路由进行最终渲染。
2240

2341
现在大家应该可以猜出,登录页面 (`/o/login`) 只会在 `OPEN_LAYOUTS` 里找合适的布局,而目前,`OPEN_LAYOUTS` 里,只有一个内置布局 `BLANK`,未来,开发者可以增加自己新的针对 OpenPage 的布局,只要添加到 `OPEN_LAYOUTS` 即可。
2442

43+
而 分析、监控 页面则只能被 `AUTH_REQUIRED_LAYOUTS` 注册使用,因为他们都需要用户登录,以及用户的特定权限。
44+
2545
## 路由指定布局
2646

27-
上面讲了布局处理器,那么我们开发一个页面,是如何指定这个页面使用哪个布局的呢? 记忆好的朋友已改已经想到 [路由](/guide/route.md#路由权限) ,提到的 `layout` 属性。没错,让我们再次打开 `src/pages/dashboard/analysis/index.tsx`
47+
上面讲了布局处理器,那么我们开发一个页面,是如何指定这个页面使用哪个布局的呢? 有朋友应该已经注意到 上面提到的 `layout` 属性。没错,让我们再次打开 `src/pages/dashboard/analysis/index.tsx`
2848

2949
```typescript
3050
import React from 'react'
@@ -45,7 +65,7 @@ Analysis.access = 'canReadDashboardAnalysis'
4565
export default Analysis
4666
```
4767

48-
## 自定义布局
68+
## 自定义布局处理器
4969

5070
我们以 `BLANK` 布局为例,打开 `src/layouts/options/Blank/index.tsx`
5171

@@ -58,10 +78,10 @@ import { isEmpty, pick } from '@/helpers/object'
5878
import Exception403 from '@/components/exception/403'
5979
import Exception404 from '@/components/exception/404'
6080

61-
import { ILayoutProps } from '@/types'
81+
import { ILayoutProps, ILayoutResolver, IERoute } from '@/types'
6282

63-
// props 必须是 ILayoutProps 或 其子集
64-
export default function Blank({ children, route, canAccess }: ILayoutProps) {
83+
// 布局组件 props 必须是 ILayoutProps
84+
function Blank({ children, route, canAccess }: ILayoutProps) {
6585
const { width, height } = useModel('useAppModel', m => pick(m, 'width', 'height'))
6686

6787
// 如果路由信息不存在,当然,显示404
@@ -74,24 +94,17 @@ export default function Blank({ children, route, canAccess }: ILayoutProps) {
7494
return <Exception403 style={{ width, height }} />
7595
}
7696

77-
// children 就是路由对应的页面组件
7897
return children
7998
}
80-
```
81-
82-
## 自定义布局处理器
83-
84-
我们依旧以 `BLANK` 的布局处理器为例(谁让它最简单呢),打开 `src/layouts/options/index.tsx`:
8599

86-
```typescript
87-
// 必须是 ILayoutResolver
100+
// 布局处理器
88101
const BlankResolver: ILayoutResolver = {
89-
// 用来判定布局入口传入的路由信息是否可以使用本布局渲染
102+
// 判定传入路由是否可以被当前布局渲染
90103
is(route?: IERoute): boolean {
91-
return isEmpty(route) || route!.layout === 'BLANK' || route?.path === '/'
104+
return isEmpty(route) || route!.layout === 'BLANK' || route!.path === '/'
92105
},
93-
// 用布局入口传入的 props 进行布局渲染。可以看到
94-
// 这里 routes 其实 Blank 布局并没有用到,所以没有传入,可以避免不要的刷新
106+
// 使用布局入口传入 props 渲染本布局
107+
// 不是所有参数都要传给布局,只传必须项可以减少不必要的 re-render
95108
get({ routes, children, route, canAccess }: ILayoutProps) {
96109
return (
97110
<Blank route={route} canAccess={canAccess}>
@@ -100,4 +113,8 @@ const BlankResolver: ILayoutResolver = {
100113
)
101114
}
102115
}
116+
117+
export default BlankResolver
103118
```
119+
120+
之后,只要把写好的布局处理器注册到 [布局处理器列表](/guide/layout.md#布局处理器列表) 里就可以使用了。

docs/guide/locale.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,22 @@
11
# 国际化
2+
3+
国际化使用详情,参考[plugin-locale](https://umijs.org/zh-CN/plugins/plugin-locale)
4+
5+
唯一要注意的是页面的 title。我们依旧以登录页面(`/src/pages/o/login/index.tsx`)为例:
6+
7+
```typescript
8+
function Login() {
9+
...
10+
...
11+
}
12+
13+
// title 在应用运行时,会被 src/locales/ 下对应语言文件里的内容替换
14+
Login.title = 'LOGIN_TITLE'
15+
Login.layout = 'BLANK'
16+
17+
export default Login
18+
```
19+
20+
请看:
21+
22+
<img :src="$withBase('/locale.gif')" alt="locale">

docs/guide/packaging.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33
## 打包到 .zip
44

55
在根目录直接运行 `bash ./shells/build.sh` 可以生成 `.zip` 包,讲其放置目标服务器上解压,进入解压后的目录,运行 `yarn serve` 即可启动应用。
6+
7+
## 打包到 docker 镜像
8+
9+
在根目录直接运行 `docker build --no-cache -t <镜像名>:<版本号> .` 可以生成 镜像。
10+
11+
使用 `docker container run -e "API_HOST=<后端API_HOST>" -e "STORAGE_DOMAIN=<token存放在cookie中的domain>" -p <宿主端口>:3000 <镜像名>:<版本号>`

generators/app/templates/src/layouts/options/Blank/index.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { isEmpty, pick } from '@/helpers/object'
66
import Exception403 from '@/components/exception/403'
77
import Exception404 from '@/components/exception/404'
88

9-
import { ILayoutProps } from '@/types'
9+
import { ILayoutProps, ILayoutResolver, IERoute } from '@/types'
1010

11-
export default function Blank({ children, route, canAccess }: ILayoutProps) {
11+
function Blank({ children, route, canAccess }: ILayoutProps) {
1212
const { width, height } = useModel('useAppModel', m => pick(m, 'width', 'height'))
1313

1414
if (isEmpty(route)) {
@@ -21,3 +21,18 @@ export default function Blank({ children, route, canAccess }: ILayoutProps) {
2121

2222
return children
2323
}
24+
25+
const BlankResolver: ILayoutResolver = {
26+
is(route?: IERoute): boolean {
27+
return isEmpty(route) || route!.layout === 'BLANK' || route?.path === '/'
28+
},
29+
get({ routes, children, route, canAccess }: ILayoutProps) {
30+
return (
31+
<Blank route={route} canAccess={canAccess}>
32+
{children}
33+
</Blank>
34+
)
35+
}
36+
}
37+
38+
export default BlankResolver

generators/app/templates/src/layouts/options/ProLayout/index.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import { Layout } from 'antd'
66
import Exception403 from '@/components/exception/403'
77
import Exception404 from '@/components/exception/404'
88

9-
import NavigationBar from './NavigationBar'
109
import SideBarTitle from './SideBarTitle'
1110
import SideBarMenu from './SideBarMenu'
1211

1312
import { isEmpty, pick } from '@/helpers/object'
1413

15-
import { ILayoutProps } from '@/types'
14+
import { ILayoutProps, ILayoutResolver, IERoute } from '@/types'
1615

1716
import styles from './index.less'
17+
import NavigationBar from './NavigationBar'
1818

19-
export default function ProLayout({ children, route, routes, canAccess }: ILayoutProps) {
19+
function ProLayout({ children, route, routes, canAccess }: ILayoutProps) {
2020
const { height } = useModel('useAppModel', m => pick(m, 'height'))
2121

2222
const { sidebarCollapsed, toggleSidebar } = useModel('useProLayoutModel', m =>
@@ -75,3 +75,18 @@ export default function ProLayout({ children, route, routes, canAccess }: ILayou
7575
</Layout>
7676
)
7777
}
78+
79+
const ProLayoutResolver: ILayoutResolver = {
80+
is(route?: IERoute): boolean {
81+
return isEmpty(route) || route!.layout === 'PRO_LAYOUT'
82+
},
83+
get({ routes, children, route, canAccess }: ILayoutProps) {
84+
return (
85+
<ProLayout routes={routes!} route={route} canAccess={canAccess}>
86+
{children}
87+
</ProLayout>
88+
)
89+
}
90+
}
91+
92+
export default ProLayoutResolver
Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,23 @@
1-
import React from 'react'
1+
import BlankResolver from './Blank'
2+
import ProLayoutResolver from './ProLayout'
3+
import { ILayoutProps, ILayoutResolver } from '@/types'
4+
import { isNotEmpty } from '@/helpers/object'
25

3-
import Blank from './Blank'
4-
import ProLayout from './ProLayout'
5-
import { isEmpty, isNotEmpty } from '@/helpers/object'
6-
7-
import { IERoute, ILayoutResolver, ILayoutProps } from '@/types'
8-
9-
const BlankResolver: ILayoutResolver = {
10-
is(route?: IERoute): boolean {
11-
return isEmpty(route) || route!.layout === 'BLANK' || route?.path === '/'
12-
},
13-
get({ routes, children, route, canAccess }: ILayoutProps) {
14-
return (
15-
<Blank route={route} canAccess={canAccess}>
16-
{children}
17-
</Blank>
18-
)
19-
}
20-
}
21-
22-
const ProLayoutResolver: ILayoutResolver = {
23-
is(route?: IERoute): boolean {
24-
return isEmpty(route) || route!.layout === 'PRO_LAYOUT'
25-
},
26-
get({ routes, children, route, canAccess }: ILayoutProps) {
27-
return (
28-
<ProLayout routes={routes!} route={route} canAccess={canAccess}>
29-
{children}
30-
</ProLayout>
31-
)
32-
}
33-
}
34-
35-
export const OPEN_LAYOUTS = [BlankResolver]
36-
export const AUTH_REQUIRED_LAYOUTS = [ProLayoutResolver, BlankResolver]
6+
const OPEN_LAYOUTS = [BlankResolver]
7+
const AUTH_REQUIRED_LAYOUTS = [ProLayoutResolver, BlankResolver]
378

389
export function resolveOpenPage({ routes, children, route, canAccess }: ILayoutProps) {
39-
const layout = OPEN_LAYOUTS.find(r => r.is(route))
40-
if (isNotEmpty<ILayoutResolver>(layout)) {
41-
return layout.get({ routes: routes!, children, route, canAccess })
10+
const resolver = OPEN_LAYOUTS.find(r => r.is(route))
11+
if (isNotEmpty<ILayoutResolver>(resolver)) {
12+
return resolver.get({ routes: routes!, children, route, canAccess })
4213
}
4314
throw new Error(`no proper layout found for ${route!.path}, please check your code`)
4415
}
4516

4617
export function resolveAuthRequiredPage({ routes, children, route, canAccess }: ILayoutProps) {
47-
const layout = AUTH_REQUIRED_LAYOUTS.find(r => r.is(route))
48-
if (isNotEmpty<ILayoutResolver>(layout)) {
49-
return layout.get({ routes: routes!, children, route, canAccess })
18+
const resolver = AUTH_REQUIRED_LAYOUTS.find(r => r.is(route))
19+
if (isNotEmpty<ILayoutResolver>(resolver)) {
20+
return resolver.get({ routes: routes!, children, route, canAccess })
5021
}
5122
throw new Error(`no proper layout found for ${route!.path}, please check your code`)
5223
}

generators/app/templates/src/models/useLoginModel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function useLoginModel() {
88
const [currentToken, setCurrentToken] = useState(getToken())
99
const [isRememberme, setCurrentRememberme] = useState(!!getRememberme())
1010

11-
const initBackgroud = useCallback(() => {
11+
const initBackground = useCallback(() => {
1212
loadScript('/bg/particles.js', 'particlesJS').then(particlesJS => {
1313
particlesJS.load('bg-animate', '/bg/particlesjs-config.json')
1414
})
@@ -47,7 +47,7 @@ export default function useLoginModel() {
4747
)
4848

4949
return {
50-
initBackgroud,
50+
initBackground,
5151
currentToken,
5252
isRememberme,
5353
toggleRememberme,

generators/app/templates/src/pages/o/login/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import styles from './index.less'
1313

1414
function Login() {
1515
const { initialState } = useModel('@@initialState')
16-
const { initBackgroud } = useModel('useLoginModel', m => pick(m, 'initBackgroud'))
16+
const { initBackground } = useModel('useLoginModel', m => pick(m, 'initBackground'))
1717
const { formatMessage } = useIntl()
1818

1919
useEffect(() => {
20-
initBackgroud()
21-
}, [initBackgroud])
20+
initBackground()
21+
}, [initBackground])
2222

2323
if (isNotEmpty(initialState) && !isString(initialState)) {
2424
return <Redirect to="/" />

generators/app/templates/src/types/layout.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,13 @@ export interface IERoute extends IRoute {
1010
export interface ILayoutProps {
1111
children: JSX.Element
1212
route: IERoute
13-
location: ILocation
14-
match: any
15-
history: History
16-
}
17-
18-
export interface IResolverOptions {
19-
routes: IERoute[]
20-
children: JSX.Element
21-
route: IERoute
13+
routes?: IERoute[]
2214
canAccess: boolean
2315
}
2416

2517
export interface ILayoutResolver {
2618
is(route?: IERoute): boolean
27-
get(options: IResolverOptions): JSX.Element
19+
get(options: ILayoutProps): JSX.Element
2820
}
2921

3022
export interface ILocation extends Location {

0 commit comments

Comments
 (0)