🐒个人主页
🏅JavaEE系列专栏
📖前言:
本篇博客主要以介绍🎀前后端分离式项目架构流程
【🎇前端】先创建Vue-cli项目,请选择此项目
执行npm run serve命令,进入浏览器,生成此页面,说明项目创建完成·
清理默认组件内容:APP.vue 、 (删除HelloWord.vue)
【🎀创建路由】
打开命令行工具,进入你的项目目录,输入下面命令。
npm i vue-router@3.5.3
1.创建 router 目录~创建 index.js 文件,在其中配置路由
【🧸在index.js配置】
import Vue from 'vue';
import router from 'vue-router'; /* 导入路由 */
import login from '../views/login'; /* 导入其他组件--【回头得改!】 */
import content from '../components/content'; /* 导入其他组件【回头得改!】 */
Vue.use(router)
/* 定义组件路由 */
var rout = new router({
routes: [{
path: '/index',
name: 'index',
component: index
},
{
path: '/content',
component: content
}
]
})
//导出路由对象
export default rout;
2.在🧸APP.vue中添加路由视图
<!-- 【路由组件】显示不同组件,就像一个画布 -->
<router-view/>
3.在🧸 main.js 中配置路由
/* 【导入路由】 */
import router from './router/index.js'
Vue.use(router);
new Vue({//【一个Vue对象】
render: h => h(App),
router,
}).$mount('#app')
【🎀配置Element UI】
🦓安装 Element UI
npm i element-ui -S
🪂在 main.js 中写入以下内容:
//【导入Element UI 并声明vue项目使用Element UI】
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
【🎀如何使背景图片最大自适应】
#divbox{
width: 100%;
height: 100vh;
background-image: url("[图片链接]"); /*将图片路径替换为你自己的图片路径*/
background-size: cover; /*保持图片比例并完全覆盖元素*/
background-position: center center; /*将图片居中对齐*/
}
【🎀登录组件模板】
<template>
<div id="divbox">
<!-- 【此图像是圆圈头像logo】 -->
<img src="https://ts1.cn.mm.bing.net/th/id/R-C.3aeeb6d5725738095a7ad521d46ce428?rik=prLV4puYz%2btYuw&riu=http%3a%2f%2fwww.gx8899.com%2fuploads%2fallimg%2f2018021008%2fjrmgrhcgro0.jpg&ehk=Im%2fy1GA0xuqdwYNnKtzfue2b09jzjym4jjUXy7e0Seo%3d&risl=&pid=ImgRaw&r=0"
alt="Your Image">
<div class="login-form">
<div id="logo">
<span style="font-family: 'Microsoft YaHei';letter-spacing: 0.5px; font-weight: bold; font-size: 40px;">
<span style="color:#4F5155"> 欢迎登录</span><span style="color:rgb(137, 204, 255) ;">宿舍管理系统</span>
</span>
</div>
<el-form ref="form" :rules="rules" :model="form" label-width="80px">
<el-form-item label="用户名" prop="account">
<el-input v-model="form.account" class="input"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="form.password" class="input"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('form')" style="width: 300px;">登录</el-button>
<br />
<span style="color: #006A5A;" @click="reg()">没有账号?点击注册</span>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
form: {
account: '',
password: '',
},
rules: {
account: [
{ required: true, message: '请输入账户', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 10 个字符~', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 1 到 10 个字符~', trigger: 'blur' }
],
}
}
},
methods: {
reg() {
alert("注册");
},
onSubmit(form) {//【登录】
//数据向后端发送进行验证
//$refs是一个引用
this.$refs[form].validate((valid) => {
if (valid) {
//如果发送成功,跳转到其他组件
//【跳转语句】
this.$message({showClose: true,message: '恭喜你,账户正确✔',type: 'success'});
/* this.$message({showClose: true,message: '输入的账户或密码错误~', type: 'error'});
this.$message({showClose: true,message: '系统忙,维修人员正在抢修!',type: 'warning'}); */
}
});
}
}
}
</script>
<style>
#divbox {
width: 100%;
height: 100vh;
/*将🎀页面背景 图片路径替换为你自己的图片路径*/
background-image: url("https://ts1.cn.mm.bing.net/th/id/R-C.b923d0630782b4e46dcbb2121b22bdbf?rik=l7wVr9wcUyyTzw&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f68%2f768.jpg&ehk=anhoZ%2fxmeecIhRHc2n9reoQbtJ2xXrvIEx0sJbLLMiI%3d&risl=&pid=ImgRaw&r=0");
/*保持图片比例并完全覆盖元素*/
background-size: cover;
background-position: center center;
/*将图片居中对齐*/
}
.login-form {
width: 442px;
height: 400px;
background-color: rgba(248, 242, 235, 0.5);
;
position: relative;
left: 455px;
top: 170px;
}
#logo {
width: 100%;
height: 60px;
/* background-color: aquamarine; */
margin-bottom: 60px;
}
.input {
max-width: 300px;
}
img {
border-radius: 50%;
width: 100px;
position: absolute;
left: 638px;
top: 50px;
}
</style>
【🎀注册组件模板】
<template>
<div id="divbox">
<!-- 【此图像是圆圈头像logo】 -->
<img src="https://ts1.cn.mm.bing.net/th/id/R-C.3aeeb6d5725738095a7ad521d46ce428?rik=prLV4puYz%2btYuw&riu=http%3a%2f%2fwww.gx8899.com%2fuploads%2fallimg%2f2018021008%2fjrmgrhcgro0.jpg&ehk=Im%2fy1GA0xuqdwYNnKtzfue2b09jzjym4jjUXy7e0Seo%3d&risl=&pid=ImgRaw&r=0"
alt="Your Image">
<div class="login-form">
<div id="logo">
<span style="font-family: 'Microsoft YaHei';letter-spacing: 0.5px; font-weight: bold; font-size: 40px;">
欢迎注册🐒平台系统
</span>
</div>
<el-form ref="form" :rules="rules" :model="form" label-width="80px">
<el-form-item label="用户名" prop="account">
<el-input v-model="form.account" class="input"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="form.password" class="input" show-password></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="password1">
<el-input type="password" v-model="form.password1" class="input" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="RegAccount('form')" style="width: 300px;">注册</el-button>
<br />
<span style="color: #006A5A;" @click="login()">已有账号?点击返回</span>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.form.password1 !== '') {
this.$refs.form.validateField('password1');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.form.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
form: {
account: '',
password: '',
password1: ''
},
rules: {
account: [{
required: true,
message: '请输入注册的管理员账户!',
trigger: 'blur'
},
{
min: 3,
max: 10,
message: '长度在 3 到 10 个字符',
trigger: 'blur'
}
],
password: [{
required: true,
message: '请输入账户密码!',
trigger: 'blur'
},
{
min: 3,
max: 10,
message: '长度在 3 到 10 个字符',
trigger: 'blur'
},{ validator: validatePass, trigger: 'blur' }
],
password1: [{
required: true,
message: '请再次确认账户密码!',
trigger: 'blur'
},
{
min: 3,
max: 10,
message: '长度在 3 到 10 个字符',
trigger: 'blur'
},{ validator: validatePass2, trigger: 'blur' }
]
}
}
},
methods: {
login() {
alert("登录页面");
//this.$router.push('/');
},
RegAccount(reffrom) { //【注册】
//数据向后端发送进行验证
//$refs是一个引用
this.$refs[reffrom].validate((valid) => {
if (valid) {
//如果发送成功,跳转到其他组件
//【跳转语句】
this.$message({
showClose: true,
message: '恭喜你,账户已注册✔',
type: 'success'
});
/* this.$message({showClose: true,message: '输入的账户或密码错误~', type: 'error'});
this.$message({showClose: true,message: '系统忙,维修人员正在抢修!',type: 'warning'}); */
}
});
}
}
}
</script>
<style>
#divbox {
width: 100%;
height: 100vh;
/*将🎀页面背景 图片路径替换为你自己的图片路径*/
background-image: url("https://ts1.cn.mm.bing.net/th/id/R-C.b923d0630782b4e46dcbb2121b22bdbf?rik=l7wVr9wcUyyTzw&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f68%2f768.jpg&ehk=anhoZ%2fxmeecIhRHc2n9reoQbtJ2xXrvIEx0sJbLLMiI%3d&risl=&pid=ImgRaw&r=0");
/*保持图片比例并完全覆盖元素*/
background-size: cover;
background-position: center center;
/*将图片居中对齐*/
}
.login-form {
width: 442px;
height: 400px;
background-color: rgba(248, 242, 235, 0.5);
;
position: relative;
left: 455px;
top: 170px;
}
#logo {
width: 100%;
height: 60px;
/* background-color: aquamarine; */
margin-bottom: 30px;
}
.input {
max-width: 300px;
}
img {
border-radius: 50%;
width: 100px;
position: absolute;
left: 638px;
top: 50px;
}
</style>
【🎀创建后端项目+数据库,先完成注册与登陆功能】
【🎇前端配置】
安装ajax(异步请求)之axios—网络请求库.
npm install axios
🎀在main.js中导入
// 导入 axios
import axios from ‘axios’;
// 设置访问后台服务器地址
/* axios.defaults.baseURL=“http://127.0.0.1:8088/TsetWebProject/”; */
axios.defaults.baseURL = “http://127.0.0.1:8088/Test/”;
// 将 axios 挂载到 vue 全局对象中,使用 this 可以直接访问
Vue.prototype.$http = axios;
🏅操作方法
this.$http.get(地址?Key=value&key2=val1).then(function(response){ }
this.$http.post(“login”,{key:“value”,key2:“val2”}).then(function(response){ }
【🎇搭建后端tomcat服务器】
🏨这是需要导入的jar包下载地址—阿里云盘
记得配置servlet处理程序的地址,配置xml文件时,记得在标签里面写,优先配置编码过滤器EcondFilter
🧸如果使用前端ajax的axios,后端记得使用“跨域请求声明”
🧸记得util包中定义commentResoult类
来规定代码传输格式:code , data , message
package util;
public class CommentResoult {
private int code;
private Object data;
private String message;
public CommentResoult(int code, Object data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
@Override
public String toString() {
return "CommentResoult{" +
"code=" + code +
", data=" + data +
", message='" + message + '\'' +
'}';
}
}
🧸可以在util包中定义JDBC的封装类(使代码更加简洁)
package util;
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Created by 罗铠威 on 2023/8/8 13:57
* type: 对jdbc代码进行一个封装
*/
public class JDBCUtil {
static {/*加载启动项*/
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
/*获取数据库连接对象*/
public static Connection getConnection(String dataBaseName) throws SQLException {
String url="jdbc:mysql://127.0.0.1:3306/"+dataBaseName+"?serverTimezone=Asia/Shanghai";
return DriverManager.getConnection(url,"root","newpassword");
}
/*关闭连接管道*/
public static void close(Connection connection, PreparedStatement ps) throws SQLException {
if(connection!=null){
connection.close();
}
if(ps!=null){
ps.close();
}
}
}
🏅【采用json格式的打印】
🪂后端借助ObjectMapper类将对象转为json格式的字符串
🪂【前端发送数据时,由于axios发出的异步请求格式为json格式,利用jsonToString()方法转为默认键值对格式】
//将json对象序列化为键=值&键=值
function jsonToString(jsonObj){
console.log(jsonObj);
var str="";
for (var s in jsonObj) {
str+=s+"="+jsonObj[s]+"&";
}
return str.substring(0,str.length-1);
}
【🎀主面板组件模板】
<template>
<div>
<el-container>
<!--头部导航栏-->
<el-header style="text-align: right; font-size: 20px;">
<span class="logo">
陕西理工大学<span style="color:rgb(137, 204, 255) ;">宿舍管理</span>系统
</span>
<el-dropdown>
<!-- <div class="school"></div> -->
<i class="el-icon-setting" style="margin-right: 15px;"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item><el-button type="text" @click="open"
icon="el-icon-s-custom">管理员信息</el-button></el-dropdown-item>
<el-dropdown-item><el-button type="text" @click="open"
icon="el-icon-s-grid">楼层信息汇总</el-button></el-dropdown-item>
<el-dropdown-item> <el-button type="text" @click="open"
icon="el-icon-switch-button">安全退出</el-button></el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button icon="el-icon-s-custom" class="icon"> <span
style="font-weight: bolder">{{account}}</span></el-button>
</el-header>
<!-- 🪂侧边栏-->
<!--左边栏-->
<el-container>
<el-aside width="240px" style="background-color: white">
<el-menu :default-openeds="['1', '5']" router>
<el-submenu index="1">
<template slot="title"> <el-button type="success" icon="el-icon-user"
style="width: 132px;">学生管理</el-button></template>
<el-menu-item-group>
<el-menu-item index="2-1"><el-button type="danger" plain
style="width: 98px;">学生信息</el-button></el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title" class="title_left"><el-button type="success"
icon="el-icon-office-building">楼栋管理</el-button></template>
<el-menu-item-group>
<template slot="title">女生公寓</template>
<el-menu-item index="1-1"><el-button type="danger" plain
style="width: 98px;">9号楼</el-button></el-menu-item>
<el-menu-item index="1-2"><el-button type="danger" plain
style="width: 98px;">10号楼</el-button></el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="男生公寓">
<el-menu-item index="1-3"><el-button type="danger" plain
style="width: 98px;">13号A栋</el-button></el-menu-item>
<el-menu-item index="1-4"><el-button type="danger" plain
style="width: 98px;">13号B栋</el-button></el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="3">
<template slot="title"> <el-button type="success" icon="el-icon-s-home"
style="width: 132px;">宿舍管理</el-button></template>
<el-menu-item-group>
<el-menu-item index="2-1"><el-button type="danger" plain
style="width: 98px;">晚签情况</el-button></el-menu-item>
<el-menu-item index="2-2"><el-button type="danger" plain
style="width: 98px;">卫生评估</el-button></el-menu-item>
<el-menu-item index="2-3"><el-button type="danger" plain
style="width: 98px;">考勤管理</el-button></el-menu-item>
<el-menu-item index="/list"><el-button type="danger" plain
style="width: 98px;">学生管理</el-button></el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="4">
<template slot="title"> <el-button type="success" icon="el-icon-s-custom"
style="width: 132px;">管理员管理</el-button></template>
<el-menu-item-group>
<el-menu-item index="2-1"><el-button type="danger" plain
style="width: 98px;">晚签情况</el-button></el-menu-item>
<el-menu-item index="2-2"><el-button type="danger" plain
style="width: 98px;">卫生评估</el-button></el-menu-item>
<el-menu-item index="2-3"><el-button type="danger" plain
style="width: 98px;">考勤管理</el-button></el-menu-item>
<el-menu-item index="/list"><el-button type="danger" plain
style="width: 98px;">学生管理</el-button></el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="5">
<template slot="title"> <el-button type="success" icon="el-icon-setting"
style="width: 132px;">设置</el-button></template>
<el-submenu index="3-4">
<template slot="title"><el-button type="text"
icon="el-icon-warning">问题上报反馈 </el-button></template>
<el-menu-item index="3-1"> <el-button type="danger" plain
style="width: 98px;">学生问题</el-button></el-menu-item>
<el-menu-item index="3-2"><el-button type="danger" plain
style="width: 98px;">楼管问题</el-button></el-menu-item>
</el-submenu>
<el-menu-item-group>
<template slot="title"></template>
<el-menu-item ><el-button type="text" @click="open"
icon="el-icon-switch-button">安全退出</el-button></el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<img src="https://pic.baike.soso.com/ugc/baikepic2/16827/20171227225852-88762951_jpg_300_300_19329.jpg/300"
alt="Your Image">
<!--中间部分-->
<el-main >
展示信息
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main",
data() {
return {
account:""
}
},
methods: {
open() {
this.$confirm('此操作将退出登录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
sessionStorage.clear(); //情况存储信息
this.$message({
type: 'success',
message: '退出成功!'
});
this.$router.push("/");
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
}
},
mounted() {
this.account = sessionStorage.account;
/*
this.account=sessionStorage.getItem("account");
if(this.account==null){//【更改为路由导航守卫】
this.$router.push("/")
}
*/
}
}
</script>
<style type="text/css" scoped>
#in1 {
background-color: white;
width: 100%;
height: 100vh;
}
.el-header {
background-color: #6da5ee;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #67C23A;
color: #333;
text-align: center;
}
.el-main {
background-color: white;
color: #333;
text-align: center;
}
body>.el-container {
margin-bottom: 40px;
}
.icon {
border-radius: 50%;
background-color: #9dd79e;
}
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
.logo {
font-weight: 900;
font-size: 38px;
background-color: #67C23A;
font-family: 楷体;
border-radius: 80px 50px 50px 80px;
position: absolute;
left: 0px;
}
img {
border-radius: 50%;
width: 70px;
position: absolute;
left: 0px;
top: -2px;
}
</style>
【🎀添加token令牌–临时会话跟踪】
🏅在index.js中配置路由导航守卫
== --目的:每次请求时验证用户是否登录==
// 为路由对象, 添加 beforeBach 导航守卫
// to - 将要访问的页面地址, from - 从哪个页面访问的, next - 放行函数
rout.beforeEach((to, from, next) => {
if (to.path == '/login') {
//如果用户访问的登录页, 直接放行
return next();
} else {
var account = sessionStorage.getItem("account");
if (account == null) {
return next("/login");
} else {
next();
}
}
})
🏅【前端配置:axios的响应拦截器、请求拦截器】=提高代码复用性
🎀我们需要在后端生成一个token令牌
,然后在浏览器的临时存储空间中存储
,每次向后端发送请求时,将token都会(加载的请求头中)随请求一同发往后端进行验证
。== 由于token令牌中携带着用户的信息,会在后端进行解析,可以使后端知道到底是哪一个用户正在操作、发出请求,并且可以验证token是否失效,限制用户在线时间==。
🎀在main.js中导入
//axios 🎀请求拦截
axios.interceptors.request.use(config => {
//为请求头对象,添加 Token 验证的 token 字段
config.headers.token = window.sessionStorage.getItem('token');
return config;
})
🎀与此同时,当后端验证完token,并且完成对应的处理操作后,响应至前端,前端会进行响应拦截,提前判断是否token失效、被伪造 或数据库连接失败
==>返回登录页面,重新进行登录。
// 🎀添加响应拦截器
axios.interceptors.response.use((resp) => { //正常响应拦截
if (resp.data.code == 500) {
ElementUI.Message({
message: resp.data.message,
type: "error"
})
}
if (resp.data.code == 202) {//token失效、被伪造
router.replace("/login");
}
return resp;
});
🏅【后端配置:添加JWT jar包、创建modle包/Admin类、在util包下创建JWTUtil类、创建TokenFilter过滤器】“json web token”
🎀添加JWT jar包
🏨这是需要导入的jar包下载地址—阿里云盘
🪂先在src路径下创建一个modle包,再创建一个Admin类(这个类是存储登录界面的账户信息的)
同时token也是根据此类的信息来生成token令牌的
提示一下
:这里的属性是根据数据库表admins的属性来写的,这个admins表是存储登录界面的账户信息的
package modle;
/**
* Created by 罗铠威 on 2023/8/11 10:16
* type:
*/
import java.util.Date;
public class Admin {
private int id;
private String account;
private String password;
private Date reg_time;
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getReg_time() {
return reg_time;
}
public void setReg_time(Date reg_time) {
this.reg_time = reg_time;
}
}
🎀添加JWT jar包
package com.example.back.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类
*/
public class JWTUtil {
/**
* 根据用户id,账号生成token
* @param u
* @return
*/
public static String getToken(User u) {
String token = "";
try {
//过期时间 为1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间
Date expireDate = new Date(new Date().getTime() + 600*1000);//10分钟后失效
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
//设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//携带id,账号信息,生成签名
token = JWT.create()
.withHeader(header)
.withClaim("id",u.getId())
.withClaim("account",u.getAccount())
.withExpiresAt(expireDate)
.sign(algorithm);
}catch (Exception e){
e.printStackTrace();
return null;
}
return token;
}
/**
* 验证token是否有效
* @param token
* @return
*/
public static boolean verify(String token){
try {
//验签
Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e) {//当传过来的token如果有问题,抛出异常
return false;
}
}
/**
* 获得token 中playload部分数据,按需使用
* @param token
* @return
*/
public static DecodedJWT getTokenInfo(String token){
return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
}
}
🧸创建TokenFilter过滤器
package filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import util.CommentResoult;
import util.JWTUtil;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class TokenFilter implements Filter {
@Override//【HttpServletRequest(子接口,定义了http请求相关的方法) extends ServletRequest(父类没有关于http相关的方法)】
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;//多态性
HttpServletResponse resp = (HttpServletResponse) servletResponse;//多态性
String token = req.getHeader("token");//【这个就是那个自定义的请求头】
System.out.println(token+"已接收");
//验证token代码是否有效--
//【verify()方法是验证token是否是伪造的、失效的,返回一个布尔类型的值】
boolean res = JWTUtil.verify(token);//false-token【失效or伪造的】
CommentResoult commentResoult=null;
if(res){//【如果是真的】
commentResoult= new CommentResoult(200, res, "token验证成功!");
filterChain.doFilter(servletRequest,servletResponse);//【向下走】
}else {
commentResoult= new CommentResoult(202, res, "token已经失效或伪造!");
PrintWriter responds = resp.getWriter();
responds.print(new ObjectMapper().writeValueAsString(commentResoult));//发送json格式的信息至前端
}
}
}
【🪀记得配置xml】
【如何·🎀解析token中的用户信息】
package Servlet;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import util.CommonResult;
import util.JWTUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class TestServlet extends HttpServlet {
@Override
//【🎀解析token语句,】
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String token = req.getHeader("token");//【这个就是那个自定义的请求头】
DecodedJWT tokenInfo = JWTUtil.getTokenInfo(token);
Integer id = tokenInfo.getClaim("id").asInt();//拿到id
String account = tokenInfo.getClaim("account").asString();//拿到账户
System.out.println("测试请求!"+id+":"+account);
//验证token代码是否有效
boolean res = JWTUtil.verify(token);//false-token【失效or伪造的】
CommonResult commonResult = new CommonResult(res?200:202, res, "token验证成功~");
PrintWriter responds=resp.getWriter();
responds.print(new ObjectMapper().writeValueAsString(commonResult));//发送json格式的信息至前端
}
}
🧸【🎀后端:】记得登录成功时,要生成一个token
【🦓获取token,记得是请求头getHeader(),而不是请求体getParameter()】
🧸【🎀前端:】记得登录成功时,从后端响应回来的数据中将token存入临时会话存储区中
🧸【组件响应的路由跳转–跳转至新页面】
在menu标签上要添加router属性,否则没有反应,不会跳转
🧸路由嵌套🎀【组件嵌套的路由跳转–局部跳转】
这是两个案例:目的是为了告诉你:要在《el-menu
》组件里加router属性,否则会没有反应!!!!
🎇【css3中的一个技巧–按时放大】
#in1{
-webkit-transform: scale(1.25);
transform: scale(1.25);
transition: 3s;
opacity: 0;
}
🏅【在一个组件中导入外部组件】
🏅计时器setTimeout(函数名,延迟时间)
🎀父组件与子组件之间的信息交互 $emit 方法
应用场景
:我们可能会遇到这种情景:当一个子组件完成任务时,我们想让父组件执行一些指令
,这时就需要子组件向父组件发送信息,让父组件去执行对应的指令。
🏅父组件给子组件发信息(这个是官方的案例)
🎀🎀🎀🎀🎀🎀这个是父组件里的内容🎀🎀🎀🎀🎀🎀🎀🎀🎀
<template>
<div>
<button @click="sendData">Send Data</button>
<child-component @custom-event="handleCustomEvent"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
methods: {
sendData() {
const data = { name: 'Hello from parent component!' };
this.$emit('custom-event', data); // 触发自定义事件
}
},
};
</script>
🎀🎀🎀🎀🎀🎀这个是子组件里的内容🎀🎀🎀🎀🎀🎀🎀🎀🎀
在子组件中监听这个自定义事件,并在回调函数中执行相应的逻辑。例如:
<!-- ChildComponent.vue -->
<template>
<div>
<button @click="updateParentData">Update Parent Data</button>
</div>
</template>
<script>
export default {
methods: {
updateParentData() {
const newData = { name: 'Hello from child component!' };
this.$emit('update-parent-data', newData); // 触发自定义事件,并传递新数据对象
}
}
};
</script>
🏅子组件给父组件发信息(个人案例)
🎀🎀🎀🎀🎀🎀这个是子组件里的内容🎀🎀🎀🎀🎀🎀🎀🎀🎀
<template>
<div>
<el-button type="primary" @click="addSudent()">保 存</el-button>
</div>
</template>
<script>
export default {
methods: {
addSudent(){
//向父组件Stulist发送更新请求🎀🎀🎀🎀🎀🎀🎀🎀
const data = true;//子组件向父组件发送的信息
this.$emit('Add-event', data); // 触发自定义Add-event响应事件
}
}
}
</script>
🎀🎀🎀🎀🎀🎀这个是父组件里的内容🎀🎀🎀🎀🎀🎀🎀🎀🎀
<template>
<div>
<Add ref="add" @Add-event="add"></Add>
<!-- 这个是导入的组件标签,@Add-event是响应事件 -->
</div>
</template>
<script>
import Add from './Add.vue';
export default {
components:{
Add
},
methods: {
add(data){//🎀🎀🎀🎀🎀🎀
if(data==true){//组件信息交互--🎀🎀🎀🎀🎀🎀🎀🎀
// 在此处执行向后端发送 get 请求的逻辑,更新表格
//这里写需要父组件执行的指令
}
}
}
}
</script>
🎀表格Table模板
这里的数据可根据需要自行更换,响应事件可自己添加(可以自己更换)
🎀源码如下:
<template>
<div>
<el-card class="box-card" style="text-align: left;">
<!-- 【🎀查询列表】 -->
<el-row :gutter="20">
<el-col :span="6"><el-input placeholder="学号" v-model="query.queryNum"/></el-col>
<el-col :span="6"><el-input placeholder="姓名" v-model="query.queryName"/></el-col>
<el-col :span="6">
<el-button type="primary" icon="el-icon-search" @click="">查询</el-button>
<el-button type="danger" icon="el-icon-delete" size="small" @click="">清空查询</el-button>
</el-col>
</el-row>
<!-- 【🎀新增按钮】 -->
<el-button type="success" icon="el-icon-circle-plus" @click="">新增</el-button>
<!-- 【🎀返回按钮】 -->
<el-button type="primary" icon="el-icon-house" @click="">返回主页</el-button>
<!-- 【🎀学生列表】 -->
<el-table :data="studentList" border style="width: 100%;" max-height="530">
<el-table-column fixed prop="oper_time" label="注册日期" width="170">
</el-table-column>
<el-table-column type="index" fixed label="序号" sortable="true" width="80">
</el-table-column>
<el-table-column prop="name" label="姓名" width="70">
</el-table-column>
<el-table-column prop="num" label="学号" width="70">
</el-table-column>
<el-table-column prop="gender" label="性别" width="60">
</el-table-column>
<el-table-column prop="majorName" label="专业" width="110">
</el-table-column>
<el-table-column prop="birthday" label="生日" width="110">
</el-table-column>
<el-table-column prop="birthday" label="生日" width="110">
</el-table-column>
<el-table-column prop="birthday" label="生日" width="110">
</el-table-column>
<el-table-column prop="address" label="地址" width="70">
</el-table-column>
<el-table-column prop="phone" label="联系方式" width="90">
</el-table-column>
<el-table-column fixed="right" label="操作" width="190">
<template slot-scope="scope">
<el-button @click="" type="warning" icon="el-icon-edit" size="small"
style="width: 40;">编辑</el-button>
<el-button @click="" size="small" type="danger" icon="el-icon-delete"
style="width: 40;">
移除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
export default{
data() {
return{
studentList: [],
query:{
queryName:"",
queryNum:"",
mark:"query"
}
}
}
}
</script>
<style>
</style>
🪀表单dalog模板
这里的数据可根据需要自行更换,响应事件可自己添加(可以自己更换)
文章来源:https://www.toymoban.com/news/detail-466116.html
🎀源码如下:
<template>
<div>
<!-- 【🎀新增信息】 -->
<el-dialog title="新增学生信息" :visible.sync="dialogFormVisible">
<el-form :model="form" style="background-color: bisque;">
<el-row><!-- 【🪀姓名】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;">姓名</el-button> </el-col>
<el-col :span="6"><el-form-item><el-input v-model="form.name" maxlength="10" show-word-limit
autocomplete="off"></el-input></el-form-item></el-col>
</el-row>
<el-row><!-- 【🪀学号】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;">学号</el-button> </el-col>
<el-col :span="5"><el-form-item><el-input v-model="form.num" maxlength="10" show-word-limit
autocomplete="off"></el-input></el-form-item></el-col>
</el-row>
<el-row><!-- 【🪀性别】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;" plain>性别</el-button> </el-col>
<el-col :span="6">
<el-radio v-model="form.gender" label="男">男</el-radio>
<el-radio v-model="form.gender" label="女">女</el-radio>
</el-col>
</el-row>
<el-row><!-- 【🪀专业】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;" plain>专业</el-button> </el-col>
<el-col :span="6">
<el-select v-model="form.majors" placeholder="请选择专业">
<el-option v-for="item in form.major" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</el-col>
</el-row>
<el-row><!-- 【🪀生日】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;" plain>生日</el-button> </el-col>
<el-col :span="7">
<div class="block">
<el-date-picker value-format="yyyy-MM-dd" v-model="form.birthday" align="right" type="date"
placeholder="选择日期" :picker-options="pickerOptions">
</el-date-picker>
</div>
</el-col>
</el-row>
<el-row><!-- 【🪀地址】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;">地址</el-button> </el-col>
<el-col :span="18">
<el-form-item>
<el-input type="textarea" :rows="2" placeholder="请输入地址" maxlength="20" show-word-limit
v-model="form.address"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row><!-- 【🪀联系方式】 -->
<el-col :span="4"><el-button type="success" style="width: 98px;">联系方式</el-button> </el-col>
<el-col :span="18"><el-form-item><el-input v-model="form.phone" maxlength="15" show-word-limit
autocomplete="off"></el-input></el-form-item></el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="concle()">取 消</el-button>
<el-button type="primary" @click="addSudent()">保 存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {/* 【是否可见】 */
dialogFormVisible: false,
form: {
name: '',
num: '',
gender: '',
birthday: '',
address: '',
phone: '',
delivery: false,
major: [],
majors: '',
mark: 'add'
},
formLabelWidth: '120px',
pickerOptions: {//【🎀日期选择器】
disabledDate(time) {
return time.getTime() > Date.now();
}
}
}
},
methods: {
concle() {
this.$message({
type: 'info',
message: '退出成功!'
});
this.dialogFormVisible = false;
},
addSudent(){
//【🎀请求事件自己添加~】
this.$message({type: 'success',message:"保存成功!"});
this.dialogFormVisible = false;//【🎀关闭对话框】
}
},
mounted() {
//【🎀获取专业信息】
/* this.$http.get("admin/studentList?mark=major").then((resp) => {
if (resp.data.code == 200) {
this.form.major = resp.data.data;
}
}); */
}
}
//将json对象序列化为键=值&键=值
function jsonToString(jsonObj) {
console.log(jsonObj);
var str = "";
for (var s in jsonObj) {
str += s + "=" + jsonObj[s] + "&";
}
return str.substring(0, str.length - 1);
}
</script>
<style scoped>
</style>
🎇刷新页面
this.$router.go();
🪂表单的二级联动
var obj=this.buildinglist.find((build)=>{
return build.id==this.form.building;
})/* 拿到当前楼栋对象 */
this.fool_num=obj.buildingClass;
【解析】
其中this.buildinglist是一种[{},{},{},{}]“数组+集合的嵌套结构”,调用find((item=>{ return 筛选条件;}));这个跟java里stream流的过滤fliter((e)->{return 筛选条件;})比较像。返回obj为符合 筛选条件的集合
这样就从[{},{},{},{}]“数组+集合的嵌套结构”变成了{}集合结构
,在进行其他操作文章来源地址https://www.toymoban.com/news/detail-466116.html
🎀【🐒持续更新中…】
到了这里,关于前后端分离式项目架构流程(爆肝两万字)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!