封装requests方法
统一错误处理
npm i axios
npm nprogress
参考封装
https://blog.csdn.net/a5120184120/article/details/132590955
# utils/request.js
// axios二次封装
// 设置baseURL和timeout
// 在请求拦截器中封装token鉴权
// 在响应拦截器中对数据过滤处理
// 在响应拦截器中http错误状态码进行判断等
//要npm i
import axios from "axios";
import "nprogress/nprogress.css";
// 引入进度条
import nprogress from "nprogress";
import { Message } from "element-ui";
import router from "@/router";
// // 通过全局配置文件载入变量,并根据当前环境 载入变量的值
// axios.defaults.baseURL = process.env.VUE_APP_Server;
// console.log("VUE_APP_Server", process.env.VUE_APP_Server);
// 创建axios新实例
const axiosInstance = axios.create({
// baseURL: "https://blog.csdn.net/", //接口根地址
timeout: 3000, //超时时间
});
/**
* 请求拦截器
*/
axiosInstance.interceptors.request.use(
(config) => {
// 进度条开始
nprogress.start();
// 封装token鉴权
const token = localStorage.getItem("Token");
if (token) {
config.headers["Authorization"] = token;
}
return config;
},
(err) => Promise.reject(err)
);
/**
* 响应拦截器
*/
axiosInstance.interceptors.response.use(
(res) => {
// 进度条结束
nprogress.done();
// 过滤数据
return res.data;
},
(err) => {
// 进度条结束
nprogress.done();
const { response } = err;
if (response) {
const status = response.status;
switch (status) {
case 404:
Message.error("资源不存在 404");
break;
case 403:
Message.error("拒绝访问 403");
router.push("/login");
break;
case 401:
Message.error("身份凭证确实 401");
router.push("/login");
break;
case 500:
Message.error("服务器错误 500");
break;
default:
Message.error("出现意想不到的错误");
break;
}
} else {
// 服务器崩溃问题或是网络问题
if (!window.navigator.onLine) {
Message.error("当前网络不可用,请检查你的网络连接");
return;
} else {
Message.error("连接服务器错误" + err?.message);
return Promise.reject(err);
}
}
return Promise.reject(err);
}
);
export default axiosInstance;
调用http请求封装
# src/apis/user.js
import request from "@/utils/request";
// 测试
export const testApi = (data) => request.post("/api/test", data);
// 注册
export const regApi = (data) => request.post("/api/reg", data);
// 登录
export const loginApi = (data) => request.post("/api/login", data);
mock模拟数据
# 调用mockServe
#main.js
import Vue from "vue";
import "../mock/mockServe";
import "reset-css";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "./plugins/element.js";
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
#mock/mockServe.js
import "./user.js";
#mock/user.js
import Mock from "mockjs";
// // 拦截接口,返回mock快速生成批量数据
// Mock.mock("/users/list", "get", {
// code: 0,
// msg: "获取成功",
// data: {
// // 生成十个如下格式的数据
// "list|10": [
// {
// "id|+1": 1, // 数字从当前数开始依次 +1
// "age|18-40": 20, // 年龄为18-40之间的随机数字
// "sex|1": ["男", "女"], // 性别是数组中随机的一个
// name: "@cname", // 名字为随机中文名字
// email: "@email", // 随机邮箱
// isShow: "@boolean", // 随机获取boolean值
// },
// ],
// },
// });
Mock.mock("/api/users", "get", {
success: true,
data: [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Jane Smith" },
],
});
// 模拟后台登录逻辑
Mock.mock("/api/login", "post", (params) => {
// console.log(params);
let loginData = JSON.parse(params.body);
if (loginData.username !== "admin") {
return {
code: 400,
msg: "用户名错误",
data: {
token: "1111111111122",
userinfo: { id: 2, name: "Jane Smith" },
},
};
}
if (loginData.pass !== "123456") {
return {
code: 200,
msg: "密码错误",
data: null,
};
}
if (loginData.code !== "1234") {
return {
code: 200,
msg: "验证码错误",
data: null,
};
}
return {
code: 200,
msg: "登录成功",
data: {
token: "1111111111122",
userinfo: { id: 2, name: "Jane Smith" },
},
};
});
// 模拟后台注册逻辑
Mock.mock("/api/reg", "post", (params) => {
// console.log(params);
let loginData = JSON.parse(params.body);
if (loginData.username === "admin") {
return {
code: 400,
msg: "用户已存在",
data: null,
};
}
return {
code: 200,
msg: "注册成功",
data: null,
};
});
封装validate.js
#utils/validate.js
export const validateUsername = (rule, value, callback) => {
if (value.length < 3 || value.length > 20) {
callback(new Error("用户名必须在3到20位字符内"));
} else {
callback();
}
console.log(value.length);
};
创建登录页面
# views/LoginView.vue
<template>
<div class="login-page">
<div class="login-box">
<h3>用户登录</h3>
<el-form
:model="loginForm"
status-icon
:rules="loginFormRules"
ref="loginForm"
label-width="70px"
class="demo-loginForm"
>
<el-form-item label="用户名" prop="username">
<el-input
type="text"
v-model="loginForm.username"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
type="password"
v-model="loginForm.password"
autocomplete="off"
></el-input>
</el-form-item>
<!-- 验证码 -->
<el-form-item label="验证码" prop="verifyCode">
<div class="verify-code">
<el-input
type="text"
v-model="loginForm.verifyCode"
autocomplete="off"
placeholder="验证码"
></el-input>
<img src="../assets/imgs/verify-code.png" alt="verifyCode" />
</div>
</el-form-item>
<div>
<el-button type="primary" @click="submitForm('loginForm')"
>登录</el-button
>
</div>
</el-form>
<div>
<el-button @click="$router.push('/reg')" type="text"
>注册用户</el-button
>
</div>
<!-- <button @click="test">测试按钮</button> -->
</div>
</div>
</template>
<script>
import { validateUsername } from "@/utils/validate";
import { loginApi } from "@/apis/user";
// import { testApi } from "@/apis/user";
export default {
name: "LoginView",
data() {
return {
loginFormRules: {
username: [
// { required: true, message: "请输入用户名", trigger: "blur" },
{ validator: validateUsername, trigger: "blur" },
],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
verifyCode: [
{ required: true, message: "请输入验证码", trigger: "blur" },
],
},
loginForm: {
username: "admin",
password: "123456",
verifyCode: "1234",
},
};
},
mounted() {},
methods: {
// test() {
// testApi().then((data) => {
// console.log(data);
// });
// },
submitForm(formName) {
this.$refs[formName].validate(async (valid) => {
if (valid) {
// 异步等待实现,只处理成功
const res = await loginApi({
username: this.loginForm.username,
pass: this.loginForm.password,
code: this.loginForm.verifyCode,
});
// console.log("res");
if (res.code === 200 && res.data && res.data.token) {
localStorage.setItem("Token", res.data.token);
this.$message({
message: "登录成功",
type: "success",
});
// 跳转到后台主页面
this.$router.push("/main");
} else {
let msg = res.msg || "登录失败";
this.$message({
message: msg,
type: "error",
});
}
// console.log("valid");
// // 异步实现
// loginApi({
// username: this.loginForm.username,
// pass: this.loginForm.password,
// code: this.loginForm.verifyCode,
// })
// .then((res) => {
// console.log("res");
// if (res.code === 200 && res.data && res.data.token) {
// localStorage.setItem("Token", res.data.token);
// this.$message({
// message: "登录成功",
// type: "success",
// });
// } else {
// this.$message({
// message: "登录失败",
// type: "error",
// });
// }
// })
// .catch(() => {
// console.log("err");
// });
// console.log("valid");
} else {
// console.log("error submit!!");
// this.$message({
// message: "表单输入内容有误",
// type: "error",
// });
return false;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.login-page {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
background-size: cover;
background-position: right center;
background-repeat: no-repeat;
background: url(../assets/imgs/bg.jpg);
}
.login-box {
padding: 40px 20px;
background-color: white;
width: 400px;
border-radius: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
h3 {
text-align: center;
font-size: 28px;
margin-bottom: 20px;
}
.el-button {
width: 100%;
margin-top: 30px;
}
.verify-code {
display: flex;
align-items: center;
justify-content: center;
img {
height: 40px;
margin-left: 10px;
// height: 40px;
}
}
}
</style>
创建注册页面
<template>
<div class="reg-page">
<div class="reg-box">
<h3>用户注册</h3>
<el-form
:model="regForm"
status-icon
:rules="regFormRules"
ref="regForm"
class="demo-regForm"
>
<el-form-item label="用户名" prop="username">
<el-input
type="text"
v-model="regForm.username"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
type="password"
v-model="regForm.password"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input
type="password"
v-model="regForm.checkPass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('regForm')"
>注册</el-button
>
</el-form-item>
</el-form>
<div>
<el-button @click="$router.push('/login')" type="text"
>返回登录</el-button
>
</div>
</div>
</div>
</template>
<script>
import { regApi } from "@/apis/user";
export default {
name: "RegView",
data() {
var validateCheckPass = (rule, value, callback) => {
if (value !== this.regForm.password) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
return {
regFormRules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
checkPass: [
{ required: true, message: "请输入确认密码", trigger: "blur" },
{
validator: validateCheckPass,
message: "确认密码不正确",
trigger: "blur",
},
],
},
regForm: {
username: "admin",
password: "123456",
checkPass: "123456",
},
};
},
mounted() {},
methods: {
submitForm(formName) {
this.$refs[formName].validate(async (valid) => {
if (valid) {
const res = await regApi({
username: this.regForm.username,
pass: this.regForm.password,
checkPass: this.regForm.checkPass,
});
if (res.code === 200) {
this.$message({
type: "success",
message: "注册成功",
});
this.$router.push("/login");
} else {
let msg = res.msg || "注册失败";
this.$message({
message: msg,
type: "error",
});
}
} else {
// 客户端验证未通过
return false;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.reg-page {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
background-size: cover;
background-position: right center;
background-repeat: no-repeat;
background: url(../assets/imgs/bg.jpg);
}
.reg-box {
padding: 20px;
background-color: white;
width: 400px;
border-radius: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
h3 {
text-align: center;
font-size: 28px;
margin-bottom: 20px;
}
.el-button {
width: 100%;
}
}
</style>
后台框架页
#views/MainView.vue
<template>
<div>后台页面</div>
</template>
<script>
export default {
name: "MainView",
data() {
return {};
},
mounted() {},
methods: {},
};
</script>
<style lang="scss" scoped></style>
配置路由
import Vue from "vue";
import VueRouter from "vue-router";
import HomeView from "../views/HomeView.vue";
import LoginView from "../views/LoginView.vue";
import RegView from "../views/RegView.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/test",
name: "test",
component: () =>
import(/* webpackChunkName: "test" */ "../views/TestView.vue"),
},
{
path: "/main",
name: "main",
component: () =>
import(/* webpackChunkName: "main" */ "../views/MainView.vue"),
},
{
path: "/reg",
name: "reg",
component: RegView,
},
{
path: "/login",
name: "login",
component: LoginView,
},
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/about",
name: "about",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;
可以设置代理服务器
# 未验证
// devServer: {
// proxy: {
// "/api": {
// //接口地址 以 api开头的都走下面的配置
// target: "http://127.0.0.1:5000", //代理目标地址为后端服务器地址
// ws: true, //是否支持 websocket 请求 支持
// changeOrigin: true, //是否启用跨域
// pathRewrite: {
// "^/api": "", //发送请求时自动去掉开头
// },
// },
// },
// },
# 不同环境设置不同环境变量
# .env.development文件
NODE_ENV='development'
VUE_APP_Server= '/api'
# .env.production文件
NODE_ENV='production'
VUE_APP_Server= 'http://115.159.153.85:5000'