@creekjs/umi-plugins 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +326 -0
- package/dist/creek-layout/index.d.ts +3 -0
- package/dist/creek-layout/index.js +193 -0
- package/dist/creek-layout/template/icons.tpl +6 -0
- package/dist/creek-layout/template/layout.tpl +123 -0
- package/dist/creek-layout/template/runtime-config.type.tpl +5 -0
- package/dist/creek-layout/template/runtime.tpl +41 -0
- package/dist/creek-layout/template/type.tpl +36 -0
- package/dist/open-api/index.d.ts +3 -0
- package/dist/open-api/index.js +76 -0
- package/dist/template/locale/i18N-package.tpl +16 -0
- package/dist/template/locale/i18N.tpl +15 -0
- package/dist/template/locale/index.tpl +2 -0
- package/dist/template/request/index.tpl +8 -0
- package/dist/template/request/request.tpl +580 -0
- package/dist/template/request/runtime-config.tpl +6 -0
- package/dist/template/request/type.tpl +6 -0
- package/dist/utils/file.d.ts +4 -0
- package/dist/utils/file.js +74 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +27 -0
- package/dist/utils/spawn.d.ts +5 -0
- package/dist/utils/spawn.js +71 -0
- package/dist/utils/withTmpPath.d.ts +6 -0
- package/dist/utils/withTmpPath.js +39 -0
- package/package.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# @creekjs/umi-plugins
|
|
2
|
+
|
|
3
|
+
Umi 框架的插件集合,提供布局管理和 OpenAPI 集成功能。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- 🎨 Creek Layout - 智能布局插件
|
|
8
|
+
- 📡 OpenAPI 集成 - 自动生成 API 服务
|
|
9
|
+
- 🔧 开箱即用的配置
|
|
10
|
+
- 🛠️ TypeScript 支持
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @creekjs/umi-plugins --save-dev
|
|
16
|
+
# 或
|
|
17
|
+
yarn add @creekjs/umi-plugins -D
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 插件列表
|
|
21
|
+
|
|
22
|
+
### 1. Creek Layout 插件
|
|
23
|
+
|
|
24
|
+
智能布局插件,自动处理路由图标和布局配置。
|
|
25
|
+
|
|
26
|
+
#### 配置
|
|
27
|
+
|
|
28
|
+
在 `.umirc.ts` 或 `config/config.ts` 中配置:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
export default {
|
|
32
|
+
plugins: [
|
|
33
|
+
'@creekjs/umi-plugins/creek-layout'
|
|
34
|
+
],
|
|
35
|
+
// 插件会自动处理布局相关配置
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### 功能特性
|
|
40
|
+
|
|
41
|
+
- **自动图标检测**: 自动检测和配置 Ant Design 图标
|
|
42
|
+
- **路由增强**: 为路由自动添加图标和布局信息
|
|
43
|
+
- **类型安全**: 提供完整的 TypeScript 类型支持
|
|
44
|
+
|
|
45
|
+
#### 路由配置示例
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// config/routes.ts
|
|
49
|
+
export default [
|
|
50
|
+
{
|
|
51
|
+
path: '/',
|
|
52
|
+
component: '@/layouts/index',
|
|
53
|
+
routes: [
|
|
54
|
+
{
|
|
55
|
+
path: '/dashboard',
|
|
56
|
+
name: '仪表盘',
|
|
57
|
+
icon: 'DashboardOutlined', // 插件会自动验证图标
|
|
58
|
+
component: './Dashboard'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
path: '/users',
|
|
62
|
+
name: '用户管理',
|
|
63
|
+
icon: 'UserOutlined',
|
|
64
|
+
component: './Users'
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. OpenAPI 插件
|
|
72
|
+
|
|
73
|
+
集成 OpenAPI 服务生成功能到 Umi 构建流程。
|
|
74
|
+
|
|
75
|
+
#### 配置
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// .umirc.ts
|
|
79
|
+
export default {
|
|
80
|
+
plugins: [
|
|
81
|
+
'@creekjs/umi-plugins/open-api'
|
|
82
|
+
],
|
|
83
|
+
openApi: {
|
|
84
|
+
requestLibPath: '@/utils/request',
|
|
85
|
+
schemaPath: 'https://api.example.com/swagger.json',
|
|
86
|
+
serversPath: './src/services',
|
|
87
|
+
projectName: 'myProject'
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### 配置选项
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
interface OpenApiConfig {
|
|
96
|
+
requestLibPath?: string; // 请求库路径
|
|
97
|
+
requestOptionsType?: string; // 请求选项类型
|
|
98
|
+
requestImportStatement?: string; // 请求导入语句
|
|
99
|
+
apiPrefix?: string | ((params: ApiPrefixParams) => string); // API 前缀
|
|
100
|
+
serversPath?: string; // 服务输出路径
|
|
101
|
+
schemaPath?: string; // OpenAPI 规范文件路径
|
|
102
|
+
projectName?: string; // 项目名称
|
|
103
|
+
namespace?: string; // 命名空间
|
|
104
|
+
enumStyle?: 'string-literal' | 'enum'; // 枚举样式
|
|
105
|
+
nullable?: boolean; // 是否允许 null
|
|
106
|
+
templatesFolder?: string; // 模板文件夹
|
|
107
|
+
dataFields?: string[]; // 数据字段
|
|
108
|
+
isCamelCase?: boolean; // 是否使用驼峰命名
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### 多配置支持
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// 支持多个 API 源
|
|
116
|
+
export default {
|
|
117
|
+
openApi: [
|
|
118
|
+
{
|
|
119
|
+
requestLibPath: '@/utils/request',
|
|
120
|
+
schemaPath: 'https://api1.example.com/swagger.json',
|
|
121
|
+
serversPath: './src/services/api1',
|
|
122
|
+
namespace: 'api1'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
requestLibPath: '@/utils/request',
|
|
126
|
+
schemaPath: 'https://api2.example.com/swagger.json',
|
|
127
|
+
serversPath: './src/services/api2',
|
|
128
|
+
namespace: 'api2'
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### 命令行使用
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# 生成 API 服务
|
|
138
|
+
umi openApi
|
|
139
|
+
|
|
140
|
+
# 在构建时自动生成
|
|
141
|
+
umi build
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 工具函数
|
|
145
|
+
|
|
146
|
+
### withTmpPath
|
|
147
|
+
|
|
148
|
+
生成临时文件路径的工具函数。
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { withTmpPath } from '@creekjs/umi-plugins/utils';
|
|
152
|
+
|
|
153
|
+
// 在插件中使用
|
|
154
|
+
export default (api: IApi) => {
|
|
155
|
+
const tmpPath = withTmpPath({
|
|
156
|
+
api,
|
|
157
|
+
path: 'my-plugin/config.ts'
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 生成临时文件
|
|
161
|
+
api.writeTmpFile({
|
|
162
|
+
path: tmpPath,
|
|
163
|
+
content: 'export default {};'
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 完整配置示例
|
|
169
|
+
|
|
170
|
+
### 基础项目配置
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// .umirc.ts
|
|
174
|
+
import { defineConfig } from '@umijs/max';
|
|
175
|
+
|
|
176
|
+
export default defineConfig({
|
|
177
|
+
// 插件配置
|
|
178
|
+
plugins: [
|
|
179
|
+
'@creekjs/umi-plugins/creek-layout',
|
|
180
|
+
'@creekjs/umi-plugins/open-api'
|
|
181
|
+
],
|
|
182
|
+
|
|
183
|
+
// OpenAPI 配置
|
|
184
|
+
openApi: {
|
|
185
|
+
requestLibPath: '@/utils/request',
|
|
186
|
+
schemaPath: process.env.API_SCHEMA_URL || 'http://localhost:3001/api-docs',
|
|
187
|
+
serversPath: './src/services',
|
|
188
|
+
projectName: 'myApp',
|
|
189
|
+
isCamelCase: true,
|
|
190
|
+
enumStyle: 'string-literal'
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// 其他 Umi 配置
|
|
194
|
+
antd: {},
|
|
195
|
+
access: {},
|
|
196
|
+
model: {},
|
|
197
|
+
initialState: {},
|
|
198
|
+
request: {},
|
|
199
|
+
layout: {
|
|
200
|
+
title: 'Creek App'
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 请求库配置
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// src/utils/request.ts
|
|
209
|
+
import { request } from '@creekjs/request';
|
|
210
|
+
import { message } from 'antd';
|
|
211
|
+
|
|
212
|
+
// 请求拦截器
|
|
213
|
+
request.instance.interceptors.request.use(
|
|
214
|
+
(config) => {
|
|
215
|
+
// 添加认证头
|
|
216
|
+
const token = localStorage.getItem('token');
|
|
217
|
+
if (token) {
|
|
218
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
219
|
+
}
|
|
220
|
+
return config;
|
|
221
|
+
},
|
|
222
|
+
(error) => Promise.reject(error)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// 响应拦截器
|
|
226
|
+
request.instance.interceptors.response.use(
|
|
227
|
+
(response) => {
|
|
228
|
+
return response.data;
|
|
229
|
+
},
|
|
230
|
+
(error) => {
|
|
231
|
+
// 统一错误处理
|
|
232
|
+
const { response } = error;
|
|
233
|
+
if (response?.status === 401) {
|
|
234
|
+
message.error('登录已过期,请重新登录');
|
|
235
|
+
// 跳转到登录页
|
|
236
|
+
window.location.href = '/login';
|
|
237
|
+
} else {
|
|
238
|
+
message.error(error.message || '请求失败');
|
|
239
|
+
}
|
|
240
|
+
return Promise.reject(error);
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
export default request;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 路由配置
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// config/routes.ts
|
|
251
|
+
export default [
|
|
252
|
+
{
|
|
253
|
+
path: '/',
|
|
254
|
+
redirect: '/dashboard'
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: '仪表盘',
|
|
258
|
+
icon: 'DashboardOutlined',
|
|
259
|
+
path: '/dashboard',
|
|
260
|
+
component: './Dashboard'
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: '用户管理',
|
|
264
|
+
icon: 'UserOutlined',
|
|
265
|
+
path: '/users',
|
|
266
|
+
routes: [
|
|
267
|
+
{
|
|
268
|
+
name: '用户列表',
|
|
269
|
+
path: '/users/list',
|
|
270
|
+
component: './Users/List'
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: '用户详情',
|
|
274
|
+
path: '/users/:id',
|
|
275
|
+
component: './Users/Detail',
|
|
276
|
+
hideInMenu: true
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: '系统设置',
|
|
282
|
+
icon: 'SettingOutlined',
|
|
283
|
+
path: '/settings',
|
|
284
|
+
component: './Settings'
|
|
285
|
+
}
|
|
286
|
+
];
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## 开发自定义插件
|
|
290
|
+
|
|
291
|
+
### 插件结构
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// src/my-plugin/index.ts
|
|
295
|
+
import { IApi } from '@umijs/max';
|
|
296
|
+
import { withTmpPath } from '@creekjs/umi-plugins/utils';
|
|
297
|
+
|
|
298
|
+
export default (api: IApi) => {
|
|
299
|
+
// 插件描述
|
|
300
|
+
api.describe({
|
|
301
|
+
key: 'myPlugin',
|
|
302
|
+
config: {
|
|
303
|
+
schema: ({ zod }) => {
|
|
304
|
+
return zod.object({
|
|
305
|
+
enabled: zod.boolean().optional()
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
enableBy: api.EnableBy.config
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// 修改配置
|
|
313
|
+
api.modifyConfig((memo) => {
|
|
314
|
+
// 修改配置逻辑
|
|
315
|
+
return memo;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// 生成临时文件
|
|
319
|
+
api.onGenerateFiles(() => {
|
|
320
|
+
const tmpPath = withTmpPath({
|
|
321
|
+
api,
|
|
322
|
+
path: 'my-plugin/runtime.ts'
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
api.writeTmpFile({
|
|
326
|
+
path: tmpPath,
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/creek-layout/index.tsx
|
|
20
|
+
var creek_layout_exports = {};
|
|
21
|
+
__export(creek_layout_exports, {
|
|
22
|
+
default: () => creek_layout_default
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(creek_layout_exports);
|
|
25
|
+
var import_max = require("@umijs/max");
|
|
26
|
+
var import_plugin_utils = require("@umijs/max/plugin-utils");
|
|
27
|
+
var import_fs = require("fs");
|
|
28
|
+
var import_path = require("path");
|
|
29
|
+
var import_utils = require("../utils");
|
|
30
|
+
var getPkgHasDep = (api, depList) => {
|
|
31
|
+
const hasDependency = (pkg2, dep) => {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
return ((_a = pkg2.dependencies) == null ? void 0 : _a[dep]) || ((_b = pkg2.devDependencies) == null ? void 0 : _b[dep]);
|
|
34
|
+
};
|
|
35
|
+
const { pkg } = api;
|
|
36
|
+
return depList.find((dep) => hasDependency(pkg, dep));
|
|
37
|
+
};
|
|
38
|
+
var getPkgPath = (api, pkgName, depList) => {
|
|
39
|
+
const _depList = depList && depList.length ? depList : [pkgName];
|
|
40
|
+
const pkgHasDep = getPkgHasDep(api, _depList);
|
|
41
|
+
if (pkgHasDep) {
|
|
42
|
+
const nodeModulesPath = (0, import_path.join)(api.cwd, "node_modules", pkgHasDep, "package.json");
|
|
43
|
+
if ((0, import_fs.existsSync)(nodeModulesPath)) {
|
|
44
|
+
return (0, import_path.join)(api.cwd, "node_modules", pkgHasDep);
|
|
45
|
+
}
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
if (api.cwd !== cwd) {
|
|
48
|
+
const altNodeModulesPath = (0, import_path.join)(cwd, "node_modules", pkgHasDep, "package.json");
|
|
49
|
+
if ((0, import_fs.existsSync)(altNodeModulesPath)) {
|
|
50
|
+
return (0, import_path.join)(cwd, "node_modules", pkgHasDep);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return (0, import_path.dirname)(require.resolve(`${pkgName}/package.json`));
|
|
55
|
+
};
|
|
56
|
+
var getIconsInfoForRoutes = (api) => {
|
|
57
|
+
const antIconsPath = (0, import_plugin_utils.winPath)(getPkgPath(api, "@ant-design/icons"));
|
|
58
|
+
const getAllIcons = () => {
|
|
59
|
+
const iconTypePath = (0, import_path.join)(antIconsPath, "./lib/icons/index.d.ts");
|
|
60
|
+
const iconTypeContent = (0, import_fs.readFileSync)(iconTypePath, "utf-8");
|
|
61
|
+
return [...iconTypeContent.matchAll(/default as (\w+)/g)].reduce((memo, cur) => {
|
|
62
|
+
memo[cur[1]] = true;
|
|
63
|
+
return memo;
|
|
64
|
+
}, {});
|
|
65
|
+
};
|
|
66
|
+
const allIcons = getAllIcons();
|
|
67
|
+
const iconsMap = Object.keys(api.appData.routes).reduce((memo, id) => {
|
|
68
|
+
const { icon } = api.appData.routes[id];
|
|
69
|
+
if (icon) {
|
|
70
|
+
const upperIcon = import_plugin_utils.lodash.upperFirst(import_plugin_utils.lodash.camelCase(icon));
|
|
71
|
+
if (allIcons[upperIcon]) {
|
|
72
|
+
memo[upperIcon] = true;
|
|
73
|
+
}
|
|
74
|
+
if (allIcons[`${upperIcon}Outlined`]) {
|
|
75
|
+
memo[`${upperIcon}Outlined`] = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return memo;
|
|
79
|
+
}, {});
|
|
80
|
+
const icons = Object.keys(iconsMap);
|
|
81
|
+
return {
|
|
82
|
+
icons,
|
|
83
|
+
antIconsPath
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
var creek_layout_default = (api) => {
|
|
87
|
+
const TEMPLATE_DIR = (0, import_path.join)(__dirname, "template");
|
|
88
|
+
const creekWebComponentsPath = (0, import_plugin_utils.winPath)(getPkgPath(api, "@creekjs/web-components"));
|
|
89
|
+
const creekIconPath = require.resolve(`${creekWebComponentsPath}/dist/creek-icon`);
|
|
90
|
+
api.describe({
|
|
91
|
+
key: "creekLayout",
|
|
92
|
+
config: {
|
|
93
|
+
schema({ zod }) {
|
|
94
|
+
return zod.object({
|
|
95
|
+
title: zod.string(),
|
|
96
|
+
iconFontCNs: zod.string().array()
|
|
97
|
+
}).partial();
|
|
98
|
+
},
|
|
99
|
+
onChange: api.ConfigChangeType.regenerateTmpFiles
|
|
100
|
+
},
|
|
101
|
+
enableBy: api.EnableBy.config
|
|
102
|
+
});
|
|
103
|
+
api.modifyAppData((memo) => {
|
|
104
|
+
const version = require(`${creekWebComponentsPath}/package.json`).version;
|
|
105
|
+
memo.pluginLayout = {
|
|
106
|
+
creekWebComponentsPath,
|
|
107
|
+
version
|
|
108
|
+
};
|
|
109
|
+
return memo;
|
|
110
|
+
});
|
|
111
|
+
api.modifyConfig((memo) => {
|
|
112
|
+
const name = require(`${creekWebComponentsPath}/package.json`).name;
|
|
113
|
+
memo.alias[name] = creekWebComponentsPath;
|
|
114
|
+
const routes = memo.routes;
|
|
115
|
+
const hasNotFoundPage = routes == null ? void 0 : routes.find((item) => [item.name, item.path].includes("404"));
|
|
116
|
+
const defaultRoutes = [
|
|
117
|
+
{
|
|
118
|
+
name: "404",
|
|
119
|
+
path: "*",
|
|
120
|
+
hideInMenu: true,
|
|
121
|
+
component: require.resolve(`${creekWebComponentsPath}/dist/creek-layout/Exception/NotFoundPage`)
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
if (!hasNotFoundPage) {
|
|
125
|
+
memo.routes = [...routes, ...defaultRoutes];
|
|
126
|
+
}
|
|
127
|
+
return memo;
|
|
128
|
+
});
|
|
129
|
+
api.onGenerateFiles(() => {
|
|
130
|
+
var _a, _b;
|
|
131
|
+
const hasInitialStatePlugin = api.config.initialState;
|
|
132
|
+
const iconsInfo = getIconsInfoForRoutes(api);
|
|
133
|
+
const iconFontCNs = Array.isArray((_a = api.userConfig.creekLayout) == null ? void 0 : _a.iconFontCNs) ? (_b = api.userConfig.creekLayout) == null ? void 0 : _b.iconFontCNs : [];
|
|
134
|
+
api.writeTmpFile({
|
|
135
|
+
path: "Layout.tsx",
|
|
136
|
+
tplPath: (0, import_path.join)(TEMPLATE_DIR, "/layout.tpl"),
|
|
137
|
+
context: {
|
|
138
|
+
creekWebComponentsPath,
|
|
139
|
+
hasInitialStatePlugin,
|
|
140
|
+
access: api.config.access,
|
|
141
|
+
creekLocaleConfig: api.config.creekLocaleConfig ? JSON.stringify(api.config.glocale, null, 2) : "undefined",
|
|
142
|
+
userConfig: JSON.stringify(api.config.creekLayout, null, 2)
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
api.writeTmpFile({
|
|
146
|
+
path: "types.d.ts",
|
|
147
|
+
tplPath: (0, import_path.join)(TEMPLATE_DIR, "/type.tpl"),
|
|
148
|
+
context: {
|
|
149
|
+
creekWebComponentsPath,
|
|
150
|
+
hasInitialStatePlugin,
|
|
151
|
+
access: api.config.access
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
api.writeTmpFile({
|
|
155
|
+
path: import_max.RUNTIME_TYPE_FILE_NAME,
|
|
156
|
+
tplPath: (0, import_path.join)(TEMPLATE_DIR, "/runtime-config.type.tpl"),
|
|
157
|
+
context: {}
|
|
158
|
+
});
|
|
159
|
+
api.writeTmpFile({
|
|
160
|
+
path: "icons.tsx",
|
|
161
|
+
tplPath: (0, import_path.join)(TEMPLATE_DIR, "/icons.tpl"),
|
|
162
|
+
context: {
|
|
163
|
+
icons: iconsInfo.icons,
|
|
164
|
+
antIconsPath: iconsInfo.antIconsPath,
|
|
165
|
+
creekIconPath
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
api.writeTmpFile({
|
|
169
|
+
path: "runtime.tsx",
|
|
170
|
+
tplPath: (0, import_path.join)(TEMPLATE_DIR, "/runtime.tpl"),
|
|
171
|
+
context: {
|
|
172
|
+
creekWebComponentsPath,
|
|
173
|
+
hasIconFontCNs: iconFontCNs.length > 0,
|
|
174
|
+
iconFontCNs: JSON.stringify(iconFontCNs)
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
api.addLayouts(() => {
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
id: "ant-design-pro-layout",
|
|
182
|
+
file: (0, import_utils.withTmpPath)({ api, path: "Layout.tsx" }),
|
|
183
|
+
test: (route) => {
|
|
184
|
+
return route.layout !== false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
});
|
|
189
|
+
api.addRuntimePluginKey(() => ["layout"]);
|
|
190
|
+
api.addRuntimePlugin(() => {
|
|
191
|
+
return [(0, import_utils.withTmpPath)({ api, path: "runtime.tsx" })];
|
|
192
|
+
});
|
|
193
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { useLocation, useAppData, IRoute, matchRoutes, Outlet, useNavigate } from 'umi';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import { CreekLayout, CreekLayoutProps } from '{{{creekWebComponentsPath}}}';
|
|
5
|
+
|
|
6
|
+
{{#hasInitialStatePlugin}}
|
|
7
|
+
import { useModel } from '@@/plugin-model';
|
|
8
|
+
{{/hasInitialStatePlugin}}
|
|
9
|
+
{{^hasInitialStatePlugin}}
|
|
10
|
+
const useModel = null;
|
|
11
|
+
{{/hasInitialStatePlugin}}
|
|
12
|
+
|
|
13
|
+
{{#access}}
|
|
14
|
+
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
|
15
|
+
{{/access}}
|
|
16
|
+
{{^access}}
|
|
17
|
+
const useAccessMarkedRoutes = (r) => r;
|
|
18
|
+
{{/access}}
|
|
19
|
+
|
|
20
|
+
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
|
|
21
|
+
const filterRoutes = (routes: IRoute[], filterFn: (route: IRoute) => boolean) => {
|
|
22
|
+
if (routes.length === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
let newRoutes = [];
|
|
26
|
+
for (const route of routes) {
|
|
27
|
+
const newRoute = { ...route };
|
|
28
|
+
if (filterFn(route)) {
|
|
29
|
+
if (Array.isArray(newRoute.routes)) {
|
|
30
|
+
newRoutes.push(...filterRoutes(newRoute.routes, filterFn));
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
if (Array.isArray(newRoute.children)) {
|
|
34
|
+
newRoute.children = filterRoutes(newRoute.children, filterFn);
|
|
35
|
+
newRoute.routes = newRoute.children;
|
|
36
|
+
}
|
|
37
|
+
newRoutes.push(newRoute);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return newRoutes;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 格式化路由 处理因 wrapper 导致的 菜单 path 不一致
|
|
45
|
+
const mapRoutes = (routes: IRoute[]) => {
|
|
46
|
+
if (routes.length === 0) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
return routes.map((route) => {
|
|
50
|
+
// 需要 copy 一份, 否则会污染原始数据
|
|
51
|
+
const newRoute = { ...route };
|
|
52
|
+
if (route.originPath) {
|
|
53
|
+
newRoute.path = route.originPath;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(route.routes)) {
|
|
57
|
+
newRoute.routes = mapRoutes(route.routes);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(route.children)) {
|
|
61
|
+
newRoute.children = mapRoutes(route.children);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return newRoute;
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const Layout = (props: CreekLayoutProps) => {
|
|
69
|
+
const location = useLocation();
|
|
70
|
+
const navigate = useNavigate();
|
|
71
|
+
const { clientRoutes, pluginManager } = useAppData();
|
|
72
|
+
const initialInfo = (useModel && useModel('@@initialState')) || {
|
|
73
|
+
initialState: undefined,
|
|
74
|
+
loading: false,
|
|
75
|
+
setInitialState: null,
|
|
76
|
+
};
|
|
77
|
+
const userConfig = {{{userConfig}}};
|
|
78
|
+
const creekLocaleConfig = {{{creekLocaleConfig}}};
|
|
79
|
+
const runtimeConfig = pluginManager.applyPlugins({
|
|
80
|
+
key: 'layout',
|
|
81
|
+
type: 'modify',
|
|
82
|
+
initialValue: {
|
|
83
|
+
...initialInfo,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 现在的 layout 及 wrapper 实现是通过父路由的形式实现的, 会导致路由数据多了冗余层级
|
|
88
|
+
const newRoutes = filterRoutes(
|
|
89
|
+
clientRoutes.filter((route) => route.id === 'ant-design-pro-layout'),
|
|
90
|
+
(route) => {
|
|
91
|
+
return (
|
|
92
|
+
(!!route.isLayout && route.id !== 'ant-design-pro-layout') || !!route.isWrapper
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
const [route] = useAccessMarkedRoutes(mapRoutes(newRoutes));
|
|
97
|
+
|
|
98
|
+
const matchedRoute = useMemo(
|
|
99
|
+
() => matchRoutes(route.children, location.pathname)?.pop?.()?.route,
|
|
100
|
+
[location.pathname]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<CreekLayout
|
|
105
|
+
location={location}
|
|
106
|
+
runtimeConfig={runtimeConfig}
|
|
107
|
+
matchedRoute={matchedRoute}
|
|
108
|
+
navigate={navigate}
|
|
109
|
+
userConfig={userConfig}
|
|
110
|
+
route={route}
|
|
111
|
+
initialInfo={initialInfo}
|
|
112
|
+
children={
|
|
113
|
+
runtimeConfig.childrenRender ? (
|
|
114
|
+
runtimeConfig.childrenRender(<Outlet />, props)
|
|
115
|
+
) : (
|
|
116
|
+
<Outlet />
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export default Layout;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CreekConfigProvider } from '{{{creekWebComponentsPath}}}';
|
|
2
|
+
|
|
3
|
+
import icons from './icons';
|
|
4
|
+
|
|
5
|
+
function formatIcon(name: string) {
|
|
6
|
+
return name
|
|
7
|
+
.replace(name[0], name[0].toUpperCase())
|
|
8
|
+
.replace(/-(\w)/g, function (all, letter) {
|
|
9
|
+
return letter.toUpperCase();
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function patchRoutes({ routes }) {
|
|
14
|
+
Object.keys(routes).forEach((key) => {
|
|
15
|
+
const { icon } = routes[key];
|
|
16
|
+
if (icon && typeof icon === 'string') {
|
|
17
|
+
const upperIcon = formatIcon(icon);
|
|
18
|
+
if (icons[upperIcon] || icons[upperIcon + 'Outlined']) {
|
|
19
|
+
const IconComponentName = icons[upperIcon] || icons[upperIcon + 'Outlined'];
|
|
20
|
+
routes[key].icon = <IconComponentName />;
|
|
21
|
+
}
|
|
22
|
+
{{#hasIconFontCNs}}
|
|
23
|
+
else {
|
|
24
|
+
const CreekIcon = icons.CreekIcon;
|
|
25
|
+
routes[key].icon = <CreekIcon type={icon} />;
|
|
26
|
+
}
|
|
27
|
+
{{/hasIconFontCNs}}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function rootContainer(container){
|
|
33
|
+
const iconFontCNs={{{iconFontCNs}}};
|
|
34
|
+
const result = <CreekConfigProvider iconFontCNs={iconFontCNs}>
|
|
35
|
+
{container}
|
|
36
|
+
</CreekConfigProvider>;
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|