日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 Vue 正文

vue-element-admin動態(tài)菜單改造,含數(shù)據(jù)庫以及前端目錄結(jié)構(gòu)參考

作者:梁一敘 更新時間: 2024-05-30 Vue

*小tip:每次重新啟動前端項目加載困難可以crlt+shift+delete清除瀏覽器緩存后再重新啟動

1.前端改造

首先我們拿到項目,得了解一下目錄結(jié)構(gòu)

他是通過src->router->index.js,根據(jù)路由來進行頁面的跳轉(zhuǎn)

constantRoutes:通用頁面路由

asyncRoutes:動態(tài)路由

1.1參數(shù)講解

各個參數(shù)的意思

有子目錄的目錄,以最常見的table舉例,table在這里,ctrl+左鍵點進去就行

參數(shù)與目錄的對照

1.2動態(tài)菜單思路

我們要做到的是,根據(jù)后端返回的json對象,動態(tài)的顯示目錄

而vue-element-admin,是寫死了的菜單,所以我們調(diào)用后端接口,實現(xiàn)目錄的拼接,最終達到實現(xiàn)動態(tài)菜單的目的

那么我們就要仿造router目錄下index.js文件,動態(tài)的生成相似的json對象

1.3前端代碼

修改router目錄下的index.js中的asyncRoutes方法,使其為空,要我們動態(tài)的加入菜單

export const asyncRoutes = [
]

首先在src->api>user.js,加入一個接口方法

//這里加一個,根據(jù)data(token)的不同,后臺會返回不同的字符串結(jié)果,動態(tài)菜單完成
export function authMenu(data) {
  const jsonData = JSON.stringify(data); // 手動轉(zhuǎn)換為 JSON 字符串

  console.log("data數(shù)據(jù)是", jsonData); // 在這里添加打印語句

  return request({
    url: '/user/selectMenu',
    method: 'post',
    data: jsonData, // 使用轉(zhuǎn)換后的 JSON 字符串
    headers: {
      'Content-Type': 'application/json'
    }
  });
}

修改store/modules/permission.js文件,在 generateRoutes方法里面調(diào)用上面的authMenu接口

import { asyncRoutes, constantRoutes } from '@/router'
import { authMenu } from '@/api/user'// 【新加入】引入請求,后面有文件,先不慌
import Layout from '@/layout'// 【新加入】引入layout


//這里自己寫方法,作用就是向 asyncRoutes 插入路由,達到動態(tài)路由的效果
/**
 * 【新加入】后臺查詢的菜單數(shù)據(jù)拼裝成路由格式的數(shù)據(jù)
 * @param routes
 */
export function generaMenu(routes, data) {
  //data挨個遍歷
data.forEach(item => {
    //path不為空的話,就新建一個對象,裝數(shù)據(jù)
  if (item.path !== '') {
      //這個就仿照目錄的機構(gòu),搭建
    const menu = {
      path: item.path,
      component: Layout, //這個不用寫data里面的內(nèi)容,引用就行了
      redirect: item.redirect,
      children: [],
      meta: {   // 使用 title 和 icon 創(chuàng)建 meta 對象
        title: item.title,
        icon: item.icon
      }
    }
    //遍歷子標簽,并加入到主目錄的children中去
    // 判斷是否有子標簽
    if (item.children && Array.isArray(item.children) && item.children.length > 0) {
      // 遍歷子標簽,并加入到主目錄的 children 中去
      item.children.forEach(childItem => {
        const menu2 = {
          path: childItem.path,
          component: (resolve) => require([`@/views${childItem.component}`], resolve),
          name: childItem.name,
          meta: {   // 使用 title 和 icon 創(chuàng)建 meta 對象
            title: childItem.title,
            icon: childItem.icon
          }
        }
        // 加入到主目錄的 children 中去
        menu.children.push(menu2)
      })
    }
    //追加
    routes.push(menu)
  }
})
//把404加到最后,因為作者說  // 404 page must be placed at the end !!!
const menu3 = {
  path: '*',
  redirect: '/404',
  hidden: true
}
//追加
routes.push(menu3)
}



const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
 generateRoutes({ commit,rootState },) {
    return new Promise(resolve => {
      const loadMenuData = [] // 保留加載動態(tài)路由的代碼

      // authMenu 調(diào)用可能也需要根據(jù)你的需要來決定是否刪除
      authMenu(rootState.user.token).then(response => {
        let data = response
        if (response.code !== 20000) {
          // 錯誤處理邏輯
        } else {
          data = response.data
          Object.assign(loadMenuData, data)
          const tempAsyncRoutes = Object.assign([], asyncRoutes)
          generaMenu(tempAsyncRoutes, loadMenuData)
          let accessedRoutes
          accessedRoutes = tempAsyncRoutes || []
          commit('SET_ROUTES', accessedRoutes)
          resolve(accessedRoutes)
        }
      })
    }).catch(error => {
      console.log(error)
    })
  }
}


export default {
  namespaced: true,
  state,
  mutations,
  actions
}

最后,修改views/login下的index.vue,dispatch 一下(在登錄成功的前提下)

向handleLogin中添加dispatch( "permission/generateRoutes",userRoles)

handleLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.loading = true;

          // 假設(shè)有一種方法用于獲取用戶角色,例如 getUserRoles()
          const userRoles = ["admin"];

          this.$store
            .dispatch("user/login", this.loginForm)
            .then(() => {
              // 將預定義的角色 "admin" 傳遞給 generateRoutes action
              return this.$store.dispatch(
                "permission/generateRoutes",
                userRoles
              );
            })
            .then((accessedRoutes) => {
              // 將動態(tài)路由添加到路由器
              this.$router.addRoutes(accessedRoutes);

              // 導航到指定路徑或默認路徑 ('/')
              this.$router.push({
                path: this.redirect || "/",
                query: this.otherQuery,
              });

              // 重置加載狀態(tài)
              this.loading = false;
            })
            .catch(() => {
              // 在登錄失敗的情況下重置加載狀態(tài)
              this.loading = false;
            });
        } else {
          console.log("提交錯誤!!");
          return false;
        }
      });
    },

2.后端改造

@Controller層

 @Resource
    private MenuService menuService;   

	/**
     * 動態(tài)菜單獲取
     */
    @PostMapping("/selectMenu")
    public MenuResponse selectMenu(@RequestBody VoToken voToken) {
        MenuResponse res = new MenuResponse();
        try {
            // 驗證token的合法和有效性
            String tokenValue = JwtUtil.verity(voToken.getToken());
            if (tokenValue != null && tokenValue.startsWith(JwtUtil.TOKEN_SUCCESS)) {
                // 從令牌中提取實際的用戶名
                String username = tokenValue.replaceFirst(JwtUtil.TOKEN_SUCCESS, "");

                // 記錄調(diào)用日志
                log.info("從令牌中提取的用戶名: {}", username);

                // 調(diào)用 MenuService 獲取菜單數(shù)據(jù)
                List<VoMenu> menus = menuService.getAllMenus();

                // 記錄菜單數(shù)量的日志
                log.info("獲取的菜單數(shù)量: {}", menus.size());

                // 構(gòu)建響應對象
                res.setCode(Constants.STATUS_OK);
                res.setMsg(Constants.MESSAGE_OK);
                res.setData(menus);
            } else {
                // 記錄token驗證失敗的日志
                log.warn("Token驗證失敗");

                // 驗證失敗
                res.setCode(Constants.STATUS_FAIL);
                res.setMsg(Constants.MESSAGE_FAIL);
            }
        } catch (Exception e) {
            // 記錄處理請求時發(fā)生的異常
            log.error("處理請求時發(fā)生異常", e);

            // 處理異常
            res.setCode(60204);
            res.setMsg("返回失敗");
        }
        return res;
    }

@Servicer

package com.mv.servicer;

import com.mv.entity.VO.VoMenu;

import java.awt.*;
import java.util.List;

public interface MenuService {
    List<VoMenu> getAllMenus();
}

@ServiceImpl

package com.mv.servicer.Impl;

import com.mv.entity.VO.VoMenu;
import com.mv.mapper.MenuMapper;
import com.mv.servicer.MenuService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.awt.*;
import java.util.List;


@Service
public class MenuServiceImpl implements MenuService {
    @Resource
    private MenuMapper menuMapper;


    @Override
    public List<VoMenu> getAllMenus() {
        List<VoMenu> allMenus = menuMapper.getAllMenus();
        return VoMenu.buildMenuTree(allMenus);
    }
    // 可以根據(jù)需要添加其他方法
}

@Mapper

package com.mv.mapper;

import com.mv.entity.VO.VoMenu;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface MenuMapper {

    List<VoMenu> getAllMenus();
}

@Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.mv.mapper.MenuMapper">
    <select id="getAllMenus" resultType="com.mv.entity.VO.VoMenu">
        SELECT * FROM menus;
    </select>
</mapper>

對應數(shù)據(jù)庫的實體類@VoMenus

package com.mv.entity.VO;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class VoMenu {

    private Integer id;        // 使用 Integer 類型,與數(shù)據(jù)庫表中的 id 類型匹配
    private String path;
    private String component;
    private String redirect;
    private String name;
    private String title;       // 新增字段 title
    private String icon;        // 新增字段 icon
    private Integer parent_id;   // 與數(shù)據(jù)庫表中的 parent_id 類型匹配
    private List<VoMenu> children;

        // Getters and setters

        // Constructors
    public Integer getId() {
        return id;
    }

    public Integer getParentId() {
        return parent_id;
    }
    public static List<VoMenu> buildMenuTree(List<VoMenu> menuList) {
        Map<Integer, VoMenu> menuMap = new HashMap<>();

        for (VoMenu menu : menuList) {
            menuMap.put(menu.getId(), menu);
        }

        List<VoMenu> tree = new ArrayList<>();

        for (VoMenu menu : menuList) {
            if (menu.getParentId() != null) {
                VoMenu parent = menuMap.get(menu.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(menu);
                }
            } else {
                // 如果沒有父菜單,說明是頂級菜單
                tree.add(menu);
            }
        }

        return tree;
    }
}

后端單方面測試 我使用的是apifox

返回數(shù)據(jù)成功。

3.數(shù)據(jù)庫menus以及前端目錄結(jié)構(gòu)參考

3.1數(shù)據(jù)庫里的父子級菜單

** 我的動態(tài)菜單獲取并沒有設(shè)置權(quán)限,因為我的后臺只有一個管理員admin

3.2.前端目錄結(jié)構(gòu)

位于原項目@/views下的permission文件夾

3.3.前后端互聯(lián)展示

3.3.1后端返回數(shù)據(jù):

{
    "code": 20000,
    "msg": "成功,",
    "data": [
        {
            "id": 1,
            "path": "/permission",
            "component": "Layout",
            "redirect": "/permission/page",
            "name": "Permission",
            "title": "Permission",
            "icon": "lock",
            "parent_id": null,
            "children": [
                {
                    "id": 2,
                    "path": "/permission/page",
                    "component": "/permission/page",
                    "redirect": null,
                    "name": "PagePermission",
                    "title": "Page Permission",
                    "icon": null,
                    "parent_id": 1,
                    "children": null,
                    "parentId": 1
                },
                {
                    "id": 3,
                    "path": "/permission/directive",
                    "component": "/permission/directive",
                    "redirect": null,
                    "name": "DirectivePermission",
                    "title": "Directive Permission",
                    "icon": null,
                    "parent_id": 1,
                    "children": null,
                    "parentId": 1
                },
                {
                    "id": 4,
                    "path": "/permission/role",
                    "component": "/permission/role",
                    "redirect": null,
                    "name": "RolePermission",
                    "title": "Role Permission",
                    "icon": null,
                    "parent_id": 1,
                    "children": null,
                    "parentId": 1
                }
            ],
            "parentId": null
        }
    ]
}

3.3.2前端渲染效果:

前后端互聯(lián)完成。

原文鏈接:https://blog.csdn.net/m0_53899978/article/details/134954039

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新