springboot + Vue前后端项目(第十六记)

news2025/1/11 8:12:29

项目实战第十六记

  • 写在前面
  • 1 第一个bug
    • 1.1 完整的Role.vue
  • 2 第二个bug
    • 2.1 修改路由router下面的index.js
  • 总结
  • 写在最后

写在前面

  • 发现bug,修复bug

1 第一个bug

分配菜单时未加入父id,导致分配菜单失效

<!-- 
    :check-strictly="true"   默认是false,父子关联;true 是不关联
    需手动勾选父选择框
-->
<el-tree
          :props="props"
          :data="menuData"
          show-checkbox
          node-key="id"
          ref="tree"
          :check-strictly="true"
          :default-expanded-keys="expends"
          :default-checked-keys="checks">
         <span class="custom-tree-node" slot-scope="{ node, data }">
            <span><i :class="data.icon"></i> {{ data.name }}</span>
         </span>
      </el-tree>

1.1 完整的Role.vue

<template>
  <div>
    <!-- 设计的查询 -->
    <div style="margin: 10px 0">
      <el-input
          style="width: 200px"
          placeholder="请输入名称"
          suffix-icon="el-icon-search"
          v-model="name"
      />
      <el-button type="primary" icon="el-icon-search" class="ml-5" @click="getList"
      >搜索</el-button>
      <el-button type="warning" icon="el-icon-reset" @click="resetQuery"
      >重置</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-button type="warning" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button>
      <el-button type="danger" :disabled="multiple" @click="handleDelete">删除 <i class="el-icon-remove-outline"></i></el-button>
    </div>

    <el-table :data="tableData" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" />
      <el-table-column prop="id" label="角色ID" width="80"></el-table-column>
      <el-table-column prop="roleKey" label="唯一标识"></el-table-column>
      <el-table-column prop="name" label="角色名称"></el-table-column>
      <el-table-column prop="description" label="角色描述"></el-table-column>
      <el-table-column label="操作">
        <template v-slot="scope">
          <el-button
              type="info"
              icon="el-icon-menu"
              @click="openMenuAllocDialog(scope.row.id)"
          >分配菜单</el-button>
          <el-button type="success" @click="handleUpdate(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-button type="danger" @click="handleDelete(scope.row)">删除 <i class="el-icon-remove-outline"></i></el-button>
        </template>
      </el-table-column>
    </el-table>

    <div style="padding: 10px 0">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[5, 10, 15]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
      >
      </el-pagination>
    </div>

    <!-- 角色添加对话框 -->
    <el-dialog title="角色信息" :visible.sync="dialogFormVisible" width="30%">
      <el-form :model="form">
        <el-form-item label="唯一标识" :label-width="formLabelWidth">
          <el-input v-model="form.roleKey" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="角色名称" :label-width="formLabelWidth">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="描述" :label-width="formLabelWidth">
          <el-input v-model="form.description" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>

    <!-- 分配菜单 -->
    <el-dialog title="菜单分配" :visible.sync="menuDialogVis" width="30%">
      <el-tree
          :props="props"
          :data="menuData"
          show-checkbox
          node-key="id"
          ref="tree"
          :check-strictly="true"
          :default-expanded-keys="expends"
          :default-checked-keys="checks">
         <span class="custom-tree-node" slot-scope="{ node, data }">
            <span><i :class="data.icon"></i> {{ data.name }}</span>
         </span>
      </el-tree>
      <div slot="footer" class="dialog-footer">
        <el-button @click="menuDialogVis = false">取 消</el-button>
        <el-button type="primary" @click="saveRoleMenu">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>
<script>
export default {
  name: "Role",
  data() {
    return {
      name: "",
      tableData: [],
      total: 0,
      pageSize: 5,
      pageNum: 1,
      dialogFormVisible: false,
      menuDialogVis: false,
      formLabelWidth: "80px",
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      form: {
        id: "",
        name: "",
        description: "",
      },
      menuData: [],
      props: {
        label: 'name',
      },
      expends: [],
      checks: [],
      roleId: undefined,
    };
  },
  //页面一创建成功
  created() {
    //请求分页查询数据
    this.getList();
  },
  methods: {
    getList() {
      this.request
          .get("/role/page", {
            params: {
              pageNum: this.pageNum,
              pageSize: this.pageSize,
              name: this.name,
            },
          })
          .then((res) => {
            if(res.code === "200"){
              this.tableData = res.data.records;
              this.total = res.data.total;
            }else{
              this.$message.error(res.msg);
            }
          });
    },
    //分配菜单
    openMenuAllocDialog(id){
      this.menuDialogVis = true;
      // 角色id赋值
      this.roleId = id;
      //请求菜单数据
      this.request.get("/menu",{
        params: {
          name: ""
        }
      }).then(res => {
        this.menuData = res.data;
        //展开菜单数据
        this.expends = this.menuData.map(v => v.id);
      })
      // 展开已拥有的菜单
      this.request.get("/role/roleMenu/"+id).then(res => {
        this.checks = res.data;
      })
    },
    //保存角色下的菜单
    saveRoleMenu(){
      console.log('======',this.$refs.tree.getCheckedKeys());
      this.request.post("/role/roleMenu/"+ this.roleId, this.$refs.tree.getCheckedKeys()).then(res => {
        if(res.code === "200"){
          this.$message.success("保存成功");
          this.menuDialogVis = false;
        }else {
          this.$message.error("保存失败");
        }
      })
    },
    // 重置按钮
    resetQuery(){
      this.username = "";
      this.pageNum = 1;
      this.pageSize = 5;
      this.getList();
    },
    handleSizeChange(val) {
      this.pageSize = val;
    },
    handleCurrentChange(val) {
      this.pageNum = val;
      this.getList();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.id);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    // 新增
    handleAdd(){
      this.dialogFormVisible = true;
      this.form = {};
    },
    save(){
      this.request.post("/role",this.form).then(res => {
        if(res.code === "200" || res.code === 200){
          this.$message.success("操作成功")
        }else {
          this.$message.error("操作失败")
        }
        this.dialogFormVisible = false;
        this.getList();
      })
    },
    // 修改
    handleUpdate(row){
      // 表单置空
      this.reset();
      // 重新查询数据
      const roleId = row.id || this.ids;
      this.request.get('/role/'+roleId).then(response => {
        this.form = response.data;
        this.dialogFormVisible = true;
      });
    },
    reset(){
      this.form.roleKey = undefined;
      this.form.name = undefined;
      this.form.description = undefined;
    },
    // 删除
    handleDelete(row){
      let _this = this;
      const roleIds = row.id || this.ids;
      this.$confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?', '删除角色', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        _this.request.delete("/role/"+roleIds).then(res=>{
          if(res.code === "200" || res.code === 200){
            _this.$message.success("删除成功")
          }else {
            _this.$message.error("删除失败")
          }
          this.getList();
        })
      }).catch(() => {
      });
    }
  },
};
</script>

2 第二个bug

启动成功前端访问地址,弹出的是404页面不是登录页面
在这里插入图片描述

2.1 修改路由router下面的index.js

修改的地方

// 第一处
{
    path: '/404',
    name: '404',
    component: () => import('../views/404.vue')
  }

// 第二处
// 路由守卫
router.beforeEach((to, from, next) => {
  // localStorage.setItem('currentPathName',to.name);   // 设置当前的路由名称,为了在Header组件中去使用
  // store.commit('setPath')    // 触发store的数据更新


  // 未找到路由情况
  if(!to.matched.length){
    const storeMenus = localStorage.getItem("menus");
    if(storeMenus){   // 有菜单没有找到路由,跳转至 404页面
      next("/404")
    }else {    // // 没有菜单,直接跳转至登录页
      next("/login")
    }
  }

  next()   // 放行路由
})


完整的代码

import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import store from "@/store";

Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [
  {
    path: '/login',
    name: '登录',
    component: () => import('../views/Login.vue')
  },
  {
    path: '/register',
    name: '注册',
    component: () => import('../views/Register.vue')
  },
  {
    path: '/404',
    name: '404',
    component: () => import('../views/404.vue')
  }

]

//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {
  const storeMenus = localStorage.getItem("menus");
  if (storeMenus) {

    // 获取当前的路由对象名称数组
    const currentRouteNames = router.getRoutes().map(v => v.name)
    if (!currentRouteNames.includes('Manage')) {
      // 拼装动态路由
      const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [
          { path: 'person', name: '个人信息', component: () => import('../views/Person.vue')},
          // { path: 'password', name: '修改密码', component: () => import('../views/Password.vue')}
        ] }
      const menus = JSON.parse(storeMenus)
      menus.forEach(item => {
        if (item.path) {  // 当且仅当path不为空的时候才去设置路由
          let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}
          manageRoute.children.push(itemMenu)
        } else if(item.children.length) {
          item.children.forEach(item => {
            if (item.path) {
              let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}
              manageRoute.children.push(itemMenu)
            }
          })
        }
      })
      // 动态添加到现在的路由对象中去
      router.addRoute(manageRoute)
    }

  }
}

// 重置我就再set一次路由
setRoutes()


// 路由守卫
router.beforeEach((to, from, next) => {
  // localStorage.setItem('currentPathName',to.name);   // 设置当前的路由名称,为了在Header组件中去使用
  // store.commit('setPath')    // 触发store的数据更新


  // 未找到路由情况
  if(!to.matched.length){
    const storeMenus = localStorage.getItem("menus");
    if(storeMenus){   // 有菜单没有找到路由,跳转至 404页面
      next("/404")
    }else {    // // 没有菜单,直接跳转至登录页
      next("/login")
    }
  }
  // 其他情况
  next()   // 放行路由
})

export default router

总结

  • 动态菜单完结,基本项目也快接近尾声。

写在最后

如果此文对您有所帮助,请帅戈靓女们务必不要吝啬你们的Zan,感谢!!不懂的可以在评论区评论,有空会及时回复。
文章会一直更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1828326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能对零售业的影响

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、人工智能如何改变零售格局二、利用人工智能实现购物体验自动化三、利用人工智能改善库存管理四、通过人工智能解决方案增强客户服务五、利用人工智能分析消费者行为六、利用 AI 打造个性化…

C++前期概念(重)

目录 命名空间 命名空间定义 1. 正常的命名空间定义 2. 命名空间可以嵌套 3.头文件中的合并 命名空间使用 命名空间的使用有三种方式&#xff1a; 1:加命名空间名称及作用域限定符&#xff08;::&#xff09; 2:用using将命名空间中某个成员引入 3:使用using namespa…

TCP协议报头详解

目录 前言 TCP特点 TCP报头 1.源端口和目的端口 2.序号 3.确认号 4.数据偏移 5.保留 6.控制位 ① 紧急URG&#xff08;URGent&#xff09; ② 确认ACK&#xff08;ACKnowledgment&#xff09; ③ 推送PSH&#xff08;PuSH&#xff09; ④复位RST&#xff08;ReSeT&…

【数据结构】初识集合深入剖析顺序表(Arraylist)

【数据结构】初识集合&深入剖析顺序表&#xff08;Arraylist&#xff09; 集合体系结构集合的遍历迭代器增强for遍历lambda表达式 List接口中的增删查改List的5种遍历ArrayList详解ArrayList的创建ArrayList的增删查改ArrayList的遍历ArrayList的底层原理 &#x1f680;所属…

UnityAPI学习之 播放游戏音频的类(AudioSource)

播放游戏音频的类&#xff08;AudioSource&#xff09; using System.Collections; using System.Collections.Generic; using UnityEngine;public class NO17AudioSource : MonoBehaviour {private AudioSource audioSource;//音频组件public AudioClip clip;//音频文件public…

预编译、函数变量提升

函数声明会覆盖变量的声明&#xff0c;也就是会提升到最前面。 形参传进来相当于变量声明&#xff0c;所以当有函数声明时&#xff0c;会被覆盖。

情绪管理:大我则定,小我则乱(王阳明)

学了很多知识&#xff0c;却还是感物易动&#xff1f;如何让心回归中正&#xff1f;王阳明一言以蔽之&#xff1a; —— 大我&#xff0c;大我则定&#xff0c;小我则乱 保持心静的方法&#xff1a;有大爱&#xff0c;为大局着想

Spark-Shuffle阶段优化-Bypass机制详解

Spark概述 Spark-Shuffle阶段优化-Bypass机制详解 Spark的Bypass机制是一种特定情况下的优化策略&#xff0c;目的是减少Shuffle过程中不必要的排序开销&#xff0c;从而提升性能。当Shuffle分区数较少且数据量不大时&#xff0c;Bypass机制可以显著加快Shuffle速度。 1.什么…

使用 Nginx 和 SSL 访问 Python Flask 应用的教程

在本教程中&#xff0c;我们将介绍如何使用 Nginx 和 SSL 来访问 Python Flask 应用。通过这种方式&#xff0c;你可以在提高安全性的同时&#xff0c;也能利用 Nginx 的反向代理功能来优化应用的性能和稳定性。 环境准备 在开始之前&#xff0c;请确保你的系统已经安装了以…

准备离职了 电脑怎么清理?离职最干净的电脑清理办法

准备离职了 电脑怎么清理&#xff1f;离职最干净的电脑清理办法 人在江湖身不由己&#xff0c;离职这个事情&#xff0c;所有人都要面对。无论是出于个人发展、工作环境、薪资待遇还是其他原因&#xff0c;离职都是人生和职业道路上的一种常态。离职是一个残酷的事实&#xff…

【每日刷题】Day66

【每日刷题】Day66 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 小乐乐改数字_牛客题霸_牛客网 (nowcoder.com) 2. 牛牛的递增之旅_牛客题霸_牛客网 (nowcoder.com)…

Zabbix Centos8 安装笔记

Zabbix 安装笔记 安装环境 Centos 8 正常发行版 安装版本 Zabbix 7 (LTS) 安装步骤 1、关闭防火墙 systemctl stop firewalld && systemctl disable firewalld && setenforce 0 && sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/c…

Elasticsearch 认证模拟题 - 22

一、题目 索引 task 索引中文档的 fielda 字段内容包括了 hello & world&#xff0c;索引后&#xff0c;要求使用 match_phrase query 查询 hello & world 或者 hello and world 都能匹配该文档 1.1 考点 分词器 1.2 答案 # 创建符合条件的 task 索引&#xff0c;…

诊断丢帧:发送端连续帧发送过快,导致接收端丢帧

项目场景: 在项目开发过程中,对于报文的接收/发送,一般来说,通信量大,选择Polling(轮询)处理模式;通信量小,选择Interrupt(中断)处理模式。具体选择没有优劣之分。结合项目的实际情况,选择适合项目的方式就好。小编将分享一个Polling模式下出现的丢帧现象。 1576…

Ps:脚本事件管理器

Ps菜单&#xff1a;文件/脚本/脚本事件管理器 Scripts/Script Events Manager 脚本事件管理器 Script Events Manager允许用户将特定的事件&#xff08;如打开、存储或导出文件&#xff09;与 JavaScript 脚本或 Photoshop 动作关联起来&#xff0c;以便在这些事件发生时自动触…

exfat文件系统无法NFS导出的问题

最近项目中移植了exfat-linux驱动&#xff0c;但发现exfat格式的U盘无法用exportfs命令在NFS上导出。这篇文章记录了分析、解决方法。 一、问题现象 问题描述&#xff1a;exfat驱动更新后&#xff0c;exfat格式的U盘用exportfs命令NFS导出会报错 $ exportfs -o ro,fsid0,no_ro…

用飞书写博客,并自动部署

feishu-vitepress 用飞书写博客,并自动部署 目前的静态博客如vitepress&#xff0c;主要是用markdown来写内容。markdown虽然可读性比较好&#xff0c;但是在文章中贴图片有点麻烦&#xff0c;需要先保存图片到asset目录下&#xff0c;再在markdown中写图片地址。 平时工作主要…

软件方案评审与模块优化:从FOC模块出发的电控平台建设

一、背景 洞悉模块发展趋势&#xff0c;定制行业应用特点&#xff0c;明确优化方向与阶段性目标 随着科技进步的飞速发展&#xff0c;模块化设计已成为众多行业产品开发的核心理念。无论是软件系统、硬件组件&#xff0c;还是复杂系统中的功能模块&#xff0c;都需要对其发展…

STM32学习 BKP

BKP就是备份寄存器的意思&#xff0c;听名字就知道它的作用就是用来备份数据的。下面是手册当中的描述。备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份域 里&#xff0c;当VDD电源被切断&#xff0c;他们仍然由VBAT维持供电。当…