mm_nav 0.0.0-stage → 1.1.1
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 +2 -3
- package/com.json +5 -0
- package/config.tpl.json +84 -0
- package/example/index.js +35 -0
- package/index.js +17 -0
- package/lib/manager.js +187 -0
- package/lib/nav.js +767 -0
- package/lib/viewmodel.js +448 -0
- package/package.json +31 -4
- package/test.js +18 -0
- package/tpl/admin_pc/page_channel.vue +305 -0
- package/tpl/admin_pc/page_config.vue +280 -0
- package/tpl/admin_pc/page_config_form.vue +194 -0
- package/tpl/admin_pc/page_default.vue +247 -0
- package/tpl/admin_pc/page_form.vue +180 -0
- package/tpl/admin_pc/page_lang.vue +264 -0
- package/tpl/admin_pc/page_nav.vue +321 -0
- package/tpl/admin_pc/page_table.vue +296 -0
- package/tpl/admin_pc/page_type.vue +321 -0
- package/tpl/admin_pc/page_view.vue +124 -0
- package/tpl/dev_pc/page_channel.vue +253 -0
- package/tpl/dev_pc/page_config.vue +253 -0
- package/tpl/dev_pc/page_default.vue +247 -0
- package/tpl/dev_pc/page_form.vue +162 -0
- package/tpl/dev_pc/page_nav.vue +253 -0
- package/tpl/dev_pc/page_table.vue +247 -0
- package/tpl/dev_pc/page_type.vue +313 -0
- package/tpl/home_pc/page_channel.vue +234 -0
- package/tpl/home_pc/page_default.vue +234 -0
- package/tpl/home_pc/page_form.vue +137 -0
- package/tpl/home_pc/page_list.vue +234 -0
- package/tpl/home_pc/page_nav.vue +221 -0
- package/tpl/home_pc/page_type.vue +234 -0
- package/tpl/home_pc/page_view.vue +125 -0
- package/tpl/home_phone/page_channel.vue +234 -0
- package/tpl/home_phone/page_default.vue +234 -0
- package/tpl/home_phone/page_form.vue +137 -0
- package/tpl/home_phone/page_list.vue +234 -0
- package/tpl/home_phone/page_nav.vue +237 -0
- package/tpl/home_phone/page_type.vue +234 -0
- package/tpl/home_phone/page_view.vue +125 -0
package/lib/nav.js
ADDED
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const Item = require("mm_mod").Base;
|
|
5
|
+
const { string, array, object, file } = require("mm_helper");
|
|
6
|
+
const Model = require("./viewmodel.js");
|
|
7
|
+
|
|
8
|
+
/** 视图模型实例 */
|
|
9
|
+
const VIEW_MODEL = new Model();
|
|
10
|
+
|
|
11
|
+
/** 模板引擎实例缓存(懒加载,undefined 表示加载失败) */
|
|
12
|
+
let tpl_instance = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 获取模板引擎实例(mm_tpl 包名引用,首次使用时创建)
|
|
16
|
+
* @returns {object|null} 模板引擎实例;mm_tpl 不可用时返回null
|
|
17
|
+
*/
|
|
18
|
+
function _getTpl() {
|
|
19
|
+
if (tpl_instance === null) {
|
|
20
|
+
try {
|
|
21
|
+
const { Tpl } = require("mm_tpl");
|
|
22
|
+
tpl_instance = new Tpl({
|
|
23
|
+
extname: ".vue",
|
|
24
|
+
chache_extname: ".cache.vue",
|
|
25
|
+
});
|
|
26
|
+
tpl_instance.dir = __dirname;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error("mm_tpl模板引擎加载失败:", err);
|
|
29
|
+
tpl_instance = undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return tpl_instance || null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 导航驱动类
|
|
37
|
+
* @augments {Item}
|
|
38
|
+
* @class
|
|
39
|
+
*/
|
|
40
|
+
class Nav extends Item {
|
|
41
|
+
static config = {
|
|
42
|
+
// 路由
|
|
43
|
+
"routes": [
|
|
44
|
+
/*
|
|
45
|
+
{
|
|
46
|
+
// 名称
|
|
47
|
+
"name": "root",
|
|
48
|
+
// 路由路径
|
|
49
|
+
"path": "/",
|
|
50
|
+
// 组件文件路径
|
|
51
|
+
"component": "",
|
|
52
|
+
// 页面显示级别
|
|
53
|
+
"level": "",
|
|
54
|
+
// 身份验证
|
|
55
|
+
"oauth": {
|
|
56
|
+
// 是否登录
|
|
57
|
+
"sign_in": false,
|
|
58
|
+
// 要求会员级别
|
|
59
|
+
"vip": 0,
|
|
60
|
+
// 要求管理级别
|
|
61
|
+
"gm": 0,
|
|
62
|
+
// 要求商户级别
|
|
63
|
+
"mc": 0,
|
|
64
|
+
// 要求用户组
|
|
65
|
+
"user_group": [],
|
|
66
|
+
// 要求管理组
|
|
67
|
+
"user_admin": []
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
*/
|
|
71
|
+
],
|
|
72
|
+
// 顶部导航
|
|
73
|
+
"top_nav": [],
|
|
74
|
+
// 左侧导航
|
|
75
|
+
"left_nav": [],
|
|
76
|
+
// 底部导航
|
|
77
|
+
"bottom_nav": [],
|
|
78
|
+
// 右侧导航
|
|
79
|
+
"right_nav": [],
|
|
80
|
+
// 桌面导航
|
|
81
|
+
"desktop_nav": [],
|
|
82
|
+
// 快捷导航
|
|
83
|
+
"quick_nav": [],
|
|
84
|
+
// 主导航
|
|
85
|
+
"main_nav": [
|
|
86
|
+
/*
|
|
87
|
+
{
|
|
88
|
+
// 标题
|
|
89
|
+
"title": "",
|
|
90
|
+
// 路径
|
|
91
|
+
"url": "",
|
|
92
|
+
// 在没有权限下是否显示
|
|
93
|
+
"show": false,
|
|
94
|
+
// 显示顺序
|
|
95
|
+
"display": 3,
|
|
96
|
+
// 子导航
|
|
97
|
+
"sub": [
|
|
98
|
+
{
|
|
99
|
+
// 标题
|
|
100
|
+
"title": "",
|
|
101
|
+
// 路径
|
|
102
|
+
"url": "",
|
|
103
|
+
// 在没有权限下是否显示
|
|
104
|
+
"show": false
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
*/
|
|
109
|
+
],
|
|
110
|
+
// 主程序文件 - 默认为空
|
|
111
|
+
"main": "",
|
|
112
|
+
// 驱动目录(方案B:手动配置,替代目录扫描)
|
|
113
|
+
"dir": "",
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* 构造函数
|
|
117
|
+
* @param {object} config 配置参数
|
|
118
|
+
* @param {object} parent 父对象(管理器实例)
|
|
119
|
+
* @class
|
|
120
|
+
*/
|
|
121
|
+
constructor(config, parent) {
|
|
122
|
+
super({ ...Nav.config, ...config });
|
|
123
|
+
|
|
124
|
+
// 父级管理器引用(用于跨模块服务注入回退)
|
|
125
|
+
this.parent = parent || null;
|
|
126
|
+
// 模板路径
|
|
127
|
+
this.tpl_path = "./tpl/";
|
|
128
|
+
// 默认启用热更新
|
|
129
|
+
this.mode = 3;
|
|
130
|
+
// API 获取函数缺失告警标记
|
|
131
|
+
this._api_warned = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 调用函数
|
|
137
|
+
* @param {object} type 导航类型
|
|
138
|
+
* @returns {object} 执行结果
|
|
139
|
+
*/
|
|
140
|
+
Nav.prototype.run = function (type) {
|
|
141
|
+
var cg = {
|
|
142
|
+
routes: this.config.routes,
|
|
143
|
+
};
|
|
144
|
+
cg[type] = this.config[type];
|
|
145
|
+
return cg;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 合并项
|
|
150
|
+
* @param {object[]} arr 目标数组
|
|
151
|
+
* @param {object[]} lt 导航项
|
|
152
|
+
*/
|
|
153
|
+
Nav.prototype.mergeSub = function (arr, lt) {
|
|
154
|
+
if (lt) {
|
|
155
|
+
var len = lt.length;
|
|
156
|
+
for (var i = 0; i < len; i++) {
|
|
157
|
+
var o = lt[i];
|
|
158
|
+
if (o.name) {
|
|
159
|
+
var item = array.getMatch(arr, o.name, "name");
|
|
160
|
+
if (item) {
|
|
161
|
+
object.push(item, o, true);
|
|
162
|
+
} else {
|
|
163
|
+
arr.push(o);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 执行导航
|
|
172
|
+
* @param {string} type 导航类型
|
|
173
|
+
* @returns {object|string} 执行结果
|
|
174
|
+
*/
|
|
175
|
+
Nav.prototype.exec = function (type) {
|
|
176
|
+
var cg = this.config;
|
|
177
|
+
if (cg[type]) {
|
|
178
|
+
return this.run(type);
|
|
179
|
+
}
|
|
180
|
+
return "";
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 排序
|
|
185
|
+
*/
|
|
186
|
+
Nav.prototype.sort = async function () {
|
|
187
|
+
var cg = this.config;
|
|
188
|
+
if (cg.top_nav) array.sortBy(cg.top_nav, "asc", "display");
|
|
189
|
+
if (cg.left_nav) array.sortBy(cg.left_nav, "asc", "display");
|
|
190
|
+
if (cg.bottom_nav) array.sortBy(cg.bottom_nav, "asc", "display");
|
|
191
|
+
if (cg.right_nav) array.sortBy(cg.right_nav, "asc", "display");
|
|
192
|
+
if (cg.desktop_nav) array.sortBy(cg.desktop_nav, "asc", "display");
|
|
193
|
+
if (cg.quick_nav) array.sortBy(cg.quick_nav, "asc", "display");
|
|
194
|
+
if (cg.main_nav) array.sortBy(cg.main_nav, "asc", "display");
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 合并配置
|
|
199
|
+
* @param {object} o 配置
|
|
200
|
+
*/
|
|
201
|
+
Nav.prototype.merge = function (o) {
|
|
202
|
+
var cg = this.config;
|
|
203
|
+
cg.name = o.name;
|
|
204
|
+
cg.sort = o.sort;
|
|
205
|
+
this.mergeSub(cg.routes, o.routes);
|
|
206
|
+
this.mergeSub(cg.top, o.top);
|
|
207
|
+
this.mergeSub(cg.left, o.left);
|
|
208
|
+
this.mergeSub(cg.bottom, o.bottom);
|
|
209
|
+
this.mergeSub(cg.right, o.right);
|
|
210
|
+
this.mergeSub(cg.desktop, o.desktop);
|
|
211
|
+
this.mergeSub(cg.quick, o.quick);
|
|
212
|
+
this.mergeSub(cg.main, o.main);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 新建导航
|
|
217
|
+
* @param {string} title 标题
|
|
218
|
+
* @param {string} name 名称
|
|
219
|
+
* @param {string} url 路径
|
|
220
|
+
* @param {number} display 显示顺序
|
|
221
|
+
* @returns {object} 导航对象
|
|
222
|
+
*/
|
|
223
|
+
Nav.prototype.newNav = function (title, name, url, display = 100) {
|
|
224
|
+
var obj = {
|
|
225
|
+
title: title,
|
|
226
|
+
// 在没有权限下是否显示
|
|
227
|
+
show: false,
|
|
228
|
+
name: name,
|
|
229
|
+
url: url,
|
|
230
|
+
display: display,
|
|
231
|
+
};
|
|
232
|
+
return obj;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 新建路由
|
|
237
|
+
* @param {string} app 应用名
|
|
238
|
+
* @param {string} plugin 插件名
|
|
239
|
+
* @param {string} name 接口名
|
|
240
|
+
* @param {number} group 权限组
|
|
241
|
+
* @param {object} oauth 身份验证
|
|
242
|
+
* @returns {object} 路由对象
|
|
243
|
+
*/
|
|
244
|
+
Nav.prototype.newRoutes = function (app, plugin, name, group, oauth) {
|
|
245
|
+
var pn = plugin === "pc" ? "" : plugin;
|
|
246
|
+
var n = name.replace(app + "_", "");
|
|
247
|
+
var obj = {
|
|
248
|
+
name: name,
|
|
249
|
+
path: "/" + app + "/" + n,
|
|
250
|
+
component: "/" + app + (pn ? "/" + pn : "") + "/src/pages/" + n + ".vue",
|
|
251
|
+
level: n.replace("_form", "").replace("_view", "").indexOf("_") === -1 ? 3 : 2,
|
|
252
|
+
oauth: oauth,
|
|
253
|
+
};
|
|
254
|
+
if (!oauth) {
|
|
255
|
+
if (!group) {
|
|
256
|
+
// 不验证身份
|
|
257
|
+
obj.oauth = {
|
|
258
|
+
sign_in: false,
|
|
259
|
+
};
|
|
260
|
+
} else if (group === 1) {
|
|
261
|
+
// 验证身份为管理员
|
|
262
|
+
obj.oauth = {
|
|
263
|
+
sign_in: true,
|
|
264
|
+
gm: 2,
|
|
265
|
+
user_admin: [],
|
|
266
|
+
};
|
|
267
|
+
} else {
|
|
268
|
+
// 验证身份为商户
|
|
269
|
+
obj.oauth = {
|
|
270
|
+
sign_in: true,
|
|
271
|
+
mc: 1,
|
|
272
|
+
user_group: [],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return obj;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 处理管理端API配置
|
|
281
|
+
* @param {string} dir 目录路径
|
|
282
|
+
* @param {string} app 应用名
|
|
283
|
+
* @param {string} plugin 插件名
|
|
284
|
+
* @param {Array} nav 导航数组
|
|
285
|
+
* @param {object} cg 配置对象
|
|
286
|
+
*/
|
|
287
|
+
Nav.prototype._processManageApis = function (dir, app, plugin, nav, cg) {
|
|
288
|
+
var d = dir + "api_manage";
|
|
289
|
+
if (!file.hasDir(d)) {
|
|
290
|
+
d = dir + `api_${app}_manage`;
|
|
291
|
+
}
|
|
292
|
+
if (!file.hasDir(d)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
var list = file.getAllFile(d, "*api.json");
|
|
297
|
+
for (var i = 0; i < list.length; i++) {
|
|
298
|
+
var o = file.loadJson(list[i]);
|
|
299
|
+
if (o) {
|
|
300
|
+
delete o.oauth.scope;
|
|
301
|
+
var name = o.name.replace("_manage", "");
|
|
302
|
+
// 添加一个列表页
|
|
303
|
+
var obj = this.newRoutes(app, plugin, name, 1, o.oauth);
|
|
304
|
+
|
|
305
|
+
// 添加一个详情页
|
|
306
|
+
var obj2 = this.newRoutes(app, plugin, name + "_form", 1, o.oauth);
|
|
307
|
+
obj2.level += 1;
|
|
308
|
+
|
|
309
|
+
cg.routes.push(obj);
|
|
310
|
+
cg.routes.push(obj2);
|
|
311
|
+
|
|
312
|
+
nav.push(this.newNav(o.title, name, obj.path));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 处理客户端API配置
|
|
319
|
+
* @param {string} dir 目录路径
|
|
320
|
+
* @param {string} app 应用名
|
|
321
|
+
* @param {string} plugin 插件名
|
|
322
|
+
* @param {Array} nav 导航数组
|
|
323
|
+
* @param {object} cg 配置对象
|
|
324
|
+
*/
|
|
325
|
+
Nav.prototype._processClientApis = function (dir, app, plugin, nav, cg) {
|
|
326
|
+
var d = dir + "api_client";
|
|
327
|
+
if (!file.hasDir(d)) {
|
|
328
|
+
d = dir + `api_${app}_client`;
|
|
329
|
+
}
|
|
330
|
+
if (!file.hasDir(d)) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var list = file.getAllFile(d, "*api.json");
|
|
335
|
+
for (var i = 0; i < list.length; i++) {
|
|
336
|
+
var o = file.loadJson(list[i]);
|
|
337
|
+
if (o && !Array.isArray(o)) {
|
|
338
|
+
if (o.oauth) {
|
|
339
|
+
delete o.oauth.scope;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
var name = o.name;
|
|
343
|
+
// 添加一个列表页
|
|
344
|
+
var obj = this.newRoutes(app, plugin, name, 0, o.oauth);
|
|
345
|
+
|
|
346
|
+
// 添加一个详情页
|
|
347
|
+
var obj2 = this.newRoutes(app, plugin, name + "_view", 0, o.oauth);
|
|
348
|
+
obj2.level += 1;
|
|
349
|
+
|
|
350
|
+
if (name.indexOf("_") === -1) {
|
|
351
|
+
obj.level += 1;
|
|
352
|
+
obj2.level += 1;
|
|
353
|
+
}
|
|
354
|
+
cg.routes.push(obj);
|
|
355
|
+
cg.routes.push(obj2);
|
|
356
|
+
|
|
357
|
+
nav.push(this.newNav(o.title, name, obj.path));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 获取应用标题
|
|
364
|
+
* @param {string} app 应用名
|
|
365
|
+
* @param {string} file 文件路径
|
|
366
|
+
* @returns {string} 应用标题
|
|
367
|
+
*/
|
|
368
|
+
Nav.prototype._getAppTitle = function (app, file) {
|
|
369
|
+
var title = "未命名";
|
|
370
|
+
var app_file = file.fullname("/app/" + app + "/app.json");
|
|
371
|
+
if (file.hasFile(app_file)) {
|
|
372
|
+
var jobj = file.loadJson(app_file);
|
|
373
|
+
if (jobj) {
|
|
374
|
+
title = jobj.title;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
var app_config = string.left(file + "", "plugin") + "app.json";
|
|
378
|
+
if (file.hasFile(app_config)) {
|
|
379
|
+
var oj = file.loadJson(app_config);
|
|
380
|
+
title = oj.title;
|
|
381
|
+
}
|
|
382
|
+
return title;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* 设置默认路由
|
|
387
|
+
* @param {object} cg 配置对象
|
|
388
|
+
* @param {string} app 应用名
|
|
389
|
+
*/
|
|
390
|
+
Nav.prototype._setDefaultRoute = function (cg, app) {
|
|
391
|
+
if (cg.routes.length > 0) {
|
|
392
|
+
cg.routes.splice(0, 0, {
|
|
393
|
+
name: app,
|
|
394
|
+
path: "/" + app,
|
|
395
|
+
redirect: cg.routes[0].path,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* 新建配置
|
|
402
|
+
* @param {string} file 文件路径
|
|
403
|
+
*/
|
|
404
|
+
Nav.prototype.newConfig = function (file) {
|
|
405
|
+
var cg = this.config;
|
|
406
|
+
var l = path.sep;
|
|
407
|
+
var plugin = string.left(string.right(file + "", "plugin" + l), l, true);
|
|
408
|
+
var dir = string.left(file + "", plugin) + "server" + l;
|
|
409
|
+
|
|
410
|
+
var app = string.left(string.right(file + "", "app" + l), l, true);
|
|
411
|
+
var nav = [];
|
|
412
|
+
|
|
413
|
+
if (plugin.indexOf("admin") !== -1) {
|
|
414
|
+
this._processManageApis(dir, app, plugin, nav, cg);
|
|
415
|
+
} else {
|
|
416
|
+
this._processClientApis(dir, app, plugin, nav, cg);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
cg.name = app + "_" + plugin;
|
|
420
|
+
var title = this._getAppTitle(app, file);
|
|
421
|
+
|
|
422
|
+
this._setDefaultRoute(cg, app);
|
|
423
|
+
|
|
424
|
+
cg.main = [
|
|
425
|
+
{
|
|
426
|
+
title: title,
|
|
427
|
+
name: app,
|
|
428
|
+
icon: '<i class="fa-cog"></i>',
|
|
429
|
+
url: "/" + app,
|
|
430
|
+
display: 0,
|
|
431
|
+
sub: nav,
|
|
432
|
+
},
|
|
433
|
+
];
|
|
434
|
+
file.saveText(file, JSON.stringify(cg, null, 4));
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 获取API作用域和路由
|
|
439
|
+
* @param {string} app 应用域名
|
|
440
|
+
* @param {object} route 路由配置
|
|
441
|
+
* @returns {object} 返回作用域和路由
|
|
442
|
+
*/
|
|
443
|
+
Nav.prototype._getApiScope = function (app, route) {
|
|
444
|
+
var { path: route_path, component } = route;
|
|
445
|
+
var scope = app;
|
|
446
|
+
var p = route_path
|
|
447
|
+
.replace("_table", "")
|
|
448
|
+
.replace("_list", "")
|
|
449
|
+
.replace("_view", "")
|
|
450
|
+
.replace("_form", "");
|
|
451
|
+
var api_route;
|
|
452
|
+
if (component.indexOf("/admin") !== -1) {
|
|
453
|
+
scope += "_manage";
|
|
454
|
+
api_route = "/apis" + p;
|
|
455
|
+
} else {
|
|
456
|
+
scope += "_client";
|
|
457
|
+
api_route = "/api" + p;
|
|
458
|
+
}
|
|
459
|
+
return { scope, api_route };
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* 按作用域获取API集合(方案B:可选注入 config.get_api,替代原全局 api 池)
|
|
464
|
+
* @param {string} scope 作用域
|
|
465
|
+
* @returns {object|null} API集合;未配置注入函数时告警并返回null
|
|
466
|
+
* @private
|
|
467
|
+
*/
|
|
468
|
+
Nav.prototype._getApiByScope = function (scope) {
|
|
469
|
+
var getter = this.parent && this.parent.config ? this.parent.config.get_api : null;
|
|
470
|
+
if (typeof getter !== "function") {
|
|
471
|
+
this._warnNoApi();
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
return getter(scope) || null;
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* API 获取函数缺失告警(仅告警一次)
|
|
479
|
+
* @returns {void}
|
|
480
|
+
* @private
|
|
481
|
+
*/
|
|
482
|
+
Nav.prototype._warnNoApi = function () {
|
|
483
|
+
if (this._api_warned) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
this._api_warned = true;
|
|
487
|
+
this.log("warn", "未配置 get_api,API配置获取返回空值");
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* 获取API配置
|
|
492
|
+
* @param {string} app 应用域名
|
|
493
|
+
* @param {object} route 路由配置
|
|
494
|
+
* @returns {object} 返回api配置
|
|
495
|
+
*/
|
|
496
|
+
Nav.prototype.getApi = function (app, route) {
|
|
497
|
+
var { scope, api_route } = this._getApiScope(app, route);
|
|
498
|
+
var api = this._getApiByScope(scope);
|
|
499
|
+
if (!api) {
|
|
500
|
+
scope = scope.replace(app + "_", "");
|
|
501
|
+
api = this._getApiByScope(scope);
|
|
502
|
+
}
|
|
503
|
+
if (!api) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
return this._findApiConfig(api.list, scope, api_route);
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* 查找API配置
|
|
511
|
+
* @param {Array} list API列表
|
|
512
|
+
* @param {string} scope 作用域
|
|
513
|
+
* @param {string} api_route API路由
|
|
514
|
+
* @returns {object} 返回api配置
|
|
515
|
+
*/
|
|
516
|
+
Nav.prototype._findApiConfig = function (list, scope, api_route) {
|
|
517
|
+
var config = { api: {}, param: {}, sql: {} };
|
|
518
|
+
for (var i = 0; i < list.length; i++) {
|
|
519
|
+
var o = list[i];
|
|
520
|
+
if (o.config.path === api_route) {
|
|
521
|
+
return {
|
|
522
|
+
scope,
|
|
523
|
+
api: o.config,
|
|
524
|
+
param: o.param.config,
|
|
525
|
+
sql: o.sql.config,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return config;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* 获取模板文件路径
|
|
534
|
+
* @param {string} name 文件名
|
|
535
|
+
* @param {string} plugin 插件名
|
|
536
|
+
* @param {string} file 文件路径
|
|
537
|
+
* @returns {string} 模板文件路径
|
|
538
|
+
*/
|
|
539
|
+
Nav.prototype._getTplPath = function (name, plugin, file) {
|
|
540
|
+
var f = this.tpl_path;
|
|
541
|
+
var l = path.sep;
|
|
542
|
+
|
|
543
|
+
f += this._getSubDir(plugin, file, l);
|
|
544
|
+
f += this._getPageFile(name);
|
|
545
|
+
|
|
546
|
+
return file.fullname(f, __dirname);
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* 获取子目录
|
|
551
|
+
* @param {string} plugin 插件名
|
|
552
|
+
* @param {string} file 文件路径
|
|
553
|
+
* @param {string} l 分隔符
|
|
554
|
+
* @returns {string} 子目录
|
|
555
|
+
* @private
|
|
556
|
+
*/
|
|
557
|
+
Nav.prototype._getSubDir = function (plugin, file, l) {
|
|
558
|
+
if (plugin) {
|
|
559
|
+
return plugin + "/";
|
|
560
|
+
}
|
|
561
|
+
var subDirs = {
|
|
562
|
+
mobile: "mobile/",
|
|
563
|
+
admin: "admin/",
|
|
564
|
+
pc: "pc/",
|
|
565
|
+
};
|
|
566
|
+
for (var k in subDirs) {
|
|
567
|
+
if (file.indexOf(k + l) !== -1) {
|
|
568
|
+
return subDirs[k];
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return "";
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* 获取页面文件名
|
|
576
|
+
* @param {string} name 文件名
|
|
577
|
+
* @returns {string} 页面文件名
|
|
578
|
+
* @private
|
|
579
|
+
*/
|
|
580
|
+
Nav.prototype._getPageFile = function (name) {
|
|
581
|
+
var pageMap = [
|
|
582
|
+
{ suffix: "_config_form", file: "page_config_form.vue" },
|
|
583
|
+
{ suffix: "_form", file: "page_form.vue" },
|
|
584
|
+
{ suffix: "_view", file: "page_view.vue" },
|
|
585
|
+
{ suffix: "_table", file: "page_table.vue" },
|
|
586
|
+
{ suffix: "_list", file: "page_list.vue" },
|
|
587
|
+
{ suffix: "channel", file: "page_channel.vue" },
|
|
588
|
+
{ suffix: "type", file: "page_type.vue" },
|
|
589
|
+
{ suffix: "nav", file: "page_nav.vue" },
|
|
590
|
+
{ suffix: "lang", file: "page_lang.vue" },
|
|
591
|
+
{ suffix: "config", file: "page_config.vue" },
|
|
592
|
+
];
|
|
593
|
+
for (var i = 0; i < pageMap.length; i++) {
|
|
594
|
+
if (name.endsWith(pageMap[i].suffix)) {
|
|
595
|
+
return pageMap[i].file;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return "page_default.vue";
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* 创建视图模型
|
|
603
|
+
* @param {string} name 文件名
|
|
604
|
+
* @param {string} app 应用名
|
|
605
|
+
* @param {string} plugin 插件名
|
|
606
|
+
* @param {string} file 文件路径
|
|
607
|
+
* @param {Array} arr 路径数组
|
|
608
|
+
* @param {object} route 路由配置
|
|
609
|
+
* @returns {object} 视图模型
|
|
610
|
+
*/
|
|
611
|
+
Nav.prototype._createViewModel = function (name, app, plugin, file, arr, route) {
|
|
612
|
+
var model = {
|
|
613
|
+
id: app + "_" + name,
|
|
614
|
+
name: name,
|
|
615
|
+
app,
|
|
616
|
+
plugin,
|
|
617
|
+
file,
|
|
618
|
+
group: arr[arr.length - 2],
|
|
619
|
+
nav_config: this.config,
|
|
620
|
+
route,
|
|
621
|
+
scope: "",
|
|
622
|
+
api: {},
|
|
623
|
+
param: {},
|
|
624
|
+
sql: {},
|
|
625
|
+
};
|
|
626
|
+
return Object.assign(model, this.getApi(app, route));
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* 渲染并保存vue文件
|
|
631
|
+
* @param {string} tpl_file 模板文件路径
|
|
632
|
+
* @param {object} vm 视图模型
|
|
633
|
+
* @param {string} file 目标文件路径
|
|
634
|
+
*/
|
|
635
|
+
Nav.prototype._renderAndSaveVue = function (tpl_file, vm, file) {
|
|
636
|
+
var tpl = _getTpl();
|
|
637
|
+
if (!tpl) {
|
|
638
|
+
this.log("error", "模板引擎不可用,跳过vue渲染", tpl_file);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
try {
|
|
642
|
+
var vue = tpl.view(tpl_file, vm);
|
|
643
|
+
if (vue) {
|
|
644
|
+
file.saveText(file, vue);
|
|
645
|
+
} else {
|
|
646
|
+
this.log("error", "更新模板失败", tpl.error);
|
|
647
|
+
}
|
|
648
|
+
} catch (err) {
|
|
649
|
+
this.log("error", "更新模板失败", err);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* 创建vue文件
|
|
655
|
+
* @param {string} filename 文件保存路径
|
|
656
|
+
* @param {object} route 路由配置
|
|
657
|
+
*/
|
|
658
|
+
Nav.prototype.createVue = async function (filename, route) {
|
|
659
|
+
var file = filename;
|
|
660
|
+
var l = path.sep;
|
|
661
|
+
file = file.replace(process.cwd(), "/");
|
|
662
|
+
var arr = file.substring(file.indexOf("app")).split(l);
|
|
663
|
+
|
|
664
|
+
var name = arr[arr.length - 1].replace(".vue", "");
|
|
665
|
+
var plugin = "";
|
|
666
|
+
var app = "";
|
|
667
|
+
|
|
668
|
+
if (arr.length > 3) {
|
|
669
|
+
plugin = arr[3];
|
|
670
|
+
app = arr[1];
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
var tpl_file = this._getTplPath(name, plugin, file);
|
|
674
|
+
var model = this._createViewModel(name, app, plugin, file, arr, route);
|
|
675
|
+
|
|
676
|
+
var vm = await VIEW_MODEL.run(model);
|
|
677
|
+
vm.JSON = JSON;
|
|
678
|
+
|
|
679
|
+
this._renderAndSaveVue(tpl_file, vm, file);
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* 创建路径
|
|
684
|
+
* @param {string} filepath 文件路径
|
|
685
|
+
*/
|
|
686
|
+
Nav.prototype.mkdir = function (filepath) {
|
|
687
|
+
var l = path.sep;
|
|
688
|
+
var arr = filepath.split(l);
|
|
689
|
+
var dir = arr[0];
|
|
690
|
+
for (var i = 1; i < arr.length; i++) {
|
|
691
|
+
if (!fs.existsSync(dir)) {
|
|
692
|
+
fs.mkdirSync(dir);
|
|
693
|
+
}
|
|
694
|
+
dir = dir + l + arr[i];
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* 获取Vue文件目录
|
|
700
|
+
* @param {Array} routes 路由列表
|
|
701
|
+
* @returns {object} 返回路径信息
|
|
702
|
+
*/
|
|
703
|
+
Nav.prototype._getVueDirInfo = function (routes) {
|
|
704
|
+
for (var i = 0; i < routes.length; i++) {
|
|
705
|
+
var f = routes[i].component;
|
|
706
|
+
if (f) {
|
|
707
|
+
var p = path.dirname(f).replace(/\\/g, "/");
|
|
708
|
+
var fl = f.replace("/", "").replace("/", "\\plugin\\").replace("/", "\\static\\");
|
|
709
|
+
var dir = path.dirname(file.fullname("./app/" + fl));
|
|
710
|
+
return { p, dir };
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return { p: "", dir: "" };
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* 创建Vue文件
|
|
718
|
+
* @param {object} route 路由配置
|
|
719
|
+
* @param {string} p 原始路径
|
|
720
|
+
* @param {string} dir 目标目录
|
|
721
|
+
* @param {boolean} cover 是否覆盖
|
|
722
|
+
*/
|
|
723
|
+
Nav.prototype._createVueFile = async function (route, p, dir, cover) {
|
|
724
|
+
var f = route.component;
|
|
725
|
+
if (!f) return;
|
|
726
|
+
var file = f.replace(p, dir).fullname();
|
|
727
|
+
if (cover || !file.hasFile(file)) {
|
|
728
|
+
this.mkdir(file);
|
|
729
|
+
await this.createVue(file, route);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* 更新路由vue文件
|
|
735
|
+
* @param {string} route_path 路由路径
|
|
736
|
+
* @param {boolean} cover 是否覆盖文件
|
|
737
|
+
*/
|
|
738
|
+
Nav.prototype.updateVue = async function (route_path, cover) {
|
|
739
|
+
var lt = this.config.routes;
|
|
740
|
+
var { p, dir } = this._getVueDirInfo(lt);
|
|
741
|
+
if (!dir) return;
|
|
742
|
+
|
|
743
|
+
for (var i = 0; i < lt.length; i++) {
|
|
744
|
+
if (route_path && lt[i].path.indexOf(route_path) === -1) continue;
|
|
745
|
+
await this._createVueFile(lt[i], p, dir, cover);
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* 获取模型
|
|
751
|
+
* @param {string} type 模型类型
|
|
752
|
+
* @returns {object} 返回获取到的模型
|
|
753
|
+
*/
|
|
754
|
+
Nav.prototype.getModel = function (type) {
|
|
755
|
+
let model = { ...this.config };
|
|
756
|
+
let dir = this.config.dir || "";
|
|
757
|
+
let l = path.sep;
|
|
758
|
+
let app_name = string.between(dir, "app" + l, l);
|
|
759
|
+
let plugin_name = string.between(dir, "plugin" + l, l);
|
|
760
|
+
let name = path.basename(dir);
|
|
761
|
+
model.app = app_name;
|
|
762
|
+
model.plugin = plugin_name;
|
|
763
|
+
model.name = model.name || name;
|
|
764
|
+
return model;
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
module.exports = Nav;
|