当前位置:   article > 正文

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

springboot3+springsecurity6+vue3

 描述

​​​​​​​

使用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

  1. declare module "*.vue" {
  2. import Vue from "vue";
  3. export default Vue;
  4. }

vite.config.ts

配置跨域

  1. import { defineConfig } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. import path from 'path'
  4. // https://vitejs.dev/config/
  5. export default defineConfig({
  6. plugins: [vue()],
  7. resolve:{
  8. alias:{
  9. //相对路径别名配置 用@符号替代src
  10. "@": path.resolve("./src")
  11. }
  12. },
  13. server: {
  14. proxy: {
  15. '/sp': {
  16. target: 'http://localhost:8083/',
  17. changeOrigin: true,
  18. //把api前置去掉
  19. rewrite: (path) => path.replace(/^\/sp/, "")
  20. }
  21. }
  22. }
  23. })

tsconfig.json

  1. {
  2. "compilerOptions": {
  3. "target": "ES2020",
  4. "useDefineForClassFields": true,
  5. "module": "ESNext",
  6. "lib": ["ES2020", "DOM", "DOM.Iterable"],
  7. "skipLibCheck": true,
  8. /* Bundler mode */
  9. "moduleResolution": "bundler",
  10. "allowImportingTsExtensions": true,
  11. "resolveJsonModule": true,
  12. "isolatedModules": true,
  13. "noEmit": true,
  14. "jsx": "preserve",
  15. /* Linting */
  16. "strict": true,
  17. "noUnusedLocals": true,
  18. "noUnusedParameters": true,
  19. "noFallthroughCasesInSwitch": true,
  20. //解析非相对模块的基地址,默认是当前目录
  21. "baseUrl": "./",
  22. "paths": {
  23. //路径映射,相当于baseUrl
  24. "@/*":["src/*"]
  25. }
  26. },
  27. "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue","vue.d.ts"],
  28. "references": [{ "path": "./tsconfig.node.json" }]
  29. }

.env.development

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

src\permisstion.ts

拦截所有路径

  1. //引入路由器
  2. import router from '@/router/index'
  3. //引入小仓库
  4. import loginStore from "@/store/modules/login";
  5. //引入大仓库
  6. import pinia from "@/store/index.ts";
  7. //获取小仓库对象
  8. let lgst=loginStore(pinia);
  9. //全局前置路由守卫,任意路由切换都会触发钩子
  10. router.beforeEach((to,from,next)=>{
  11. //console.log(to.path)
  12. //如果是从login进来
  13. if(to.path=='/login'){
  14. //放行往下走
  15. next()
  16. }else{
  17. //判断是否存在token
  18. if(!lgst.token){
  19. //如果未登录,直接跳到登录界面
  20. next('/login')
  21. }else{
  22. //获取登录用户权限信息
  23. let res=lgst.useMyAuth();
  24. // console.log('path', to.path);
  25. // console.log('auth', to.meta.auth);
  26. // console.log('to', to);
  27. res.then((x)=>{
  28. if(!to.meta.auth){
  29. //如果权限是空的 也放行
  30. next()
  31. }else if(x.indexOf(to.meta.auth)!==-1){
  32. //如果登录了 权限也有放行
  33. next()
  34. }else{
  35. //如果都不满足 调到无权限界面
  36. next('/per')
  37. }
  38. })
  39. }
  40. }
  41. })
  42. //全局后置守卫
  43. router.afterEach((to,from)=>{
  44. })

src\main.ts

入口文件

  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. import ElementPlus from 'element-plus'
  4. import 'element-plus/dist/index.css'
  5. //国际化
  6. //@ts-ignore 忽略当前ts文件报红的错误,不设置这个在打包的时候会报错
  7. import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
  8. //引入element-plus的icons
  9. import * as ElementPlusIconsVue from '@element-plus/icons-vue'
  10. //引入路由
  11. import router from './router/index'
  12. //引入大仓库
  13. import pinia from './store/index'
  14. //引入全局路由守卫
  15. import '@/permisstion.ts'
  16. const app = createApp(App)
  17. //使用ElementPlus,并设置国际化
  18. app.use(ElementPlus, {
  19. locale: zhCn,
  20. })
  21. //使用路由
  22. app.use(router)
  23. //使用大仓库
  24. app.use(pinia)
  25. //全局注册icon图标
  26. for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  27. app.component(key, component)
  28. }
  29. //挂载到app上
  30. app.mount('#app')

src\App.vue

  1. <template>
  2. <div>
  3. <router-view></router-view>
  4. </div>
  5. </template>
  6. <script setup lang="ts">
  7. </script>
  8. <style scoped>
  9. </style>

src\api\index.ts

后端接口文件

  1. import request from "@/utils/request";
  2. //统一管理接口
  3. enum API{
  4. //登录后端接口
  5. LOGIN="/dengLu",
  6. //获取当前登录人的菜单
  7. GET_MY_MENU_LIST="/getMyMenuList",
  8. //获取当前登录人的权限
  9. GET_MY_AUTH="/getMyAuth",
  10. //获取所有用户
  11. GET_USER_LIST="/getUserList",
  12. //获取所有角色
  13. GET_ROLE_LIST="/getRoleList",
  14. //获取所有菜单
  15. GET_MENU_LIST="/getMenuList",
  16. //添加用户
  17. ADD_USER="/addUser",
  18. //获取当前登录人信息
  19. GET_USER_INFO="/getUserInfo",
  20. //为用户分配角色
  21. SET_USER_ROLE="/setUserRole",
  22. //查看用户对应的角色id
  23. GET_USER_ROLE_IDS="/getUserRoleIds",
  24. //添加角色
  25. ADD_ROLE="/addRole",
  26. //修改角色
  27. EDIT_ROLE="/editRole",
  28. //删除角色
  29. DELETE_ROLE="/deleteRole",
  30. //添加菜单
  31. ADD_MENU="/addMenu",
  32. //修改菜单
  33. EDIT_MENU="/editMenu",
  34. //删除菜单
  35. DELETE_MENU="/deleteMenu",
  36. //查看角色对应的权限
  37. GET_ROLE_AUTH="/getRoleAuth",
  38. //为角色分配权限
  39. SET_ROLE_AUTH="/setRoleAuth",
  40. }
  41. //登录接口方法
  42. export const reqLogin=(data:any)=> request.post<any,any>(API.LOGIN,data)
  43. //获取当前登录人的菜单
  44. export const getMyMenuList=()=> request.post<any>(API.GET_MY_MENU_LIST)
  45. //获取当前登录人的权限
  46. export const getMyAuth=()=> request.post<any>(API.GET_MY_AUTH)
  47. //获取所有用户
  48. export const getUserList=()=> request.post<any>(API.GET_USER_LIST)
  49. //获取所有角色
  50. export const getRoleList=()=> request.post<any>(API.GET_ROLE_LIST)
  51. //获取所有菜单
  52. export const getMenuList=()=> request.post<any>(API.GET_MENU_LIST)
  53. //添加用户
  54. export const addUser=(data:any)=> request.post<any>(API.ADD_USER,data)
  55. //获取当前登录人信息
  56. export const getUserInfo=()=> request.post<any>(API.GET_USER_INFO)
  57. //为用户分配角色
  58. export const setUserRole=(data:any)=> request.post<any>(API.SET_USER_ROLE,data)
  59. //查看用户对应的角色id
  60. export const getUserRoleIds=(data:any)=> request.post<any>(API.GET_USER_ROLE_IDS,data)
  61. //添加角色
  62. export const addRole=(data:any)=> request.post<any>(API.ADD_ROLE,data)
  63. //修改角色
  64. export const editRole=(data:any)=> request.post<any>(API.EDIT_ROLE,data)
  65. //删除角色
  66. export const deleteRole=(data:any)=> request.post<any>(API.DELETE_ROLE,data)
  67. //添加菜单
  68. export const addMenu=(data:any)=> request.post<any>(API.ADD_MENU,data)
  69. //修改菜单
  70. export const editMenu=(data:any)=> request.post<any>(API.EDIT_MENU,data)
  71. //删除菜单
  72. export const deleteMenu=(data:any)=> request.post<any>(API.DELETE_MENU,data)
  73. //查看角色对应的权限
  74. export const getRoleAuth=(data:any)=> request.post<any>(API.GET_ROLE_AUTH,data)
  75. //为角色分配权限
  76. export const setRoleAuth=(data:any)=> request.post<any>(API.SET_ROLE_AUTH,data)

src\router\index.ts

  1. //通过vue-router插件实现模版路由配置
  2. import { createRouter,createWebHashHistory } from 'vue-router';
  3. //引入路由数组
  4. import {luYou} from '@/router/routers'
  5. //创建路由器
  6. const router=createRouter({
  7. //路由模式
  8. history: createWebHashHistory(),
  9. //注意单词 别写错了
  10. //注意单词 别写错了
  11. routes: luYou
  12. })
  13. export default router;

src\router\routers.ts

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

  1. //单独暴露路由
  2. export const luYou=[
  3. {
  4. path:'/login',
  5. component:()=> import('@/views/login/index.vue'),
  6. name:'login',
  7. meta:{
  8. //隐藏不显示到菜单上 true:隐藏 ,false:显示
  9. hidden:true,
  10. //菜单的名称
  11. title:'login',
  12. //饿了么ui图标的名字 固定写法
  13. icon:'Plus',
  14. auth:''
  15. }
  16. },
  17. {
  18. path:'/per',
  19. component:()=> import('@/views/per/index.vue'),
  20. name:'per',
  21. meta:{
  22. hidden:true,
  23. title:'per',
  24. icon:'Plus',
  25. auth:''
  26. }
  27. },
  28. {
  29. path:'/404',
  30. component:()=> import('@/views/404/index.vue'),
  31. name:'404',
  32. meta:{
  33. hidden:true,
  34. title:'404',
  35. icon:'Plus',
  36. auth:''
  37. }
  38. },
  39. {
  40. //根页面
  41. path:'/',
  42. component:()=> import('@/views/menu/index.vue'),
  43. name:'menu',
  44. meta:{
  45. hidden:true,
  46. title:'login',
  47. icon:'Plus',
  48. auth:''
  49. }
  50. },
  51. {
  52. //匹配到不存在的路径就跳转404
  53. path:'/:pathMatch(.*)*',
  54. //重定向到404
  55. redirect: '/404',
  56. //任意路由
  57. name:'any',
  58. meta:{
  59. hidden:true,
  60. title:'any',
  61. icon:'Plus',
  62. auth:''
  63. }
  64. },
  65. {
  66. path:'/org',
  67. component:()=> import('@/views/menu/index.vue'),
  68. name:'org',
  69. meta:{
  70. hidden:true,
  71. title:'组织管理',
  72. icon:'Plus',
  73. auth:'org'
  74. },
  75. children:[
  76. {
  77. path:'/org/er',
  78. component:()=> import('@/views/org/index.vue'),
  79. name:'orgEr',
  80. meta:{
  81. hidden:true,
  82. title:'组织管理二级菜单',
  83. icon:'Plus',
  84. auth:'org:er'
  85. }
  86. }
  87. ]
  88. },
  89. {
  90. path:'/user',
  91. component:()=> import('@/views/menu/index.vue'),
  92. name:'user',
  93. meta:{
  94. hidden:true,
  95. title:'用户管理',
  96. icon:'Plus',
  97. auth:'user'
  98. },
  99. children:[
  100. {
  101. path:'/user/er',
  102. component:()=> import('@/views/user/index.vue'),
  103. name:'userEr',
  104. meta:{
  105. hidden:true,
  106. title:'用户管理二级菜单',
  107. icon:'Plus',
  108. auth:'user:er'
  109. }
  110. }
  111. ]
  112. },
  113. {
  114. path:'/role',
  115. component:()=> import('@/views/menu/index.vue'),
  116. name:'role',
  117. meta:{
  118. hidden:true,
  119. title:'角色管理',
  120. icon:'Plus',
  121. auth:'role'
  122. },
  123. children:[
  124. {
  125. path:'/role/er',
  126. component:()=> import('@/views/role/index.vue'),
  127. name:'roleEr',
  128. meta:{
  129. hidden:true,
  130. title:'角色管理二级菜单',
  131. icon:'Plus',
  132. auth:'role:er'
  133. }
  134. }
  135. ]
  136. },
  137. {
  138. path:'/auth',
  139. component:()=> import('@/views/menu/index.vue'),
  140. name:'auth',
  141. meta:{
  142. hidden:true,
  143. title:'权限管理',
  144. icon:'Plus',
  145. auth:'auth'
  146. },
  147. children:[
  148. {
  149. path:'/auth/er',
  150. component:()=> import('@/views/auth/index.vue'),
  151. name:'authEr',
  152. meta:{
  153. hidden:true,
  154. title:'权限管理二级菜单',
  155. icon:'Plus',
  156. auth:'auth:er'
  157. }
  158. }
  159. ]
  160. },
  161. {
  162. path:'/apply',
  163. component:()=> import('@/views/menu/index.vue'),
  164. name:'apply',
  165. meta:{
  166. hidden:true,
  167. title:'应用管理',
  168. icon:'Plus',
  169. auth:'apply'
  170. },
  171. children:[
  172. {
  173. path:'/apply/er',
  174. component:()=> import('@/views/apply/index.vue'),
  175. name:'applyEr',
  176. meta:{
  177. hidden:true,
  178. title:'应用管理二级菜单',
  179. icon:'Plus',
  180. auth:'apply:er'
  181. }
  182. }
  183. ]
  184. },
  185. ]

src\store\modules\login.ts

  1. //创建用户相关的小仓库
  2. import {defineStore} from 'pinia'
  3. //引入接口
  4. import { reqLogin,getUserInfo,getMyAuth } from '../../api/index';
  5. //创建登录小仓库
  6. let loginStore=defineStore('login',{
  7. //小仓库存储数据的地方
  8. state:()=>{
  9. return {
  10. //从本地拿到token
  11. token: localStorage.getItem('TOKEN'),
  12. //当前登录人姓名
  13. name:'',
  14. }
  15. },
  16. //异步| 逻辑处理的地方,在方法前面定义async,那么必须在拿到对应的方法前面加await 一起使用
  17. actions:{
  18. async userLogin(data: any){
  19. //拿到登录后的结果
  20. let res:any=await reqLogin(data);
  21. if(res){
  22. let code=res.code;
  23. let msg=res.msg;
  24. let data=res.data;
  25. if(code=='200'){
  26. //刷新token
  27. this.token=data;
  28. //把token放入本地
  29. localStorage.setItem('TOKEN',data);
  30. return 'ok';
  31. }else{
  32. //失败返回失败对象
  33. return Promise.reject(Error(msg))
  34. }
  35. }else{
  36. //失败返回失败对象
  37. return Promise.reject(Error('账号密码输入错误'))
  38. }
  39. },
  40. async userInfo(){
  41. //登录后获取用户信息 请求头自带token
  42. let res=await getUserInfo();
  43. this.name=res.data.name;
  44. },
  45. userLogout(){
  46. //退出登录 清空用户信息 和token
  47. this.token=''
  48. this.name=''
  49. localStorage.removeItem('TOKEN');
  50. },
  51. async useMyAuth(){
  52. //登录后获取用户信息 请求头自带token
  53. let res=await getMyAuth();
  54. return res.data;
  55. },
  56. },
  57. getters:{
  58. }
  59. })
  60. //对外暴露小仓库的方法
  61. export default loginStore;

src\store\modules\luyou.ts

  1. //创建用户相关的小仓库
  2. import {defineStore} from 'pinia'
  3. //引入路由数组
  4. import {luYou} from '@/router/routers'
  5. //引入接口
  6. import { getMyAuth } from '../../api/index';
  7. //创建小仓库
  8. let luYouStore=defineStore('luYou',{
  9. //小仓库存储数据的地方
  10. state:()=>{
  11. return {
  12. //路由
  13. ly:[]
  14. }
  15. },
  16. //异步| 逻辑处理的地方,在方法前面定义async,那么必须在拿到对应的方法前面加await 一起使用
  17. actions:{
  18. async useMyMenuList(){
  19. //获取当前登录人的权限集合
  20. let res=await getMyAuth();
  21. let auths=res.data;
  22. //赋值给新的数组
  23. let newLy = luYou.map(x => {
  24. if(auths.indexOf(x.meta.auth) !== -1){
  25. //如果路径匹配 那么设置为显示菜单
  26. x.meta.hidden=false
  27. }
  28. let children=x.children;
  29. if(children){
  30. children.map(y=>{
  31. if(auths.indexOf(y.meta.auth) !== -1){
  32. //如果路径匹配 那么设置为显示菜单
  33. y.meta.hidden=false
  34. }
  35. return y;
  36. })
  37. }
  38. return x;
  39. });
  40. //更新state的数据
  41. this.ly=newLy;
  42. }
  43. },
  44. getters:{
  45. }
  46. })
  47. //对外暴露小仓库的方法
  48. export default luYouStore;

src\store\index.ts

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

src\utils\request.ts

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

  1. import axios from "axios";
  2. //引入消息提示
  3. import {ElMessage} from 'element-plus';
  4. //引入登录小仓库
  5. import loginStore from "@/store/modules/login";
  6. //利用axios对象的create方法,去创建axios实例
  7. let request=axios.create({
  8. //基础路径 自动带上前缀
  9. baseURL: import.meta.env.VITE_APP_BASE_API,
  10. //超时时间,超过时间就获取不到后端的数据了
  11. timeout: 5000
  12. })
  13. //request实例添加请求拦截器
  14. request.interceptors.request.use((config)=>{
  15. let lgst=loginStore();
  16. if(lgst.token){
  17. //config配置对象,headers属性请求头,给后台携带公共参数
  18. //设置token 放到请求头上
  19. config.headers['Authorization']='Bearer '+lgst.token;
  20. }
  21. //console.log('请求拦截器:',config)
  22. return config;
  23. })
  24. //添加响应拦截器
  25. request.interceptors.response.use((response)=>{
  26. //成功回调
  27. //console.log('响应拦截器:',response)
  28. return response.data;
  29. },(error)=>{
  30. //http状态码
  31. //console.log('错误状态:',error.response)
  32. if(error.response==undefined){
  33. ElMessage({
  34. type:'error',
  35. message:'网络异常'
  36. })
  37. }else{
  38. ElMessage({
  39. type:'error',
  40. message:error.response.data.msg
  41. })
  42. }
  43. //返回一个失败的Promise对象
  44. return Promise.reject(error);
  45. })
  46. //对外暴露 才能被外部使用
  47. export default request;

src\views\404\index.vue

  1. <template>
  2. <div>
  3. 我是404
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style>
  11. </style>

src\views\apply\index.vue

  1. <template>
  2. 我是应用
  3. </template>
  4. <script>
  5. export default {
  6. }
  7. </script>
  8. <style>
  9. </style>

src\views\auth\index.vue

  1. <template>
  2. <div>
  3. <el-card class="box-card">
  4. <template #header>
  5. <div class="card-header">
  6. <span>权限管理</span>
  7. </div>
  8. </template>
  9. <el-button type="primary" icon="Plus" @click="add(0,'1')" v-if="authErAdd">添加一级菜单</el-button>
  10. <el-table
  11. :data="tableData"
  12. style="width: 100%; margin-bottom: 20px"
  13. row-key="id"
  14. border
  15. default-expand-all
  16. >
  17. <el-table-column prop="name" label="菜单名称/按钮名称" sortable width="200"/>
  18. <el-table-column prop="auth" label="权限名称" sortable width="180"/>
  19. <el-table-column prop="path" label="路径" sortable width="180"/>
  20. <el-table-column prop="createTime" label="创建时间" sortable width="180"/>
  21. <el-table-column prop="desc" label="备注" sortable width="180"/>
  22. <el-table-column
  23. prop="isType"
  24. label="类型"
  25. width="180"
  26. >
  27. <template #default="scope">
  28. <el-tag v-if="scope.row.isType=='1'">
  29. 菜单
  30. </el-tag>
  31. <el-tag v-if="scope.row.isType=='2'">
  32. 按钮
  33. </el-tag>
  34. </template>
  35. </el-table-column>
  36. <el-table-column prop="caozuo" label="操作">
  37. <!--具名插槽-->
  38. <template #default="caozuo">
  39. <!--一级菜单 可以添加二级菜单-->
  40. <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>
  41. <!--二级菜单 可以添加按钮-->
  42. <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>
  43. <el-button type="primary" v-if="authErEdit" icon="edit" @click="edit(caozuo.row)">修改</el-button>
  44. <el-button type="primary" v-if="authErDel" icon="delete" @click="del(caozuo.row.id)">删除</el-button>
  45. </template>
  46. </el-table-column>
  47. </el-table>
  48. </el-card>
  49. <!--点击新增弹出对话框-->
  50. <el-dialog
  51. v-model="dialogVisible"
  52. title="添加"
  53. width="30%"
  54. :before-close="handleClose"
  55. >
  56. <el-form :model="addBean">
  57. <el-form-item label="父级id" >
  58. <el-input label-width="80%" v-model="addBean.parentId" disabled/>
  59. </el-form-item>
  60. <el-form-item label="名称" >
  61. <el-input label-width="80%" v-model="addBean.name"/>
  62. </el-form-item>
  63. <el-form-item label="权限名称" >
  64. <el-input label-width="80%" v-model="addBean.auth"/>
  65. </el-form-item>
  66. <el-form-item label="路径" >
  67. <el-input label-width="80%" v-model="addBean.path"/>
  68. </el-form-item>
  69. <el-form-item label="排序" >
  70. <el-input-number :min="1" :max="1000" v-model="addBean.paiXu" />
  71. </el-form-item>
  72. <el-form-item label="类型" >
  73. <el-select v-model="addBean.isType" disabled>
  74. <el-option
  75. v-for="item in options"
  76. :key="item.value"
  77. :label="item.label"
  78. :value="item.value"
  79. />
  80. </el-select>
  81. </el-form-item>
  82. </el-form>
  83. <!--底部插槽-->
  84. <template #footer>
  85. <span class="dialog-footer">
  86. <el-button @click="dialogVisible = false">取消</el-button>
  87. <el-button type="primary" @click="insert">确定</el-button>
  88. </span>
  89. </template>
  90. </el-dialog>
  91. <!--点击修改弹出对话框-->
  92. <el-dialog
  93. v-model="editDialog"
  94. title="修改"
  95. width="30%"
  96. :before-close="handleClose"
  97. >
  98. <el-form :model="updateBean">
  99. <el-form-item label="菜单id">
  100. <el-input label-width="80%" v-model="updateBean.id" disabled/>
  101. </el-form-item>
  102. <el-form-item label="名称" >
  103. <el-input label-width="80%" v-model="updateBean.name"/>
  104. </el-form-item>
  105. <el-form-item label="权限名称" >
  106. <el-input label-width="80%" v-model="updateBean.auth"/>
  107. </el-form-item>
  108. <el-form-item label="路径" >
  109. <el-input label-width="80%" v-model="updateBean.path"/>
  110. </el-form-item>
  111. <el-form-item label="排序" >
  112. <el-input-number :min="1" :max="1000" v-model="updateBean.paiXu" />
  113. </el-form-item>
  114. <el-form-item label="类型" >
  115. <el-select v-model="updateBean.isType" disabled>
  116. <el-option
  117. v-for="item in options"
  118. :key="item.value"
  119. :label="item.label"
  120. :value="item.value"
  121. />
  122. </el-select>
  123. </el-form-item>
  124. </el-form>
  125. <!--底部插槽-->
  126. <template #footer>
  127. <span class="dialog-footer">
  128. <el-button @click="editDialog = false">取消</el-button>
  129. <el-button type="primary" @click="update">确定</el-button>
  130. </span>
  131. </template>
  132. </el-dialog>
  133. </div>
  134. </template>
  135. <script setup lang="ts">
  136. import {ref,onMounted,reactive} from 'vue'
  137. //引入后端接口
  138. import {getMenuList,addMenu,editMenu,deleteMenu,getMyAuth} from '@/api/index'
  139. //引入提示
  140. import { ElMessage,ElMessageBox } from 'element-plus'
  141. import { fa } from 'element-plus/es/locale/index.mjs';
  142. //表格数据
  143. let tableData=ref([]);
  144. //获取分页数据
  145. let getData=async ()=>{
  146. //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  147. let res=await getMenuList();
  148. tableData.value=res.data;
  149. }
  150. //加载时调用
  151. onMounted(()=>{
  152. //调用查询
  153. getData();
  154. //获取按钮权限
  155. getAuthData();
  156. })
  157. //下拉框内容
  158. const options = [
  159. {
  160. value: '1',
  161. label: '菜单',
  162. },
  163. {
  164. value: '2',
  165. label: '按钮',
  166. }
  167. ]
  168. //----------------------添加-----------------------
  169. //修改对象
  170. let addBean=reactive({
  171. parentId:null,
  172. name:'',
  173. auth:'',
  174. isType:'',
  175. paiXu:'',
  176. path:''
  177. })
  178. //默认不显示对话框
  179. let dialogVisible=ref(false)
  180. //添加显示对话框
  181. let add=(parentId,isType)=>{
  182. //显示对话框
  183. dialogVisible.value=true;
  184. //把输入框显示的内容清空
  185. addBean.parentId=parentId;
  186. addBean.name='';
  187. addBean.auth='';
  188. addBean.isType=isType;
  189. addBean.paiXu='';
  190. addBean.path='';
  191. }
  192. //添加权限 调用后端接口
  193. let insert= async ()=>{
  194. //调用后端添加品牌方法
  195. let res=await addMenu(addBean);
  196. console.log(res);
  197. if(res.code=='200'){
  198. //执行成功后关闭对话框
  199. dialogVisible.value=false;
  200. ElMessage.success('添加成功')
  201. //调用查询刷新界面
  202. getData();
  203. }else{
  204. //弹窗错误消息
  205. ElMessage.error(res.msg);
  206. }
  207. }
  208. //----------------------删除-----------------------
  209. let del=async (id)=>{
  210. await ElMessageBox.confirm(
  211. '确定删除吗?',
  212. '删除',
  213. {
  214. confirmButtonText: 'OK',
  215. cancelButtonText: 'Cancel',
  216. type: 'warning',
  217. }
  218. )
  219. .then(() => {
  220. deleteMenu({
  221. id:id
  222. }).then((res)=>{
  223. if(res.code=='200'){
  224. ElMessage.success('删除成功')
  225. //调用查询刷新界面
  226. getData();
  227. }else{
  228. //弹窗错误消息
  229. ElMessage.error(res.msg);
  230. }
  231. })
  232. })
  233. .catch(() => {
  234. })
  235. }
  236. //----------------------修改-----------------------
  237. //修改对象
  238. let updateBean=reactive({
  239. id:null,
  240. name:'',
  241. auth:'',
  242. isType:'',
  243. paiXu:'',
  244. path:''
  245. })
  246. //默认不显示对话框
  247. let editDialog=ref(false)
  248. //显示对话框
  249. let edit=(row)=>{
  250. //显示对话框
  251. editDialog.value=true;
  252. //把输入框显示内容赋值
  253. updateBean.id=row.id;
  254. updateBean.name=row.name;
  255. updateBean.auth=row.auth;
  256. updateBean.isType=row.isType;
  257. updateBean.paiXu=row.paiXu;
  258. updateBean.path=row.path;
  259. }
  260. //修改权限 调用后端接口
  261. let update= async ()=>{
  262. //调用后端添加品牌方法
  263. let res=await editMenu(updateBean);
  264. if(res.code=='200'){
  265. //执行成功后关闭对话框
  266. editDialog.value=false;
  267. ElMessage.success('修改成功')
  268. //调用查询刷新界面
  269. getData();
  270. }else{
  271. //弹窗错误消息
  272. ElMessage.error(res.msg);
  273. }
  274. }
  275. //--------------------------按钮显示隐藏----------------------------
  276. //按钮权限
  277. //添加一级菜单按钮默认不显示
  278. let authErAdd=ref(false);
  279. //添加二级菜单按钮默认不显示
  280. let authErAddSub=ref(false);
  281. //修改按钮默认不显示
  282. let authErEdit=ref(false);
  283. //删除按钮默认不显示
  284. let authErDel=ref(false);
  285. //添加子级按钮 默认不显示
  286. let authErBtn=ref(false);
  287. let getAuthData= async()=>{
  288. //获取菜单权限
  289. let res=await getMyAuth();
  290. //如果存在对应的权限 才显示按钮
  291. if(res.data.indexOf('auth:er:add')!==-1){
  292. authErAdd.value=true;
  293. }
  294. if(res.data.indexOf('auth:er:del')!==-1){
  295. authErDel.value=true;
  296. }
  297. if(res.data.indexOf('auth:er:edit')!==-1){
  298. authErEdit.value=true;
  299. }
  300. if(res.data.indexOf('auth:er:add:sub')!==-1){
  301. authErAddSub.value=true;
  302. }
  303. if(res.data.indexOf('auth:er:btn')!==-1){
  304. authErBtn.value=true;
  305. }
  306. }
  307. </script>
  308. <style scoped>
  309. </style>
  310. <style>
  311. </style>

src\views\home\index.vue

  1. <template>
  2. <div>
  3. 我是首页
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style>
  11. </style>

src\views\login\index.vue

  1. <template>
  2. <el-card class="box-card">
  3. <template #header>
  4. <div class="card-header">
  5. <span>权限管理系统</span>
  6. </div>
  7. </template>
  8. <el-form
  9. ref="ruleFormRef"
  10. :model="ruleForm"
  11. :rules="rules"
  12. label-width="120px"
  13. >
  14. <el-form-item label="账号" prop="account">
  15. <el-input v-model="ruleForm.account" />
  16. </el-form-item>
  17. <el-form-item label="密码" prop="password">
  18. <el-input
  19. v-model="ruleForm.password"
  20. type="password"
  21. placeholder="请输入密码"
  22. show-password
  23. />
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button type="primary" @click="login()">
  27. 登录
  28. </el-button>
  29. </el-form-item>
  30. </el-form>
  31. </el-card>
  32. </template>
  33. <script lang="ts" setup>
  34. import { reactive, ref } from 'vue'
  35. import type { FormInstance, FormRules } from 'element-plus'
  36. import { ElMessage } from 'element-plus'
  37. //引入路由
  38. import {useRouter} from 'vue-router'
  39. //引入登录小仓库
  40. import loginStore from '../../store/modules/login.ts';
  41. //定义接口
  42. interface RuleForm {
  43. account: string
  44. password: string
  45. }
  46. //获取路由器
  47. let router=useRouter();
  48. const ruleFormRef = ref<RuleForm>()
  49. //使用接口 并且 为每一个文本框赋 默认值
  50. const ruleForm = reactive<RuleForm>({
  51. account: 'user',
  52. password: '123456'
  53. })
  54. //校验文本框的内容
  55. const rules = reactive<FormRules<RuleForm>>({
  56. account: [
  57. { required: true, message: '账号不能为空', trigger: 'blur' },
  58. { min: 3, max: 10, message: '长度在3到10之间', trigger: 'blur' },
  59. ],
  60. password: [
  61. {
  62. required: true,
  63. message: '密码不能为空',
  64. trigger: 'change',
  65. },
  66. ]
  67. })
  68. //登录方法
  69. let login= async ()=>{
  70. //阻塞form 表单的校验 必须完成之后才能往下走 因为validate是一个async方法
  71. await ruleFormRef.value.validate();
  72. try {
  73. //阻塞拿到结果才往下走
  74. await loginStore().userLogin(ruleForm);
  75. //弹框
  76. ElMessage({
  77. showClose: true,
  78. message: '登录成功',
  79. type: 'success',
  80. })
  81. //跳转根路径
  82. router.push("/")
  83. } catch (error) {
  84. ElMessage({
  85. showClose: true,
  86. message: (error as Error).message,
  87. type: 'error',
  88. })
  89. }
  90. }
  91. </script>

src\views\menu\index.vue

  1. <template>
  2. <div>
  3. <el-row>
  4. <el-col :span="3">
  5. <!--显示左侧菜单-->
  6. <!--组件传参 把菜单数组传过去-->
  7. <Sub :menuList="lyst.ly"></Sub>
  8. </el-col>
  9. <el-col :span="21">
  10. <div>
  11. <el-row>
  12. <el-col :span="21"></el-col>
  13. <el-col :span="3">
  14. <!--显示用户信息-->
  15. 当前登录人:{{ lgStore.name }}
  16. <el-button type="primary" @click="logout">退出登录</el-button>
  17. </el-col>
  18. </el-row>
  19. </div>
  20. <div>
  21. <!--显示菜单点击后的内容-->
  22. <router-view></router-view>
  23. </div>
  24. </el-col>
  25. </el-row>
  26. </div>
  27. </template>
  28. <script lang="ts" setup>
  29. import Sub from '@/views/sub/index.vue'
  30. //引入小仓库
  31. import luYouStore from '@/store/modules/luyou';
  32. import { onMounted } from 'vue';
  33. //引入路由
  34. import {useRouter} from 'vue-router'
  35. //引入登录小仓库
  36. import loginStore from '../../store/modules/login.ts';
  37. //获取路由器
  38. let router=useRouter();
  39. //使用小仓库
  40. let lyst=luYouStore()
  41. //拿到登录仓库对象
  42. let lgStore=loginStore();
  43. //生命周期挂载
  44. onMounted(()=>{
  45. //初始化菜单
  46. lyst.useMyMenuList();
  47. //获取用户信息
  48. lgStore.userInfo();
  49. })
  50. //退出登录
  51. let logout=()=>{
  52. loginStore().userLogout()
  53. //退出后跳转到登录界面
  54. router.push('/login')
  55. }
  56. </script>
  57. <style scoped>
  58. </style>

src\views\org\index.vue

  1. <template>
  2. 我是组织架构
  3. </template>
  4. <script>
  5. export default {
  6. }
  7. </script>
  8. <style>
  9. </style>

src\views\per\index.vue

  1. <template>
  2. <h1>
  3. 无权限
  4. </h1>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style>
  11. </style>

src\views\role\index.vue

  1. <template>
  2. <div>
  3. <el-card class="box-card">
  4. <template #header>
  5. <div class="card-header">
  6. <span>角色管理</span>
  7. </div>
  8. </template>
  9. <el-button type="primary" icon="Plus" @click="add" v-if="roleErAdd">添加</el-button>
  10. <el-table :data="tableData" style="width: 100%" >
  11. <el-table-column label="序号" width="180" type="index"/>
  12. <el-table-column prop="roleCode" label="角色编码" width="180" />
  13. <el-table-column prop="roleName" label="角色名称" width="180" />
  14. <el-table-column prop="caozuo" label="操作">
  15. <!--具名插槽-->
  16. <template #default="caozuo">
  17. <el-button type="primary" icon="edit" @click="edit(caozuo.row)" v-if="roleErEdit">修改</el-button>
  18. <el-button type="primary" icon="delete" @click="del(caozuo.row.id)" v-if="roleErDel">删除</el-button>
  19. <el-button type="primary" icon="Setting" @click="dakai(caozuo.row.id)" v-if="roleErFp">分配权限</el-button>
  20. </template>
  21. </el-table-column>
  22. </el-table>
  23. </el-card>
  24. <!--点击新增按钮弹出对话框-->
  25. <el-dialog
  26. v-model="dialogVisible"
  27. title="添加"
  28. width="30%"
  29. :before-close="handleClose"
  30. >
  31. <el-form :model="addBean">
  32. <el-form-item label="角色编码" >
  33. <el-input label-width="80%" v-model="addBean.roleCode"/>
  34. </el-form-item>
  35. <el-form-item label="角色名称" >
  36. <el-input label-width="80%" v-model="addBean.roleName"/>
  37. </el-form-item>
  38. </el-form>
  39. <!--底部插槽-->
  40. <template #footer>
  41. <span class="dialog-footer">
  42. <el-button @click="dialogVisible = false">取消</el-button>
  43. <el-button type="primary" @click="insert">确定</el-button>
  44. </span>
  45. </template>
  46. </el-dialog>
  47. <!--点击修改按钮弹出对话框-->
  48. <el-dialog
  49. v-model="editDialog"
  50. title="修改"
  51. width="30%"
  52. :before-close="handleClose"
  53. >
  54. <el-form :model="updateBean">
  55. <el-form-item label="角色id" >
  56. <el-input label-width="80%" v-model="updateBean.id" disabled/>
  57. </el-form-item>
  58. <el-form-item label="角色编码" >
  59. <el-input label-width="80%" v-model="updateBean.roleCode"/>
  60. </el-form-item>
  61. <el-form-item label="角色名称" >
  62. <el-input label-width="80%" v-model="updateBean.roleName"/>
  63. </el-form-item>
  64. </el-form>
  65. <!--底部插槽-->
  66. <template #footer>
  67. <span class="dialog-footer">
  68. <el-button @click="editDialog = false">取消</el-button>
  69. <el-button type="primary" @click="update">确定</el-button>
  70. </span>
  71. </template>
  72. </el-dialog>
  73. <!--点击分配权限按钮弹出对话框-->
  74. <el-dialog
  75. v-model="fpDialog"
  76. title="分配权限"
  77. width="30%"
  78. :before-close="handleClose"
  79. >
  80. <el-tree
  81. ref="tree"
  82. :data="data"
  83. show-checkbox
  84. node-key="id"
  85. default-expand-all
  86. :default-checked-keys="checkedArr"
  87. :props="defaultProps"
  88. >
  89. </el-tree>
  90. <el-button @click="fpDialog = false">取消</el-button>
  91. <el-button type="success" @click="fpAuth">确定</el-button>
  92. </el-dialog>
  93. </div>
  94. </template>
  95. <script setup lang="ts">
  96. import {ref,onMounted,reactive} from 'vue'
  97. //引入后端接口
  98. import {getRoleList,addRole,deleteRole,editRole,getMenuList,getRoleAuth,setRoleAuth,getMyAuth} from '@/api/index'
  99. import { ElMessage, ElMessageBox } from 'element-plus'
  100. //----------------------查询-----------------------
  101. //表格数据
  102. let tableData=ref([]);
  103. //获取分页数据
  104. let getData=async ()=>{
  105. //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  106. let res=await getRoleList();
  107. tableData.value=res.data;
  108. }
  109. //加载时调用
  110. onMounted(()=>{
  111. //调用查询
  112. getData();
  113. //获取按钮权限
  114. getAuthData();
  115. })
  116. //----------------------添加-----------------------
  117. //添加对象
  118. let addBean=reactive({
  119. roleCode:'',
  120. roleName:''
  121. })
  122. //默认不显示对话框
  123. let dialogVisible=ref(false)
  124. //添加显示对话框
  125. let add=()=>{
  126. //显示对话框
  127. dialogVisible.value=true;
  128. //把输入框显示的内容清空
  129. addBean.roleCode='';
  130. addBean.roleName='';
  131. }
  132. //添加角色 调用后端接口
  133. let insert= async ()=>{
  134. //调用后端添加品牌方法
  135. let res=await addRole(addBean);
  136. console.log(res);
  137. if(res.code=='200'){
  138. //执行成功后关闭对话框
  139. dialogVisible.value=false;
  140. ElMessage.success('添加成功')
  141. //调用查询刷新界面
  142. getData();
  143. }else{
  144. //弹窗错误消息
  145. ElMessage.error(res.msg);
  146. }
  147. }
  148. //----------------------修改-----------------------
  149. //修改对象
  150. let updateBean=reactive({
  151. id:null,
  152. roleCode:'',
  153. roleName:''
  154. })
  155. //默认不显示对话框
  156. let editDialog=ref(false)
  157. //显示对话框
  158. let edit=(row)=>{
  159. //显示对话框
  160. editDialog.value=true;
  161. //把输入框显示内容赋值
  162. updateBean.id=row.id;
  163. updateBean.roleCode=row.roleCode;
  164. updateBean.roleName=row.roleName;
  165. }
  166. //修改角色 调用后端接口
  167. let update= async ()=>{
  168. //调用后端添加品牌方法
  169. let res=await editRole(updateBean);
  170. console.log(res);
  171. if(res.code=='200'){
  172. //执行成功后关闭对话框
  173. editDialog.value=false;
  174. ElMessage.success('修改成功')
  175. //调用查询刷新界面
  176. getData();
  177. }else{
  178. //弹窗错误消息
  179. ElMessage.error(res.msg);
  180. }
  181. }
  182. //----------------------删除-----------------------
  183. let del=async (id)=>{
  184. await ElMessageBox.confirm(
  185. '确定删除吗?',
  186. '删除',
  187. {
  188. confirmButtonText: 'OK',
  189. cancelButtonText: 'Cancel',
  190. type: 'warning',
  191. }
  192. )
  193. .then(() => {
  194. deleteRole({
  195. id:id
  196. }).then((res)=>{
  197. if(res.code=='200'){
  198. ElMessage.success('删除成功')
  199. //调用查询刷新界面
  200. getData();
  201. }else{
  202. //弹窗错误消息
  203. ElMessage.error(res.msg);
  204. }
  205. })
  206. })
  207. .catch(() => {
  208. })
  209. }
  210. //----------------------分配权限-----------------------
  211. const fpDialog = ref(false)
  212. //定义后端的显示字段
  213. const defaultProps = {
  214. children: 'children',
  215. label: 'name',
  216. }
  217. //显示的数组
  218. let data=ref([])
  219. //选中的数组
  220. let checkedArr=ref([])
  221. //选中的树的id数组
  222. let tree=ref([])
  223. //分配权限 那一列的 角色id
  224. let authRoleId=ref()
  225. //打开树
  226. let dakai=async(id)=>{
  227. //先把选择的清空掉
  228. checkedArr.value=null;
  229. //调用后端权限方法
  230. let res=await getMenuList();
  231. if(res.code=='200'){
  232. data.value=res.data;
  233. //赋值给角色id
  234. authRoleId.value=id;
  235. //获取当前角色对应的权限
  236. getRoleAuth({
  237. roleId:id
  238. }).then((auths)=>{
  239. //选中的数组赋值
  240. checkedArr.value= getCheck(data.value,[],auths.data);
  241. fpDialog.value=true;
  242. });
  243. }
  244. }
  245. //选中的数组遍历
  246. let getCheck=(item:any,arr:any,auths:any)=>{
  247. item.forEach(x => {
  248. if(x.isType=='2' && auths.indexOf(x.auth)!==-1){
  249. console.log(x.auth,'------',auths)
  250. //如果是按钮 并且选中了 那么设置树展示
  251. arr.push(x.id)
  252. }
  253. if(x.children && x.children.length>0){
  254. //如果有子集那么递归子集
  255. getCheck(x.children,arr,auths)
  256. }
  257. });
  258. return arr;
  259. }
  260. //分配权限方法
  261. let fpAuth= async()=>{
  262. //拿到当前选中的节点的id数组
  263. let menuIds=tree.value.getCheckedKeys();
  264. //为角色分配权限
  265. let res=await setRoleAuth({
  266. roleId:authRoleId.value,
  267. menuIds:menuIds
  268. });
  269. if(res.code=='200'){
  270. authRoleId.value='';
  271. ElMessage.success('分配成功')
  272. //调用查询刷新界面
  273. getData();
  274. }else{
  275. //弹窗错误消息
  276. ElMessage.error(res.msg);
  277. }
  278. //关闭窗口
  279. fpDialog.value = false;
  280. }
  281. //--------------------------按钮显示隐藏----------------------------
  282. //按钮权限
  283. //添加按钮默认不显示
  284. let roleErAdd=ref(false);
  285. //分配权限按钮默认不显示
  286. let roleErFp=ref(false);
  287. //修改按钮默认不显示
  288. let roleErEdit=ref(false);
  289. //删除按钮默认不显示
  290. let roleErDel=ref(false);
  291. let getAuthData= async()=>{
  292. //获取菜单权限
  293. let res=await getMyAuth();
  294. //如果存在对应的权限 才显示按钮
  295. if(res.data.indexOf('role:er:add')!==-1){
  296. roleErAdd.value=true;
  297. }
  298. if(res.data.indexOf('role:er:del')!==-1){
  299. roleErDel.value=true;
  300. }
  301. if(res.data.indexOf('role:er:edit')!==-1){
  302. roleErEdit.value=true;
  303. }
  304. if(res.data.indexOf('role:er:fp')!==-1){
  305. roleErFp.value=true;
  306. }
  307. }
  308. </script>
  309. <style scoped>
  310. </style>
  311. <style>
  312. </style>

src\views\sub\index.vue

  1. <template>
  2. <!--菜单-->
  3. <el-menu v-for="x in menuList" :key="x.path">
  4. <!--根据权限 是否展示一级菜单-->
  5. <el-sub-menu :index="x.path" v-if="!x.meta.hidden">
  6. <!--插槽占位 显示一级菜单-->
  7. <template #title>
  8. <!--显示图标-->
  9. <el-icon>
  10. <component :is="x.meta.icon"></component>
  11. </el-icon>
  12. <span>{{x.meta.title}}</span>
  13. </template>
  14. <!--显示二级菜单-->
  15. <template v-for="y in x.children" :key="y.path" >
  16. <!--根据权限 是否展示二级菜单-->
  17. <el-menu-item :index="y.path" @click="goRouter" v-if="!y.meta.hidden">
  18. <!--显示图标-->
  19. <el-icon>
  20. <component :is="y.meta.icon"></component>
  21. </el-icon>
  22. <!--显示标题-->
  23. {{y.meta.title}}
  24. </el-menu-item>
  25. </template>
  26. </el-sub-menu>
  27. </el-menu>
  28. </template>
  29. <script setup lang="ts">
  30. //引入路由器
  31. import {useRouter} from 'vue-router'
  32. //接收父组件传过来的菜单数组
  33. defineProps(['menuList'])
  34. //获取路由器对象
  35. let router=useRouter();
  36. //点击菜单触发的方法
  37. let goRouter=(vc:any)=>{
  38. //通过路由器跳转界面
  39. router.push(vc.index)
  40. }
  41. </script>
  42. <style>
  43. </style>

src\views\user\index.vue

  1. <template>
  2. <div>
  3. <el-card class="box-card">
  4. <template #header>
  5. <div class="card-header">
  6. <span>用户管理</span>
  7. </div>
  8. </template>
  9. <!--有权限才显示-->
  10. <el-button type="primary" icon="Plus" @click="add" v-if="userErAdd">添加</el-button>
  11. <el-table :data="tableData" style="width: 100%" >
  12. <el-table-column label="序号" width="180" type="index"/>
  13. <el-table-column prop="account" label="账号" width="180" />
  14. <el-table-column prop="name" label="姓名" width="180" />
  15. <el-table-column prop="createTime" label="创建时间" width="200" />
  16. <el-table-column prop="caozuo" label="操作">
  17. <!--具名插槽-->
  18. <template #default="caozuo">
  19. <!--把表格的主键传递过去-->
  20. <el-button type="primary" icon="Setting" @click="fenPeiRole(caozuo.row.id)" v-if="userErFp">分配角色</el-button>
  21. </template>
  22. </el-table-column>
  23. </el-table>
  24. </el-card>
  25. <!--点击新增按钮弹出对话框-->
  26. <el-dialog
  27. v-model="dialogVisible"
  28. title="添加"
  29. width="30%"
  30. :before-close="handleClose"
  31. >
  32. <el-form :model="addBean">
  33. <el-form-item label="账号" >
  34. <el-input label-width="80%" v-model="addBean.account"/>
  35. </el-form-item>
  36. <el-form-item label="姓名" >
  37. <el-input label-width="80%" v-model="addBean.name"/>
  38. </el-form-item>
  39. <el-form-item label="密码" >
  40. <el-input label-width="80%" v-model="addBean.password"/>
  41. </el-form-item>
  42. </el-form>
  43. <!--底部插槽-->
  44. <template #footer>
  45. <span class="dialog-footer">
  46. <el-button @click="dialogVisible = false">取消</el-button>
  47. <el-button type="primary" @click="insert">确定</el-button>
  48. </span>
  49. </template>
  50. </el-dialog>
  51. <!--弹出分配角色对话框-->
  52. <el-dialog
  53. v-model="dialogRole"
  54. title="分配角色"
  55. width="30%"
  56. :before-close="handleClose"
  57. >
  58. <el-form :model="setBean">
  59. <el-form-item label="用户id">
  60. <el-input label-width="80%" v-model="setBean.userId" disabled/>
  61. </el-form-item>
  62. <el-form-item label="角色" >
  63. <el-select
  64. v-model="roleIds"
  65. multiple
  66. clearable
  67. collapse-tags
  68. placeholder=""
  69. popper-class="custom-header"
  70. :max-collapse-tags="1"
  71. >
  72. <template #header>
  73. <el-checkbox
  74. v-model="checkAll"
  75. :indeterminate="indeterminate"
  76. @change="handleCheckAll"
  77. >
  78. All
  79. </el-checkbox>
  80. </template>
  81. <el-option
  82. v-for="item in cities"
  83. :key="item.id"
  84. :label="item.roleName"
  85. :value="item.id"
  86. />
  87. </el-select>
  88. </el-form-item>
  89. </el-form>
  90. <!--底部插槽-->
  91. <template #footer>
  92. <span class="dialog-footer">
  93. <el-button @click="dialogRole = false">取消</el-button>
  94. <el-button type="primary" @click="updateRole">确定</el-button>
  95. </span>
  96. </template>
  97. </el-dialog>
  98. </div>
  99. </template>
  100. <script setup lang="ts">
  101. import {ref,onMounted,reactive,watch} from 'vue'
  102. //引入后端接口
  103. import {getUserList,addUser,getRoleList,setUserRole,getUserRoleIds,getMyAuth} from '@/api/index'
  104. import type { CheckboxValueType } from 'element-plus'
  105. //引入提示
  106. import { ElMessage } from 'element-plus'
  107. //表格数据
  108. let tableData=ref([]);
  109. //获取分页数据
  110. let getData=async ()=>{
  111. //必须异步阻塞去获取 否则拿到的就是一个Promise对象
  112. let res=await getUserList();
  113. tableData.value=res.data;
  114. }
  115. //--------------------------按钮显示隐藏----------------------------
  116. //添加按钮默认不显示
  117. let userErFp=ref(false);
  118. //分配角色按钮默认不显示
  119. let userErAdd=ref(false);
  120. let getAuthData= async()=>{
  121. //获取菜单权限
  122. let res=await getMyAuth();
  123. //如果存在对应的权限 才显示按钮
  124. if(res.data.indexOf('user:er:add')!==-1){
  125. userErAdd.value=true;
  126. }
  127. if(res.data.indexOf('user:er:fp')!==-1){
  128. userErFp.value=true;
  129. }
  130. }
  131. //加载时调用
  132. onMounted(()=>{
  133. //调用查询
  134. getData();
  135. //调用权限
  136. getAuthData();
  137. })
  138. //添加对象
  139. let addBean=reactive({
  140. name:'',
  141. account:'',
  142. password:''
  143. })
  144. //默认不显示对话框
  145. let dialogVisible=ref(false)
  146. //添加显示对话框
  147. let add=()=>{
  148. //显示对话框
  149. dialogVisible.value=true;
  150. //把输入框显示的内容清空
  151. addBean.name='';
  152. addBean.account='';
  153. addBean.password='';
  154. }
  155. //添加用户 调用后端接口
  156. let insert= async ()=>{
  157. //调用后端添加品牌方法
  158. let res=await addUser(addBean);
  159. console.log(res);
  160. if(res.code=='200'){
  161. //执行成功后关闭对话框
  162. dialogVisible.value=false;
  163. ElMessage.success('添加成功')
  164. //调用查询刷新界面
  165. getData();
  166. }else{
  167. //弹窗错误消息
  168. ElMessage.error(res.msg);
  169. }
  170. }
  171. //角色信息
  172. const cities = ref([])
  173. //全选
  174. const checkAll = ref(false)
  175. //监听值的变化
  176. const indeterminate = ref(false)
  177. //角色id 集合
  178. const roleIds = ref<CheckboxValueType[]>([])
  179. //分配角色对象
  180. let setBean=reactive({
  181. userId:'',
  182. roleIds:[]
  183. })
  184. //默认不显示分配角色对话框
  185. let dialogRole=ref(false)
  186. //显示分配角色对话框
  187. let fenPeiRole= async(id)=>{
  188. //显示对话框
  189. dialogRole.value=true;
  190. //获取后台角色列表接口
  191. let res=await getRoleList();
  192. cities.value=res.data;
  193. //赋值用户id
  194. setBean.userId=id;
  195. //获取已经分配过的角色id
  196. let response=await getUserRoleIds({
  197. userId:id
  198. });
  199. //把已分配过的角色id数组,给roleIds赋值 使得下拉框默认选中
  200. roleIds.value=response.data;
  201. }
  202. // //添加用户 调用后端接口
  203. let updateRole= async ()=>{
  204. //把角色id集合赋值 给后台对象
  205. setBean.roleIds=roleIds.value;
  206. //调用后端分配角色方法
  207. let res=await setUserRole(setBean);
  208. console.log(res);
  209. if(res.code=='200'){
  210. //执行成功后关闭对话框
  211. dialogRole.value=false;
  212. ElMessage.success('分配成功')
  213. //调用查询刷新界面
  214. getData();
  215. }else{
  216. //弹窗错误消息
  217. ElMessage.error(res.msg);
  218. }
  219. }
  220. //监听下拉框发生的变化
  221. watch(roleIds, (val) => {
  222. if (val.length === 0) {
  223. checkAll.value = false
  224. indeterminate.value = false
  225. } else if (val.length === cities.value.length) {
  226. checkAll.value = true
  227. indeterminate.value = false
  228. } else {
  229. indeterminate.value = true
  230. }
  231. })
  232. //点击All 方法
  233. const handleCheckAll = (val: CheckboxValueType) => {
  234. indeterminate.value = false
  235. if (val) {
  236. roleIds.value = cities.value.map((_) => _.id)
  237. } else {
  238. roleIds.value = []
  239. }
  240. }
  241. </script>
  242. <style lang="scss">
  243. .custom-header {
  244. .el-checkbox {
  245. display: flex;
  246. height: unset;
  247. }
  248. }
  249. </style>

启动项目

npm run dev

http://localhost:5173/#/login

 

用户关联角色

角色分配权限

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

 

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

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

 

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

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

访问错误的路径会显示404

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

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

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

用户名:zhangsan

密码:123456

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/53940
推荐阅读
相关标签
  

闽ICP备14008679号