layui后台系统--子页面修改父页面Tab(新增,删除)

本文介绍了一种在layui后台模板中实现新增和关闭tab选项卡的方法。提供了具体的JavaScript代码示例,包括如何添加新的tab及如何通过触发点击事件来关闭当前选中的tab。

使用layui后台模板时发现原生的新增,关闭tab选项卡功能有问题。以下是可实现的代码:

    1.新增

$('#aaa').on('click', function () { 
        var e = $(this),
            i = '../Departments/Index',
            t = 222;
        layui.router();
        //F.tabsPage.elem = e;
        var l = parent === self ? layui : top.layui;
        l.index.openTabsPage(i, t || e.text())
    })

2.删除

 function CloseTab()
        {      
$("#LAY_app_tabsheader").children(".layui-this").children(".layui-tab-close").trigger("click");
        }

ps:新增方法可直接写在子页面中。删除方法写在父页面中,然后在子页面调用父页面方法(parent.xxx)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>后台管理</title> <link rel="shortcut icon" href="https://ceepsp-1251007425.cos.ap-shanghai.myqcloud.com/20220609113851230946420220609113851"> <link rel="stylesheet" href="{$Think.config.homepagenoindex}public/layui/css/layui.css" media="all" /> <link rel="stylesheet" href="{$Think.config.homepagenoindex}public/css/admin.css" media="all" /> <link rel="stylesheet" href="{$Think.config.homepagenoindex}public/iconfont/iconfont.css" media="all" /> <script type="text/javascript" src="{$Think.config.homepagenoindex}public/js/jquery-1.12.4-min.js"></script> <script type="text/javascript" src="{$Think.config.homepagenoindex}public/js/shuiyin.js"></script> <script type="text/javascript" src="{$Think.config.homepagenoindex}public/layui/layui.js"></script> <script src="{$Think.config.homepagenoindex}public/js/cos-js-sdk-v5.min.js"></script> <style> ol li a { background-color: rgba(0, 0, 0, 1) !important; } .three_this { background-color: #ebba92 !important; } .layui-form { margin-top: 15px !important; } /* 关键修改:无下级菜单靠左样式 + 覆盖Layui默认缩进 */ .menu-type1 { padding-left: 20px !important; } .menu-type1 a { padding-left: 20px !important; margin-left: 0 !important; } .layui-nav-tree .layui-nav-child a { padding-left: 20px !important; } .layui-nav-tree { padding-left: 0 !important; } /* 新增Tab容器样式,填满右侧区域 */ .layui-tab-admin { position: absolute; top: 60px; right: 0; bottom: 0; left: 200px; margin: 0; box-sizing: border-box; } .layui-tab-content-admin { height: calc(100% - 40px); padding: 0; } .layui-tab-item-admin { height: 100%; overflow: hidden; } .layui-tab-item-admin iframe { width: 100%; height: 100%; border: none; } </style> <!-- 定义全局变量,供JS使用 --> <script> var currentAction = "{$(string)request()->action()}"; var homePage = "{$Think.config.homepage}"; var homePageNoIndex = "{$Think.config.homepagenoindex}"; </script> </head> <body class="layui-layout-body"> <div id="LAY_app" class="layadmin-tabspage-none"> <div class="layui-layout layui-layout-admin"> <div class="layui-header"> <!-- 头部区域(保留原有代码) --> <ul class="layui-nav layui-layout-left"> <li class="layui-nav-item layui-hide-xs" lay-unselect=""> <a href="" target="_blank" title="前台"> <i class="layui-icon layui-icon-website"></i> </a> </li> <li class="layui-nav-item"> <a href=""> <i class="layui-icon layui-icon-notice"></i> </a> </li> </ul> <ul class="layui-nav layui-layout-right"> <li class="layui-nav-item"> <a href="javascript:void(0);" class="phone">{$Think.session.loupan.name}-{$Think.session.loupan.phone}</a> <dl class="layui-nav-child layui-anim layui-anim-upbit"> <dd><a href="javascript:open1();">修改密码</a></dd> <hr> <dd><a href="javascript:logout();">退    出</a></dd> </dl> </li> </ul> </div> <div class="layui-side layui-side-menu"> <div class="layui-side-scroll"> <div class="layui-logo" lay-href=""> <span>后台管理</span> </div> <!-- 菜单容器:清空原有静态内容,仅保留空的UL --> <ul class="layui-nav layui-nav-tree ul00" lay-filter="test"></ul> </div> </div> <!-- 关键修改:替换原{__CONTENT__}为Layui选项卡容器 --> <div class="layui-tab layui-tab-card layui-tab-admin" lay-filter="adminTab"> <ul class="layui-tab-title"></ul> <div class="layui-tab-content layui-tab-content-admin"> </div> </div> </div> </div> <script> // 全局存储Layui的element和layer对象 var element, layer; /** * 动态渲染左侧菜单(适配新接口返回结构) */ function list() { $.ajax({ type: 'post', url: homePage + "Admin/juese/juese", dataType: "json", success: function (res) { if (res.code !== 0) { layer.msg(res.msg || '菜单加载失败', {icon: 2}); return; } var menuList = res.menuList || []; if (menuList.length === 0) { layer.msg('暂无可用菜单', {icon: 0}); return; } var $menuContainer = $('.ul00'); $menuContainer.empty(); // 遍历菜单数据,分类型渲染 menuList.forEach(menu => { if (menu.type === 1) { renderType1Menu($menuContainer, menu); } else if (menu.type === 2) { renderType2Menu($menuContainer, menu); } }); // 重新渲染Layui导航组件 element.render('nav'); }, error: function (xhr, status, error) { console.error('菜单加载失败:', error); layer.msg('菜单加载失败,请刷新页面', {icon: 2}); } }); } /** * 渲染类型1菜单(无集)- 移除href跳转,添加自定义属性 * @param $container 父容器 * @param menu 菜单数据 */ function renderType1Menu($container, menu) { var menuAction = menu.url.split('/').pop(); var isActive = menuAction === currentAction ? 'layui-this' : ''; // 关键修改:href改为javascript:void(0),添加data-url/data-title存储跳转信息 var menuHtml = ` <li id="${menu.id}" class="layui-nav-item ${isActive} menu-type1"> <a href="javascript:void(0);" data-url="${homePage + menu.url}" data-title="${menu.name}"> <i class="layui-icon "></i><cite>${menu.name}</cite> </a> </li> `; $container.append(menuHtml); } /** * 渲染类型2菜单(有集)- 同样移除href跳转 * @param $container 父容器 * @param menu 菜单数据 */ function renderType2Menu($container, menu) { var childActions = (menu.data || []).map(item => item.url.split('/').pop()); var isExpanded = childActions.includes(currentAction) ? 'layui-nav-itemed' : ''; var childMenuHtml = ''; (menu.data || []).forEach(child => { var childAction = child.url.split('/').pop(); var isChildActive = childAction === currentAction ? 'layui-this' : ''; // 关键修改菜单同样添加自定义属性 childMenuHtml += ` <dl class="layui-nav-child"> <dd class="${isChildActive}"> <a href="javascript:void(0);" data-url="${homePage + child.url}" data-title="${child.name}"> <i class="layui-icon "></i><cite style="margin-left: 20px">${child.name}</cite> </a> </dd> </dl> `; }); var menuHtml = ` <li id="${menu.id}" class="layui-nav-item ${isExpanded}"> <a href="javascript:void(0);"> <i class="layui-icon layui-icon-release"></i><cite>${menu.name}</cite> </a> ${childMenuHtml} </li> `; $container.append(menuHtml); } /** * 新增:添加/切换Tab面板 * @param title Tab标题 * @param url 页面链接 * @param id Tab唯一标识(默认用url的哈希值) */ function addTab(title, url, id) { // 生成唯一ID(避免重复) var tabId = id || 'tab_' + url.replace(/\//g, '_').replace(/\:/g, '_'); // 判断Tab是否已存在 if ($('.layui-tab-title li[lay-id="' + tabId + '"]').length > 0) { // 存在则切换Tab element.tabChange('adminTab', tabId); } else { // 不存在则新增Tab element.tabAdd('adminTab', { title: title, content: `<div class="layui-tab-item-admin"><iframe src="${url}"></iframe></div>`, id: tabId }); // 切换到新Tab element.tabChange('adminTab', tabId); } } // 初始化Layui组件 layui.use(['element', 'layer'], function () { element = layui.element; layer = layui.layer; // 关键修改:菜单点击事件委托(动态生成的菜单需用事件委托) $('.ul00').on('click', 'a[data-url]', function (e) { // 阻止默认跳转行为 e.preventDefault(); // 获取自定义属性中的标题和链接 var title = $(this).data('title'); var url = $(this).data('url'); // 添加/切换Tab addTab(title, url); }); // 原有事件绑定逻辑保留 $(".menu_three").on("click", function () { $(this).next().toggle(); }); $("ol").on("click", "li a", function () { $.each($(this).parent().siblings(), function (i, e) { $(e).find("a").removeClass('three_this') }); $(this).addClass('three_this'); }); // 加载并渲染菜单 list(); }); // 窗口大小调整逻辑(保留原有) $(window).resize(function () { if (document.body.clientWidth < 1200) { window.resizeTo(document.body.clientWidth); } }()); // 退出登录逻辑(保留原有) function logout() { $.ajax({ type: 'post', url: homePage + "Admin/login/logout", dataType: "json", success: function (data) { layer.msg(data.msg, { time: 2000 }, function () { if (data.code == '0') { window.location.href = homePage + "Admin/login/index"; } else { window.location.reload(); } }); }, }) } // 修改密码逻辑(保留原有) function open1() { layer.open({ type: 2, content: [homePage + "Admin/login/editpass", 'no'], area: ['500px', '300px'], btn: ['确定', '取消'], yes: function (index, layero) { var res = window["layui-layer-iframe" + index].callbackdata(); $.ajax({ type: 'post', url: homePage + "Admin/login/editpassword", data: { id: "{$Think.session.loupan.uid}", oldpass: res.oldpass, newpass: res.newpass, repass: res.repass }, dataType: "json", success: function (data) { layer.msg(data.msg, { time: 2000 }, function () { if (data.code == 0) { layer.close(index); } }); }, }) } }); } </script> </body> </html> 解决点击左侧菜单栏页面整体刷新 不要重新渲染左侧菜单栏选择的时候
最新发布
11-27
<think> 我们面对的问题:点击左侧菜单栏时,右侧内容区域需要刷新(重新加载),但左侧菜单栏本身不应该重新渲染(保持状态不变,例如展开的菜单、选中的菜单项等)。 根据引用内容,我们可以总结几种常见的解决方案: 1. 使用Vue的`provide/inject`机制,在父组件中提供一个`reload`方法,该方法通过控制一个标志位(如`isRouterAlive`)来重新渲染右侧内容区域(通常是`<router-view>`所在区域)。当重复点击同一个菜单时(即当前路由与目标路由相同),调用`reload`方法触发右侧内容刷新,而左侧菜单栏不受影响。 2. 监听路由变化,在路由变化时更新右侧内容,但保持左侧菜单栏的状态。这可以通过在左侧菜单组件中使用`watch`来监听`$route`,并手动更新菜单的选中状态(active状态)和展开状态(open状态),而不重新渲染整个菜单。 3. 使用Vue Router的导航守卫,在重复点击同一菜单时,强制刷新右侧组件。 结合用户需求:避免重新渲染左侧菜单栏,同时右侧内容在点击菜单时刷新(包括重复点击相同菜单)。 根据引用[4]中提供的方法,我们可以采用第一种方案(`provide/inject`)并结合路由的`key`属性来控制右侧区域的刷新,同时左侧菜单栏不重新渲染。 具体步骤: 步骤1:在布局组件(通常包含左侧菜单和右侧内容区域)中,为右侧内容区域(例如`<router-view>`)设置一个`key`属性,该`key`绑定到当前路由的路径(或全路径)。这样,当路由路径改变时,`key`会改变,从而强制重新渲染右侧区域。但是,重复点击同一菜单(相同路由)时,`key`不变,右侧不会刷新。 因此,我们需要在重复点击同一菜单时,改变`key`以触发刷新。我们可以使用一个额外的变量(如`reloadKey`)来作为`key`的一部分,并在需要刷新时改变这个变量。 步骤2:在父组件中提供`reload`方法,该方法会改变`reloadKey`,从而强制刷新右侧区域。 步骤3:在左侧菜单组件中,当点击菜单项时,判断当前路由是否与目标路由相同。如果相同,则调用`reload`方法;如果不同,则正常跳转。 步骤4:为了避免左侧菜单栏重新渲染,我们需要确保左侧菜单栏的`key`不会因为右侧刷新而改变,并且菜单的状态(如展开状态)应该由菜单组件自己管理(例如使用`el-menu`时,注意绑定`default-active`和`default-openeds`,并在路由变化时更新`active`状态,但不要重新创建整个菜单)。 下面我们根据引用[4]中的方法进行改进: 在布局组件(Layout)中: ```vue <template> <div class="app-wrapper"> <!-- 左侧菜单栏 --> <sidebar class="sidebar-container" /> <!-- 右侧内容区域 --> <div class="main-container"> <app-main :key="key"></app-main> </div> </div> </template> <script> import Sidebar from './components/Sidebar' import AppMain from './components/AppMain' export default { name: 'Layout', components: { Sidebar, AppMain }, data() { return { reloadKey: 0 // 用于强制刷新AppMain的key } }, computed: { key() { // 将路由的路径和reloadKey组合,这样改变reloadKey就会刷新 return this.$route.path + '?' + this.reloadKey } }, methods: { reload() { this.reloadKey += 1 } }, provide() { return { reload: this.reload } } } </script> ``` 在左侧菜单栏组件(Sidebar)中: ```vue <template> <el-menu :default-active="activeMenu" :collapse="isCollapse" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF" @select="handleSelect" > <!-- 菜单项 --> </el-menu> </template> <script> export default { name: 'Sidebar', inject: ['reload'], // 注入父组件提供的reload方法 computed: { activeMenu() { // 根据当前路由设置激活的菜单项 const route = this.$route const { meta, path } = route if (meta.activeMenu) { return meta.activeMenu } return path } }, methods: { handleSelect(index) { // 如果点击的菜单项对应的路由就是当前路由,则调用reload方法刷新右侧区域 if (index === this.$route.path) { this.reload() } } } } </script> ``` 这样,当重复点击同一菜单时,会调用`reload`方法,该方法会改变`reloadKey`,从而改变`AppMain`组件的`key`,导致右侧内容区域重新渲染。而左侧菜单栏由于没有绑定这个`key`,所以不会重新渲染,且通过`activeMenu`计算属性保持当前激活状态。 注意:这种方法在点击不同菜单时,由于路由改变,`key`中的`$route.path`会变化,所以也会刷新右侧区域,同时左侧菜单栏的激活状态会更新(因为`activeMenu`计算属性依赖于`$route`)。 另外,如果左侧菜单栏有展开的菜单,需要保持展开状态,那么我们需要确保`el-menu`的`default-openeds`绑定到一个数组,并且这个数组在菜单重新渲染时不会重置(例如,将展开的菜单index数组保存在Vuex中,或者使用组件内部状态并监听路由变化来更新)。但在这个方案中,由于左侧菜单栏不会因为右侧刷新而重新创建,所以展开状态不会丢失。 补充:为了避免在点击不同菜单时左侧菜单栏的展开状态丢失,我们可以将展开状态保存在组件的data中,并在路由变化时更新激活状态,但保持展开状态不变。 如果使用Element UI的`el-menu`,它内部会管理展开状态,只要我们不重新创建菜单组件(即不改变其key),展开状态就会保留。因此,我们只需确保左侧菜单组件不会因为父组件的刷新而重新创建即可。在上面的布局中,左侧菜单(`<sidebar>`)没有绑定`key`,所以它只在初始化时创建一次,后续不会重新创建。 因此,上述方案可以满足需求。 但是,如果用户希望每次点击菜单(即使是不同菜单)都不重新渲染左侧菜单栏,那么我们需要确保左侧菜单栏独立于右侧内容区域,并且其状态由组件自身维护(不依赖外部状态的变化而重新渲染)。上面的代码已经做到了这一点。 总结:我们通过控制右侧内容区域的`key`来触发刷新,左侧菜单栏保持独立,不参与刷新。重复点击同一菜单时,通过注入的方法触发右侧刷新;点击不同菜单时,路由变化自然触发右侧刷新,左侧菜单栏的激活状态通过计算属性更新(计算属性基于当前路由),而展开状态由组件自身维护。 注意:如果左侧菜单栏的数据是动态的(例如从后端获取),那么我们需要确保菜单数据获取后不会因为路由变化而重新获取(除非必要)。可以在菜单组件中,在`created`或`mounted`钩中获取一次,然后缓存起来。 最后,关于右侧内容区域的刷新,我们使用的是改变`key`的方法,这会强制重新创建右侧组件(包括重新执行组件的生命周期)。这是一种比较彻底的刷新方式。 如果用户不想整个右侧区域都刷新,而只是刷新某个组件(例如,只有内容区域,而顶部导航栏不刷新),那么需要将`<app-main>`设计为仅包含`<router-view>`,并且通过改变`key`来刷新`<router-view>`。 在`AppMain.vue`中: ```vue <template> <router-view :key="key"></router-view> </template> ``` 然后在父组件(Layout)中,将`key`传递给`AppMain`组件(如上面的布局组件所示)。 这样,只有`<router-view>`会刷新,而`AppMain`中的其他内容(如果有)不会刷新。 根据用户需求,我们解决了点击左侧菜单栏时页面整体刷新的问题,同时避免了左侧菜单栏的重新渲染。 如果用户使用的是Element UI,那么上述代码可以直接使用。如果是其他UI库,原理相同,具体实现可能略有不同。 请根据实际项目结构进行调整。 </think>针对点击左侧菜单栏时避免页面整体刷新且不重新渲染左侧菜单栏的问题,我将提供清晰的解决方案: ### 解决方案(使用Vue+Element UI) ```vue <!-- 布局组件 Layout.vue --> <template> <div class="app-container"> <!-- 左侧菜单栏 (不需要key或条件渲染) --> <left-menu :active-menu="activeMenu" @menu-select="handleMenuSelect"/> <!-- 右侧内容区域 --> <div class="main-content"> <router-view v-if="isRouterAlive"/> </div> </div> </template> <script> import LeftMenu from './LeftMenu.vue' export default { components: { LeftMenu }, provide() { // 向组件提供刷新方法 return { reload: this.reload } }, data() { return { isRouterAlive: true, // 控制右侧内容刷新 activeMenu: '' // 当前激活菜单项 } }, methods: { // 菜单选择处理 handleMenuSelect(menuPath) { if (menuPath === this.$route.path) { this.reload() // 相同菜单:刷新右侧内容 } else { this.activeMenu = menuPath // 不同菜单:更新激活状态 this.$router.push(menuPath) // 正常导航 } }, // 刷新右侧内容区域 reload() { this.isRouterAlive = false this.$nextTick(() => { this.isRouterAlive = true }) } } } </script> ``` ```vue <!-- 左侧菜单组件 LeftMenu.vue --> <template> <el-menu :default-active="activeMenu" @select="handleSelect"> <!-- 菜单项... --> </el-menu> </template> <script> export default { props: ['activeMenu'], // 接收激活状态 methods: { handleSelect(index) { this.$emit('menu-select', index) // 触发菜单选择事件 } } } </script> ``` ### 关键实现原理 1. **状态分离控制** - 左侧菜单状态由`activeMenu`单独管理 - 右侧内容通过`isRouterAlive`控制刷新 - 两者状态互不影响[^1] 2. **智能刷新策略** ```javascript handleMenuSelect(menuPath) { if (menuPath === this.$route.path) { this.reload() // 相同菜单 => 仅刷新右侧 } else { this.activeMenu = menuPath // 不同菜单 => 更新激活状态 this.$router.push(menuPath) } } ``` 3. **局部刷新技术** - 使用`v-if`+`reload()`仅重置右侧路由视图 - `$nextTick`确保DOM更新后重新渲染[^4] - 左侧菜单保持无状态组件特性 4. **组件通信优化** - 通过`provide/inject`跨层级传递刷新方法 - 事件驱动状态更新(`@menu-select`) - 避免直接操作DOM或强制刷新[^2] ### 优化建议 1. **添加路由监听**(防止URL直接访问时状态不同步) ```javascript watch: { $route(to) { this.activeMenu = to.path } } ``` 2. **菜单状态持久化** ```javascript // 使用Vuex或localStorage存储展开状态 saveMenuState() { localStorage.setItem('menuState', JSON.stringify(this.openedMenus)) } ``` 3. **性能优化** ```javascript // 使用keep-alive缓存右侧组件 <keep-alive> <router-view v-if="isRouterAlive"/> </keep-alive> ``` > 此方案确保:1) 点击相同菜单仅刷新右侧内容 2) 左侧菜单保持状态不重置 3) 切换不同菜单正常导航[^3]。通过组件状态分离和智能路由控制,完美解决菜单刷新问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值