深圳高端营销网站模板,互联网活动策划方案,网站建设任务和标准,深圳产品设计培训学校nodemysqllayuiejs实现左侧导航菜单动态显示 实现思路效果图数据库技术栈代码实现main.html#xff08;前端首页页面#xff09;查询资源菜单方法 jsapp.js配置ejs模板 node入门到入土项目实战开始#xff0c;前端篇项目适合node小白入门#xff0c;因为我也是小白来学习no… nodemysqllayuiejs实现左侧导航菜单动态显示 实现思路效果图数据库技术栈代码实现main.html前端首页页面查询资源菜单方法 jsapp.js配置ejs模板 node入门到入土项目实战开始前端篇项目适合node小白入门因为我也是小白来学习node前端的代码不是很简洁优雅各位读者多多包涵一下。 实现思路
账户表中编写一个字段role_id字段用来存储该账户所拥有的相关角色权限然后创建资源表用来存储相关项目的菜单资源创建角色权限表用来存储相关的角色权限创建角色权限资源中间表用来存储每个角色 拥有哪些资源。 账户在登陆界面输入账户相关信息进行登录时查询该数据库中的相关账户是否存在如果存在且登录成功则将该账户的角色id值提取出来进行菜单资源查询查询成功以后跳转到系统首页如果该账户角色id为空或者该角色下没有任何资源菜单时跳转至账户授权提示页面。
效果图 数据库
这里用到四个表进行导航资源的动态显示资源表tb_resource),角色表tb_role) 角色资源中间表tb_rolr_resource),账户表tb_account
技术栈
node.js layui layui(消息插件notify) mysql2 ejs Express
代码实现
main.html前端首页页面
!DOCTYPE html
html
headmeta charsetutf-8title暖意书栈-首页/title!-- 设置系统图标 --link relshortcut icon href../icon/main.ico typeimage/x-icon /meta namerenderer contentwebkitmeta http-equivX-UA-Compatible contentIEedge,chrome1meta nameviewport contentwidthdevice-width, initial-scale1link href../layui/css/layui.css relstylesheetlink href../css/main.css relstylesheet/head
body
div classlayui-layout layui-layout-admindiv classlayui-headerdiv classlayui-logo layui-hide-xs layui-bg-blacki classlayui-icon layui-icon-read stylecolor: #cff60cd3;font-size: 22px;/istrong stylefont-family: 华文行楷;font-size: 25px; color: #a6b5afd3;暖意书栈/strong /div!-- 头部区域可配合layui 已有的水平导航 --ul classlayui-nav layui-layout-left!-- 移动端显示 --li classlayui-nav-item layui-show-xs-inline-block layui-hide-sm lay-header-eventmenuLefti classlayui-icon layui-icon-spread-left/i/lili classlayui-nav-item layui-hide-xsa hrefjavascript:;书籍借阅/a/lili classlayui-nav-item layui-hide-xsa hrefjavascript:;座位预约/a/lili classlayui-nav-item layui-hide-xsa hrefjavascript:;贴吧/a/lili classlayui-nav-itema hrefjavascript:;更多/adl classlayui-nav-childdda hrefjavascript:;意见反馈/a/dddda hrefjavascript:;违规处理/a/dddda hrefjavascript:;联系我们/a/dd/dl/li/ulul classlayui-nav layui-layout-rightli classlayui-nav-item layui-hide layui-show-sm-inline-blocka hrefjavascript:;img src../image/admin.jpeg classlayui-nav-img我的/adl classlayui-nav-childdda hrefjavascript:;我的资料/a/dddda hrefjavascript:;我的借阅/a/dddda hrefjavascript:;我的预约/a/dddda hrefjavascript:;安全管理/a/dddda hrefjavascript:;退出登录/a/dd/dl/lili classlayui-nav-item lay-header-eventmenuRight lay-unselecta hrefjavascript:;i classlayui-icon layui-icon-notice/i 通知/公告/a/li/ul/divdiv classlayui-side layui-bg-blackdiv classlayui-side-scrollul classlayui-nav layui-nav-tree lay-filtertest% Object.keys(navItems).forEach(parentId { %% if (parentId 0) { %% navItems[parentId].forEach(item { %li classlayui-nav-itema data-id% item.re_id %i classlayui-icon % item.re_icon %/ispan % item.re_title %/span/a% if (navItems[item.re_id] navItems[item.re_id].length 0) { %dl classlayui-nav-child% navItems[item.re_id].forEach(subItem { %dda data-id% subItem.re_id % data-url% subItem.re_url % hrefjavascript:void(0);i classlayui-icon % subItem.re_icon %/i % subItem.re_title %/a/dd% }) %/dl% } %/li% }) %% } %% }) %/ul/div/divdiv classlayui-body layui-formdiv classlayui-tab marg0 layui-tab-brief lay-filterbodyTab idtop_tabs_box lay-allowclosetrueul classlayui-tab-title top_tab idtop_tabsli classlayui-this lay-allowclosefalsei classlayui-icon layui-icon-home/icite首页/cite/li/ul!-- 当前页面操作 --ul classlayui-nav closeBoxli classlayui-nav-itema hrefjavascript:;页面操作/adl classlayui-nav-childdda hrefjavascript:; classrefresh refreshThisi classlayui-icon layui-icon-refresh-3/i 刷新当前/a/dddda hrefjavascript:; classclosePageOtheri classlayui-icon layui-icon-clear/i 关闭其他/a/dddda hrefjavascript:; classclosePageAllstrongi classlayui-icon layui-icon-close/i/strong 关闭全部/a/dd/dl/li/uldiv classlayui-tab-content clildFramediv classlayui-tab-item layui-showiframe src/index/iframe/div/div/div/divdiv classlayui-footer!-- 底部固定区域 --底部固定区域/div
/div
script src../jquery/jquery-3.7.1.min.js/script
script src../layui/layui.js/script
script src../notify/notify.js/script
script
//JS
layui.use([element, layer, util,notify], function(){var element layui.element;var layer layui.layer;var util layui.util;var $ layui.$;var notify layui.notify;// 监听左侧导航的二级菜单点击事件$(.layui-nav .layui-nav-child).on(click, a[data-url], function(e) {e.preventDefault(); // 阻止默认行为避免页面跳转var $this $(this),// 获取当前点击的a元素tabTitle $this.text().trim(),// 获取当前点击的a元素的文本内容tabId $this.data(id),// 获取当前点击的a元素的data-id属性tabUrl $this.data(url);// 获取当前点击的a元素的data-url属性// 检查是否已经有此tabvar hasTab $(#top_tabs li).filter(function() {return $(this).find(cite).text() tabTitle;// 使用filter方法筛选出匹配的元素});if (!hasTab.length) {// 如果没有则添加新的tabelement.tabAdd(bodyTab, {// 调用element.tabAdd方法添加新的tabtitle: cite tabTitle /cite,// 设置tab的标题content: iframe src tabUrl frameborder0 scrollingauto/iframe,// 设置tab的内容id: tabId // 设置tab的id});}// 切换到该tabelement.tabChange(bodyTab, tabId);// 调用element.tabChange方法切换到该tab});//点击刷新当前$(.refresh).on(click,function(){ //if($(this).hasClass(refreshThis)){// 判断是否是点击刷新当前$(this).removeClass(refreshThis);// 移除refreshThis类// 获取当前页面的iframe元素并调用其contentWindow属性的location属性的reload方法刷新页面$(.clildFrame .layui-tab-item.layui-show).find(iframe)[0].contentWindow.location.reload(true);setTimeout(function(){$(.refresh).addClass(refreshThis);// 添加refreshThis类},2000)}else{notify.info({msg:您点击的速度超过了服务器的响应速度还是等两秒再刷新吧,position:vcenter,shadow:true, closable:false,duration:1500});}});// 当点击 .closePageOther 元素时触发此事件处理程序,关闭其他 就是把除了当前窗口意外的其他窗口关闭 首页除外$(.closePageOther).on(click, function () {// 获取当前激活的标签页即被选中的标签页var $currentTab $(#top_tabs li.layui-this),// 从当前激活的标签页中获取标题文本currentTitle $currentTab.find(cite).text(),// 从 sessionStorage 中获取名为 menu 的数据并将其解析为 JavaScript 对象// 如果 sessionStorage 中没有 menu 数据则使用空数组menu JSON.parse(window.sessionStorage.getItem(menu)) || [],// 计算非首页的标签页数量nonHomeTabsCount $(#top_tabs li:not(.layui-this)).not([cite首页]).length;// 如果当前标签页是 首页 并且存在其他非首页标签页if (currentTitle 首页 nonHomeTabsCount 0) {// 关闭所有其他非首页标签页并清空 sessionStorage$(#top_tabs li[lay-id]).not(.layui-this).each(function() {// 获取当前标签页的 lay-id 属性值var layId $(this).attr(lay-id);// 使用 element.tabDelete 方法删除当前标签页并调用 init 方法刷新界面element.tabDelete(bodyTab, layId).init();});// 清除 sessionStorage 中的所有数据sessionStorage.clear();} else if (currentTitle ! 首页 nonHomeTabsCount 1) { // 如果当前不是首页并且存在其他非首页标签页// 关闭所有其他非当前标签页$(#top_tabs li[lay-id]).not(.layui-this).each(function() {// 获取当前标签页的 lay-id 属性值var layId $(this).attr(lay-id);// 使用 element.tabDelete 方法删除当前标签页并调用 init 方法刷新界面element.tabDelete(bodyTab, layId).init();});// 更新 sessionStorage 中的 menu 数组只包含当前标签页的信息// 使用 Array.prototype.filter 方法过滤数组只保留当前标签页的项menu menu.filter(item item.title currentTitle);// 将更新后的 menu 数组保存回 sessionStoragesessionStorage.setItem(menu, JSON.stringify(menu));} else {// 如果只剩下首页和当前页面时显示提示信息notify.info({msg: 没有可以关闭的窗口了哦,position: vcenter, // 提示信息的位置设置为中心shadow: true, // 是否启用阴影效果closable: false, // 是否允许手动关闭提示信息duration: 1000 // 提示信息显示的持续时间毫秒});}// 调用 tab.tabMove 方法重新渲染顶部的标签页tab.tabMove();});//关闭全部窗口 只留下 首页$(.closePageAll).on(click,function(){if($(#top_tabs li).length 1){$(#top_tabs li).each(function(){if($(this).attr(lay-id) ! ){element.tabDelete(bodyTab,$(this).attr(lay-id)).init();window.sessionStorage.removeItem(menu);menu [];window.sessionStorage.removeItem(curmenu);}})}else{notify.info({msg:没有可以关闭的窗口了!,position:vcenter,shadow:true, closable:false,duration:1000});}//渲染顶部窗口tab.tabMove();})
});
/script
/body
/html查询资源菜单方法 js // 创建一个对象来保存 roleId
const roleManager {// 初始化时可以设定一个默认值_roleId: null,// 方法用于设置 roleIdsetRoleId: function (roleId1) {this._roleId roleId1;},// 方法用于获取 roleIdgetRoleId: function () {return this._roleId;}
};router.get(/main, (req, res) {// 获取用户角色 IDconst roleId roleManager.getRoleId();// 使用数据库连接池执行 SQL 查询获取与角色 ID 关联的所有资源 IDpool.query(userSQL.queryAllResource, [roleId], (err, resourceIds) {if (err) throw err; // 如果发生错误则抛出异常// 如果没有找到任何资源 ID则重定向到指定页面if (resourceIds.length 0) {return res.redirect(/forbidden); // 假设这是跳转到的页面}// 将查询结果中的所有资源 ID 提取到一个数组中const resourceIdsList resourceIds.map(id id.resource_id);// 构建 SQL 查询字符串用于查询具体的资源详情const query SELECT * FROM tb_resource WHERE re_id IN (${resourceIdsList.join(,)});// 使用数据库连接池执行 SQL 查询获取具体的资源详情pool.query(query, (err, resources) {if (err) throw err; // 如果发生错误则抛出异常// 如果查询到的资源为空则重定向到指定页面if (resources.length 0) {return res.redirect(/forbidden); // 假设这是跳转到的页面}// 对查询到的资源进行分组处理按父资源 ID 分组const groupedResources groupResourcesByParentId(resources);// 渲染 main 视图并传递分组后的资源作为数据res.render(main, { navItems: groupedResources });});});
});
/*** 将资源按照其父ID分组。* * param {Array} resources - 包含资源信息的数组其中每个资源对象都应包含 re_parentId 属性。* returns {Object} - 返回一个对象其中键是父ID值是一个包含具有相同父ID的资源的数组。*/
function groupResourcesByParentId(resources) {// 使用 reduce 函数对资源数组进行处理。// reduce 接收一个回调函数作为参数该回调函数定义了如何累积结果。// 第一个参数是累加器accumulator初始值为空对象 {}。// 第二个参数是当前元素current。return resources.reduce((acc, curr) {// 检查累加器对象中是否存在当前元素的 re_parentId 键。// 如果不存在则在累加器对象上创建一个新的空数组。if (!acc[curr.re_parentId]) {acc[curr.re_parentId] [];}// 将当前元素推入与它的 re_parentId 相关联的数组中。acc[curr.re_parentId].push(curr);// 返回累加器对象以便 reduce 函数继续处理下一个元素。return acc;}, {});
}数据库查询语句 //查询每个角色拥有的所有资源queryAllResource: SELECT resource_id FROM tb_role_resource WHERE role_id ?,app.js配置ejs模板
//安装ejs模板
npm install ejs// 设置模板引擎
app.set(view engine, html);
app.set(views,path.join(__dirname, views/login));
// 设置后缀名的文件使用什么模板引擎
app.engine(html, require(ejs).renderFile);