/** * Layui Admin Index Module - Theme v2.7.1 * 功能:处理后台管理系统的标签页(Tabs)逻辑,包括打开新标签、切换标签、URL路由监听等。 */ layui.define(["ywxapp", "http"], function (exports) { // 获取 Layui 核心模块引用 var $ = layui.$; var element = layui.element; var ywxapp = layui.ywxapp; var http = layui.http; var setter = layui.setter; var view = layui.view; var util = layui.util; // 状态与配置常量 var tabsPageState = ywxapp.tabsPage; // 当前标签页状态 var TABS_CONTAINER_ID = "layadmin-layout-tabs"; // 标签容器ID /** * 核心函数:打开或切换标签页 * @param {Object} options - 配置对象 * @param {String} options.url - 页面URL * @param {String} options.title - 页面标题 * @param {Boolean} options.escape - 是否转义标题(默认true) * @param {String} options.highlight - 标题高亮样式 */ function openOrSwitchTab(options) { // 合并默认配置 options = $.extend({ url: "", escape: true }, options); var isTabExists = false; var tabHeaders = $("#LAY_app_tabsheader > li"); // 提取用于匹配的唯一标识(去除协议头和查询参数) var matchKey = options.url.replace(/(^http(s*):)|(\?[\s\S]*$)/g, ""); // 1. 检查标签是否已存在 tabHeaders.each(function (index) { if ($(this).attr("lay-id") === options.url) { isTabExists = true; tabsPageState.index = index; // 更新当前激活标签索引 } }); // 设置默认标题 if (!options.title) { options.title = (tabsPageState.index === 0) ? "" : "\u65b0\u6807\u7b7e\u9875"; // "新标签页" } /** * 执行标签切换动作 */ function performTabChange() { element.tabChange(TABS_CONTAINER_ID, options.url); ywxapp.tabsBodyChange(tabsPageState.index, { url: options.url, title: options.title }); } // 2. 根据配置决定是新增标签还是刷新现有iframe if (setter.pageTabs) { // 如果开启了多标签模式 if (!isTabExists) { // 延迟添加DOM,确保渲染顺序 setTimeout(function () { var iframeHtml = [ '
', '', "
" ].join(""); $("#LAY_app_body").append(iframeHtml); performTabChange(); }, 10); // 添加标签头 var displayTitle = options.title; if (options.escape) { displayTitle = util.escape(displayTitle); } // 处理标题高亮 var titleHtml = displayTitle; if (options.highlight) { titleHtml = '' + displayTitle + ""; } element.tabAdd(TABS_CONTAINER_ID, { title: "" + titleHtml + "", id: options.url, attr: matchKey }); // 更新索引为新添加的标签 tabsPageState.index = tabHeaders.length; } else { // 标签已存在,直接切换 performTabChange(); } } else { // 如果未开启多标签模式,直接在当前iframe中跳转 var currentIframe = ywxapp.tabsBody(ywxapp.tabsPage.index).find(".layadmin-iframe")[0]; if (currentIframe && currentIframe.contentWindow) { currentIframe.contentWindow.location.href = options.url; } // 即使不新增标签,也可能需要更新内部状态 performTabChange(); } } /** * 递归构建侧边菜单的HTML结构(无限极) * 约定:有 child 且非空 => 父节点(可展开);否则 => 叶子节点(可点击跳转 lay-href) * @param {Array} data 当前层级菜单数组 * @param {Number} depth 递归深度(0 为顶级
  • ,>=1 为
    子项) * @returns {String} */ function buildSideMenu(data, depth) { depth = depth || 0; var html = ''; $.each(data, function (i, item) { var hasChild = item.child && item.child.length > 0; var iconHtml = item.icon ? '' : ''; var titleHtml = '' + item.title + ''; if (depth === 0) { // 顶级:
  • if (hasChild) { html += '
  • '; html += ' '; html += ' ' + iconHtml + titleHtml; html += ' '; html += '
    '; html += buildSideMenu(item.child, depth + 1); html += '
    '; html += '
  • '; } else { html += '
  • '; html += ' '; html += ' ' + iconHtml + titleHtml; html += ' '; html += '
  • '; } } else { // 子级:
    if (hasChild) { html += '
    '; html += ' ' + iconHtml + item.title + ''; html += '
    '; html += buildSideMenu(item.child, depth + 1); html += '
    '; html += '
    '; } else { html += '
    '; html += ' ' + iconHtml + item.title + ''; html += '
    '; } } }); return html; } // 加载侧边菜单(与后端菜单保持一致:使用绝对路径命中会员菜单接口,并始终拉取最新数据) var _appShown = false; function showApp() { if (_appShown) return; _appShown = true; $('#' + setter.container).css('visibility', 'visible'); } function renderMenu(data) { var html = buildSideMenu(data || []); $('#LAY-system-side-menu').html(html); element.init(); // 重新渲染导航 showApp(); // 菜单就绪后立即显示主体,消除白屏等待 } // 菜单接口不可用时的兜底静态菜单(仅保证框架可点击,避免侧栏留白) var FALLBACK_MENU = [ { name: 'console', title: '控制台', icon: 'layui-icon-console', route: '/static/member/views/console/index.html' }, { name: 'profile', title: '账户中心', icon: 'layui-icon-user', child: [ { name: 'info', title: '个人资料', route: '/user/profile/index' }, { name: 'avatar', title: '头像设置', route: '/user/profile/avatar' }, { name: 'password', title: '密码修改', route: '/user/profile/password' }, { name: 'settings', title: '账户设置', route: '/user/settings/index' } ]}, { name: 'payment', title: '充值中心', icon: 'layui-icon-rmb', route: '/user/payment/index' } ]; http.get('/user/index/menu').then(function (res) { if (res.code === 0 && res.data && res.data.length > 0) { renderMenu(res.data); } else { if (res.code !== 0) { layer.msg('菜单加载失败,已使用默认菜单: ' + (res.msg || ''), { icon: 2 }); } renderMenu(FALLBACK_MENU); } }).catch(function (error) { layer.msg('菜单加载失败,已使用默认菜单: ' + error, { icon: 2 }); renderMenu(FALLBACK_MENU); }); /** * 初始化路由监听与自动渲染 */ (function initRouter() { // 自动渲染视图 view().autoRender(); // 监听 Hash 变化 var hashObj = layui.url().hash; var recordConfig = setter.record || {}; var path = hashObj.href.replace(/^\//, ""); // 去除开头的斜杠 // 尝试从本地存储获取标题 var storedData = layui.data(setter.tableName).record || {}; var savedTitle = storedData[path] || ""; // 如果配置了记录功能且路径有效 if (recordConfig.url && path) { var cleanPath = $.trim(path); // 安全检查:如果是外部链接且不属于当前域名,则通过 openOrSwitchTab 处理 // 注意:原代码逻辑中,正则测试失败(即不是外部链接)或者属于当前域名时,可能不走这个分支, // 但原代码结构是:如果满足条件则调用 n (即 openOrSwitchTab)。 // 原逻辑:/^(\w*:)*\/\/.+/.test(i) && -1 === i.indexOf(location.hostname) // 意思是:如果是绝对路径且域名不同,则作为新标签打开。 if (/^(\w*:)*\/\/.+/.test(cleanPath) && cleanPath.indexOf(location.hostname) === -1) { openOrSwitchTab({ url: cleanPath, title: savedTitle }); } } // 保护超时:即便菜单接口异常,最多等待 1.2s 也显示主体(正常情况由 renderMenu 提前显示) setTimeout(showApp, 1200); })(); // 导出模块接口 var exportsData = { openTabsPage: openOrSwitchTab }; // 扩展 ywxapp 模块的功能 $.extend(ywxapp, exportsData); // 完成模块定义 exports("index",{}); });