Files
YwxAppThink/public/assets/ywxapp/modules/route.js
T

162 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* YwxApp 路由模块(ThinkPHP 风格 URL 生成)
* ------------------------------------------------------------------
* 通过后端模板注入的全局 layui.app 获取部署配置(root / module /
* routeBase / controller / action / assetUrl),对外提供 path / asset /
* api / route 四个方法,供任意 layui 模块以模块化方式调用:
*
* layui.use(['route'], function () {
* var route = layui.route;
* route.route('edit', 'user', null, { id: 1 }); // -> /admin/user/edit?id=1
* route.asset('ywxapp/css/style.css');
* });
*
* 同时本模块会把方法挂回 layui.app,保持对旧代码的向后兼容
* (旧的 layui.app.route(...) 仍可正常工作)。
*/
layui.define(function (exports) {
// 基础配置来自后端模板注入的全局 layui.app
// 注意:必须在每次调用时实时读取 layui.app,不能在此处缓存。
// 因为本模块(route.js)作为 layui 模块被异步加载,若此时 layui.app
// 尚未被 layout.html 的同步脚本赋值,缓存到的就是空对象,导致后续
// route() 生成的 URL 缺少应用前缀(如 /index/menu 而非 /admin/index/menu)。
var getConf = function () {
return (typeof layui !== 'undefined' && layui.app) || window.layui.app || {};
};
var trim = function (s) {
return String(s).replace(/(^\/+|\/+$)/g, '');
};
/**
* 页面路径拼接(ThinkPHP 风格:/模块/控制器/方法)
* 控制器、模块参数均可选,模块缺省使用当前模块(即后端注入的虚拟名):
* route.path('index') -> /<当前模块>/index
* route.path('index', 'user') -> /<当前模块>/user/index
* route.path('index', 'user', 'admin') -> /admin/user/index
*/
var path = function (method, controller, module) {
var conf = getConf();
var cfgRoot = conf.root || '/';
var cfgModule = conf.module || '';
var r = String(cfgRoot || '/').replace(/\/+$/, '');
if (module === undefined || module === null || module === '') {
module = cfgModule;
}
var parts = [];
if (module) parts.push(trim(module));
if (controller) parts.push(trim(controller));
if (method) parts.push(trim(method));
if (parts.length === 0) return r + '/';
return r + '/' + parts.join('/');
};
/**
* 静态资源路径拼接
* route.asset('ywxapp/css/style.css') -> /assets/ywxapp/css/style.css
*/
var asset = function (p) {
var conf = getConf();
var base = String(conf.assetUrl || '/assets').replace(/\/+$/, '');
p = String(p || '').replace(/^\/+/, '');
return base + '/' + p;
};
/**
* API 路径拼接,同 path()(控制器、模块均可选,模块缺省为当前模块)
* route.api('save') -> /<当前模块>/save
* route.api('save', 'user') -> /<当前模块>/user/save
* route.api('save', 'user', 'admin')-> /admin/user/save
*/
var api = function (method, controller, module) {
return path(method, controller, module);
};
/**
* 后端 Route::buildUrl() 的 JS 版:与后端路由规则完全一致
* 签名同 path()route(方法, 控制器, 模块),控制器/模块可选(缺省用当前模块)。
* 额外支持参数与锚点:route('edit','user', null, {id:5}, 'top')
* -> /admin/user/edit?id=5#top
*/
var route = function (method, controller, module, vars, anchor) {
// 实时读取后端注入的 layui.app(避免模块异步加载时缓存到空的 layui.app)
var conf = getConf();
var cfgRoot = conf.root || '/';
var cfgModule = conf.module || '';
var cfgRouteBase = conf.routeBase || '';
var cfgController = conf.controller || '';
var cfgAction = conf.action || '';
// 基准 URL:优先用后端注入的 cfgRouteBase(形如 /admin/{__CTRL__}/{__ACT__})。
// fallback(后端未注入时)基于 root+module 拼带占位符的合法基准,
// 绝不再用 path('{__CTRL__}','{__ACT__}') —— 那会产出裸 /{__CTRL__}/{__ACT__}。
var base;
if (cfgRouteBase) {
base = String(cfgRouteBase);
// 防御:若后端注入的 routeBase 不含占位符(异常),补上后缀,避免漏替换
if (base.indexOf('{__CTRL__}') === -1) {
base = base.replace(/\/+$/, '') + '/{__CTRL__}/{__ACT__}';
}
} else {
base = String(cfgRoot || '/').replace(/\/+$/, '')
+ (cfgModule ? '/' + trim(cfgModule) : '')
+ '/{__CTRL__}/{__ACT__}';
}
if (module === undefined || module === null || module === '') {
module = cfgModule;
}
// 未显式传 controller 时,优先用后端注入的 cfgController,再兜底默认 'index'
// (绝不再裸用字面量 '{__CTRL__}',否则会拼出 /{__CTRL__}/xxx 这类非法 URL
var ctrl = controller ? trim(controller) : (cfgController || 'index');
var act = method ? trim(method) : (cfgAction || 'index');
// 若显式传了 module,则替换掉基准里的应用前缀
if (module) {
base = base.replace(/\/[^\/]+\/\{__CTRL__\}/, '/' + trim(module) + '/{__CTRL__}');
}
var url = base
.replace('{__CTRL__}', ctrl)
.replace('{__ACT__}', act);
// 拼装查询参数
if (vars && typeof vars === 'object') {
var q = [];
for (var k in vars) {
if (Object.prototype.hasOwnProperty.call(vars, k) && vars[k] !== '' && vars[k] !== null && vars[k] !== undefined) {
q.push(encodeURIComponent(k) + '=' + encodeURIComponent(vars[k]));
}
}
if (q.length) url += (url.indexOf('?') >= 0 ? '&' : '?') + q.join('&');
}
if (anchor) url += '#' + String(anchor).replace(/^#/, '');
return url;
};
var routeModule = {
// 配置快照(只读,便于调试)—— 实时读取 layui.app
config: function () {
var c = getConf();
return {
root: c.root || '/',
assetUrl: c.assetUrl || '/assets',
module: c.module || '',
routeBase: c.routeBase || '',
controller: c.controller || '',
action: c.action || ''
};
},
path: path,
asset: asset,
api: api,
route: route
};
// 向后兼容:把方法挂回全局 layui.app,旧代码 layui.app.route() 继续可用
if (layui.app) {
layui.app.path = path;
layui.app.asset = asset;
layui.app.api = api;
layui.app.route = route;
layui.app.config = routeModule.config;
}
exports('route', routeModule);
});