后端人员直接跳过一二三节,可从第四节开始,前端部署可看第三节最后步骤
文章使用:
拓展使用:
对比参考 👉🏾👉🏾二、项目环境搭建
- 安装 node js
- 安装vue的脚手架工具
npm install -g vue-cli
或/cnpm install -g vue-cli
- 创建项目,必须到对应的一个项目里面
vue init webpack vue-admin
- 生成node_modules文件
cnpm install
或npm install
- 运行项目
npm run dev
前端依然选择的是 Vue+ElementUI 的组合,主要还是因为这个主流呀。针对Vue,如果还不熟悉的同胞们,建议去看看这个视频 【4个小时带你快速入门vue】,或 狂神讲解的这个 我也是学这个视频入门的,就学了半天,哈哈。主要Js基础扎实点学起来挺快的。
首先我们安装vue的环境,我实践的环境是windows 10哈。
1、首先我们上 node.js官网,下载最新的长期版本,直接运行安装完成之后,我们就已经具备了node和npm的环境啦。
安装成功测试 👇🏾👇🏾 安装参考 一、Node 的安装
这就表示你已经安装成功啦,牛逼开始的第一步!
2、接下来,我们安装vue的环境
# 安装淘宝npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# vue-cli 安装依赖包
cnpm install --g vue-cli
# 打开vue的可视化管理工具界面
vue ui
上面我们分别安装了淘宝npm,cnpm是为了提高我们安装依赖的速度。vue ui是@vue/cli3.0增加一个可视化项目管理工具,可以运行项目、打包项目,检查等操作。对于初学者来说,可以少记一些命令,哈哈。
出现的问题解决 👉🏾👉🏾七、使用vue ui命令没有反应
3、从而跳转至vue可视化工具界面,创建vue-admin前端项目
- 会为我们打开一个http://localhost:8080 的页面:
我们将在这个页面完成我们的前端Vue项目的新建。然后切换到【创建】,注意创建的目录最好是和你运行vue ui同一级。这样方便管理和切换。
- 然后点击按钮【在此创建新项目】下一步中,项目文件夹中输入项目名称“vue-admin”,其他不用改。
- 点击下一步,选择【手动】,再点击下一步,如图点击按钮,勾选上路由Router、状态管理Vuex,去掉js的校验。
- 下一步中,也选上【Use history mode for router】,点击创建项目,然后弹窗中选择按钮【创建项目,不保存预设】,就进入项目创建啦。
- 稍等片刻之后,项目就初始化完成了。上面的步骤中,我们创建了一个vue项目,并且安装了Router、Vuex。这样我们后面就可以直接使用。
- Router: WebApp的链接路径管理系统,简单就是建立起url和页面之间的映射关系
- Vuex: 一个专为 Vue.js 应用程序开发的状态管理模式,简单来说就是为了方便数据的操作而建立的一个临时” 前端数据库“,用于各个组件间共享和检测数据变化。
- 创建成功并运行此项目,显示如下
注: 前提是idea中安装vue.js插件
接下来我们引入 element-ui组件,这样我们就可以获得好看的vue组件,开发好看的后台管理系统的界面啦~
命令很简单:
# 切换到项目根目录
cd vue admin-vue
# 或者直接在idea中执行下面命令
# 安装element-ui
cnpm install element-ui --save
然后我们打开项目src目录下的main.js,引入element-ui依赖。
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
拷贝到 main.js
这样我们就可以愉快得在官网上选择组件复制代码到我们项目中直接使用啦。
axios
:一个基于 promise 的 HTTP 库,类ajaxqs
:查询参数序列化和解析库mockjs
:为我们生成随机数据的工具库
- 接下来,我们来安装 axios,axios是一个基于 promise 的 HTTP 库,这样我们进行前后端对接的时候,使用这个工具可以提高我们的开发效率。安装命令:
cnpm install axios --save
- 然后同样我们在main.js中全局引入axios。
import axios from 'axios'
Vue.prototype.$axios = axios
- 组件中,我们就可以通过
this.$axios.get()
来发起我们的请求了哈。当然了,后面我们添加axios拦截的时候我们需要修改引入的编写。 同时,我们同步安装一个qs,什么是qs?qs是一个流行的查询参数序列化和解析库。可以将一个普通的object序列化成一个查询字符串,或者反过来将一个查询字符串解析成一个object,帮助我们查询字符串解析和序列化字符串。
cnpm install qs --save
- 然后因为后台我们现在还没有搭建,无法与前端完成数据交互,因此我们这里需要mock数据,因此我们引入 mockjs,方便后续我们提供api返回数据。
cnpm install mockjs --save-dev
- 然后我们在src目录下新建mock.js文件,用于编写随机数据的api,然后我们需要在main.js中引入这个文件:
- src/main.js
require("./mock"); //引入mock数据,关闭则注释该行
后面我们mackjs会自动为我们拦截ajax,并自动匹配路径返回数据!
- 删除下面生成的三个vue
接下来,在开发页面之前我们需要先定义路由。传统项目开发,我们都是通过链接到达控制器然后再到页面渲染的。而类似于Vue这样的前后端分离性质的框架,我们是先访问页面,然后再异步加载数据渲染。而在Vue中,路由的管理是有个专门的组件叫 Router 管理的。我们在新建项目的时候也提了一下,大家还记得吧。我们当时说:
Router
:WebApp的链接路径管理系统,简单就是建立起url和页面之间的映射关系
所以我们要打开页面然后开发页面,我们需要先配置路由,然后再开发,这样我们可以试试看到效果。项目中,src\router\index.js 就是用来配置路由的。我们在views文件夹下定义几个页面:
Login.vue
(登录页面)Index.vue
(首页)
我们新建Vue页面的时候可以这样新建:
启动项目 npm run serve
element-ui 引入assets文件夹下面的图片
由1.2.3 引入代码
Login.vue
<template>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6">
<h3>欢迎来到Daniel的vue-admin管理系统h3>
<el-image :src="require('@/assets/public.jpg')">el-image>
<p>公众号 爪哇知识库p>
<p> 扫码二维码,即可获取更多精彩文章p>
el-col>
<el-col :span="1">
<el-divider direction="vertical">el-divider>
el-col>
<el-col :span="6">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="ruleForm.username">el-input>
el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="ruleForm.password">el-input>
el-form-item>
<el-form-item label="验证码" prop="code">
<el-input v-model="ruleForm.code">el-input>
<el-image src="">el-image>
el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建el-button>
<el-button @click="resetForm('ruleForm')">重置el-button>
el-form-item>
el-form>
el-col>
el-row>
template>
<script>
export default {
name: "Login.vue",
data() {
return {
ruleForm: {
username: '',
password: '',
code: '',
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度为5个字符', trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
script>
<style scoped>
style>
通过 F12 查看模型加恰当的样式,如下
<style >
.el-row{
background-color: #fafafa;
height: 600px;
display: flex;
align-items: center;
text-align: center;
justify-content: center;
}
.el-divider{
height: 200px;
}
.captchaImg {
float: left;
margin-left: 8px;
border-radius: 4px;
}
style>
刷新显示 http://localhost:8080/Login
完整代码:
<template>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6">
<h3>欢迎来到Daniel的vue-admin管理系统h3>
<el-image :src="require('@/assets/public.jpg')" style="width: 180px; height: 180px" >el-image>
<p>公众号 爪哇知识库p>
<p> 扫码二维码,即可获取更多精彩文章p>
el-col>
<el-col :span="1">
<el-divider direction="vertical">el-divider>
el-col>
<el-col :span="6">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username" style="width: 340px;">
<el-input v-model="ruleForm.username">el-input>
el-form-item>
<el-form-item label="密码" prop="password" style="width: 340px;">
<el-input v-model="ruleForm.password" >el-input>
el-form-item>
<el-form-item label="验证码" prop="code" style="width: 340px;">
<el-input v-model="ruleForm.code" style="width: 172px; float: left" maxlength="5">el-input>
<el-image :src="captchaImg" class="captchaImg">el-image>
el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建el-button>
<el-button @click="resetForm('ruleForm')">重置el-button>
el-form-item>
el-form>
el-col>
el-row>
template>
<script>
export default {
name: "Login.vue",
data() {
return {
ruleForm: {
username: '',
password: '',
code: '',
token:'',
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度为5个字符', trigger: 'blur' }
]
},
captchaImg:null
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
//alert('submit!');
this.$axios.post('/login',this.ruleForm).then(resp=>{
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
//验证码
getCaptcha(){
this.$axios.post('/captcha').then(resp=>{
this.ruleForm.token = resp.data.data.code;
this.captchaImg = resp.data.data.captchaImg
})
}
}
}
script>
<style >
.el-row{
background-color: #fafafa;
height: 600px;
display: flex;
align-items: center;
text-align: center;
justify-content: center;
}
.el-divider{
height: 200px;
}
.captchaImg {
float: left;
margin-left: 8px;
border-radius: 4px;
}
style>
没有后端的时候我们怎么请求呢,看下面 👇🏾👇🏾
因为后台系统我们暂时还没有开发,所以这里我们需要自己mock数据完成交互。前面我们已经引入了mockjs,所以我们到mock.js文件中开发我们的api。
登录交互过程
我们梳理一下交互流程:
ok,这样我们就知道mock应该弄成什么样的api了。
- mock.js - 获取登录验证码(直接拷贝下面代码到你的 mock.js中即可)
// 引入mockjs
const Mock = require('mockjs');
// 获取 mock.Random 对象
// 参考:https://github.com/nuysoft/Mock/wiki/Mock.Random
const Random = Mock.Random;
let Result = {
code: 200,
msg: '操作成功',
data: null
};
/** *
* Mock.mock( url, post/get , function(options));
* url 表示需要拦截的 URL,
* post/get 需要拦截的 Ajax 请求类型
* 用于生成响应数据的函数
*/
// 获取验证码图片base64编码以及一个随机码
Mock.mock('/captcha', 'post', () => {
Result.data = {
token: Random.string(32), // 获取一个32位的随机字符串,
captchaImg: Random.dataImage("120x40", "p7n5w") //生成验证码为11111的base64图片编码
};
return Result
}
mock生成数据还算简单,一般都是利用Mock.Random对象来生成一些随机数据,具体的用法可以参考 这里。然后Result是为了统一返回结果,因为后台设计的时候,前后端交互,一般都有固定的返回格式,所以就有了Result。
Mock我们不需要什么处理,只需要放回的数据符合前端的要求就行哈。这样我们前端就可以继续往后面开发。
然后编写登录页面的js
- src/views/Login.vue
token的状态同步再总结一下,submitForm方法中,提交表单之后做了几个动作,从Header中获取用户的authorization,也就是含有用户登录信息的jwt,然后提交到store中进行状态管理。
①④ Login.vue
this.$axios.post('/login',this.ruleForm).then(resp=>{
const jwt = resp.headers['authorization'];
// 将jwt存储到应用store中
this.$store.commit('SET_TOKEN',jwt)
this.$router.push("/index")
})
this.$store.commit(“SET_TOKEN”, jwt)表示调用store中的SET_TOKEN方法,所以我们需要在store中编写方法:
② index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token:''
},
getters: {
},
mutations: {
SET_TOKEN:(state,token)=>{
state.token = token;
localStorage.setItem("token",token)
}
},
actions: {
},
modules: {
}
})
这样登录之后获取到的jwt就可以存储到应用的store以及localStorage中,方便使用直接从
localStorage
中获取即可! 这样用户登录成功之后就会跳转到/index页面this.$router.push("/index")
。
⑤ mock.js
Mock.mock('/login', 'post',() => {
return Result
});
测试:随便输入用户名密码验证码 点击跳转至首页
附 Login.vue当前完整代码(调整了下用户密码验证码输入框的样式):
<template>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6">
<h3>欢迎来到Daniel的vue-admin管理系统h3>
<el-image :src="require('@/assets/public.jpg')" style="width: 180px; height: 180px" >el-image>
<p>公众号 爪哇知识库p>
<p> 扫码二维码,即可获取更多精彩文章p>
el-col>
<el-col :span="1">
<el-divider direction="vertical">el-divider>
el-col>
<el-col :span="6">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username" style="width: 380px;">
<el-input v-model="ruleForm.username">el-input>
el-form-item>
<el-form-item label="密码" prop="password" style="width: 380px;">
<el-input v-model="ruleForm.password" >el-input>
el-form-item>
<el-form-item label="验证码" prop="code" style="width: 380px;">
<el-input v-model="ruleForm.code" style="width: 180px; float: left" maxlength="5">el-input>
<el-image :src="captchaImg" class="captchaImg" style="width: 86px;height: 38px">el-image>
el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建el-button>
<el-button @click="resetForm('ruleForm')">重置el-button>
el-form-item>
el-form>
el-col>
el-row>
template>
<script>
export default {
name: "Login.vue",
data() {
return {
ruleForm: {
username: '',
password: '',
code: '',
token:''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 5, max: 5, message: '长度为5个字符', trigger: 'blur' }
]
},
captchaImg:null
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
//alert('submit!');
this.$axios.post('/login',this.ruleForm).then(resp=>{
const jwt = resp.headers['authorization'];
// 将jwt存储到应用store中
this.$store.commit('SET_TOKEN',jwt)
this.$router.push("/index")
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
//验证码
getCaptcha(){
this.$axios.get('/captcha').then(resp=>{
console.log('/captcha');
console.log(resp);
this.ruleForm.token = resp.data.data.token; //resp.data 表示结果再.data表示结果里面的一个属性
this.captchaImg = resp.data.data.captchaImg
})
}
},
created(){
this.getCaptcha();
}
}
script>
<style >
.el-row{
background-color: #fafafa;
height: 600px;
display: flex;
align-items: center;
text-align: center;
justify-content: center;
}
.el-divider{
height: 200px;
}
.captchaImg {
float: left;
margin-left: 8px;
border-radius: 4px;
}
style>
这里有个问题,那么如果登录失败,我们是需要弹窗显示错误的,比如验证码错误,用户名或密码不正确等。不仅仅是这个登录接口,所有的接口调用都会有这个情况,所以我们想做个拦截器,对返回的结果进行分析,如果是异常就直接弹窗显示错误,这样我们就省得每个接口都写一遍了。
在src目录下创建一个文件axios.js(与main.js同级),定义axios的拦截:
- src/
axios.js
axios.js
import axios from "axios";
import Element from 'element-ui'
//axios.defaults.baseURL = "http://localhost:8081";
const request = axios.create({
timeout: 5000,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
});
request.interceptors.request.use(config => {
config.headers['Authorization'] = localStorage.getItem("token") // 请求头带上token
return config
});
request.interceptors.response.use(
response => {
console.log("response ->"+response);
let res = response.data;
if (res.code === 200) {
return response
} else {
Element.Message.error(!res.msg ? '系统异常':res.msg);
return Promise.reject(response.data.msg)
}
},
error => {
console.log(error);
if (error.response.data) {
error.message = error.response.data.msg
}
if (error.response.status === 401) {
router.push("/login")
}
Element.Message.error(error.message, {duration: 3 * 1000});
return Promise.reject(error)
}
);
export default request
前置拦截,其实可以统一为所有需要权限的请求装配上header的token信息,后置拦截中,判断status.code和error.response.status,如果是401未登录没权限的就调到登录页面,其他的就直接弹窗显示错误。
- 然后再main.js中导入axios.js,同时,记得去掉我们之前添加的
这样axios每次请求都会被前置拦截器和后置拦截器拦截了。 登录异常弹窗效果如下: