记录一下上课学到的东西,主要内容来自projects/M03_基于RBAC的通用后台管理系统设计 · 北京大学-张齐勋/敏捷软件开发 - 码云 - 开源中国 (gitee.com)
但是因为老师的代码没有每个都注释,所以加上了一些小白的备注和自己的代码。
1.创建Vue项目
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
❯◉ CSS Pre-processors
◯ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
2.目录结构
后台管理系统项目中,包含了如下几个目录:
- components:包含了项目中所有的公共组件,例如表格、树形菜单、图表等组件。
- api: 包含了项目中所有的后端接口相关的文件,包括接口的封装和请求方式的定义等。
- router:包含了项目中的前端路由配置文件,用于定义前端路由规则。
- store:包含了项目中的 Vuex 状态管理模块,用于管理应用程序的状态和数据流。
- views:包含了项目中所有的业务组件,例如系统管理、任务调度等组件,每个组件对应一个页面。
- utils:包含了项目中的工具类文件,例如日期处理、字符串处理、请求(axios)封装等工具类文件。
- layout:用于存放系统的基本布局模板。该目录下的文件通常包括顶部导航栏、左侧菜
- 单栏、底部版权信息等。
3.用户登录
a.安装依赖包
npm i element-ui -S
npm i axios -S
b.集成element UI
在main.js中加入
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
注解:
/ 通常情况下,我们会在main.js中引入一些全局的配置,例如路由、状态管理器、第三方库等等,然后在Vue实例中注入它们。而在组件中,我们通常只引入和使用组件相关的依赖。
// 但是,在实际开发中,并没有什么固定的规则。有时候,我们可能需要在组件中引入一些全局的依赖,例如Element UI、Axios等等。在这种情况下,我们需要在组件中单独引入它们。
// 总的来说,如果一个依赖是在整个应用中都要使用的,就应该在main.js中引入;如果一个依赖只在某个组件中使用,就应该在组件中引入。
// 在这个文件中,需要引入以下值:
// App.vue:这是应用程序的根组件,包含应用程序的主要布局和逻辑。
// router:这是 Vue Router 的实例,包含应用程序的所有路由信息和导航守卫。
// store:这是 Vuex 的实例,包含应用程序的状态管理逻辑。
// ElementUI:这是 UI 框架 ElementUI 的实例,用于渲染应用程序的 UI 组件和样式。
// 除此之外,如果你的应用程序需要其他的库或模块,也可以在这里引入它们。
c.设计登陆页面
在views/LoginView.vue中
代码略(详见Gitee)
d.设置路由
在router/inde.js中
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '@/layout/IndexPage.vue'
Vue.use(VueRouter)
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue')
},
// path:路由匹配的路径,可以是字符串、正则表达式或者一个包含路径的数组。
// name:路由的名称,方便在程序中进行路由跳转。
// component:对应的组件,可以是通过 import 引入的组件,也可以是一个懒加载的组件。
// children:子路由,是一个数组,可以嵌套多层。
// redirect:重定向,当用户访问某个路径时,可以自动跳转到另外一个路径。
// meta:元信息,可以在路由配置中添加一些自定义的数据,比如页面标题、页面描述等。
// props:传递给组件的 props 数据,可以是一个对象或者是一个布尔值。
f.封装axios请求
在utils/request.js中
// 这个文件导出了一个名为service的Axios实例,主要用于封装与后端API的交互。
// 在这个文件中,使用了axios.create方法创建了一个axios实例,其中指定了基础URL和超时时间。
// 接着,使用了service.interceptors.request.use和service.interceptors.response.use来拦截请求和响应,并在请求或响应前对数据进行处理。
// 通过这种方式,在其他文件中引入这个service实例,可以直接调用Axios提供的各种HTTP请求方法,
// 同时也可以通过在拦截器中进行一些统一的处理,比如添加token、请求头等。这样可以提高代码的复用性和可维护性。
//对axios进行封装
import axios from 'axios';
const service = axios.create({
baseURL: 'https://mock.apifox.cn/m1/2428381-0-default/admin-api',
timeout: 5000
});
// 使用service.interceptors.request.use()方法添加了请求拦截器,在请求发送之前可以对请求的配置进行一些处理,
// 如设置请求头、请求参数等,可以在config参数中进行配置。处理完后必须返回config对象或者一个promise对象,用于后续的处理。
// 如果请求出错,则可以在error参数中进行处理。
service.interceptors.request.use(
config => {
// 在请求发送之前对请求数据进行处理
// ...
return config;
},
error => {
// 对请求错误做些什么
console.log(error);
return Promise.reject(error);
}
);
// 上述代码使用service.interceptors.response.use()方法添加了响应拦截器,在获取响应数据之前可以对响应数据进行一些处理,如解析数据、判断状态码等,可以在response参数中进行处理。
// 处理完后必须返回response对象或者一个promise对象,用于后续的处理。
// 如果响应出错,则可以在error参数中进行处理
service.interceptors.response.use(
response => {
// 对响应数据进行处理
// ...
return response.data;
},
error => {
// 对响应错误做些什么
console.log(error);
return Promise.reject(error);
}
);
// 最后,通过export default导出该axios实例,
// 以便在其他模块中引用该实例进行请求。
export default service;
g.封装api请求
在api/login.js中
import request from '@/utils/request'
// 用户登录
export function login(username, password) {
const data = {
username,
password
}
return request({
url: '/user/login',
method: 'post',
data: data
})
}
// 获取用户信息
export function getInfo() {
return request({
url: '/user/getInfo',
method: 'get'
})
}
// import request from 'request.js' 这行代码实际上是从一个名为 request.js 的文件中导入默认导出的模块,并将其赋值给 request 变量。
// 具体来说,它会:
// 根据相对路径 'request.js' 找到对应的文件。
// 加载该文件,并执行其中的代码。
// 找到文件中的默认导出,该导出可以是任意类型的值(对象、函数、字符串等),但必须是唯一的。
// 将该导出赋值给 request 变量,以便在当前文件中使用它。
h.处理登陆操作
import {login} from '@/api/login.js'
login(this.username,this.password).then(res =>{
this.$router.push({ path: '/' })
}).catch(() => {
// 登录失败,显示错误提示
this.$message.error('用户名或密码错误')
this.loading = false
})
// <el-form ref="form" :model="loginForm" :rules="rules" label-position="left" label-width="0">
// 使用id的情况:const form = document.getElementById('login-form')
// validate 是 element-ui 表单组件提供的一个方法,用于表单验证。
// 可以通过 this.$refs.form.validate() 方法来调用,其中 form 是表单组件的 ref 属性值。
// validate 方法会执行表单验证规则,如果表单验证通过则返回 true,否则返回 false。
i.登陆成功页面
代码略
j.菜单增加路由跳转
@click="$router.push('/rbac/user/list')"
4.路由与菜单
a.layout布局
layout/index.vue
代码略
路由嵌套:
import Layout from '@/layout/IndexPage.vue'
// 如果 @/layout 目录下只有一个 index.vue 文件,那么这个 Layout 组件很有可能就是该文件导出的默认 Vue 组件。在这种情况下,import Layout from '@/layout' 就相当于从 @/layout 目录中导入默认的 Vue 组件,并将其赋值给名为 Layout 的变量。在代码中,这个 Layout 组件可以通过变量 Layout 访问和使用。
// 如果 layout 目录下有多个文件,通常情况下会存在一个默认导出的文件,如 index.vue,它通常被用作布局组件的容器。同时也可以定义其他子组件,这些组件可以是单独的 .vue 文件,也可以是 .js 或 .ts 文件,然后再由布局组件去引用它们。最终,这些组件会被组合起来构成一个完整的布局。布局组件的具体结构和设计根据项目需求和开发者的喜好而定。
{
path: '/',
component: Layout,
children:[{
path:"/",
name: 'home',
component:HomeView
}]
},
b.设计子页面:代码略
5.权限控制
a.处理Token
在utils/auth.js中
// 这段代码是与应用程序中使用的令牌有关的代码,具体来说是使用了 localStorage 来存储和检索访问令牌。其中 ACCESS_TOKEN 是一个常量,用于定义存储在 localStorage 中的键名,即用于保存访问令牌的键名。
// getAccessToken():从 localStorage 中获取访问令牌。
// setToken(token):将访问令牌存储到 localStorage 中。
// removeToken():从 localStorage 中删除访问令牌。
const AccessTokenKey = 'ACCESS_TOKEN'
// ========== Token 相关 ==========
export function getAccessToken() {
return localStorage.getItem(AccessTokenKey)
}
// localStorage 是 HTML5 提供的一种本地存储方式,可以用来存储简单的键值对。它可以在浏览器关闭后仍然保留存储的数据,因此非常适合存储用户的持久化数据,如用户的偏好设置、登录信息等。
// localStorage 只能存储字符串类型的数据,因此如果要存储其他类型的数据,需要将它们转换成字符串类型再进行存储。可以使用 JSON.stringify() 将 JavaScript 对象转换为 JSON 格式的字符串,使用 JSON.parse() 将 JSON 格式的字符串转换为 JavaScript 对象。
export function setToken(token) {
localStorage.setItem(AccessTokenKey, token)
}
export function removeToken() {
localStorage.removeItem(AccessTokenKey)
}
b.成功后保存token
在view/LoginView.vue中
import {setToken} from '@/utils/auth'
setToken(res.data.token)
c.权限检查
在src中建立permission.js
import router from './router'
import { getAccessToken } from '@/utils/auth'
router.beforeEach((to, from, next) => {
if (getAccessToken()) {
if (to.path === '/login') {
next({ path: '/' })
}
next()
} else {
// 没有token
if (to.path === '/login') {
// 直接进入
next()
} else {
next('/login') // 否则全部重定向到登录页
}
}
})
router.afterEach(() => {
})
在main.js中引入使用
import './permission'
这样如果有token的话直接进入,不用重复登陆,否则转入登陆页面,必须先登录才能进入管理页面。
d.退出功能
增加退出菜单,代码略
清除token(在Layout页面)
<script>
import {removeToken} from '@/utils/auth'
export default {
name: "MyComponent",
methods: {
logout() {
this.$confirm('确定注销并退出系统吗?', '提示').then(() => {
removeToken()
this.$router.push({ path: '/login' })
}).catch(() => {});
},
},
};
</script>
每次请求时携带token(在LoginView.vue文件)
import {getAccessToken} from '@/utils/auth'
// 在请求发送之前对请求数据进行处理
// ...
if (getAccessToken() ) {
config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
加上一些整活之后自己做出来是这样: