@pixel-shell/elpis 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/.eslintgnore +1 -0
- package/.eslintrc +55 -0
- package/README.md +194 -0
- package/app/controller/base.js +40 -0
- package/app/controller/project.js +56 -0
- package/app/controller/view.js +18 -0
- package/app/extend/logger.js +37 -0
- package/app/middleware/api-params-verify.js +71 -0
- package/app/middleware/api-sign-verify.js +32 -0
- package/app/middleware/error-handler.js +33 -0
- package/app/middleware/project-handler.js +27 -0
- package/app/middleware.js +31 -0
- package/app/pages/asserts/custom.css +12 -0
- package/app/pages/boot.js +47 -0
- package/app/pages/common/curl.js +88 -0
- package/app/pages/common/utils.js +2 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +17 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +129 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +43 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +37 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +114 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +22 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +85 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +92 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +106 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +123 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +88 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +18 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +117 -0
- package/app/pages/dashboard/dashboard.vue +89 -0
- package/app/pages/dashboard/entry.dashboard.js +42 -0
- package/app/pages/store/index.js +5 -0
- package/app/pages/store/menu.js +57 -0
- package/app/pages/store/project.js +15 -0
- package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
- package/app/pages/widgets/header-container/asserts/logo.png +0 -0
- package/app/pages/widgets/header-container/header-container.vue +102 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +129 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +130 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +116 -0
- package/app/pages/widgets/schema-form/form-item-config.js +23 -0
- package/app/pages/widgets/schema-form/schema-form.vue +121 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +41 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +51 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +34 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +36 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +121 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +227 -0
- package/app/pages/widgets/sider-container/sider-container.vue +29 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +241 -0
- package/app/router/project.js +6 -0
- package/app/router/view.js +8 -0
- package/app/router-schema/project.js +30 -0
- package/app/service/base.js +10 -0
- package/app/service/project.js +40 -0
- package/app/view/entry.tpl +25 -0
- package/app/webpack/build.js +18 -0
- package/app/webpack/config/webpack.base.js +234 -0
- package/app/webpack/config/webpack.dev.js +58 -0
- package/app/webpack/config/webpack.prod.js +113 -0
- package/app/webpack/dev.js +60 -0
- package/app/webpack/libs/blank.js +1 -0
- package/app/webpack/prod.js +22 -0
- package/config/config.default.js +3 -0
- package/docs/dashboard-mode.js +141 -0
- package/elpis-core/env.js +16 -0
- package/elpis-core/index.js +70 -0
- package/elpis-core/loader/config.js +51 -0
- package/elpis-core/loader/controller.js +71 -0
- package/elpis-core/loader/extend.js +61 -0
- package/elpis-core/loader/middleware.js +69 -0
- package/elpis-core/loader/roter-schema.js +54 -0
- package/elpis-core/loader/router.js +42 -0
- package/elpis-core/loader/service.js +70 -0
- package/index.js +35 -0
- package/model/index.js +86 -0
- package/package.json +92 -0
- package/test/controller/project.test.js +194 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
mode: "dashboard", //模板类型,不同模板类型对应不一样的模板数据结构
|
|
3
|
+
name: "", //名称
|
|
4
|
+
desc: "", //描述
|
|
5
|
+
icon: "", //图标
|
|
6
|
+
homePage: "", // 首页路径
|
|
7
|
+
// 头部菜单配置
|
|
8
|
+
menu: [
|
|
9
|
+
{
|
|
10
|
+
key: "", //唯一标识
|
|
11
|
+
name: "", //菜单名称
|
|
12
|
+
menuType: "", //菜单类型,枚举值:'group'、'module'
|
|
13
|
+
// 当menuType为'group'时,子菜单列表
|
|
14
|
+
subMenu: [{}],
|
|
15
|
+
// 当menuType为'module'时,模块配置
|
|
16
|
+
moduleType: "", // 模块类型,枚举值:sider,iframe,custom,schema
|
|
17
|
+
// 当moduleType为sider时,模块配置
|
|
18
|
+
siderConfig: {
|
|
19
|
+
menu: [{}], // 侧边栏菜单列表
|
|
20
|
+
},
|
|
21
|
+
// 当moduleType为iframe时,模块配置
|
|
22
|
+
iframeConfig: {
|
|
23
|
+
path: "", // iframe路径
|
|
24
|
+
},
|
|
25
|
+
// 当moduleType为custom时,模块配置
|
|
26
|
+
customConfig: {
|
|
27
|
+
path: "", // 自定义路径
|
|
28
|
+
},
|
|
29
|
+
// 当moduleType为schema时,模块配置
|
|
30
|
+
schemaConfig: {
|
|
31
|
+
api: "", // 数据源API(遵循RESTful API规范)
|
|
32
|
+
schema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
key: {
|
|
36
|
+
...schema, // 标准schema配置
|
|
37
|
+
type: "", // 字段类型
|
|
38
|
+
label: "", // 字段中文名
|
|
39
|
+
// 字段在table中的相关配置
|
|
40
|
+
tableOption: {
|
|
41
|
+
...elTableColumnConfig, //标准 el-table-column 配置
|
|
42
|
+
toFixed: 0, // 数值类型字段保留的小数位数
|
|
43
|
+
visible: true, // 是否在table中显示
|
|
44
|
+
},
|
|
45
|
+
// 字段在search-bar中的相关配置
|
|
46
|
+
searchOption: {
|
|
47
|
+
...elComponentConfig, //标准 el-component-column 配置
|
|
48
|
+
comType: "", // 配置组件类型 ,枚举值:input,select 。。。
|
|
49
|
+
defaule: "", // 默认值
|
|
50
|
+
|
|
51
|
+
// comType === select 时,相关配置
|
|
52
|
+
enumList: [], // 下拉选项列表
|
|
53
|
+
|
|
54
|
+
// comType === dynamic-select 时,相关配置
|
|
55
|
+
api: "", // 下拉选项API
|
|
56
|
+
},
|
|
57
|
+
// 字段在component中的相关配置,前缀对应componentConfig中的键值
|
|
58
|
+
// 如:componentConfig.createForm,这里对应 createFormOption
|
|
59
|
+
// 字段在createFormz中的相关配置
|
|
60
|
+
createFormOption: {
|
|
61
|
+
...elComponentConfig, //标准 el-component 配置
|
|
62
|
+
comType: "", // 控件类型,枚举值:input,select 。。。
|
|
63
|
+
visible: true, // 是否显示
|
|
64
|
+
disabled: false, // 是否禁用
|
|
65
|
+
|
|
66
|
+
// comType === select 时,相关配置
|
|
67
|
+
enumList: [], // 下拉选项列表
|
|
68
|
+
},
|
|
69
|
+
// 字段在editForm中的相关配置
|
|
70
|
+
editFormOption: {
|
|
71
|
+
...elComponentConfig, //标准 el-component 配置
|
|
72
|
+
comType: "", // 控件类型,枚举值:input,select 。。。
|
|
73
|
+
visible: true, // 是否显示
|
|
74
|
+
disabled: false, // 是否禁用
|
|
75
|
+
|
|
76
|
+
// comType === select 时,相关配置
|
|
77
|
+
enumList: [], // 下拉选项列表
|
|
78
|
+
},
|
|
79
|
+
detailPanelOption: {
|
|
80
|
+
...elComponentConfig, //标准 el-component 配置
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: [], // 必填字段列表
|
|
85
|
+
},
|
|
86
|
+
tableConfig: {
|
|
87
|
+
headerButtons: [
|
|
88
|
+
{
|
|
89
|
+
label: "", // 按钮中文名
|
|
90
|
+
eventKey: "", // 按钮事件名
|
|
91
|
+
// 按钮事件具体配置
|
|
92
|
+
eventOption: {
|
|
93
|
+
// 当eventKey === "showComponent"
|
|
94
|
+
comName: "", // 组件名称
|
|
95
|
+
},
|
|
96
|
+
...elButtonConfig, // 标准 el-button 配置
|
|
97
|
+
}
|
|
98
|
+
], // 表格头部按钮列表
|
|
99
|
+
rowButtons: [
|
|
100
|
+
{
|
|
101
|
+
label: "", // 按钮中文名
|
|
102
|
+
eventKey: "", // 按钮事件名
|
|
103
|
+
eventOption: {
|
|
104
|
+
// 当eventKey === "showComponent"
|
|
105
|
+
comName: "", // 组件名称
|
|
106
|
+
// 当eventKey === 'remove
|
|
107
|
+
params: {
|
|
108
|
+
// paramKey = 参数的键值
|
|
109
|
+
// rowValueKey = 参数值(当格式为 schema::tableKey的时候, 到table中找相应的字段)
|
|
110
|
+
paramKey: rowValueKey
|
|
111
|
+
}
|
|
112
|
+
}, // 按钮事件具体配置
|
|
113
|
+
...elButtonConfig, // 标准 el-button 配置
|
|
114
|
+
}
|
|
115
|
+
], // 表格行按钮列表
|
|
116
|
+
},
|
|
117
|
+
searchConfig: {},
|
|
118
|
+
// 动态组件相关配置
|
|
119
|
+
componentConfig: {
|
|
120
|
+
// create-form 表单相关配置
|
|
121
|
+
createForm: {
|
|
122
|
+
title: "", // 表单标题
|
|
123
|
+
saveBtnText: "", // 保存按钮文本
|
|
124
|
+
},
|
|
125
|
+
// edit-form 表单相关配置
|
|
126
|
+
editForm: {
|
|
127
|
+
mainKey: "", // 主键字段名,用于唯一标识要修改的数据对象
|
|
128
|
+
title: "", // 表单标题
|
|
129
|
+
saveBtnText: "", // 保存按钮文本
|
|
130
|
+
},
|
|
131
|
+
// detail-panel 相关配置
|
|
132
|
+
detailPanel: {
|
|
133
|
+
mainKey: "", // 主键字段名,用于唯一标识要修改的数据对象
|
|
134
|
+
title: "", // 表单标题
|
|
135
|
+
},
|
|
136
|
+
// ...支持用户动态扩展
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = (app) => {
|
|
2
|
+
return {
|
|
3
|
+
isLocal: () => {
|
|
4
|
+
return process.env._ENV === "local";
|
|
5
|
+
},
|
|
6
|
+
isBeta: () => {
|
|
7
|
+
return process.env._ENV === "beta";
|
|
8
|
+
},
|
|
9
|
+
isProduction: () => {
|
|
10
|
+
return process.env._ENV === "production";
|
|
11
|
+
},
|
|
12
|
+
get: () => {
|
|
13
|
+
return process.env._ENV ?? "local";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const Koa = require("koa");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { sep } = path;
|
|
4
|
+
const env = require("./env");
|
|
5
|
+
const configLoader = require("./loader/config");
|
|
6
|
+
const extendLoader = require("./loader/extend");
|
|
7
|
+
const serviceLoader = require("./loader/service");
|
|
8
|
+
const controllerLoader = require("./loader/controller");
|
|
9
|
+
const middlewareLoader = require("./loader/middleware");
|
|
10
|
+
const routerSchemaLoader = require("./loader/roter-schema");
|
|
11
|
+
const routerLoader = require("./loader/router");
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
/**
|
|
15
|
+
* 启动服务
|
|
16
|
+
* @param {Object} options 配置项
|
|
17
|
+
*/
|
|
18
|
+
start(options = {}) {
|
|
19
|
+
// 创建koa实例
|
|
20
|
+
const app = new Koa();
|
|
21
|
+
// 应用配置
|
|
22
|
+
app.options = options;
|
|
23
|
+
// 基础路径
|
|
24
|
+
app.baseDir = options.baseDir || process.cwd();
|
|
25
|
+
// 业务文件路径
|
|
26
|
+
app.businessPath = path.resolve(app.baseDir, `.${sep}app`);
|
|
27
|
+
// 初始化环境配置
|
|
28
|
+
app.env = env();
|
|
29
|
+
|
|
30
|
+
// 加载config
|
|
31
|
+
configLoader(app);
|
|
32
|
+
// 加载extendLoader
|
|
33
|
+
extendLoader(app);
|
|
34
|
+
// 加载service
|
|
35
|
+
serviceLoader(app);
|
|
36
|
+
// 加载controller
|
|
37
|
+
controllerLoader(app);
|
|
38
|
+
// 加载middleware
|
|
39
|
+
middlewareLoader(app);
|
|
40
|
+
// 加载routerSchema
|
|
41
|
+
routerSchemaLoader(app);
|
|
42
|
+
|
|
43
|
+
// 注册 elpis 全局中间件
|
|
44
|
+
const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}app${sep}middleware.js`);
|
|
45
|
+
const elpisMiddleware = require(elpisMiddlewarePath);
|
|
46
|
+
elpisMiddleware(app);
|
|
47
|
+
console.log(`-- [start] load global elpis middleware done --`);
|
|
48
|
+
|
|
49
|
+
// 注册全局中间件
|
|
50
|
+
try {
|
|
51
|
+
require(`${app.businessPath}${sep}middleware.js`)(app);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.log('[exception] there is no global business middleware file', error);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 注册路由
|
|
57
|
+
routerLoader(app);
|
|
58
|
+
|
|
59
|
+
// 启动服务
|
|
60
|
+
try {
|
|
61
|
+
const port = process.env.PORT || 8080;
|
|
62
|
+
const host = process.env.IP || "0.0.0.0";
|
|
63
|
+
app.listen(port, host);
|
|
64
|
+
console.log(`Server is running at http://${host}:${port}`);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(error);
|
|
67
|
+
}
|
|
68
|
+
return app;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const { sep } = path;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* config loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 配置区分 本地、测试、生产环境,通过env环境读取不同文件配置 env.config
|
|
9
|
+
* 通过 env.config 覆盖 default.config 加载到 app.config 中
|
|
10
|
+
*
|
|
11
|
+
* 目录下对应的 config 配置
|
|
12
|
+
* 默认配置 config/config.default.js
|
|
13
|
+
* 本地配置 config/config.local.js
|
|
14
|
+
* 测试配置 config/config.beta.js
|
|
15
|
+
* 生产配置 config/config.prod.js
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
module.exports = (app) => {
|
|
19
|
+
// elpis config 目录及相关文件
|
|
20
|
+
const elpisConfigPath = path.resolve(__dirname, `..${sep}..${sep}config`);
|
|
21
|
+
let defaultConfig = require(path.resolve(elpisConfigPath, `.${sep}config.default.js`));
|
|
22
|
+
|
|
23
|
+
// 业务 config 目录及相关文件
|
|
24
|
+
const businessConfigPath = path.resolve(process.cwd(), `.${sep}config`);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
defaultConfig = {
|
|
28
|
+
...defaultConfig,
|
|
29
|
+
...require(path.resolve(businessConfigPath, `.${sep}config.default.js`)),
|
|
30
|
+
};
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.log("[exception] there is no config.default file");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 获取 env.config
|
|
36
|
+
let envConfig = {};
|
|
37
|
+
try {
|
|
38
|
+
if(app.env.isLocal()) {
|
|
39
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.local.js`));
|
|
40
|
+
} else if(app.env.isBeta()) {
|
|
41
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.beta.js`));
|
|
42
|
+
} else if(app.env.isProduction()) {
|
|
43
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.prod.js`));
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log("[exception] there is no env.config file");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 覆盖加载 config 配置
|
|
50
|
+
app.config = Object.assign({}, defaultConfig, envConfig);
|
|
51
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const glob = require("glob");
|
|
3
|
+
const { sep } = path;
|
|
4
|
+
/**
|
|
5
|
+
* controller loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* controller 目录结构, 可通过 'app.controller.${目录}.${文件}' 访问
|
|
9
|
+
*
|
|
10
|
+
* 例子:
|
|
11
|
+
* app/controller
|
|
12
|
+
│
|
|
13
|
+
│ -- custom-module
|
|
14
|
+
│
|
|
15
|
+
│ -- custom-controller.js
|
|
16
|
+
|
|
17
|
+
=> app.controller.customModule.customController
|
|
18
|
+
*/
|
|
19
|
+
module.exports = (app) => {
|
|
20
|
+
const controller = {};
|
|
21
|
+
|
|
22
|
+
// 读取 elpis/app/controller 目录下的所有文件
|
|
23
|
+
const elpisControllerPath = path.resolve(
|
|
24
|
+
__dirname,
|
|
25
|
+
`..${sep}..${sep}app${sep}controller`,
|
|
26
|
+
);
|
|
27
|
+
const elpisFileList = glob.sync(
|
|
28
|
+
path.resolve(elpisControllerPath, `.${sep}**${sep}*.js`),
|
|
29
|
+
);
|
|
30
|
+
// 遍历文件列表
|
|
31
|
+
elpisFileList.forEach((file) => handleFile(file));
|
|
32
|
+
// 读取 业务根目录/app/controller 目录下的所有文件
|
|
33
|
+
const businessControllerPath = path.resolve(
|
|
34
|
+
app.businessPath,
|
|
35
|
+
`.${sep}controller`,
|
|
36
|
+
);
|
|
37
|
+
const businessFileList = glob.sync(
|
|
38
|
+
path.resolve(businessControllerPath, `.${sep}**${sep}*.js`),
|
|
39
|
+
);
|
|
40
|
+
businessFileList.forEach((file) => handleFile(file));
|
|
41
|
+
|
|
42
|
+
function handleFile(file) {
|
|
43
|
+
// 提取文件名称
|
|
44
|
+
let name = path.resolve(file);
|
|
45
|
+
|
|
46
|
+
// 截取路径 app/controller/custom-module/custom-controller.js => customModule.customController
|
|
47
|
+
name = name.substring(
|
|
48
|
+
name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length,
|
|
49
|
+
name.lastIndexOf(".js"),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// 将文件名称转换为驼峰命名
|
|
53
|
+
name = name.replace(/-([a-z])/g, (all, letter) => letter.toUpperCase());
|
|
54
|
+
|
|
55
|
+
// 挂载 controller 到内存 app 对象中
|
|
56
|
+
let tempController = controller;
|
|
57
|
+
const names = name.split(sep);
|
|
58
|
+
for (let i = 0; i < names.length; i++) {
|
|
59
|
+
if (i === names.length - 1) {
|
|
60
|
+
const ControllerMoule = require(path.resolve(file))(app);
|
|
61
|
+
tempController[names[i]] = new ControllerMoule();
|
|
62
|
+
} else {
|
|
63
|
+
if (!tempController[names[i]]) {
|
|
64
|
+
tempController[names[i]] = {};
|
|
65
|
+
}
|
|
66
|
+
tempController = tempController[names[i]];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
app.controller = controller;
|
|
71
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const glob = require("glob");
|
|
3
|
+
const { sep } = path;
|
|
4
|
+
/***
|
|
5
|
+
* extend loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 加载所有extend, 可通过 'app.extend.${文件}' 访问
|
|
9
|
+
*
|
|
10
|
+
* 例子:
|
|
11
|
+
* app/extend
|
|
12
|
+
│
|
|
13
|
+
│ -- custom-extend.js
|
|
14
|
+
|
|
15
|
+
=> app.extend.customExtend 访问
|
|
16
|
+
*/
|
|
17
|
+
module.exports = (app) => {
|
|
18
|
+
// 读取 elpis/app/extend/**/**.js 目录下的所有文件
|
|
19
|
+
const elpisExtendPath = path.resolve(
|
|
20
|
+
__dirname,
|
|
21
|
+
`..${sep}..${sep}app${sep}extend`,
|
|
22
|
+
);
|
|
23
|
+
const elpisfileList = glob.sync(
|
|
24
|
+
path.resolve(elpisExtendPath, `.${sep}**${sep}*.js`),
|
|
25
|
+
);
|
|
26
|
+
// 遍历文件目录,把内容挂载到app.extend中
|
|
27
|
+
elpisfileList.forEach((file) => handleFile(file));
|
|
28
|
+
|
|
29
|
+
// 读取 业务/app/extend/**/**.js 目录下的所有文件
|
|
30
|
+
const businessExtendPath = path.resolve(app.businessPath, `.${sep}extend`);
|
|
31
|
+
const businessFileList = glob.sync(
|
|
32
|
+
path.resolve(businessExtendPath, `.${sep}**${sep}*.js`),
|
|
33
|
+
);
|
|
34
|
+
// 遍历文件目录,把内容挂载到app.extend中
|
|
35
|
+
businessFileList.forEach((file) => handleFile(file));
|
|
36
|
+
|
|
37
|
+
function handleFile(file) {
|
|
38
|
+
// 提取文件名称
|
|
39
|
+
let name = path.resolve(file);
|
|
40
|
+
|
|
41
|
+
// 截取路径 app/extend/custom-extend.js => custom-extend
|
|
42
|
+
name = name.substring(
|
|
43
|
+
name.lastIndexOf(`extend${sep}`) + `extend${sep}`.length,
|
|
44
|
+
name.lastIndexOf(".js"),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 将文件名称转换为驼峰命名
|
|
48
|
+
name = name.replace(/-([a-z])/g, (all, letter) => letter.toUpperCase());
|
|
49
|
+
|
|
50
|
+
// 过滤 app 已经存在的 key
|
|
51
|
+
for (const key in app) {
|
|
52
|
+
if (key === name) {
|
|
53
|
+
console.log(`[extend load error] name:${name} is already in app`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 挂载到 app.extend 中
|
|
59
|
+
app[name] = require(path.resolve(file))(app);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const glob = require("glob");
|
|
3
|
+
const { sep } = path;
|
|
4
|
+
/**
|
|
5
|
+
* middleware loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 加载所有middleware, 可通过 'app.middleware.${目录}.${文件}' 访问
|
|
9
|
+
*
|
|
10
|
+
* 例子:
|
|
11
|
+
* app/middleware
|
|
12
|
+
│
|
|
13
|
+
│ -- custom-module
|
|
14
|
+
│
|
|
15
|
+
│ -- custom-middleware.js
|
|
16
|
+
|
|
17
|
+
=> app.middleware.customModule.customMiddleware
|
|
18
|
+
*/
|
|
19
|
+
module.exports = (app) => {
|
|
20
|
+
const middlewares = {};
|
|
21
|
+
|
|
22
|
+
// 读取 elpis/app/middleware/**/**.js 下的所有文件
|
|
23
|
+
const elpisMiddlewarePath = path.resolve(
|
|
24
|
+
__dirname,
|
|
25
|
+
`..${sep}..${sep}app${sep}middleware`,
|
|
26
|
+
);
|
|
27
|
+
const elpisFileList = glob.sync(
|
|
28
|
+
path.resolve(elpisMiddlewarePath, `.${sep}**${sep}*.js`),
|
|
29
|
+
);
|
|
30
|
+
elpisFileList.forEach((file) => handleFile(file));
|
|
31
|
+
// 读取 业务根目录/app/middleware/**/**.js 下的所有文件
|
|
32
|
+
const businessMiddlewarePath = path.resolve(
|
|
33
|
+
app.businessPath,
|
|
34
|
+
`.${sep}middleware`,
|
|
35
|
+
);
|
|
36
|
+
const businessFileList = glob.sync(
|
|
37
|
+
path.resolve(businessMiddlewarePath, `.${sep}**${sep}*.js`),
|
|
38
|
+
);
|
|
39
|
+
businessFileList.forEach((file) => handleFile(file));
|
|
40
|
+
// 遍历文件目录,把内容挂载到app.middleware中
|
|
41
|
+
function handleFile(file) {
|
|
42
|
+
// 提取文件名称
|
|
43
|
+
let name = path.resolve(file);
|
|
44
|
+
|
|
45
|
+
// 截取路径 app/middleware/custom-module/custom-middleware.js => customModule.customMiddleware
|
|
46
|
+
name = name.substring(
|
|
47
|
+
name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length,
|
|
48
|
+
name.lastIndexOf(".js"),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// 将文件名称转换为驼峰命名
|
|
52
|
+
name = name.replace(/-([a-z])/g, (all, letter) => letter.toUpperCase());
|
|
53
|
+
|
|
54
|
+
// 挂载 middleware 到内存 app 对象中
|
|
55
|
+
let tempMiddleware = middlewares;
|
|
56
|
+
const names = name.split(sep);
|
|
57
|
+
for (let i = 0; i < names.length; i++) {
|
|
58
|
+
if (i === names.length - 1) {
|
|
59
|
+
tempMiddleware[names[i]] = require(path.resolve(file))(app);
|
|
60
|
+
} else {
|
|
61
|
+
if (!tempMiddleware[names[i]]) {
|
|
62
|
+
tempMiddleware[names[i]] = {};
|
|
63
|
+
}
|
|
64
|
+
tempMiddleware = tempMiddleware[names[i]];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
app.middlewares = middlewares;
|
|
69
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const { sep } = path;
|
|
3
|
+
const glob = require("glob");
|
|
4
|
+
/**
|
|
5
|
+
* router-schema loader
|
|
6
|
+
* @param {object} app - koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 通过 'json-schema & ajv' 对 API 规则进行约束,配合 api-params-verifier 中间件使用
|
|
9
|
+
*
|
|
10
|
+
* app/router-schema/**.js
|
|
11
|
+
输出:
|
|
12
|
+
app.rouSchema = {
|
|
13
|
+
`${api1}: ${jsonSchema}`,
|
|
14
|
+
`${api2}: ${jsonSchema}`,
|
|
15
|
+
`${api3}: ${jsonSchema}`,
|
|
16
|
+
`${api4}: ${jsonSchema}`,
|
|
17
|
+
}
|
|
18
|
+
*/
|
|
19
|
+
module.exports = (app) => {
|
|
20
|
+
// 注册 routerSchema
|
|
21
|
+
let routerSchema = {};
|
|
22
|
+
|
|
23
|
+
// 读取 elpis/app/router-schema/**/**.js 下的所有文件
|
|
24
|
+
const elpisRouterSchemaPath = path.resolve(
|
|
25
|
+
__dirname,
|
|
26
|
+
`..${sep}..${sep}app${sep}router-schema`,
|
|
27
|
+
);
|
|
28
|
+
const elpisFileList = glob.sync(
|
|
29
|
+
path.resolve(elpisRouterSchemaPath, `.${sep}**${sep}*.js`),
|
|
30
|
+
);
|
|
31
|
+
elpisFileList.forEach((file) => {
|
|
32
|
+
handleFile(file);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const businessRouterSchemaPath = path.resolve(
|
|
36
|
+
app.businessPath,
|
|
37
|
+
`.${sep}router-schema`,
|
|
38
|
+
);
|
|
39
|
+
const businessFileList = glob.sync(
|
|
40
|
+
path.resolve(businessRouterSchemaPath, `.${sep}**${sep}*.js`),
|
|
41
|
+
);
|
|
42
|
+
businessFileList.forEach((file) => {
|
|
43
|
+
handleFile(file);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function handleFile(file) {
|
|
47
|
+
routerSchema = {
|
|
48
|
+
...routerSchema,
|
|
49
|
+
...require(path.resolve(file)),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
app.routerSchema = routerSchema;
|
|
54
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const KoaRouter = require("koa-router");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const glob = require("glob");
|
|
4
|
+
const { sep } = path;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* router loader
|
|
8
|
+
* @param {object} app Koa 实例
|
|
9
|
+
*
|
|
10
|
+
* 解析多有 app/router/下所有 JS 文件, 加载到 KoaRouter 中
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
module.exports = (app) => {
|
|
14
|
+
//实例化 KoaRouter
|
|
15
|
+
const router = new KoaRouter();
|
|
16
|
+
|
|
17
|
+
// 找到 elpis 路由文件路径
|
|
18
|
+
const elpisRouterPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}router`);
|
|
19
|
+
// 注册所有elpis路由
|
|
20
|
+
const elpisFileList = glob.sync(path.resolve(elpisRouterPath, `.${sep}**${sep}**.js`));
|
|
21
|
+
elpisFileList.forEach((file) => {
|
|
22
|
+
require(path.resolve(file))(app, router);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// 找到业务路由文件路径
|
|
26
|
+
const businessRouterPath = path.resolve(app.businessPath, `.${sep}router`);
|
|
27
|
+
// 注册所有业务路由
|
|
28
|
+
const businessFileList = glob.sync(path.resolve(businessRouterPath, `.${sep}**${sep}**.js`));
|
|
29
|
+
businessFileList.forEach((file) => {
|
|
30
|
+
require(path.resolve(file))(app, router);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 路由兜底
|
|
34
|
+
router.get("*", async (ctx, next) => {
|
|
35
|
+
ctx.status = 302; // 临时重定向
|
|
36
|
+
ctx.redirect(`${app?.options?.homePage ?? "/"}`);
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// 路由注册到 app 上
|
|
40
|
+
app.use(router.routes());
|
|
41
|
+
app.use(router.allowedMethods());
|
|
42
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const glob = require("glob");
|
|
3
|
+
const { sep } = path;
|
|
4
|
+
/**
|
|
5
|
+
* service loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* service 目录结构, 可通过 'app.service.${目录}.${文件}' 访问
|
|
9
|
+
*
|
|
10
|
+
* 例子:
|
|
11
|
+
* app/service
|
|
12
|
+
│
|
|
13
|
+
│ -- custom-module
|
|
14
|
+
│
|
|
15
|
+
│ -- custom-service.js
|
|
16
|
+
|
|
17
|
+
=> app.service.customModule.customService
|
|
18
|
+
*/
|
|
19
|
+
module.exports = (app) => {
|
|
20
|
+
const service = {};
|
|
21
|
+
|
|
22
|
+
// 读取 elpis/app/service/**/**.js 目录下的所有文件
|
|
23
|
+
const elpisServicePath = path.resolve(
|
|
24
|
+
__dirname,
|
|
25
|
+
`..${sep}..${sep}app${sep}service`,
|
|
26
|
+
);
|
|
27
|
+
const elpisFileList = glob.sync(
|
|
28
|
+
path.resolve(elpisServicePath, `.${sep}**${sep}*.js`),
|
|
29
|
+
);
|
|
30
|
+
// 遍历文件列表
|
|
31
|
+
elpisFileList.forEach((file) => handleFile(file));
|
|
32
|
+
|
|
33
|
+
// 读取 业务根目录/app/service/**/**.js 目录下的所有文件
|
|
34
|
+
const businessServicePath = path.resolve(app.businessPath, `.${sep}service`);
|
|
35
|
+
const businessFileList = glob.sync(
|
|
36
|
+
path.resolve(businessServicePath, `.${sep}**${sep}*.js`),
|
|
37
|
+
);
|
|
38
|
+
// 遍历文件列表
|
|
39
|
+
businessFileList.forEach((file) => handleFile(file));
|
|
40
|
+
|
|
41
|
+
function handleFile(file) {
|
|
42
|
+
// 提取文件名称
|
|
43
|
+
let name = path.resolve(file);
|
|
44
|
+
|
|
45
|
+
// 截取路径 app/service/custom-module/custom-service.js => customModule.customService
|
|
46
|
+
name = name.substring(
|
|
47
|
+
name.lastIndexOf(`service${sep}`) + `service${sep}`.length,
|
|
48
|
+
name.lastIndexOf(".js"),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// 将文件名称转换为驼峰命名
|
|
52
|
+
name = name.replace(/-([a-z])/g, (all, letter) => letter.toUpperCase());
|
|
53
|
+
|
|
54
|
+
// 挂载 service 到内存 app 对象中
|
|
55
|
+
let tempService = service;
|
|
56
|
+
const names = name.split(sep);
|
|
57
|
+
for (let i = 0; i < names.length; i++) {
|
|
58
|
+
if (i === names.length - 1) {
|
|
59
|
+
const ServiceMoule = require(path.resolve(file))(app);
|
|
60
|
+
tempService[names[i]] = new ServiceMoule();
|
|
61
|
+
} else {
|
|
62
|
+
if (!tempService[names[i]]) {
|
|
63
|
+
tempService[names[i]] = {};
|
|
64
|
+
}
|
|
65
|
+
tempService = tempService[names[i]];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
app.service = service;
|
|
70
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const ElpisCore = require("./elpis-core");
|
|
2
|
+
// 引入前端工程化构建
|
|
3
|
+
const FEBuildDev = require("./app/webpack/dev.js");
|
|
4
|
+
const FEBuildProd = require("./app/webpack/prod.js");
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
/**
|
|
8
|
+
* 服务端基础
|
|
9
|
+
*/
|
|
10
|
+
Controller: {
|
|
11
|
+
Base: require("./app/controller/base.js")
|
|
12
|
+
},
|
|
13
|
+
Service: {
|
|
14
|
+
Base: require("./app/service/base.js")
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* 编译构建前端工程
|
|
18
|
+
* @params env 环境变量, dev 或 prod
|
|
19
|
+
*/
|
|
20
|
+
frontendBuild(env) {
|
|
21
|
+
if(env === "local") {
|
|
22
|
+
FEBuildDev();
|
|
23
|
+
}else if(env === "production") {
|
|
24
|
+
FEBuildProd();
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* 启动elpis
|
|
29
|
+
* @params options 项目配置, 透传到elpis-core
|
|
30
|
+
*/
|
|
31
|
+
serverStart(options={}) {
|
|
32
|
+
const app = ElpisCore.start(options);
|
|
33
|
+
return app;
|
|
34
|
+
}
|
|
35
|
+
};
|