springboot3.0+spring security6+vue3 前后分离之前端部分

 描述

描述地址
源码地址springboot3.0+spring security6+vue3 前后分离: springboot3.0+spring security6+vue3 前后分离

springboot3.0+spring security6+vue3 前后分离之后端部分

springboot3.0+spring security6+vue3 前后分离之后端部分-CSDN博客

​​​​​​​

使用vite创建项目

npm create vite

输入项目的名字

选中vue

选择TypeScript

进入项目目录

npm install

启动项目 

浏览器访问

http://localhost:5173/

项目路径 

安装插件

npm i axios

npm i element-plus @element-plus/icons-vue

npm i vue-router

npm i pinia

vue.d.ts

declare module "*.vue" {
    import Vue from "vue";
    export default Vue;
}

vite.config.ts

配置跨域

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve:{
    alias:{
      //相对路径别名配置 用@符号替代src
      "@": path.resolve("./src")
    }
  },
  server: {
    proxy: {
      '/sp': {
        target: 'http://localhost:8083/',
        changeOrigin: true,
        //把api前置去掉
        rewrite: (path) => path.replace(/^\/sp/, "")
      }
    }
  }
})

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    //解析非相对模块的基地址,默认是当前目录
    "baseUrl": "./",
    "paths": {
      //路径映射,相当于baseUrl
      "@/*":["src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue","vue.d.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

.env.development

#测试环境标识
NODE_ENV="dev"
VITE_APP_TITLE="应用标题"
#基础路径
VITE_APP_BASE_API="http://localhost:8083"
#后端服务器的域名
VITE_SERVE="http://aa.com"

src\permisstion.ts

拦截所有路径

//引入路由器
import router from '@/router/index'
//引入小仓库
import  loginStore  from "@/store/modules/login";
//引入大仓库
import pinia  from "@/store/index.ts";
//获取小仓库对象
let lgst=loginStore(pinia);
 
//全局前置路由守卫,任意路由切换都会触发钩子
router.beforeEach((to,from,next)=>{
 
    //console.log(to.path)
    //如果是从login进来
    if(to.path=='/login'){
        //放行往下走
        next()
    }else{
        //判断是否存在token
        if(!lgst.token){
            //如果未登录,直接跳到登录界面
            next('/login')    
        }else{
            //获取登录用户权限信息
             let res=lgst.useMyAuth();
            //  console.log('path', to.path);
            //  console.log('auth', to.meta.auth);
            //  console.log('to', to);
             res.then((x)=>{
                if(!to.meta.auth){
                    //如果权限是空的 也放行
                    next()
                }else if(x.indexOf(to.meta.auth)!==-1){
                    //如果登录了 权限也有放行
                    next()
                }else{
                    //如果都不满足 调到无权限界面
                    next('/per')
                }
             })
        }
    }
})
 
//全局后置守卫
router.afterEach((to,from)=>{
 
})
 
 

src\main.ts

入口文件

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//国际化
//@ts-ignore 忽略当前ts文件报红的错误,不设置这个在打包的时候会报错
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
//引入element-plus的icons
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
//引入路由
import router from './router/index'
//引入大仓库
import pinia from './store/index'
//引入全局路由守卫
import '@/permisstion.ts'

const app = createApp(App)
 
//使用ElementPlus,并设置国际化
app.use(ElementPlus, {
    locale: zhCn,
})

//使用路由
app.use(router)

//使用大仓库
app.use(pinia)

//全局注册icon图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
}
 
//挂载到app上
app.mount('#app')

src\App.vue

<template>
  <div>
     <router-view></router-view>
   </div>
</template>
 
<script setup lang="ts">
 
 
</script>
 
<style scoped>
 
</style>

src\api\index.ts

后端接口文件

import request from "@/utils/request";
//统一管理接口
enum API{
    //登录后端接口
    LOGIN="/dengLu",
    //获取当前登录人的菜单
    GET_MY_MENU_LIST="/getMyMenuList",
    //获取当前登录人的权限
    GET_MY_AUTH="/getMyAuth",
    //获取所有用户
    GET_USER_LIST="/getUserList",
    //获取所有角色
    GET_ROLE_LIST="/getRoleList",
    //获取所有菜单
    GET_MENU_LIST="/getMenuList",

    //添加用户
    ADD_USER="/addUser",
    //获取当前登录人信息
    GET_USER_INFO="/getUserInfo",
    //为用户分配角色
    SET_USER_ROLE="/setUserRole",
    //查看用户对应的角色id
    GET_USER_ROLE_IDS="/getUserRoleIds",

    //添加角色
    ADD_ROLE="/addRole",
    //修改角色
    EDIT_ROLE="/editRole",
    //删除角色
    DELETE_ROLE="/deleteRole",

    //添加菜单
    ADD_MENU="/addMenu",
    //修改菜单
    EDIT_MENU="/editMenu",
    //删除菜单
    DELETE_MENU="/deleteMenu",

    //查看角色对应的权限
    GET_ROLE_AUTH="/getRoleAuth",
    //为角色分配权限
    SET_ROLE_AUTH="/setRoleAuth",


}


//登录接口方法
export const reqLogin=(data:any)=> request.post<any,any>(API.LOGIN,data)
//获取当前登录人的菜单
export const getMyMenuList=()=> request.post<any>(API.GET_MY_MENU_LIST)
//获取当前登录人的权限
export const getMyAuth=()=> request.post<any>(API.GET_MY_AUTH)
//获取所有用户
export const getUserList=()=> request.post<any>(API.GET_USER_LIST)
//获取所有角色
export const getRoleList=()=> request.post<any>(API.GET_ROLE_LIST)
//获取所有菜单
export const getMenuList=()=> request.post<any>(API.GET_MENU_LIST)


//添加用户
export const addUser=(data:any)=> request.post<any>(API.ADD_USER,data)
//获取当前登录人信息
export const getUserInfo=()=> request.post<any>(API.GET_USER_INFO)
//为用户分配角色
export const setUserRole=(data:any)=> request.post<any>(API.SET_USER_ROLE,data)
//查看用户对应的角色id
export const getUserRoleIds=(data:any)=> request.post<any>(API.GET_USER_ROLE_IDS,data)


//添加角色
export const addRole=(data:any)=> request.post<any>(API.ADD_ROLE,data)
//修改角色
export const editRole=(data:any)=> request.post<any>(API.EDIT_ROLE,data)
//删除角色
export const deleteRole=(data:any)=> request.post<any>(API.DELETE_ROLE,data)


//添加菜单
export const addMenu=(data:any)=> request.post<any>(API.ADD_MENU,data)
//修改菜单
export const editMenu=(data:any)=> request.post<any>(API.EDIT_MENU,data)
//删除菜单
export const deleteMenu=(data:any)=> request.post<any>(API.DELETE_MENU,data)

//查看角色对应的权限
export const getRoleAuth=(data:any)=> request.post<any>(API.GET_ROLE_AUTH,data)

//为角色分配权限
export const setRoleAuth=(data:any)=> request.post<any>(API.SET_ROLE_AUTH,data)

src\router\index.ts

//通过vue-router插件实现模版路由配置
import { createRouter,createWebHashHistory } from 'vue-router';
 //引入路由数组
import {luYou} from '@/router/routers'
//创建路由器
const router=createRouter({
    //路由模式
    history: createWebHashHistory(),
    //注意单词 别写错了
     //注意单词 别写错了
    routes: luYou
})
 
export default router;

src\router\routers.ts

路由数组 就是往那个vue界面去跳转

//单独暴露路由
export const luYou=[
    {
        path:'/login',
        component:()=> import('@/views/login/index.vue'),
        name:'login',
        meta:{
            //隐藏不显示到菜单上 true:隐藏 ,false:显示
            hidden:true,
            //菜单的名称
            title:'login',
            //饿了么ui图标的名字 固定写法
            icon:'Plus',
            auth:''
        }
    },
    {
        path:'/per',
        component:()=> import('@/views/per/index.vue'),
        name:'per',
        meta:{
            hidden:true,
            title:'per',
            icon:'Plus',
            auth:''
        }
    },
    {
        path:'/404',
        component:()=> import('@/views/404/index.vue'),
        name:'404',
        meta:{
            hidden:true,
            title:'404',
            icon:'Plus',
            auth:''
        }
    },
    {
        //根页面
        path:'/',
        component:()=> import('@/views/menu/index.vue'),
        name:'menu',
        meta:{
            hidden:true,
            title:'login',
            icon:'Plus',
            auth:''
        }
    },
    {
        //匹配到不存在的路径就跳转404
        path:'/:pathMatch(.*)*',
        //重定向到404
        redirect: '/404',
        //任意路由
        name:'any',
        meta:{
            hidden:true,
            title:'any',
            icon:'Plus',
            auth:''
        }
    },
    {
        path:'/org',
        component:()=> import('@/views/menu/index.vue'),
        name:'org',
        meta:{
            hidden:true,
            title:'组织管理',
            icon:'Plus',
            auth:'org'
        },
        children:[
            {
                path:'/org/er',
                component:()=> import('@/views/org/index.vue'),
                name:'orgEr',
                meta:{
                    hidden:true,
                    title:'组织管理二级菜单',
                    icon:'Plus',
                    auth:'org:er'
                }
            }
        ]
    },
    {
        path:'/user',
        component:()=> import('@/views/menu/index.vue'),
        name:'user',
        meta:{
            hidden:true,
            title:'用户管理',
            icon:'Plus',
            auth:'user'
        },
        children:[
            {
                path:'/user/er',
                component:()=> import('@/views/user/index.vue'),
                name:'userEr',
                meta:{
                    hidden:true,
                    title:'用户管理二级菜单',
                    icon:'Plus',
                    auth:'user:er'
                }
            }
        ]
    },
    {
        path:'/role',
        component:()=> import('@/views/menu/index.vue'),
        name:'role',
        meta:{
            hidden:true,
            title:'角色管理',
            icon:'Plus',
            auth:'role'
        },
        children:[
            {
                path:'/role/er',
                component:()=> import('@/views/role/index.vue'),
                name:'roleEr',
                meta:{
                    hidden:true,
                    title:'角色管理二级菜单',
                    icon:'Plus',
                    auth:'role:er'
                }
            }
        ]
    },
    {
        path:'/auth',
        component:()=> import('@/views/menu/index.vue'),
        name:'auth',
        meta:{
            hidden:true,
            title:'权限管理',
            icon:'Plus',
            auth:'auth'
        },
        children:[
            {
                path:'/auth/er',
                component:()=> import('@/views/auth/index.vue'),
                name:'authEr',
                meta:{
                    hidden:true,
                    title:'权限管理二级菜单',
                    icon:'Plus',
                    auth:'auth:er'
                }
            }
        ]
    },
    {
        path:'/apply',
        component:()=> import('@/views/menu/index.vue'),
        name:'apply',
        meta:{
            hidden:true,
            title:'应用管理',
            icon:'Plus',
            auth:'apply'
        },
        children:[
            {
                path:'/apply/er',
                component:()=> import('@/views/apply/index.vue'),
                name:'applyEr',
                meta:{
                    hidden:true,
                    title:'应用管理二级菜单',
                    icon:'Plus',
                    auth:'apply:er'
                }
            }
        ]
    },
]

src\store\modules\login.ts

//创建用户相关的小仓库
import {defineStore} from 'pinia'
//引入接口
import { reqLogin,getUserInfo,getMyAuth } from '../../api/index';
 
 
//创建登录小仓库
let loginStore=defineStore('login',{
        //小仓库存储数据的地方
        state:()=>{
           return {
                //从本地拿到token
                token: localStorage.getItem('TOKEN'),
                //当前登录人姓名
                name:'',
           }     
        },
        //异步| 逻辑处理的地方,在方法前面定义async,那么必须在拿到对应的方法前面加await 一起使用
        actions:{
            async userLogin(data: any){
                //拿到登录后的结果
                let res:any=await reqLogin(data);
                if(res){
                     let code=res.code;
                     let msg=res.msg;
                     let data=res.data;
                    if(code=='200'){
                        //刷新token
                        this.token=data;
                        //把token放入本地
                        localStorage.setItem('TOKEN',data);
                        return 'ok';
                    }else{
                        //失败返回失败对象
                        return Promise.reject(Error(msg))
                    }
                }else{
                    //失败返回失败对象
                    return Promise.reject(Error('账号密码输入错误'))
                }
                
            },
            async userInfo(){
                //登录后获取用户信息 请求头自带token    
                let res=await getUserInfo();
                this.name=res.data.name;
            },    
            userLogout(){
                //退出登录 清空用户信息 和token
                this.token=''
                this.name=''
                localStorage.removeItem('TOKEN');
            },
            async useMyAuth(){
                //登录后获取用户信息 请求头自带token    
                let res=await getMyAuth();
                return res.data;
            }, 
        },
        getters:{
 
        }
})
//对外暴露小仓库的方法
export default loginStore;

src\store\modules\luyou.ts

//创建用户相关的小仓库
import {defineStore} from 'pinia'
//引入路由数组
import {luYou} from '@/router/routers'
//引入接口
import { getMyAuth } from '../../api/index';
 
//创建小仓库
let luYouStore=defineStore('luYou',{
        //小仓库存储数据的地方
        state:()=>{
           return {
              //路由
              ly:[]
           }
        },
        //异步| 逻辑处理的地方,在方法前面定义async,那么必须在拿到对应的方法前面加await 一起使用
        actions:{
               async useMyMenuList(){
                  //获取当前登录人的权限集合   
                  let res=await getMyAuth();
                  let auths=res.data;
                  //赋值给新的数组
                  let newLy = luYou.map(x => {
                     
                     if(auths.indexOf(x.meta.auth) !== -1){
                         //如果路径匹配 那么设置为显示菜单
                         x.meta.hidden=false
                     }
                     let children=x.children;   
                     if(children){
                        children.map(y=>{
                           if(auths.indexOf(y.meta.auth) !== -1){
                              //如果路径匹配 那么设置为显示菜单
                              y.meta.hidden=false
                           }
                           return y;
                        })
                     }
                     
                     return x;
                  });
                 
                  //更新state的数据
                  this.ly=newLy;
                 
            }
        },
        getters:{
 
        }
})
//对外暴露小仓库的方法
export default luYouStore;

src\store\index.ts

//引入pinia
import {createPinia} from 'pinia'
//创建大仓库
let pinia=createPinia();
//暴露大仓库 入口文件需要安装仓库
export default pinia;

src\utils\request.ts

axios 二次封装 请求头 自动带token

import axios from "axios";
//引入消息提示
import {ElMessage} from 'element-plus';
//引入登录小仓库
import loginStore from "@/store/modules/login";

//利用axios对象的create方法,去创建axios实例
let request=axios.create({
    //基础路径 自动带上前缀
    baseURL: import.meta.env.VITE_APP_BASE_API,
    //超时时间,超过时间就获取不到后端的数据了
    timeout: 5000
})
//request实例添加请求拦截器
request.interceptors.request.use((config)=>{
    let lgst=loginStore();
    if(lgst.token){
        //config配置对象,headers属性请求头,给后台携带公共参数
        //设置token 放到请求头上 
        config.headers['Authorization']='Bearer '+lgst.token;
    }
    //console.log('请求拦截器:',config)
    return config;
})
//添加响应拦截器
request.interceptors.response.use((response)=>{
    //成功回调
    //console.log('响应拦截器:',response)
    return response.data;
},(error)=>{
    //http状态码
    //console.log('错误状态:',error.response)
    if(error.response==undefined){
        ElMessage({
            type:'error',
            message:'网络异常'
        })
    }else{
        ElMessage({
            type:'error',
            message:error.response.data.msg
        })
    }
    
    //返回一个失败的Promise对象
    return Promise.reject(error);
})
 
//对外暴露 才能被外部使用
export default request;

src\views\404\index.vue

<template>
    <div>
      我是404
    </div>
  </template>
  
  <script>
  export default {
  
  }
  </script>
  
  <style>
  
  </style>

src\views\apply\index.vue

<template>
  我是应用
</template>

<script>
export default {

}
</script>

<style>

</style>

src\views\auth\index.vue

<template>
  <div>
      <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>权限管理</span>
            </div>
          </template>
          <el-button type="primary" icon="Plus" @click="add(0,'1')" v-if="authErAdd">添加一级菜单</el-button>
          
          <el-table
          :data="tableData"
          style="width: 100%; margin-bottom: 20px"
          row-key="id"
          border
          default-expand-all
        >
          <el-table-column prop="name" label="菜单名称/按钮名称" sortable width="200"/>
          <el-table-column prop="auth" label="权限名称" sortable  width="180"/>
          <el-table-column prop="path" label="路径" sortable  width="180"/>
          <el-table-column prop="createTime" label="创建时间" sortable  width="180"/>
          <el-table-column prop="desc" label="备注" sortable  width="180"/>
          <el-table-column
              prop="isType"
              label="类型"
              width="180" 
              >
              <template #default="scope">
                <el-tag v-if="scope.row.isType=='1'">
                    菜单
                </el-tag>
                <el-tag v-if="scope.row.isType=='2'">
                  按钮
                </el-tag>
              
              </template>
           </el-table-column>

           <el-table-column prop="caozuo" label="操作">
              <!--具名插槽-->
              <template #default="caozuo">

                  <!--一级菜单 可以添加二级菜单-->
                  <el-button type="primary" icon="plus" v-if="authErAddSub && caozuo.row.isType=='1' && caozuo.row.parentId==0" @click="add(caozuo.row.id,'1')">添加二级菜单</el-button>
                  <!--二级菜单 可以添加按钮-->
                  <el-button type="primary" icon="plus" v-if="authErBtn && caozuo.row.isType=='1' && caozuo.row.parentId!=0" @click="add(caozuo.row.id,'2')">添加按钮</el-button>
                  <el-button type="primary" v-if="authErEdit" icon="edit" @click="edit(caozuo.row)">修改</el-button>
                  <el-button type="primary" v-if="authErDel" icon="delete" @click="del(caozuo.row.id)">删除</el-button>
              </template>
           </el-table-column> 
          </el-table>
        
        </el-card>


         <!--点击新增弹出对话框-->
         <el-dialog
              v-model="dialogVisible"
              title="添加"
              width="30%"
              :before-close="handleClose"
          >
           <el-form :model="addBean">
             <el-form-item label="父级id" >
              <el-input  label-width="80%" v-model="addBean.parentId" disabled/>
             </el-form-item>
              <el-form-item label="名称" >
                <el-input  label-width="80%" v-model="addBean.name"/>
              </el-form-item>
              <el-form-item label="权限名称" >
                <el-input  label-width="80%" v-model="addBean.auth"/>
              </el-form-item>
              <el-form-item label="路径" >
                <el-input  label-width="80%" v-model="addBean.path"/>
              </el-form-item>
              <el-form-item label="排序" >
                <el-input-number :min="1" :max="1000" v-model="addBean.paiXu" />
              </el-form-item>
              <el-form-item label="类型" >
                  <el-select v-model="addBean.isType" disabled>
                    <el-option
                      v-for="item in options"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    />
                  </el-select>
              </el-form-item>
            </el-form>

              <!--底部插槽-->
              <template #footer>
              <span class="dialog-footer">
                  <el-button @click="dialogVisible = false">取消</el-button>
                  <el-button type="primary" @click="insert">确定</el-button>
              </span>
              </template>
          </el-dialog>

        <!--点击修改弹出对话框-->
        <el-dialog
              v-model="editDialog"
              title="修改"
              width="30%"
              :before-close="handleClose"
          >
           <el-form :model="updateBean">
            <el-form-item label="菜单id">
              <el-input  label-width="80%" v-model="updateBean.id" disabled/>
            </el-form-item>
              <el-form-item label="名称" >
                <el-input  label-width="80%" v-model="updateBean.name"/>
              </el-form-item>
              <el-form-item label="权限名称" >
                <el-input  label-width="80%" v-model="updateBean.auth"/>
              </el-form-item>
              <el-form-item label="路径" >
                <el-input  label-width="80%" v-model="updateBean.path"/>
              </el-form-item>
              <el-form-item label="排序" >
                <el-input-number :min="1" :max="1000" v-model="updateBean.paiXu" />
              </el-form-item>
              <el-form-item label="类型" >
                  <el-select v-model="updateBean.isType" disabled>
                    <el-option
                      v-for="item in options"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    />
                  </el-select>
              </el-form-item>
            </el-form>

              <!--底部插槽-->
              <template #footer>
              <span class="dialog-footer">
                  <el-button @click="editDialog = false">取消</el-button>
                  <el-button type="primary" @click="update">确定</el-button>
              </span>
              </template>
          </el-dialog>
       
  </div>
</template>

<script setup lang="ts">
import {ref,onMounted,reactive} from 'vue'
//引入后端接口
import {getMenuList,addMenu,editMenu,deleteMenu,getMyAuth} from '@/api/index'

//引入提示
import { ElMessage,ElMessageBox } from 'element-plus'
import { fa } from 'element-plus/es/locale/index.mjs';

//表格数据
let tableData=ref([]);

//获取分页数据
let getData=async ()=>{
  //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  let res=await getMenuList();
  tableData.value=res.data;
}

//加载时调用
onMounted(()=>{
  //调用查询
  getData();
  //获取按钮权限
  getAuthData();
})

//下拉框内容
const options = [
  {
    value: '1',
    label: '菜单',
  },
  {
    value: '2',
    label: '按钮',
  }
]


//----------------------添加-----------------------

//修改对象
let addBean=reactive({
  parentId:null,
  name:'',
  auth:'',
  isType:'',
  paiXu:'',
  path:''
})
//默认不显示对话框
let dialogVisible=ref(false)
//添加显示对话框
let add=(parentId,isType)=>{
    //显示对话框
    dialogVisible.value=true;
    
    //把输入框显示的内容清空
    addBean.parentId=parentId;
    addBean.name='';
    addBean.auth='';
    addBean.isType=isType;
    addBean.paiXu='';
    addBean.path='';
}

//添加权限 调用后端接口
let insert= async ()=>{
    //调用后端添加品牌方法
    let res=await addMenu(addBean);
    console.log(res);
    if(res.code=='200'){
      //执行成功后关闭对话框
      dialogVisible.value=false;
      ElMessage.success('添加成功')
      //调用查询刷新界面
      getData();
    }else{
      //弹窗错误消息
      ElMessage.error(res.msg);
    }
}

//----------------------删除-----------------------
let del=async (id)=>{
  await ElMessageBox.confirm(
    '确定删除吗?',
    '删除',
    {
      confirmButtonText: 'OK',
      cancelButtonText: 'Cancel',
      type: 'warning',
    }
  )
    .then(() => {
      deleteMenu({
        id:id
      }).then((res)=>{
        if(res.code=='200'){
            ElMessage.success('删除成功')
            //调用查询刷新界面
            getData();
          }else{
            //弹窗错误消息
            ElMessage.error(res.msg);
          }
      })
      
     
    })
    .catch(() => {
      
    })


    
}


//----------------------修改-----------------------

//修改对象
let updateBean=reactive({
  id:null,
  name:'',
  auth:'',
  isType:'',
  paiXu:'',
  path:''
})
//默认不显示对话框
let editDialog=ref(false)
//显示对话框
let edit=(row)=>{
    //显示对话框
    editDialog.value=true;
    
    //把输入框显示内容赋值
    updateBean.id=row.id;
    updateBean.name=row.name;
    updateBean.auth=row.auth;
    updateBean.isType=row.isType;
    updateBean.paiXu=row.paiXu;
    updateBean.path=row.path;
}

//修改权限 调用后端接口
let update= async ()=>{
    //调用后端添加品牌方法
    let res=await editMenu(updateBean);
    if(res.code=='200'){
      //执行成功后关闭对话框
      editDialog.value=false;
      ElMessage.success('修改成功')
      //调用查询刷新界面
      getData();
    }else{
      //弹窗错误消息
      ElMessage.error(res.msg);
    }
}

//--------------------------按钮显示隐藏----------------------------
//按钮权限
//添加一级菜单按钮默认不显示
let authErAdd=ref(false);
//添加二级菜单按钮默认不显示
let authErAddSub=ref(false);
//修改按钮默认不显示
let authErEdit=ref(false);
//删除按钮默认不显示
let authErDel=ref(false);
//添加子级按钮 默认不显示
let authErBtn=ref(false);

let getAuthData= async()=>{
  //获取菜单权限
  let res=await getMyAuth();
  //如果存在对应的权限 才显示按钮
  if(res.data.indexOf('auth:er:add')!==-1){
    authErAdd.value=true;
  }
  if(res.data.indexOf('auth:er:del')!==-1){
    authErDel.value=true;
  }
  if(res.data.indexOf('auth:er:edit')!==-1){
    authErEdit.value=true;
  }
  if(res.data.indexOf('auth:er:add:sub')!==-1){
    authErAddSub.value=true;
  }
  if(res.data.indexOf('auth:er:btn')!==-1){
    authErBtn.value=true;
  }
 
  
}

</script>

<style scoped>

</style>

<style>

</style>

src\views\home\index.vue

<template>
    <div>
      我是首页
    </div>
  </template>
  
  <script>
  export default {
  
  }
  </script>
  
  <style>
  
  </style>

src\views\login\index.vue

<template>

  <el-card class="box-card">
    <template #header>
      <div class="card-header">
        <span>权限管理系统</span>

      </div>
      
    </template>
        <el-form
        ref="ruleFormRef"
        :model="ruleForm"
        :rules="rules"
        label-width="120px"
        
      >
        <el-form-item label="账号" prop="account">
          <el-input v-model="ruleForm.account" />
        </el-form-item>

        <el-form-item label="密码" prop="password">
          <el-input
                  v-model="ruleForm.password"
                  type="password"
                  placeholder="请输入密码"
                  show-password
              />
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="login()">
            登录
          </el-button>
        </el-form-item>
        
      </el-form>
  </el-card>



    
  </template>
  
  <script lang="ts" setup>
  import { reactive, ref } from 'vue'
  import type { FormInstance, FormRules } from 'element-plus'
  import { ElMessage } from 'element-plus'
  //引入路由
  import {useRouter} from 'vue-router'
  //引入登录小仓库
  import loginStore from '../../store/modules/login.ts';

  
  //定义接口
  interface RuleForm {
    account: string
    password: string
  }

  //获取路由器
  let router=useRouter();
  

  const ruleFormRef = ref<RuleForm>()
  //使用接口 并且 为每一个文本框赋 默认值
  const ruleForm = reactive<RuleForm>({
    account: 'user',
    password: '123456'
  })
  
  //校验文本框的内容
  const rules = reactive<FormRules<RuleForm>>({
    account: [
      { required: true, message: '账号不能为空', trigger: 'blur' },
      { min: 3, max: 10, message: '长度在3到10之间', trigger: 'blur' },
    ],
    password: [
      {
        required: true,
        message: '密码不能为空',
        trigger: 'change',
      },
    ]
  })



  //登录方法
  let login= async ()=>{
    //阻塞form 表单的校验 必须完成之后才能往下走 因为validate是一个async方法
    await ruleFormRef.value.validate();
    try {
      //阻塞拿到结果才往下走
      await loginStore().userLogin(ruleForm);
      //弹框
      ElMessage({
          showClose: true,
          message: '登录成功',
          type: 'success',
      })
      //跳转根路径
      router.push("/")  
    } catch (error) {
        ElMessage({
          showClose: true,
          message: (error as Error).message,
          type: 'error',
        })
    }
  }

  </script>
  

src\views\menu\index.vue

<template>
    <div>
        <el-row>
         
                <el-col :span="3">
                        <!--显示左侧菜单--> 
                        <!--组件传参 把菜单数组传过去-->
                        <Sub :menuList="lyst.ly"></Sub>
                </el-col>
       

        <el-col :span="21">
                <div>
                        <el-row>
                          <el-col :span="21"></el-col>
                          <el-col :span="3">
                                <!--显示用户信息-->
                                当前登录人:{{ lgStore.name }}
                                <el-button type="primary" @click="logout">退出登录</el-button>
                          </el-col>
                        </el-row>
                       
                </div>
                <div>
                        <!--显示菜单点击后的内容-->
                        <router-view></router-view>
                </div>
        </el-col>

       
   </el-row>
    </div>    

   
  </template>
  
  <script lang="ts" setup>
  import Sub from '@/views/sub/index.vue'
  //引入小仓库
  import luYouStore from '@/store/modules/luyou';
  import { onMounted } from 'vue';
  //引入路由
  import {useRouter} from 'vue-router'
  //引入登录小仓库
  import loginStore from '../../store/modules/login.ts';

  //获取路由器
  let router=useRouter();

  //使用小仓库
  let lyst=luYouStore()

  
  //拿到登录仓库对象
  let lgStore=loginStore();

  //生命周期挂载
  onMounted(()=>{
        //初始化菜单
        lyst.useMyMenuList();
        //获取用户信息
        lgStore.userInfo();
      
  })

  
//退出登录
let logout=()=>{
   loginStore().userLogout()
   //退出后跳转到登录界面
   router.push('/login')
}

  </script>

  <style scoped>

  </style>
  

src\views\org\index.vue

<template>
  我是组织架构
</template>

<script>
export default {

}
</script>

<style>

</style>

src\views\per\index.vue

<template>
<h1>
    无权限
</h1>  
</template>

<script>
export default {

}
</script>

<style>

</style>

src\views\role\index.vue

<template>
  <div>
      <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>角色管理</span>
            </div>
          </template>
          <el-button type="primary" icon="Plus" @click="add" v-if="roleErAdd">添加</el-button>
          <el-table :data="tableData" style="width: 100%" >
              <el-table-column label="序号" width="180" type="index"/>
              <el-table-column prop="roleCode" label="角色编码" width="180" />
              <el-table-column prop="roleName" label="角色名称" width="180" />
              <el-table-column prop="caozuo" label="操作">
                      <!--具名插槽-->
                      <template #default="caozuo">
                          <el-button type="primary" icon="edit" @click="edit(caozuo.row)" v-if="roleErEdit">修改</el-button>
                          <el-button type="primary" icon="delete" @click="del(caozuo.row.id)" v-if="roleErDel">删除</el-button>
                          <el-button type="primary" icon="Setting" @click="dakai(caozuo.row.id)" v-if="roleErFp">分配权限</el-button>
                      </template>
              </el-table-column>
          </el-table>

        
        </el-card>

        
         <!--点击新增按钮弹出对话框-->
         <el-dialog
              v-model="dialogVisible"
              title="添加"
              width="30%"
              :before-close="handleClose"
          >
           <el-form :model="addBean">
              <el-form-item label="角色编码" >
                <el-input  label-width="80%" v-model="addBean.roleCode"/>
              </el-form-item>
              <el-form-item label="角色名称" >
                <el-input  label-width="80%" v-model="addBean.roleName"/>
              </el-form-item>
            </el-form>

              <!--底部插槽-->
              <template #footer>
              <span class="dialog-footer">
                  <el-button @click="dialogVisible = false">取消</el-button>
                  <el-button type="primary" @click="insert">确定</el-button>
              </span>
              </template>
          </el-dialog>



          <!--点击修改按钮弹出对话框-->
          <el-dialog
                v-model="editDialog"
                title="修改"
                width="30%"
                :before-close="handleClose"
            >
            <el-form :model="updateBean">
               <el-form-item label="角色id" >
                 <el-input  label-width="80%" v-model="updateBean.id" disabled/>
               </el-form-item>
                <el-form-item label="角色编码" >
                  <el-input  label-width="80%" v-model="updateBean.roleCode"/>
                </el-form-item>
                <el-form-item label="角色名称" >
                  <el-input  label-width="80%" v-model="updateBean.roleName"/>
                </el-form-item>
              </el-form>

                <!--底部插槽-->
                <template #footer>
                <span class="dialog-footer">
                    <el-button @click="editDialog = false">取消</el-button>
                    <el-button type="primary" @click="update">确定</el-button>
                </span>
                </template>
            </el-dialog>


          <!--点击分配权限按钮弹出对话框-->

            <el-dialog
            v-model="fpDialog"
            title="分配权限"
            width="30%"
            :before-close="handleClose"
        >
            <el-tree
              ref="tree"
              :data="data"
              show-checkbox
              node-key="id"
              default-expand-all
              :default-checked-keys="checkedArr"
              :props="defaultProps"
            >
           </el-tree>
           <el-button @click="fpDialog = false">取消</el-button>
           <el-button type="success" @click="fpAuth">确定</el-button>
          </el-dialog>
       
  </div>
</template>

<script setup lang="ts">
import {ref,onMounted,reactive} from 'vue'
//引入后端接口
import {getRoleList,addRole,deleteRole,editRole,getMenuList,getRoleAuth,setRoleAuth,getMyAuth} from '@/api/index'
import { ElMessage, ElMessageBox } from 'element-plus'

//----------------------查询-----------------------
//表格数据
let tableData=ref([]);

//获取分页数据
let getData=async ()=>{
  //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  let res=await getRoleList();
  tableData.value=res.data;
}

//加载时调用
onMounted(()=>{
  //调用查询
  getData();
  //获取按钮权限
  getAuthData();
})

//----------------------添加-----------------------

//添加对象
let addBean=reactive({
  roleCode:'',
  roleName:''
})
//默认不显示对话框
let dialogVisible=ref(false)
//添加显示对话框
let add=()=>{
    //显示对话框
    dialogVisible.value=true;
    
    //把输入框显示的内容清空
    addBean.roleCode='';
    addBean.roleName='';
}

//添加角色 调用后端接口
let insert= async ()=>{
    //调用后端添加品牌方法
    let res=await addRole(addBean);
    console.log(res);
    if(res.code=='200'){
      //执行成功后关闭对话框
      dialogVisible.value=false;
      ElMessage.success('添加成功')
      //调用查询刷新界面
      getData();
    }else{
      //弹窗错误消息
      ElMessage.error(res.msg);
    }
}

//----------------------修改-----------------------

//修改对象
let updateBean=reactive({
  id:null,
  roleCode:'',
  roleName:''
})
//默认不显示对话框
let editDialog=ref(false)
//显示对话框
let edit=(row)=>{
    //显示对话框
    editDialog.value=true;
    
    //把输入框显示内容赋值
    updateBean.id=row.id;
    updateBean.roleCode=row.roleCode;
    updateBean.roleName=row.roleName;

}

//修改角色 调用后端接口
let update= async ()=>{
    //调用后端添加品牌方法
    let res=await editRole(updateBean);
    console.log(res);
    if(res.code=='200'){
      //执行成功后关闭对话框
      editDialog.value=false;
      ElMessage.success('修改成功')
      //调用查询刷新界面
      getData();
    }else{
      //弹窗错误消息
      ElMessage.error(res.msg);
    }
}

//----------------------删除-----------------------
let del=async (id)=>{
  await ElMessageBox.confirm(
    '确定删除吗?',
    '删除',
    {
      confirmButtonText: 'OK',
      cancelButtonText: 'Cancel',
      type: 'warning',
    }
  )
    .then(() => {
      deleteRole({
        id:id
      }).then((res)=>{
        if(res.code=='200'){
            ElMessage.success('删除成功')
            //调用查询刷新界面
            getData();
          }else{
            //弹窗错误消息
            ElMessage.error(res.msg);
          }
      })
      
     
    })
    .catch(() => {
      
    })


    
}



//----------------------分配权限-----------------------
const fpDialog = ref(false)
 

//定义后端的显示字段
const defaultProps = {
  children: 'children',
  label: 'name',
}
 
//显示的数组
let data=ref([])
 
//选中的数组
let checkedArr=ref([])
 
//选中的树的id数组
let tree=ref([])
 
//分配权限 那一列的 角色id
let authRoleId=ref()
 
 //打开树
let dakai=async(id)=>{
  //先把选择的清空掉
  checkedArr.value=null;
  //调用后端权限方法
  let res=await getMenuList();
  if(res.code=='200'){
    data.value=res.data;
    //赋值给角色id
    authRoleId.value=id;
    //获取当前角色对应的权限
    getRoleAuth({
      roleId:id
    }).then((auths)=>{
        //选中的数组赋值
        checkedArr.value= getCheck(data.value,[],auths.data);
        fpDialog.value=true;
    });
  }
}
 
//选中的数组遍历
let getCheck=(item:any,arr:any,auths:any)=>{
  item.forEach(x => {
         if(x.isType=='2' && auths.indexOf(x.auth)!==-1){
            console.log(x.auth,'------',auths)
            //如果是按钮 并且选中了 那么设置树展示
            arr.push(x.id)
         }
         if(x.children && x.children.length>0){
            //如果有子集那么递归子集
            getCheck(x.children,arr,auths)
         }
  });
  return arr;
}
 
//分配权限方法
let fpAuth= async()=>{
  //拿到当前选中的节点的id数组
  let menuIds=tree.value.getCheckedKeys();
  //为角色分配权限
  let res=await setRoleAuth({
    roleId:authRoleId.value,
    menuIds:menuIds
  });
  if(res.code=='200'){
    authRoleId.value='';
    ElMessage.success('分配成功')
    //调用查询刷新界面
    getData();
  }else{
    //弹窗错误消息
    ElMessage.error(res.msg);
  }
  //关闭窗口
  fpDialog.value = false;
  
  
}

//--------------------------按钮显示隐藏----------------------------
//按钮权限
//添加按钮默认不显示
let roleErAdd=ref(false);
//分配权限按钮默认不显示
let roleErFp=ref(false);
//修改按钮默认不显示
let roleErEdit=ref(false);
//删除按钮默认不显示
let roleErDel=ref(false);

let getAuthData= async()=>{
  //获取菜单权限
  let res=await getMyAuth();
  //如果存在对应的权限 才显示按钮
  if(res.data.indexOf('role:er:add')!==-1){
    roleErAdd.value=true;
  }
  if(res.data.indexOf('role:er:del')!==-1){
    roleErDel.value=true;
  }
  if(res.data.indexOf('role:er:edit')!==-1){
    roleErEdit.value=true;
  }
  if(res.data.indexOf('role:er:fp')!==-1){
    roleErFp.value=true;
  }
  
}
 

</script>

<style scoped>

</style>

<style>

</style>

src\views\sub\index.vue

<template>
  <!--菜单-->
  <el-menu v-for="x in menuList" :key="x.path">
    <!--根据权限 是否展示一级菜单-->
   <el-sub-menu :index="x.path" v-if="!x.meta.hidden">
     <!--插槽占位 显示一级菜单-->
     <template #title>
       <!--显示图标-->
       <el-icon>
         <component :is="x.meta.icon"></component>
       </el-icon>
       <span>{{x.meta.title}}</span>
     </template>
       <!--显示二级菜单-->
       <template v-for="y in x.children" :key="y.path" >
           <!--根据权限 是否展示二级菜单-->
         <el-menu-item :index="y.path" @click="goRouter" v-if="!y.meta.hidden">
           <!--显示图标-->
           <el-icon>
             <component :is="y.meta.icon"></component>
           </el-icon>
           <!--显示标题-->
           {{y.meta.title}}
         </el-menu-item>
       </template>
   </el-sub-menu>

</el-menu>
</template>

<script setup lang="ts">
//引入路由器
import {useRouter} from 'vue-router'

//接收父组件传过来的菜单数组
defineProps(['menuList'])

//获取路由器对象
let router=useRouter();

//点击菜单触发的方法
let goRouter=(vc:any)=>{
 //通过路由器跳转界面 
 router.push(vc.index)
}


</script>

<style>

</style>

src\views\user\index.vue

<template>
  <div>
      <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>用户管理</span>
            </div>
          </template>

          <!--有权限才显示-->  
          <el-button type="primary" icon="Plus" @click="add" v-if="userErAdd">添加</el-button>


          <el-table :data="tableData" style="width: 100%" >
              <el-table-column label="序号" width="180" type="index"/>
              <el-table-column prop="account" label="账号" width="180" />
              <el-table-column prop="name" label="姓名" width="180" />
              <el-table-column prop="createTime" label="创建时间" width="200" />

              <el-table-column prop="caozuo" label="操作">
                      <!--具名插槽-->
                      <template #default="caozuo">
                          <!--把表格的主键传递过去-->
                          <el-button type="primary" icon="Setting" @click="fenPeiRole(caozuo.row.id)" v-if="userErFp">分配角色</el-button>
                      </template>
              </el-table-column>
          </el-table>

        
        </el-card>


         <!--点击新增按钮弹出对话框-->
         <el-dialog
              v-model="dialogVisible"
              title="添加"
              width="30%"
              :before-close="handleClose"
          >
           <el-form :model="addBean">
              <el-form-item label="账号" >
                <el-input  label-width="80%" v-model="addBean.account"/>
              </el-form-item>
              <el-form-item label="姓名" >
                <el-input  label-width="80%" v-model="addBean.name"/>
              </el-form-item>
              <el-form-item label="密码" >
                <el-input  label-width="80%" v-model="addBean.password"/>
              </el-form-item>
            </el-form>

              <!--底部插槽-->
              <template #footer>
              <span class="dialog-footer">
                  <el-button @click="dialogVisible = false">取消</el-button>
                  <el-button type="primary" @click="insert">确定</el-button>
              </span>
              </template>
          </el-dialog>

          <!--弹出分配角色对话框--> 
          <el-dialog
              v-model="dialogRole"
              title="分配角色"
              width="30%"
              :before-close="handleClose"
          >
          <el-form :model="setBean">
             <el-form-item label="用户id">
              <el-input  label-width="80%" v-model="setBean.userId" disabled/>
             </el-form-item>

             <el-form-item label="角色" >
                <el-select
                    v-model="roleIds"
                    multiple
                    clearable
                    collapse-tags
                    placeholder=""
                    popper-class="custom-header"
                    :max-collapse-tags="1"
                    
                  >
                    <template #header>
                      <el-checkbox
                        v-model="checkAll"
                        :indeterminate="indeterminate"
                        @change="handleCheckAll"
                      >
                        All
                      </el-checkbox>
                    </template>
                    <el-option
                      v-for="item in cities"
                      :key="item.id"
                      :label="item.roleName"
                      :value="item.id"
                    />
                  </el-select>

              </el-form-item>
              
            </el-form>

              <!--底部插槽-->
              <template #footer>
              <span class="dialog-footer">
                  <el-button @click="dialogRole = false">取消</el-button>
                  <el-button type="primary" @click="updateRole">确定</el-button>
              </span>
              </template>
          </el-dialog>

       
  </div>
</template>

<script setup lang="ts">
import {ref,onMounted,reactive,watch} from 'vue'
//引入后端接口
import {getUserList,addUser,getRoleList,setUserRole,getUserRoleIds,getMyAuth} from '@/api/index'
import type { CheckboxValueType } from 'element-plus'
//引入提示
import { ElMessage } from 'element-plus'





//表格数据
let tableData=ref([]);

//获取分页数据
let getData=async ()=>{
  //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  let res=await getUserList();
  tableData.value=res.data;
}

//--------------------------按钮显示隐藏----------------------------
//添加按钮默认不显示
let userErFp=ref(false);
//分配角色按钮默认不显示
let userErAdd=ref(false);

let getAuthData= async()=>{
  //获取菜单权限
  let res=await getMyAuth();
  //如果存在对应的权限 才显示按钮
  if(res.data.indexOf('user:er:add')!==-1){
    userErAdd.value=true;
  }
  if(res.data.indexOf('user:er:fp')!==-1){
    userErFp.value=true;
  }
  
}

//加载时调用
onMounted(()=>{
  //调用查询
  getData();
  //调用权限
  getAuthData();
  
})


//添加对象
let addBean=reactive({
    name:'',
    account:'',
    password:''
})
//默认不显示对话框
let dialogVisible=ref(false)
//添加显示对话框
let add=()=>{
    //显示对话框
    dialogVisible.value=true;
    
    //把输入框显示的内容清空
    addBean.name='';
    addBean.account='';
    addBean.password='';
}

//添加用户 调用后端接口
let insert= async ()=>{
    //调用后端添加品牌方法
    let res=await addUser(addBean);
    console.log(res);
    if(res.code=='200'){
      //执行成功后关闭对话框
      dialogVisible.value=false;
      ElMessage.success('添加成功')
      //调用查询刷新界面
      getData();
    }else{
      //弹窗错误消息
      ElMessage.error(res.msg);
    }
}

//角色信息
const cities = ref([])
//全选
const checkAll = ref(false)
//监听值的变化
const indeterminate = ref(false)
//角色id 集合
const roleIds = ref<CheckboxValueType[]>([])
//分配角色对象
let setBean=reactive({
    userId:'',
    roleIds:[]
})
//默认不显示分配角色对话框
let dialogRole=ref(false)
//显示分配角色对话框
let fenPeiRole= async(id)=>{
    //显示对话框
    dialogRole.value=true;
    //获取后台角色列表接口
    let res=await getRoleList();
    cities.value=res.data;
    //赋值用户id 
    setBean.userId=id;
    //获取已经分配过的角色id
    let response=await getUserRoleIds({
      userId:id
    });
    //把已分配过的角色id数组,给roleIds赋值 使得下拉框默认选中
    roleIds.value=response.data;
}



// //添加用户 调用后端接口
let updateRole= async ()=>{
  //把角色id集合赋值 给后台对象
  setBean.roleIds=roleIds.value;
  //调用后端分配角色方法
  let res=await setUserRole(setBean);
  console.log(res);
  if(res.code=='200'){
    //执行成功后关闭对话框
    dialogRole.value=false;
    ElMessage.success('分配成功')
    //调用查询刷新界面
    getData();
  }else{
    //弹窗错误消息
    ElMessage.error(res.msg);
  }
}

//监听下拉框发生的变化
watch(roleIds, (val) => {

  if (val.length === 0) {
    checkAll.value = false
    indeterminate.value = false
  } else if (val.length === cities.value.length) {
    checkAll.value = true
    indeterminate.value = false
  } else {
    indeterminate.value = true
  }
})

//点击All 方法
const handleCheckAll = (val: CheckboxValueType) => {
  indeterminate.value = false
  if (val) {
    roleIds.value = cities.value.map((_) => _.id)
  } else {
    roleIds.value = []
  }
}

</script>

<style lang="scss">
.custom-header {
  .el-checkbox {
    display: flex;
    height: unset;
  }
}
</style>

启动项目

npm run dev

http://localhost:5173/#/login

 

用户关联角色

角色分配权限

界面上的菜单和按钮 都会动态显示或者隐藏 

 

如果按钮都没有选择 那么这个二级菜单 不会显示

如果按钮选择一个 那么二级菜单也能访问 

 

退出登录后 会清空token和用户信息 

没有token 自动跳转到登录界面

访问错误的路径会显示404

没有路径的权限 访问会进入无权限的界面

注册用户之后 密码都是加密的

在登录的时候 会把密码加密进行对比

用户名:zhangsan

密码:123456

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值