@james-zhang/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/.eslintignore +3 -0
- package/.eslintrc +55 -0
- package/.tmp-check.js +47 -0
- package/README.md +287 -0
- package/app/controller/base.js +37 -0
- package/app/controller/project.js +76 -0
- package/app/controller/view.js +17 -0
- package/app/extend/logger.js +36 -0
- package/app/middleware/api-params-verify.js +78 -0
- package/app/middleware/api-sign-verify.js +31 -0
- package/app/middleware/error-handler.js +36 -0
- package/app/middleware/project-handler.js +26 -0
- package/app/middleware.js +69 -0
- package/app/pages/asserts/custom.css +10 -0
- package/app/pages/boot.js +44 -0
- package/app/pages/common/curl.js +76 -0
- package/app/pages/common/utils.js +2 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +19 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +131 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +29 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-pannel/search-pannel.vue +36 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-pannel/table-pannel.vue +125 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +19 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +119 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +131 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +149 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +126 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +111 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +28 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +138 -0
- package/app/pages/dashboard/dashboard.vue +121 -0
- package/app/pages/dashboard/entry.dashboard.js +52 -0
- package/app/pages/dashboard/router.js +3 -0
- package/app/pages/dashboard/todo/todo.vue +10 -0
- package/app/pages/store/index.js +3 -0
- package/app/pages/store/menu.js +71 -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 +118 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +165 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +164 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +138 -0
- package/app/pages/widgets/schema-form/form-item-config.js +20 -0
- package/app/pages/widgets/schema-form/schema-form.vue +154 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +49 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +65 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +44 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +50 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +134 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +277 -0
- package/app/pages/widgets/sider-container/sider-container.vue +42 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +239 -0
- package/app/router/project.js +7 -0
- package/app/router/view.js +7 -0
- package/app/router-schema/project.js +34 -0
- package/app/service/base.js +10 -0
- package/app/service/project.js +43 -0
- package/app/view/entry.tpl +31 -0
- package/app/webpack/config/webpack.base.js +224 -0
- package/app/webpack/config/webpack.dev.js +53 -0
- package/app/webpack/config/webpack.prod.js +104 -0
- package/app/webpack/dev.js +54 -0
- package/app/webpack/libs/blank.js +1 -0
- package/app/webpack/prod.js +17 -0
- package/config/config.default.js +3 -0
- package/docs/dashboard-model.js +150 -0
- package/elpis-core/env.js +21 -0
- package/elpis-core/index.js +82 -0
- package/elpis-core/loader/config.js +58 -0
- package/elpis-core/loader/controller.js +55 -0
- package/elpis-core/loader/extend.js +42 -0
- package/elpis-core/loader/middleware.js +50 -0
- package/elpis-core/loader/router-schema.js +28 -0
- package/elpis-core/loader/router.js +42 -0
- package/elpis-core/loader/service.js +51 -0
- package/index.js +37 -0
- package/logs/application.log +50 -0
- package/logs/application.log.2026-06-13 +159 -0
- package/model/index.js +119 -0
- package/package.json +88 -0
- package/scripts/generate-cover.py +93 -0
- package/scripts/generate-diagrams.js +80 -0
- package/test/controller/project.test.js +218 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { rowContextKey } from "element-plus";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
mode:'dashboard', //模版类型,不同模版类型对应不一样的模版数据结构
|
|
5
|
+
name:'',
|
|
6
|
+
desc:'',
|
|
7
|
+
icon:'',
|
|
8
|
+
homePage:'', //首页(项目配置)
|
|
9
|
+
//头部菜单
|
|
10
|
+
menu:[{
|
|
11
|
+
key:'',
|
|
12
|
+
name:'',
|
|
13
|
+
menuType:'', //枚举值 group/module
|
|
14
|
+
|
|
15
|
+
//当menuType为group时,子菜单列表
|
|
16
|
+
subMenu:[{
|
|
17
|
+
//可递归menuItem
|
|
18
|
+
}],
|
|
19
|
+
//当menuType为module时,模块路径
|
|
20
|
+
moduleType:'', //枚举值 iframe/custom/schema
|
|
21
|
+
|
|
22
|
+
//当moduleType为sider时,sider路径(先点击跳出侧边栏,再点击侧边栏菜单,即可跳转到对应模块)
|
|
23
|
+
siderConfig:{
|
|
24
|
+
menu:[{
|
|
25
|
+
//可递归menuItem(除moduleType为sider时,其他moduleType均支持)
|
|
26
|
+
}]
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
//以下点击头部菜单,直接跳转到对应模块
|
|
30
|
+
//当moduleType为iframe时,iframe路径
|
|
31
|
+
iframeConfig:{
|
|
32
|
+
path:''
|
|
33
|
+
},
|
|
34
|
+
//当moduleType为custom时,自定义模块路径
|
|
35
|
+
customConfig:{
|
|
36
|
+
path:''
|
|
37
|
+
},
|
|
38
|
+
//当moduleType为schema时,schema路径
|
|
39
|
+
schemaConfig:{
|
|
40
|
+
api:'', //数据源API遵循RESTFUL规范
|
|
41
|
+
schema:{
|
|
42
|
+
//板块数据结构
|
|
43
|
+
type:'object',
|
|
44
|
+
properties:{
|
|
45
|
+
key:{
|
|
46
|
+
...schema, //标准schema配置
|
|
47
|
+
type:'', //字段类型
|
|
48
|
+
label:'', //字段的中文名
|
|
49
|
+
//字段在table中的相关配置
|
|
50
|
+
tableOption:{
|
|
51
|
+
...elTableConfig,//标准el-table-column配置
|
|
52
|
+
toFixed:0,//保留小数位数
|
|
53
|
+
visible:true, //是否在table中显示
|
|
54
|
+
},
|
|
55
|
+
//字段在search-bar中的相关配置
|
|
56
|
+
searchOption:{
|
|
57
|
+
...elComponentConfig,//标准el-component-column配置
|
|
58
|
+
comType:'',// 配置组件类型 input/select...
|
|
59
|
+
default:'', //默认值
|
|
60
|
+
|
|
61
|
+
//comType===select时,enumList为下拉选项列表
|
|
62
|
+
enumList:[], //下拉可选项
|
|
63
|
+
//comType===dynamicSelect时,enumList为动态下拉选项列表
|
|
64
|
+
api:'' //动态下拉可选项
|
|
65
|
+
},
|
|
66
|
+
//字段在不同动态component中的相关配置,前缀对应componentConfig中的key值
|
|
67
|
+
//如:componentConfig.createForm,这里对应createFormOption的配置
|
|
68
|
+
//字段在createForm中的相关配置
|
|
69
|
+
createFormOption:{
|
|
70
|
+
...elComponentConfig,//标准el-component-column配置
|
|
71
|
+
comType:'',// 配置组件类型 input/select...
|
|
72
|
+
visible:true, //是否在createForm中显示
|
|
73
|
+
disabled:false, //是否禁用createForm中的该字段
|
|
74
|
+
default:'', //默认值
|
|
75
|
+
|
|
76
|
+
//comType===select时,enumList为下拉选项列表
|
|
77
|
+
enumList:[], //下拉可选项
|
|
78
|
+
//comType===dynamicSelect时,enumList为动态下拉选项列表
|
|
79
|
+
},
|
|
80
|
+
//字段在editForm中的相关配置
|
|
81
|
+
editFormOption:{
|
|
82
|
+
...elComponentConfig,//标准el-component-column配置
|
|
83
|
+
comType:'',// 配置组件类型 input/select...
|
|
84
|
+
visible:true, //是否在editForm中显示
|
|
85
|
+
disabled:false, //是否禁用editForm中的该字段
|
|
86
|
+
default:'', //默认值
|
|
87
|
+
|
|
88
|
+
//comType===select时,enumList为下拉选项列表
|
|
89
|
+
enumList:[], //下拉可选项
|
|
90
|
+
//comType===dynamicSelect时,enumList为动态下拉选项列表
|
|
91
|
+
},
|
|
92
|
+
//字段在detailPanel中的相关配置
|
|
93
|
+
detailPanelOption:{
|
|
94
|
+
...elComponentConfig,//标准el-component-column配置
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
required:[
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
tableConfig:{
|
|
102
|
+
headerButtons:[{
|
|
103
|
+
label:'',//按钮中文名
|
|
104
|
+
eventKey:'',//按钮点击事件名
|
|
105
|
+
eventOption:{
|
|
106
|
+
//当 eventKey===‘showComponent’时,params为组件的key值
|
|
107
|
+
comName:'' //组件名称
|
|
108
|
+
}, //按钮点击事件参数
|
|
109
|
+
...elButtonConfig,//标准按钮配置
|
|
110
|
+
}],
|
|
111
|
+
rowButtons:[
|
|
112
|
+
{
|
|
113
|
+
label:'',//按钮中文名
|
|
114
|
+
eventKey:'',//按钮点击事件名
|
|
115
|
+
eventOption:{
|
|
116
|
+
//当 eventKey===‘showComponent’时,params为组件的key值
|
|
117
|
+
comName:'', //组件名称
|
|
118
|
+
|
|
119
|
+
params:{
|
|
120
|
+
//paramKey = 参数的键值
|
|
121
|
+
//rowValueKey = 参数值(当格式为 schema::tableKey时,到table中找响应的字段)
|
|
122
|
+
paramKey:rowContextKey,
|
|
123
|
+
}
|
|
124
|
+
}, //按钮点击事件参数
|
|
125
|
+
...elButtonConfig,//标准按钮配置
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}, //table配置,包含table的列配置、分页配置等
|
|
129
|
+
searchConfig:{}, //search配置,包含search的列配置、分页配置等
|
|
130
|
+
//动态组件 相关配置
|
|
131
|
+
componentConfig:{
|
|
132
|
+
//create-form 表单相关配置
|
|
133
|
+
createForm:{
|
|
134
|
+
title:'',//表单标题
|
|
135
|
+
saveBtnText:'',//保存按钮文本
|
|
136
|
+
},
|
|
137
|
+
editForm:{
|
|
138
|
+
mainKey:'',//主键字段名
|
|
139
|
+
title:'',//表单标题
|
|
140
|
+
saveBtnText:'',//保存按钮文本
|
|
141
|
+
},
|
|
142
|
+
detailPanel:{
|
|
143
|
+
mainKey:'',//主键字段名
|
|
144
|
+
title:'',//表单标题
|
|
145
|
+
saveBtnText:'',//保存按钮文本
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}]
|
|
150
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function getEnv(){
|
|
2
|
+
return process.env._ENV?.trim().replace(/^['"]|['"]$/g,'') ?? 'local'
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
module.exports=()=>{
|
|
6
|
+
return {
|
|
7
|
+
isLocal(){
|
|
8
|
+
return getEnv()==='local'
|
|
9
|
+
},
|
|
10
|
+
isBeta(){
|
|
11
|
+
return getEnv()==='beta'
|
|
12
|
+
},
|
|
13
|
+
isProd(){
|
|
14
|
+
return getEnv()==='prod'
|
|
15
|
+
},
|
|
16
|
+
get(){
|
|
17
|
+
return getEnv()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const Koa=require('koa')
|
|
2
|
+
const path=require('path')
|
|
3
|
+
const fs=require('fs')
|
|
4
|
+
const { sep }=path //路径分隔符
|
|
5
|
+
const env=require('./env')
|
|
6
|
+
|
|
7
|
+
//引入loader
|
|
8
|
+
const configLoader=require('./loader/config')
|
|
9
|
+
const extendLoader=require('./loader/extend')
|
|
10
|
+
const serviceLoader=require('./loader/service')
|
|
11
|
+
const controllerLoader=require('./loader/controller')
|
|
12
|
+
const routerSchemaLoader=require('./loader/router-schema')
|
|
13
|
+
const routerLoader=require('./loader/router')
|
|
14
|
+
const middlewareLoader=require('./loader/middleware')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
module.exports={
|
|
19
|
+
start(options={}){
|
|
20
|
+
const app=new Koa()
|
|
21
|
+
|
|
22
|
+
//应用配置
|
|
23
|
+
app.options=options||{}
|
|
24
|
+
//应用根目录
|
|
25
|
+
app.baseDir=process.cwd()||'/'
|
|
26
|
+
//业务文件路径
|
|
27
|
+
app.businessDir=path.resolve(app.baseDir,`.${sep}app`)
|
|
28
|
+
//初始化环境配置
|
|
29
|
+
app.env=env()
|
|
30
|
+
|
|
31
|
+
//加载中间件
|
|
32
|
+
middlewareLoader(app)
|
|
33
|
+
console.log("Middleware loaded")
|
|
34
|
+
//加载路由模式
|
|
35
|
+
routerSchemaLoader(app)
|
|
36
|
+
console.log("RouterSchema loaded")
|
|
37
|
+
//加载配置(须先于 service/controller,否则基类中 app.config 为 undefined)
|
|
38
|
+
configLoader(app)
|
|
39
|
+
console.log("Config loaded")
|
|
40
|
+
//加载扩展
|
|
41
|
+
extendLoader(app)
|
|
42
|
+
console.log("Extend loaded")
|
|
43
|
+
//加载服务层
|
|
44
|
+
serviceLoader(app)
|
|
45
|
+
console.log("Service loaded")
|
|
46
|
+
//加载业务控制器
|
|
47
|
+
controllerLoader(app)
|
|
48
|
+
console.log("Controller loaded")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
// 注册 elpis 全局中间件
|
|
52
|
+
const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}app${sep}middleware.js`);
|
|
53
|
+
const elpisMiddleware = require(elpisMiddlewarePath);
|
|
54
|
+
elpisMiddleware(app);
|
|
55
|
+
console.log(`-- [start] load global elpis middleware done --`);
|
|
56
|
+
|
|
57
|
+
// 注册业务全局中间件
|
|
58
|
+
try {
|
|
59
|
+
require(`${app.businessPath}${sep}middleware.js`)(app);
|
|
60
|
+
console.log(`-- [start] load global business middleware done --`);
|
|
61
|
+
} catch(e) {
|
|
62
|
+
console.log('[exception] there is no global business middleware file.')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//加载路由
|
|
66
|
+
routerLoader(app)
|
|
67
|
+
console.log("Router loaded")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
try{
|
|
71
|
+
const port=process.env.PORT||8080
|
|
72
|
+
const host=process.env.HOST||'0.0.0.0'
|
|
73
|
+
app.server=app.listen(port,host,()=>{
|
|
74
|
+
console.log(`Server is running on http://${host}:${port}`)
|
|
75
|
+
})
|
|
76
|
+
}catch(err){
|
|
77
|
+
console.error(err)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return app
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const path=require('path')
|
|
2
|
+
const { sep }=path //路径分隔符
|
|
3
|
+
/**
|
|
4
|
+
* config loader
|
|
5
|
+
*
|
|
6
|
+
* 配置区分本地/生产/测试环境,通过.env文件指定环境变量
|
|
7
|
+
* 通过 env.config 覆盖 default.js 配置文件 挂载到 app.config 对象中
|
|
8
|
+
*
|
|
9
|
+
* 核心作用:自动扫描指定目录下所有 JS 配置文件,按目录层级 + 文件名规则,批量挂载到 app.config 对象,
|
|
10
|
+
* 实现配置自动化引入,无需手动 require
|
|
11
|
+
*
|
|
12
|
+
* 默认配置 config/config.default.js
|
|
13
|
+
* 本地配置 config/config.local.js
|
|
14
|
+
* 生产配置 config/config.prod.js
|
|
15
|
+
* 测试配置 config/config.beta.js
|
|
16
|
+
*
|
|
17
|
+
* @param {*} app
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
module.exports=(app)=>{
|
|
21
|
+
//elpis config 目录及相关文件
|
|
22
|
+
const elpisConfigPath=path.resolve(__dirname,`..${sep}..${sep}config`)
|
|
23
|
+
let defaultConfig=require(path.resolve(elpisConfigPath,`.${sep}config.default.js`))
|
|
24
|
+
|
|
25
|
+
//业务config 目录及相关文件
|
|
26
|
+
const businessConfigPath=path.resolve(process.cwd(),`.${sep}config`)
|
|
27
|
+
|
|
28
|
+
//获取default.js配置(默认配置文件)
|
|
29
|
+
try{
|
|
30
|
+
defaultConfig={
|
|
31
|
+
...defaultConfig,
|
|
32
|
+
...require(path.resolve(businessConfigPath,`.${sep}config.default.js`))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
catch(err){
|
|
37
|
+
console.log(`default.js 配置文件不存在,使用空配置`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let envConfig={}
|
|
41
|
+
try{
|
|
42
|
+
if(app.env.isLocal()){//本地环境
|
|
43
|
+
envConfig=require(path.resolve(businessConfigPath,`.${sep}config.local.js`))
|
|
44
|
+
}
|
|
45
|
+
else if(app.env.isProd()){//生产环境
|
|
46
|
+
envConfig=require(path.resolve(businessConfigPath,`.${sep}config.prod.js`))
|
|
47
|
+
}
|
|
48
|
+
else if(app.env.isBeta()){//测试环境
|
|
49
|
+
envConfig=require(path.resolve(businessConfigPath,`.${sep}config.beta.js`))
|
|
50
|
+
}
|
|
51
|
+
}catch(e){
|
|
52
|
+
console.log(`环境配置文件不存在,使用空配置`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//覆盖并加载config配置
|
|
56
|
+
app.config=Object.assign({},defaultConfig,envConfig)
|
|
57
|
+
}
|
|
58
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//核心作用:自动扫描指定目录下所有 JS 中间件文件,按目录层级 + 文件名规则,批量挂载到 app.middlewares 对象,
|
|
2
|
+
// 实现中间件自动化引入,无需手动 require
|
|
3
|
+
|
|
4
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
5
|
+
const path=require('path')
|
|
6
|
+
const { sep }=path //路径分隔符(兼容Windows和Linux)
|
|
7
|
+
|
|
8
|
+
module.exports=(app)=>{
|
|
9
|
+
const controllers = {}
|
|
10
|
+
|
|
11
|
+
//读取 elpis/app/controller 目录下的所有 js 文件
|
|
12
|
+
const elpisControllerPath=path.resolve(__dirname,`..${sep}..${sep}app${sep}controller`)
|
|
13
|
+
const elpisFileList=glob.sync(path.resolve(elpisControllerPath,`.${sep}**${sep}*.js`))
|
|
14
|
+
elpisFileList.forEach(file=>handleFile(file))
|
|
15
|
+
|
|
16
|
+
//读取 业务/app/controller 目录下的所有 js 文件
|
|
17
|
+
const businessControllerPath=path.resolve(app.businessDir,`.${sep}controller`)
|
|
18
|
+
const businessFileList=glob.sync(path.resolve(businessControllerPath,`.${sep}**${sep}*.js`))
|
|
19
|
+
businessFileList.forEach(file=>handleFile(file))
|
|
20
|
+
|
|
21
|
+
//遍历文件列表,提取文件名称并加载控制器
|
|
22
|
+
function handleFile(file){
|
|
23
|
+
//提取文件名称
|
|
24
|
+
let name=path.resolve(file)
|
|
25
|
+
|
|
26
|
+
//截取路径 app/controller/custom-module/custom-controller.js => custom-module/custom-controller.js
|
|
27
|
+
name=name.substring(name.lastIndexOf(`controller${sep}`)+`controller${sep}`.length,name.lastIndexOf('.'))
|
|
28
|
+
|
|
29
|
+
//把'-'统一改为驼峰式,custom-module-custom-controller.js => CustomModuleCustomController.js
|
|
30
|
+
name=name.replace(/[_-][a-z]/ig,(s)=>s.substring(1).toUpperCase())
|
|
31
|
+
|
|
32
|
+
//挂载controller到内存app对象中
|
|
33
|
+
let tempControllers=controllers
|
|
34
|
+
const names = name.split(sep) //[['a','b','customModule','customController']]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
for(let i=0;i<names.length;++i){
|
|
38
|
+
//递归挂载controller
|
|
39
|
+
if(i===names.length-1){
|
|
40
|
+
// 文件:最后一项:加载模块并赋值(真正挂载控制器)
|
|
41
|
+
//将控制器加载改为控制器加载,变量名需要更新
|
|
42
|
+
const ControllerModule=require(path.resolve(file))(app)
|
|
43
|
+
tempControllers[names[i]]=new ControllerModule()
|
|
44
|
+
}
|
|
45
|
+
else{
|
|
46
|
+
// 文件夹:非最后一项:创建空对象,作为嵌套层级
|
|
47
|
+
if(!tempControllers[names[i]]){
|
|
48
|
+
tempControllers[names[i]]={}
|
|
49
|
+
}
|
|
50
|
+
tempControllers=tempControllers[names[i]]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
app.controller=controllers;
|
|
55
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//核心作用:加载自定义扩展模块,实现自定义功能的扩展
|
|
2
|
+
//只有一级目录
|
|
3
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
4
|
+
const path=require('path')
|
|
5
|
+
const { sep }=path //路径分隔符(兼容Windows和Linux)
|
|
6
|
+
|
|
7
|
+
module.exports=(app)=>{
|
|
8
|
+
//读取 elpis/extend/**.js 目录下的所有 js 文件
|
|
9
|
+
const elpisExtendPath=path.resolve(__dirname,`..${sep}..${sep}app${sep}extend`)
|
|
10
|
+
const elpisExtendFileList=glob.sync(path.resolve(elpisExtendPath,`.${sep}**${sep}**.js`))
|
|
11
|
+
elpisExtendFileList.forEach(file=>handleFile(file))
|
|
12
|
+
|
|
13
|
+
//读取 业务/extend/**.js 目录下的所有 js 文件
|
|
14
|
+
const businessExtendPath=path.resolve(app.businessDir,`.${sep}extend`)
|
|
15
|
+
const businessExtendFileList=glob.sync(path.resolve(businessExtendPath,`.${sep}**${sep}**.js`))
|
|
16
|
+
businessExtendFileList.forEach(file=>handleFile(file))
|
|
17
|
+
|
|
18
|
+
//遍历文件列表,提取文件名称并加载扩展
|
|
19
|
+
function handleFile(file){
|
|
20
|
+
//提取文件名称
|
|
21
|
+
let name=path.resolve(file)
|
|
22
|
+
|
|
23
|
+
//截取路径 app/extend/custom-module/custom-extension.js => custom-module/custom-extension.js
|
|
24
|
+
name=name.substring(name.lastIndexOf(`extend${sep}`)+`extend${sep}`.length,name.lastIndexOf('.'))
|
|
25
|
+
|
|
26
|
+
//把'-'统一改为驼峰式,custom-module-custom-extension.js => CustomModuleCustomExtension.js
|
|
27
|
+
name=name.replace(/[_-][a-z]/ig,(s)=>s.substring(1).toUpperCase())
|
|
28
|
+
|
|
29
|
+
//过滤已经存在的key
|
|
30
|
+
for(const key in app){
|
|
31
|
+
if(key === name){
|
|
32
|
+
console.log('error')
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//挂载extend到内存app对象中
|
|
38
|
+
app[name]=require(path.resolve(file))(app)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//核心作用:自动扫描指定目录下所有 JS 中间件文件,按目录层级 + 文件名规则,批量挂载到 app.middlewares 对象,
|
|
2
|
+
// 实现中间件自动化引入,无需手动 require
|
|
3
|
+
|
|
4
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
5
|
+
const path=require('path')
|
|
6
|
+
const { sep }=path //路径分隔符
|
|
7
|
+
|
|
8
|
+
module.exports=(app)=>{
|
|
9
|
+
//遍历文件列表,提取文件名称并加载中间件
|
|
10
|
+
const middlewares = {}
|
|
11
|
+
//读取 elpis/app/middleware 目录下的所有 js 文件
|
|
12
|
+
const elpisMiddlewarePath=path.resolve(__dirname,`..${sep}..${sep}app${sep}middleware`)
|
|
13
|
+
const elpisFileList=glob.sync(path.resolve(elpisMiddlewarePath,`.${sep}**${sep}**.js`))
|
|
14
|
+
elpisFileList.forEach(file=>handleFile(file))
|
|
15
|
+
|
|
16
|
+
//读取 business/middleware 业务根目录下的所有 js 文件
|
|
17
|
+
const businessMiddlewarePath=path.resolve(app.businessDir,`.${sep}middleware`)
|
|
18
|
+
const businessFileList=glob.sync(path.resolve(businessMiddlewarePath,`.${sep}**${sep}**.js`))
|
|
19
|
+
businessFileList.forEach(file=>handleFile(file))
|
|
20
|
+
|
|
21
|
+
function handleFile(file){
|
|
22
|
+
//提取文件名称
|
|
23
|
+
let name=path.resolve(file)
|
|
24
|
+
|
|
25
|
+
//截取路径 app/middlewares/custom-module/custom-middleware.js => custom-module/custom-middleware.js
|
|
26
|
+
name=name.substring(name.lastIndexOf(`middleware${sep}`)+`middleware${sep}`.length,name.lastIndexOf('.'))
|
|
27
|
+
|
|
28
|
+
//把'-'统一改为驼峰式,custom-module-custom-middleware.js => CustomModuleCustomMiddleware.js
|
|
29
|
+
name=name.replace(/[_-][a-z]/ig,(s)=>s.substring(1).toUpperCase())
|
|
30
|
+
|
|
31
|
+
//挂载middleware到内存app对象中
|
|
32
|
+
let tempMiddleware=middlewares
|
|
33
|
+
const names = name.split(sep) //[['customModule','customMiddleware']]
|
|
34
|
+
for(let i=0;i<names.length;++i){
|
|
35
|
+
//递归挂载middleware
|
|
36
|
+
if(i===names.length-1){
|
|
37
|
+
// 文件:最后一项:加载模块并赋值(真正挂载中间件)
|
|
38
|
+
tempMiddleware[names[i]]=require(path.resolve(file))(app)
|
|
39
|
+
}
|
|
40
|
+
else{
|
|
41
|
+
// 文件夹:非最后一项:创建空对象,作为嵌套层级
|
|
42
|
+
if(!tempMiddleware[names[i]]){
|
|
43
|
+
tempMiddleware[names[i]]={}
|
|
44
|
+
}
|
|
45
|
+
tempMiddleware=tempMiddleware[names[i]]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
app.middlewares=middlewares;
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
2
|
+
const path=require('path')
|
|
3
|
+
const { sep }=path //路径分隔符
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module.exports=(app)=>{
|
|
7
|
+
let routerSchema = {}
|
|
8
|
+
|
|
9
|
+
//读取 elpis/app/router-schema 目录下的所有 js 文件
|
|
10
|
+
const elpisRouterSchemaPath=path.resolve(__dirname,`..${sep}..${sep}app${sep}router-schema`)
|
|
11
|
+
const elpisFileList=glob.sync(path.resolve(elpisRouterSchemaPath,`.${sep}**${sep}**.js`))
|
|
12
|
+
elpisFileList.forEach(file=>handleFile(file))
|
|
13
|
+
|
|
14
|
+
//读取 业务/app/router-schema 目录下的所有 js 文件
|
|
15
|
+
const businessRouterSchemaPath=path.resolve(app.businessDir,`.${sep}router-schema`)
|
|
16
|
+
const businessFileList=glob.sync(path.resolve(businessRouterSchemaPath,`.${sep}**${sep}**.js`))
|
|
17
|
+
businessFileList.forEach(file=>handleFile(file))
|
|
18
|
+
|
|
19
|
+
//遍历文件列表,提取文件名称并加载中间件
|
|
20
|
+
function handleFile(file){
|
|
21
|
+
routerSchema = {
|
|
22
|
+
...routerSchema,
|
|
23
|
+
...require(path.resolve(file))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
app.routerSchemas=routerSchema;
|
|
28
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const KoaRouter=require('koa-router')
|
|
2
|
+
const path=require('path')
|
|
3
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
4
|
+
const { sep }=path //路径分隔符(兼容Windows和Linux)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
module.exports=(app)=>{
|
|
8
|
+
//实例化KoaRouter
|
|
9
|
+
const router=new KoaRouter()
|
|
10
|
+
|
|
11
|
+
//找到elpis 路由文件路径
|
|
12
|
+
const elpisRouterPath=path.resolve(__dirname,`..${sep}..${sep}app${sep}router`)
|
|
13
|
+
//注册所有业务路由
|
|
14
|
+
const elpisRouterFileList=glob.sync(path.resolve(elpisRouterPath,`.${sep}**${sep}*.js`))
|
|
15
|
+
elpisRouterFileList.forEach(file=>{
|
|
16
|
+
//挂载路由
|
|
17
|
+
require(path.resolve(file))(app,router)
|
|
18
|
+
})
|
|
19
|
+
//找到业务路由文件路径
|
|
20
|
+
const businessRouterPath=path.resolve(app.businessDir,`.${sep}router`)
|
|
21
|
+
//注册所有业务路由
|
|
22
|
+
const businessRouterFileList=glob.sync(path.resolve(businessRouterPath,`.${sep}**${sep}*.js`))
|
|
23
|
+
businessRouterFileList.forEach(file=>{
|
|
24
|
+
//挂载路由
|
|
25
|
+
require(path.resolve(file))(app,router)
|
|
26
|
+
})
|
|
27
|
+
//路由兜底:排除根路径,避免重定向循环
|
|
28
|
+
router.get('*',async(ctx,next)=>{
|
|
29
|
+
if(ctx.path==='/'){
|
|
30
|
+
ctx.status=404
|
|
31
|
+
ctx.body='Not Found'
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
//设置状态码
|
|
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,51 @@
|
|
|
1
|
+
//核心作用:自动扫描指定目录下所有 JS 服务层文件,按目录层级 + 文件名规则,批量挂载到 app.services 对象,
|
|
2
|
+
// 实现服务层自动化引入,无需手动 require
|
|
3
|
+
|
|
4
|
+
const glob=require('glob') //文件通配符扫描工具
|
|
5
|
+
const path=require('path')
|
|
6
|
+
const { sep }=path //路径分隔符
|
|
7
|
+
|
|
8
|
+
module.exports=(app)=>{
|
|
9
|
+
const services = {}
|
|
10
|
+
//elpis/app/service 目录下的所有 js 文件
|
|
11
|
+
const elpisServicePath=path.resolve(__dirname,`..${sep}..${sep}app${sep}service`)
|
|
12
|
+
const elpisFileList=glob.sync(path.resolve(elpisServicePath,`.${sep}**${sep}**.js`))
|
|
13
|
+
elpisFileList.forEach(file=>handleFile(file))
|
|
14
|
+
|
|
15
|
+
//读取业务/app/service 目录下的所有 js 文件
|
|
16
|
+
const businessServicePath=path.resolve(app.businessDir,`.${sep}service`)
|
|
17
|
+
const businessFileList=glob.sync(path.resolve(businessServicePath,`.${sep}**${sep}**.js`))
|
|
18
|
+
businessFileList.forEach(file=>handleFile(file))
|
|
19
|
+
|
|
20
|
+
//遍历文件列表,提取文件名称并加载服务层
|
|
21
|
+
function handleFile(file){
|
|
22
|
+
//提取文件名称
|
|
23
|
+
let name=path.resolve(file)
|
|
24
|
+
|
|
25
|
+
//截取路径 app/services/custom-module/custom-service.js => custom-module/custom-service.js
|
|
26
|
+
name=name.substring(name.lastIndexOf(`service${sep}`)+`service${sep}`.length,name.lastIndexOf('.'))
|
|
27
|
+
|
|
28
|
+
//把'-'统一改为驼峰式,custom-module-custom-service.js => CustomModuleCustomService.js
|
|
29
|
+
name=name.replace(/[_-][a-z]/ig,(s)=>s.substring(1).toUpperCase())
|
|
30
|
+
|
|
31
|
+
//挂载service到内存app对象中
|
|
32
|
+
let tempService=services
|
|
33
|
+
const names = name.split(sep) //[['customModule','customService']]
|
|
34
|
+
for(let i=0;i<names.length;++i){
|
|
35
|
+
//递归挂载service
|
|
36
|
+
if(i===names.length-1){
|
|
37
|
+
// 文件:最后一项:加载模块并赋值(真正挂载服务层)
|
|
38
|
+
const ServiceModule=require(path.resolve(file))(app)
|
|
39
|
+
tempService[names[i]]=new ServiceModule()
|
|
40
|
+
}
|
|
41
|
+
else{
|
|
42
|
+
// 文件夹:非最后一项:创建空对象,作为嵌套层级
|
|
43
|
+
if(!tempService[names[i]]){
|
|
44
|
+
tempService[names[i]]={}
|
|
45
|
+
}
|
|
46
|
+
tempService=tempService[names[i]]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
app.service=services;
|
|
51
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// 引入elpis-core模块
|
|
2
|
+
const ElpisCore=require('./elpis-core')
|
|
3
|
+
//引入前端工程化构建方法
|
|
4
|
+
const FEBuildDev=require('./app/webpack/dev.js')
|
|
5
|
+
const FEBuildProd=require('./app/webpack/prod.js')
|
|
6
|
+
|
|
7
|
+
module.exports={
|
|
8
|
+
/**
|
|
9
|
+
* 服务端基础
|
|
10
|
+
*/
|
|
11
|
+
Controller:{
|
|
12
|
+
Base:require('./app/controller/base.js')
|
|
13
|
+
},
|
|
14
|
+
Service:{
|
|
15
|
+
Base:require('./app/service/base.js')
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 前端工程化构建方法
|
|
21
|
+
*/
|
|
22
|
+
frontendBuild(env){
|
|
23
|
+
if(env==='local'){
|
|
24
|
+
FEBuildDev()
|
|
25
|
+
}else if(env==='prod'){
|
|
26
|
+
FEBuildProd()
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* 启动elpis框架
|
|
31
|
+
* @param {*} options 项目配置,透传到elpis-core
|
|
32
|
+
*/
|
|
33
|
+
serverStart(options={}){
|
|
34
|
+
const app=ElpisCore.start(options)
|
|
35
|
+
return app
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[2026-07-17T00:43:04.655] [INFO] default - {"cause":{},"isOperational":true}
|
|
2
|
+
[2026-07-17T00:43:04.657] [ERROR] default - [--exception--]: OperationalError: template not found: output/entry.page1.tpl
|
|
3
|
+
at createTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:234:15)
|
|
4
|
+
... 14 lines matching cause stack trace ...
|
|
5
|
+
at new Promise (<anonymous>) {
|
|
6
|
+
cause: Error: template not found: output/entry.page1.tpl
|
|
7
|
+
at createTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:234:15)
|
|
8
|
+
at next (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:260:7)
|
|
9
|
+
at handle (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:267:11)
|
|
10
|
+
at D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:276:9
|
|
11
|
+
at next (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:258:7)
|
|
12
|
+
at Object.asyncIter (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:263:3)
|
|
13
|
+
at Environment.getTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:259:9)
|
|
14
|
+
at Environment.render (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:295:10)
|
|
15
|
+
at Environment.promisified [as renderAsync] (D:\CV_project\elpis-learning\node_modules\.store\bluebird@3.7.2\node_modules\bluebird\js\release\promisify.js:223:16)
|
|
16
|
+
at _callee$ (D:\CV_project\elpis-learning\node_modules\.store\koa-nunjucks-2@3.0.2\node_modules\koa-nunjucks-2\compiled.js:109:64)
|
|
17
|
+
at tryCatch (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:62:40)
|
|
18
|
+
at Generator.invoke [as _invoke] (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:296:22)
|
|
19
|
+
at prototype.<computed> [as next] (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:114:21)
|
|
20
|
+
at step (D:\CV_project\elpis-learning\node_modules\.store\babel-runtime@6.26.0\node_modules\babel-runtime\helpers\asyncToGenerator.js:17:30)
|
|
21
|
+
at D:\CV_project\elpis-learning\node_modules\.store\babel-runtime@6.26.0\node_modules\babel-runtime\helpers\asyncToGenerator.js:35:14
|
|
22
|
+
at new Promise (<anonymous>),
|
|
23
|
+
isOperational: true
|
|
24
|
+
}
|
|
25
|
+
[2026-07-17T00:43:04.659] [ERROR] default - [--exception--]: undefined template not found: output/entry.page1.tpl undefined
|
|
26
|
+
[2026-07-17T00:45:07.583] [INFO] default - {"cause":{},"isOperational":true}
|
|
27
|
+
[2026-07-17T00:45:07.584] [ERROR] default - [--exception--]: OperationalError: template not found: output/entry.page1.tpl
|
|
28
|
+
at createTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:234:15)
|
|
29
|
+
... 14 lines matching cause stack trace ...
|
|
30
|
+
at new Promise (<anonymous>) {
|
|
31
|
+
cause: Error: template not found: output/entry.page1.tpl
|
|
32
|
+
at createTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:234:15)
|
|
33
|
+
at next (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:260:7)
|
|
34
|
+
at handle (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:267:11)
|
|
35
|
+
at D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:276:9
|
|
36
|
+
at next (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:258:7)
|
|
37
|
+
at Object.asyncIter (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\lib.js:263:3)
|
|
38
|
+
at Environment.getTemplate (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:259:9)
|
|
39
|
+
at Environment.render (D:\CV_project\elpis-learning\node_modules\.store\nunjucks@3.2.4\node_modules\nunjucks\src\environment.js:295:10)
|
|
40
|
+
at Environment.promisified [as renderAsync] (D:\CV_project\elpis-learning\node_modules\.store\bluebird@3.7.2\node_modules\bluebird\js\release\promisify.js:223:16)
|
|
41
|
+
at _callee$ (D:\CV_project\elpis-learning\node_modules\.store\koa-nunjucks-2@3.0.2\node_modules\koa-nunjucks-2\compiled.js:109:64)
|
|
42
|
+
at tryCatch (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:62:40)
|
|
43
|
+
at Generator.invoke [as _invoke] (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:296:22)
|
|
44
|
+
at prototype.<computed> [as next] (D:\CV_project\elpis-learning\node_modules\.store\regenerator-runtime@0.11.1\node_modules\regenerator-runtime\runtime.js:114:21)
|
|
45
|
+
at step (D:\CV_project\elpis-learning\node_modules\.store\babel-runtime@6.26.0\node_modules\babel-runtime\helpers\asyncToGenerator.js:17:30)
|
|
46
|
+
at D:\CV_project\elpis-learning\node_modules\.store\babel-runtime@6.26.0\node_modules\babel-runtime\helpers\asyncToGenerator.js:35:14
|
|
47
|
+
at new Promise (<anonymous>),
|
|
48
|
+
isOperational: true
|
|
49
|
+
}
|
|
50
|
+
[2026-07-17T00:45:07.586] [ERROR] default - [--exception--]: undefined template not found: output/entry.page1.tpl undefined
|