今天主要完成了
首先完善了昨天剩下的基本查询,基于user_name ,phonenumber,status,begin_time,end_time的多变量查询。用到了Mybatis的动态sql,这样可以在管理端没有输入数据的时候鸡血查询符合条件的中医生首先展示一下
这样的话就是可以实现查询,
- 首先在doctor.js里面添加一个function queryByParams 用于检索中医生,类型为post。
export function queryByParams (data) {
return request({
url: '/queryByParams',
method: 'post',
data: data
})
}
- 在view里面添加一个elementui自带的一个日期选择器,写一个查询参数数组,还有一个获取时间改变的数组,
</el-form-item>
<el-form-item label="时间">
<el-date-picker v-model="queryParamsTime"
type="datetimerange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchUser">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="handlePageReset">重置</el-button>
</el-form-item
queryParams: { // 查询参数
user_name: null,
phonenumber: null,
status: null,
begin_time: null,
end_time: null
},
queryParamsTime:[],
- 添加监听日期
watch: {
queryParamsTime(newTime){//监听日期选择
console.log('newTime', newTime)
this.queryParams.beginTime = newTime[0].toISOString()
this.queryParams.endTime = newTime[1].toISOString()
}
},
- 写对应搜索的点击事件
queryByParams(this.queryParams).then(res => {
this.doctorList = res.data
this.queryFlag = true
}).catch(error => {
console.log(error)
})
},
- 重置事件
handlePageReset () {
this.queryFlag = false
this.page.pageNum = 1
this.page.pageSize = 6
this.page.listTotal = 0
this.$refs.queryParamsRef.resetFields()
this.fetchUserDoctorList()
}
这样前段的代码基本完成以后开始写对应的后端接口mapper和controller层
- 建一个QueryUser实体类放在util里面 添加两个data
@Data
public class QueryUser {
private String user_name;
private String phonenumber;
private String status;
private Date beginTime;
private Date endTime;
}
- 然后再mapper里面添加一个queryByParams接口,返回类型为List,在xml文件里面实现对应的动态sql
<select id="queryByParams"
parameterType="bysj.bysj.model.util.QueryUser"
resultType="bysj.bysj.model.UserDoctor">
select user_id, avatar, user_name, age, sex, phonenumber, status, prove, register_time
from user_doctor
<where>
<if test="user_name != null">
and user_name like concat('%', #{user_name}, '%')
</if>
<if test="phonenumber != null">
and phonenumber like concat('%', #{phonenumber}, '%')
</if>
<if test="status != null">
and status = #{status}
</if>
<if test="beginTime != null">
and date_format(register_time, '%y%m%d') >= date_format(#{beginTime}, '%y%m%d')
</if>
<if test="endTime != null">
and date_format(register_time, '%y%m%d') <= date_format(#{endTime}, '%y%m%d')
</if>
</where>
</select>
- 然后再controller里面实现数据的处理
@PostMapping("queryByParams") // 此处 post 实际充当 get 使用
public AjaxResult queryByParams (HttpServletRequest request, @RequestBody QueryUser queryUser) {
try {
String token = request.getHeader("Authorization");
try {
Claims claims = JwtUtils.checkToken(token);
if (claims == null) {
return AjaxResult.error(50012, "未传输 token");
}
} catch (ExpiredJwtException e) {
return AjaxResult.error(50013, "token 已过期");
} catch (Exception e) {
return AjaxResult.error(50014, "无效 token");
}
List<UserDoctor> userDoctorList = userDoctorMapper.queryByParams(queryUser);
return AjaxResult.success(200, "查询中医生成功", userDoctorList);
} catch (Exception e) {
return AjaxResult.error(5005, "查询中医生失败");
}
}
这里贴的代码是已经实现了后端通用类型的之后的写法
--------------------这是分解符------------------
后端通用返回类型
使用的是Ajax的三个部分
状态码code 返回内容msg 数据对象 data里面是根据 ajax标准实现的 我的200是正确的 其他的都是错的…
在controller里面建一个AjaxResult类
package bysj.bysj.controller;
import java.util.HashMap;
public class AjaxResult extends HashMap<String, Object> {
/** 状态码 */
private static final String CODE_TAG = "code";
/** 返回内容 */
private static final String MSG_TAG = "msg";
/** 数据对象 */
private static final String DATA_TAG = "data";
public AjaxResult(int code, String msg, Object data)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (data != null)
{
super.put(DATA_TAG, data);
}
}
public static AjaxResult success(Integer code, String msg, Object data)
{
return new AjaxResult(code, msg, data);
}
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
}
这样整体的后端返回给前面的类型就变了 这样需要在前面增加一个拦截器 将数据类型转回来
// response 拦截器
service.interceptors.response.use(
response => {
const res = response.data
console.log('response 拦截器', response) // for debug
if (res.code !== 200) {
Message({ message: res.msg || 'Error', type: 'error', duration: 2 * 1000 })
return Promise.reject(new Error(res.msg || 'Error'))
} else {
return res
}
},
error => {
console.log('response 拦截器 error' + error) // for debug
Message({ message: error.message, type: 'error', duration: 2 * 1000 }) // 提示
return Promise.reject(error)
}
)
记得给每一个请求都改了 就行了 这样后端通用返回类型也就变了。
-----------这是一个分解符------------
添加token
在自己的后端项目添加
<!--token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
<systemPath>${project.basedir}/src/libs/jjwt-0.9.1.jar</systemPath>
</dependency>
我的idea是不可以下载的所以我在网上下载了一个jar包,然后再项目里面添加了一个libs文件夹然后方进去 最后加一行systemPath就可以了 其他人过不可以下载包的话可以效仿这个方法
在工具类里面添加一个jwt工具类
用于后端创建token 还有checkToken
package bysj.bysj.utils;
import io.jsonwebtoken.*;
import java.util.Date;
import java.util.UUID;
/**
* JWT 工具类
*/
public class JwtUtils {
private static long time = 1000 * 60 * 30; // 30 分钟
private static String signature = "jiandanyixieyexing"; // 签名
/**
* 生成 Token
* @return 生成的 Token 值
*/
public static String createToken(String user_name, Integer user_id ) {
JwtBuilder jwtBuilder = Jwts.builder();
String token = jwtBuilder
// 设置 Token 的 Header 部分
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
// 设置 Token 的 Payload 部分
.claim("user_name", user_name)
.claim("user_id", user_id)
// 设置 Token 的有效期
.setExpiration(new Date(System.currentTimeMillis() + time))
// 设置 Token 的签发时间
.setIssuedAt(new Date(System.currentTimeMillis()))
.setId(UUID.randomUUID().toString())
// 设置 Token 的 Signature 部分:第一个参数为加密类型,第二个参数为密钥
.signWith(SignatureAlgorithm.HS256, signature)
// 将 Token 的三部分进行连接
.compact();
return token;
}
/**
* 校验 Token 的存在以及有效性
* @param token token值
* @return 检测结果
*/
public static Claims checkToken(String token) {
if(token == null) return null; // 如果 token 为 null,校验失败,直接返回 null
try { // 解析 token,如果解析失败,会抛出异常,表示 token 无效,否则 token 有效
Claims claims = Jwts.parser()
.setSigningKey(signature)
.parseClaimsJws(token)
.getBody();
return claims;
} catch (ExpiredJwtException e) {
throw e;
} catch (Exception e) {
throw e;
}
}
这样后在接受或者发送请求的时候可以检查对应请求的token具体注释代码里有就不过多赘述了
现在我代码写的比较初级所以直接在所有的代码里面都加入了trycatch 检查是否token有没有效正不正确
try {
String token = request.getHeader("Authorization");
try {
Claims claims = JwtUtils.checkToken(token);
if (claims == null) {
return AjaxResult.error(50012, "未传输 token");
}
} catch (ExpiredJwtException e) {
return AjaxResult.error(50013, "token 已过期");
} catch (Exception e) {
return AjaxResult.error(50014, "无效 token");
}
简单的实例一下
@GetMapping("getUserDoctorList")
public AjaxResult getUserDoctorList (HttpServletRequest request, @RequestParam Integer pageNum, @RequestParam Integer pageSize) {
try {
String token = request.getHeader("Authorization");
try {
Claims claims = JwtUtils.checkToken(token);
if (claims == null) {
return AjaxResult.error(50012, "未传输 token");
}
} catch (ExpiredJwtException e) {
return AjaxResult.error(50013, "token 已过期");
} catch (Exception e) {
return AjaxResult.error(50014, "无效 token");
}
PageHelper.startPage(pageNum, pageSize);
List<UserDoctor> userDoctorList = userDoctorMapper.findDoctorList();
return AjaxResult.success(200, "获取中医生列表数据成功", userDoctorList);
} catch (Exception e) {
return AjaxResult.error(5000, "获取中医生列表数据失败");
}
}
这是我获取中医生的操作
到这里后端的token创建和检查实现了
接下来是前段对于后端token 的处理还有前段发送待有token请求给后端进行处理
首先在utils里面创建一个auth.js存一下token处理function 是token和cookie之间的处理。
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function setToken (token) { // 将 token 存入 cookie
return Cookies.set(TokenKey, token)
}
export function getToken () { // 从 cookie 中取出 token
return Cookies.get(TokenKey)
}
export function removeToken () { // 将 cookie 中的 token 移除
return Cookies.remove(TokenKey)
}
然后将这里的getToken引入到request.js 在里面涉及一个request拦截器,这样 让每一个请求都带上token
// request 拦截器
service.interceptors.request.use(
config => {
if (store.state.app.token) { // 让每个请求携带 token,['X-Token'] 是一个自定义 headers key
config.headers.Authorization = getToken()
}
return config
},
error => { // 处理请求错误
console.log(error) // for debug
return Promise.reject(error)
}
)
这样前后端所发出的请求都带上了token 同时 这里的token 也不仅仅只存在cookie,会话对应的sessionMessge里面也存有
-------------------这里是分解符--------------------
弄完token后就是整管理员登录。使用token鉴定状态。
- 首先创建一个登录界面,里面写上登录对应的方法还有输入密码简单的规则
<template>
<div class="login-wrapper">
<el-form class="login-form" :model='loginForm' :rules='loginRules' ref="ruleForm">
<h3 class="login-title">后台管理系统</h3>
<el-form-item prop='user_name'>
<el-input v-model="loginForm.user_name"
prefix-icon='el-icon-user-solid' placeholder='请输入账号'>
</el-input>
</el-form-item>
<el-form-item prop='password'>
<el-input v-model="loginForm.password"
prefix-icon='el-icon-lock' placeholder='请输入密码'
type='password'>
</el-input>
</el-form-item>
<el-button type='primary' @click="handleLogin">登录</el-button>
</el-form>
</div>
</template>
<script>
export default {
name: 'Login',
data () {
return {
loginForm: {
user_name: '', // 用户名
password: '' // 密码
},
loginRules: {
user_name: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 10, message: '长度在 6 到 10 个字符', trigger: 'blur' }
]
}
}
},
methods: {
handleLogin () {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.$store.dispatch('app/login', this.loginForm).then(() => {
this.$router.push({ path: '/' })
}).catch((err) => {
console.log(`登录失败: ${err}`)
})
} else {
return false
}
})
}
}
}
</script>
<style lang="less" scoped>
.login-wrapper {
background-image: url('http://vue.ruoyi.vip/static/img/login-background.f9f49138.jpg');
background-size: cover;
height: 100vh;
position: relative;
.login-form {
width: 400px;
border-radius: 6px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, .5);
box-shadow: 0 0 10px 0 #ccc;
box-sizing: border-box;
padding: 50px 30px;
padding-top: 20px;
.login-title {
width: 100%;
text-align: center;
color: #000;
}
.el-button {
width: 100%;
}
}
}
</style>
- 然后再router里面为他分配路由和前面的管理界面是并列。
{
path: '/login',
component: () =>import('../login'),
}
- 在仓库里面添加login方法
login ({ commit }, userInfo) {
return adminLogin(userInfo).then(res => {
commit('set_token', res.data.token) // 将 token 存储进 Vuex 实例中,相当于内存
sessionStorage.setItem('userinfo', JSON.stringify(res.data))
setToken(res.data.token) // 将 token 存储进 cookie
}).catch(error => {
console.log(error)
})
},
- 最后添加一个admin.js登录请求
import request from '../request'
export function adminLogin (data) {
return request({
url: '/adminLogin',
method: 'post',
data: data
})
}
前端的登录就写完了。
接下来是后端的登录验证
- 首先实体化一个UserAdmin实体类
package bysj.bysj.model;
import lombok.Data;
@Data
public class UserAdmin {
private Integer user_id;
private String user_name;
private String password;
private String avatar;
private String token;
}
- 然后再mapper里面建一个UserAdminMapper接口
package bysj.bysj.mapper;
import bysj.bysj.model.UserAdmin;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserAdminMapper {
UserAdmin adminLogin(UserAdmin userAdmin);
}
- 然后再xml文件创建一个UserAdminMapper写sql语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 指明 接口的全类名 -->
<mapper namespace="bysj.bysj.mapper.UserAdminMapper">
<!-- select * from user_admin where user_name = 'zqh' and password = '654321' -->
<select id="adminLogin"
resultType="bysj.bysj.model.UserAdmin">
select user_id, user_name from user_admin where user_name = #{user_name} and password = #{password}
</select>
</mapper>
- 最后在controller层添加一个UserAdminrController
package bysj.bysj.controller;
import bysj.bysj.mapper.UserAdminMapper;
import bysj.bysj.model.UserAdmin;
import bysj.bysj.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@CrossOrigin(originPatterns = "*", methods = { RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE })
public class UserAdminController {
@Autowired
private UserAdminMapper userAdminMapper;
@PostMapping("adminLogin")
public AjaxResult adminLogin (HttpServletRequest request, @RequestBody UserAdmin userAdmin) {
try {
UserAdmin loginResult = userAdminMapper.adminLogin(userAdmin);
if(loginResult != null) {
loginResult.setToken(JwtUtils.createToken(loginResult.getUser_name(), loginResult.getUser_id()));
return AjaxResult.success(200, "管理员" + userAdmin .getUser_name() +"登录成功", loginResult);
} else {
return AjaxResult.error(5006, "管理员" + userAdmin .getUser_name() +"登录失败");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error(5006, "管理员" + userAdmin .getUser_name() +"登录失败");
}
}
}
后端的登录验证也就完成了。
---------------这是分解符----------------------
还有一个退出功能 今天就准备睡觉觉了
实现是在前段的header里面加一个text按钮
<template>
<div id="layout-header">
<hamburger id="hamburger" :is-active='sidebar.opened' @toggleClick='toggleClick'/>
<div>
<span style="color: red; font-size: 14px;"> 欢迎,{{ user_name }} </span>
<el-button type="text" @click="handleLogout">退出</el-button>
</div>
</div>
</template>
写一个handleLogout 的click事件
handleLogout: function () { // 退出登录
this.$confirm('此操作将退出登录 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
return this.$store.dispatch('app/logout')
}).then(() => {
this.$router.push({ path: '/login' })
}).catch(error => {
console.log(error)
})
}
在store里面的module的app.js里面写logout对应的token操作 也就是销毁token 这样就完成了退出别忘记存在session里面的userinfo
logout ({ commit }) {
return new Promise((resolve, reject) => {
try {
commit('remove_toke')
removeToken()
sessionStorage.removeItem('userinfo')
resolve()
} catch (e) {
reject(e)
}
})
}