UNPKG

mm_os

Version:

这是超级美眉服务端框架,用于快速构建应用程序。

549 lines (518 loc) 12.3 kB
const fs = require('fs'); const Item = require('mm_machine').Item; const ViewModel = require('./viewmodel.js'); const tpl = new $.Tpl({ extname: ".vue", cache_extname: '.cache.vue' }); tpl.dir = __dirname; const viewModel = new ViewModel(); /** * Catch抓包驱动类 * @extends {Item} * @class */ class Drive extends Item { /** * 构造函数 * @param {String} dir 当前目录 * @param {String} scope 作用域 * @constructor */ constructor(dir, scope) { super(dir, __dirname); this.default_file = "./" + scope + ".nav.json"; // 模板路径 this.tpl_path = "./tpl/"; // 默认启用热更新 this.mode = 3; /* 通用项 */ // 配置参数 this.config = { // 名称, 由中英文和下“_”组成, 用于修改或卸载 例如: demo "name": "", // 状态 0未启用,1启用 "state": 1, // 加载顺序,数字越大越后面加载 "sort": 1, // 路由 "routes": [ /* { // 名称 "name": "root", // 路由路径 "path": "/", // 组件文件路径 "component": "", // 页面显示级别 "level": "", // 身份验证 "oauth": { // 是否登录 "signIn": false, // 要求会员级别 "vip": 0, // 要求管理级别 "gm": 0, // 要求商户级别 "mc": 0, // 要求用户组 "user_group": [], // 要求管理组 "user_admin": [] } } */ ], // 顶部导航 "top": [], // 左侧导航 "left": [], // 底部导航 "bottom": [], // 右侧导航 "right": [], // 桌面导航 "desktop": [], // 快捷导航 "quick": [], // 主导航 "main": [ /* { // 标题 "title": "", // 路径 "url": "", // 在没有权限下是否显示 "show": false, // 显示顺序 "display": 3, // 子导航 "sub": [ { // 标题 "title": "", // 路径 "url": "", // 在没有权限下是否显示 "show": false } ] } */ ] } } }; /** * 调用函数 * @param {Object} type * @return {Object} 执行结果 */ Drive.prototype.run = function(type) { var cg = { routes: this.config.routes } cg[type] = this.config[type]; return cg }; /** * 合并项 * @param {Object} ctx 请求上下文 * @param {Object} db 数据管理器 * @return {Object|String} */ Drive.prototype.merge_sub = function(arr, lt) { if (lt) { var len = lt.length; for (var i = 0; i < len; i++) { var o = lt[i]; if (o.name) { var item = arr.getMatch(o.name, 'name'); if (item) { $.push(item, o, true); } else { arr.push(o); } } } } }; /** * 执行导航 * @param {Object} ctx 请求上下文 * @param {Object} db 数据管理器 * @return {Object|String} */ Drive.prototype.sort = async function() { var cg = this.config; cg.top.sortBy('asc', 'display'); cg.left.sortBy('asc', 'display'); cg.bottom.sortBy('asc', 'display'); cg.right.sortBy('asc', 'display'); cg.desktop.sortBy('asc', 'display'); cg.quick.sortBy('asc', 'display'); cg.main.sortBy('asc', 'display'); }; /** * @param 合并配置 * @param {Object} o 配置 */ Drive.prototype.merge = function(o) { var cg = this.config; cg.name = o.name; cg.sort = o.sort; this.merge_sub(cg.routes, o.routes); this.merge_sub(cg.top, o.top); this.merge_sub(cg.left, o.left); this.merge_sub(cg.bottom, o.bottom); this.merge_sub(cg.right, o.right); this.merge_sub(cg.desktop, o.desktop); this.merge_sub(cg.quick, o.quick); this.merge_sub(cg.main, o.main); }; /** * 新建导航 * @param {String} app 应用名 * @param {String} name 插件名 * @param {String} gm 是否管理员 */ Drive.prototype.new_nav = function(title, name, url) { var obj = { "title": title, // 在没有权限下是否显示 "show": false, "name": name, "url": url }; return obj; }; /** * 新建路由 * @param {String} app 应用名 * @param {String} plugin 插件名 * @param {String} name 接口名 * @param {String} gm 是否管理员 * @param {Object} oauth 身份验证 */ Drive.prototype.new_routes = function(app, plugin, name, group, oauth) { var pn = plugin === 'pc' ? '' : plugin; var n = name.replace(app + "_", ''); var obj = { "name": name, "path": "/" + app + "/" + n, "component": "/" + app + (pn ? "/" + pn : '') + "/src/pages/" + n + ".vue", "level": n.replace("_form", '').replace("_view", '').indexOf('_') === -1 ? 3 : 2, "oauth": oauth }; if (!oauth) { if (!group) { // 不验证身份 obj.oauth = { "signIn": false }; } else if (group === 1) { // 验证身份为管理员 obj.oauth = { "signIn": true, "gm": 2, "user_admin": [] }; } else { // 验证身份为商户 obj.oauth = { "signIn": true, "mc": 1, "user_group": [] } } } return obj; }; /** * 新建配置 * @param {String} file */ Drive.prototype.new_config = function(file) { var cg = this.config; var plugin = (file + '').right('plugin' + $.slash).left($.slash, true); var dir = (file + '').left(plugin) + "server" + $.slash; var _this = this; var app = (file + '').right('app' + $.slash).left($.slash, true); var nav = []; if (plugin.indexOf('admin') !== -1) { var d = dir + 'api_manage'; if (!d.hasDir()) { d = dir + `api_${app}_manage`; } if (d.hasDir()) { var list = $.file.getAll(d, '*api.json'); list.map(function(f) { var o = f.loadJson(); if (o) { delete o.oauth.scope; var name = o.name.replace('_manage', ''); // 添加一个列表页 var obj = _this.new_routes(app, plugin, name, 1, o.oauth); //添加一个详情页 var obj2 = _this.new_routes(app, plugin, name + '_form', 1, o.oauth); obj2.level += 1; cg.routes.push(obj); cg.routes.push(obj2); nav.push(_this.new_nav(o.title, name, obj.path)); } }); } } else { var d = dir + 'api_client'; if (!d.hasDir()) { d = dir + `api_${app}_client`; } if (d.hasDir()) { var list = $.file.getAll(d, '*api.json'); list.map(function(f) { var o = f.loadJson(); if (o && !Array.isArray(o)) { if (o.oauth) { delete o.oauth.scope; } var name = o.name; // 添加一个列表页 var obj = _this.new_routes(app, plugin, name, 0, o.oauth); //添加一个详情页 var obj2 = _this.new_routes(app, plugin, name + '_view', 0, o.oauth); obj2.level += 1; if (name.indexOf('_') === -1) { obj.level += 1; obj2.level += 1; } cg.routes.push(obj); cg.routes.push(obj2); nav.push(_this.new_nav(o.title, name, obj.path)); } }); } } cg.name = app + "_" + plugin; var title = "未命名"; var app_file = ("/app/" + app + "/app.json").fullname(); if (app_file.hasFile()) { var jobj = app_file.loadJson(); if (jobj) { title = jobj.title; } } var app_config = (file + '').left('plugin') + 'app.json'; if (app_config.hasFile()) { var oj = app_config.loadJson(); title = oj.title; } if (cg.routes.length > 0) { cg.routes.splice(0, 0, { name: app, path: "/" + app, redirect: cg.routes[0].path }); } cg.main = [{ "title": title, "name": app, "icon": '<i class="fa-cog"></i>', "url": "/" + app, "display": 0, "sub": nav }]; file.saveText(JSON.stringify(cg, null, 4)); }; /** * 获取API配置 * @param {String} app 应用域名 * @param {Object} route 路由配置 * @return {Object} 返回api配置 */ Drive.prototype.get_api = function(app, route) { var { path, component } = route; var scope = app; var api_route = ""; var p = path.replace('_table', '').replace('_list', '').replace('_view', '').replace('_form', ''); if (component.indexOf('/admin') !== -1) { scope += "_manage"; api_route = "/apis" + p } else { scope += "_client"; api_route = "/api" + p } var api = $.pool.api[scope]; if (!api) { scope = scope.replace(app + '_', ''); api = $.pool.api[scope]; } if (!api) { return null; } var lt = api.list; var config = { api: {}, param: {}, sql: {} }; for (var i = 0; i < lt.length; i++) { var o = lt[i]; if (o.config.path === api_route) { config = { scope, api: o.config, param: o.param.config, sql: o.sql.config }; break; } } // console.log(scope, app, config, api_route); return config; }; /** * 创建vue文件 * @param {String} file 文件保存路径 * @param {Object} route 路由配置 */ Drive.prototype.create_vue = async function(file, route) { var l = $.slash; file = file.replace($.runPath, '/'); var arr = file.substring(file.indexOf('app')).split(l); var name = arr[arr.length - 1].replace('.vue', ''); var f = this.tpl_path; var plugin = ""; var app = ""; if (arr.length > 3) { plugin = arr[3]; app = arr[1]; f += plugin + '/'; } else if (file.indexOf('mobile' + l) !== -1) { f += "mobile/"; } else if (file.indexOf('admin' + l) !== -1) { f += "admin/"; } else if (file.indexOf('pc' + l) !== -1) { f += "pc/"; } if (name.endsWith('_config_form')) { f += 'page_config_form.vue'; } if (name.endsWith('_form')) { f += 'page_form.vue'; } else if (name.endsWith('_view')) { f += 'page_view.vue'; } else if (name.endsWith('_table')) { f += 'page_table.vue'; } else if (name.endsWith('_list')) { f += 'page_list.vue'; } else if (name.endsWith('channel')) { f += 'page_channel.vue'; } else if (name.endsWith('type')) { f += 'page_type.vue'; } else if (name.endsWith('nav')) { f += 'page_nav.vue'; } else if (name.endsWith('lang')) { f += 'page_lang.vue'; } else if (name.endsWith('config')) { f += 'page_config.vue'; } else { f += 'page_default.vue'; } f = f.fullname(__dirname); var model = { id: app + '_' + name, name: name, app, plugin, file, group: arr[arr.length - 2], nav_config: this.config, route, scope: "", api: {}, param: {}, sql: {} }; $.log.debug('更新vue文件:', file); var m = Object.assign(model, this.get_api(app, route)); var vm = await viewModel.run(m); vm.JSON = JSON; try { var vue = tpl.view(f, vm); if (vue) { file.saveText(vue); } else { $.log.error("更新模板失败", tpl.error); } } catch (err) { $.log.error("更新模板失败", err); } }; /** * @创建路径 * @param {String} filepath 文件路径 */ Drive.prototype.mkdir = function(filepath) { var l = $.slash; var arr = filepath.split(l); var dir = arr[0]; for (var i = 1; i < arr.length; i++) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir); } dir = dir + l + arr[i]; } } /** * 更新路由vue文件 * @param {Boolean} cover 是否覆盖文件 * @param {String} route_path 路由路径 */ Drive.prototype.update_vue = async function(route_path, cover) { var lt = this.config.routes; var dir = ''; var p = ''; var len = lt.length; for (var i = 0; i < len; i++) { var o = lt[i]; var f = o.component; if (f) { p = f.dirname().replace(/\\/g, '/'); var fl = f.replace('/', '').replace('/', '\\plugin\\').replace('/', '\\static\\'); dir = ('./app/' + fl).fullname().dirname(); break; } } if (route_path) { for (var i = 0; i < len; i++) { var o = lt[i]; var f = o.component; if (f && o.path.indexOf(route_path) !== -1) { var file = f.replace(p, dir).fullname(); if (cover || !file.hasFile()) { this.mkdir(file); await this.create_vue(file, o); } } } } else { for (var i = 0; i < len; i++) { var o = lt[i]; var f = o.component; if (f) { var file = f.replace(p, dir).fullname(); if (cover || !file.hasFile()) { this.mkdir(file); await this.create_vue(file, o); } } } } }; module.exports = Drive;