今天完成了客户端的基本布局,用户端的登录,注册页面。先上效果。
怎样创建vue项目还有导入elementui依赖这里就不讲了,可以直接百度查一下很简单。创建完成后 添加一个layout(布局) login(登录) 目录 api里面分别创建一个community还有dcotor用于存不同的两个角色的请求,我才用的布局是简单的上下布局 所以在layout里面创建了header还有content,utils存工具(目前只有一个用于token的设置,获取还有销毁)
视图自然里面有四个部分,article,mycenter,service,shopping这是我最后的结构。
头部 写了四个小标题还有一个测试界面 为了更加美观,所以我看见网上一个鼠标在上面会显示一个下划线还有焦点在此
同时设置头部为最上层 也就是不会被下面的内容覆盖,只会一直维持一个下滑 将几个服务都放在了一个ul里面然后重写这个ul的样式,下划线的显示是用了伪元素选择器 内容为空,这里有一个就是怎么给那个下划线放在正中间,如果说直接放的话是会以中间为原点开始,这样的话就会出现在右边,所以这里减去百分之50就可以了,同时这里还有一个逻辑关系就是当存在vuex里面的用户信息为空的时候显示登陆注册按钮,不然显示欢迎zqh后面再显示一个退出的text的按钮
<template>
<div id="layout-header">
<ul id="top-nav">
<li @click="handleNavgation('/article')" :class="{ active: currentPage === '/article' }">文章阅读</li>
<li @click="handleNavgation('/service')" :class="{ active: currentPage === '/service' }">服务选择</li>
<li @click="handleNavgation('/shoppingcart')" :class="{ active: currentPage === '/shoppingcart' }">购物车</li>
<li @click="handleNavgation('/mycenter')" :class="{ active: currentPage === '/mycenter' }">个人中心</li>
<li @click="handleNavgation('/test')" :class="{ active: currentPage === '/test' }">测试页面</li>
</ul>
<div v-if="$store.state.user.userinfo === null" class="loginButton">
<el-button type="primary" size="mini" @click="handleLogin">登录/注册</el-button>
</div>
<div v-else class="welcome">
<span style="color: red; font-size: 14px;"> 欢迎, {{ $store.state.user.userinfo.user_name }}</span>
<el-button type="text" @click="handleLogout">退出</el-button>
</div>
</div>
</template>
<script>
export default {
name: 'LayoutHeader',
data () {
return {
currentPage: ''
}
},
created () {
this.currentPage = this.$route.path
},
methods: {
handleLogin () {
this.$router.push({ path: '/login' })
},
handleNavgation (target) {
this.currentPage = target
this.$router.push({ path: target })
},
handleLogout: function () { // 退出登录
this.$confirm('此操作将退出登录 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
return this.$store.dispatch('app/logout')
}).then(() => {
this.$router.push({ path: '/login' })
window.location.reload()
}).catch(error => {
console.log(error)
})
}
}
}
</script>
<style lang="less" scoped>
#layout-header {
width: 100%; box-shadow: 0 0 10px 0 #efefef;
position: fixed; top: 0;
z-index: 10000;
display: flex; align-items: center;
}
.loginButton {
margin: 0 25px;
}
.welcome {
margin: 0 25px;
}
#top-nav {
list-style-type: none; margin: 0; padding: 0; // 重置 ul li 样式
display: flex; flex: 1;
height: 80px; box-sizing: border-box;
li:first-child {
padding-left: 25px;
}
li {
width: 20%; padding: 25px 0; text-align: center;
background-color: #fff;
cursor: pointer; position: relative;
color: #333d48; font-weight: bold; font-size: 26px;
transition: .5s;
&:hover { // 伪类选择器
color: #49b849;
}
}
.active::after { // 伪元素选择器
content: '';
display: block;
width: 40px; height: 4px; border-radius: 2px;
background-color: #49b849;
position: absolute; // 子绝父相
transform: translateX(-50%);
bottom: 8px;
left: 50%;
}
}
</style>
代码如上,里面还有登陆的请求退出销毁token啥的,别急慢慢来。
---------------------------------这是分割线-------------------------------------------
下面是登录界面模仿的也是若依登陆系统的登录界面,不过我需要一个单选框用于表示身份,单选框el-radio 的label标签表示1的时候是我是中医生0为社区人员,点击登录会查询不同的表,数据库里面有两个表,同时还有前面的输入规范,当进入注册页面的时候,同样需要选择角色,这样能给不同的注册用户添加到不同的表里面,这里还有一个比较重要的就是在选择我是中医生的我这里会弹出一个多选框,多选框的内容是来时基本技能列表的数据库中(findAllBasicSkill)这里写了一个请求,同时注册需要两次输入密码 比较两侧密码一致继续,为了让那一行提示和下面的复选框并列,我复写了她的width属性 采用了!important方法设置覆盖
<template>
<div id="wrapper">
<div id="wrapper_inner">
<div id="state-change">
<span :class="{ acitve: isLogin }" @click="hanldeChange(true)">登录</span><span :class="{ acitve: !isLogin }" @click="hanldeChange(false)">注册</span>
</div>
<template v-if="isLogin">
<el-form :model="loginForm" :rules="loginRules" ref="loginRef" label-position="right" label-width="100px">
<el-form-item label="用户名" 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 label="密码" prop='password'>
<el-input v-model="loginForm.password"
prefix-icon='el-icon-lock' placeholder='请输入密码'
type='password'>
</el-input>
</el-form-item>
<el-form-item label="身份" prop='role'>
<el-radio-group v-model="loginForm.role">
<el-radio label="1">我是中医生</el-radio>
<el-radio label="0">我是社区人员</el-radio>
</el-radio-group>
</el-form-item>
<el-button type='primary' @click="handleLogin">登录</el-button>
</el-form>
</template>
<template v-else>
<el-form :model='registerForm' :rules='registerRules' label-position="right" label-width="100px">
<el-form-item prop='user_name' label="用户名">
<el-input v-model="registerForm.user_name"
prefix-icon='el-icon-user-solid' placeholder='请输入用户名'>
</el-input>
</el-form-item>
<el-form-item prop='password' label="密码">
<el-input v-model="registerForm.password"
prefix-icon='el-icon-lock' placeholder='请输入密码'
type='password'>
</el-input>
</el-form-item>
<el-form-item prop='againPassword' label="再次输入">
<el-input v-model="registerForm.againPassword"
prefix-icon='el-icon-lock' placeholder='请再次输入密码'
type='password'>
</el-input>
</el-form-item>
<el-form-item label="身份">
<el-radio-group v-model="registerForm.role"
@change="handleIsDoctor">
<el-radio label="1">我是中医生</el-radio>
<el-radio label="0">我是社区人员</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="basicSkillList">
<el-form-item class="resetClass" label="请勾选你所会的技能,特殊技能请注册后申请">
<el-checkbox-group v-model="registerForm.basicSkillList">
<el-checkbox v-for="item in basicSkillList" :key="item.skill_id" :label="item.skill_id" name="type">{{ item.skill_name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</template>
<el-button type='primary' @click="handleLogin">注册</el-button>
</el-form>
</template>
</div>
</div>
</template>
<script>
import { findAllBasicSkill } from '../api/doctor/register'
export default {
name: 'Login',
data () {
return {
isLogin: true,
loginForm: {
user_name: '', // 用户名
password: '', // 密码
role: '0'
},
loginRules: {
user_name: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 10, message: '长度在 6 到 10 个字符', trigger: 'blur' }
],
role: [
{ required: true, message: '请选择身份', trigger: 'blur' }
]
},
registerForm: {
user_name: '', // 注册用户名
password: '', // 注册密码
againPassword: '', // 再次输入的密码
role: '0', // 身份:0 表示社区人员,1 表示中医生
basicSkillList: [] // 中医生的基础技能列表
},
basicSkillList: null,
registerRules: {
user_name: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 10, message: '长度在 6 到 10 个字符', trigger: 'blur' }
],
againPassword: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 10, message: '长度在 6 到 10 个字符', trigger: 'blur' }
],
role: [
{ required: true, message: '请选择身份', trigger: 'blur' }
]
}
}
},
methods: {
hanldeChange (state) {
this.isLogin = state
},
handleIsDoctor (label) {
if (label === '1') {
findAllBasicSkill().then(res => {
this.basicSkillList = res.data
})
} else if (label === '0') {
this.basicSkillList = null
}
},
handleLogin () {
this.$refs.loginRef.validate((valid) => {
if (valid) {
if (this.loginForm.role === '1') {
console.log('我是中医生,我要登录')
} else if (this.loginForm.role === '0') {
this.$store.dispatch('app/communityLogin', this.loginForm).then(() => {
this.$router.push({ path: '/' })
window.location.reload() // ?
}).catch((err) => {
console.log(`登录失败: ${err}`)
})
}
} else {
return false
}
})
}
}
}
</script>
<style lang="less" scoped>
#wrapper {
background-image: url('http://vue.ruoyi.vip/static/img/login-background.f9f49138.jpg');
background-size: cover; height: calc(100vh - 90px); position: relative;
#wrapper_inner {
width: 600px; border-radius: 6px; box-shadow: 0 0 10px 0 #ccc;
box-sizing: border-box; padding: 50px 30px; padding-top: 20px;
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, .7);
#state-change {
text-align: center;
margin-bottom: 20px;
span {
padding: 10px 15px;
font-weight: bold;
position: relative;
}
.acitve::after {
content: ''; width: 24px; height: 2px; border-radius: 2px;
background-color: #000; display: block;
position: absolute;
bottom: 0; left: 50%;
transform: translateX(-50%);
}
}
.el-button {
width: 100%;
}
.resetClass /deep/ .el-form-item__label {
width: 100% !important; text-align: center !important;
}
.resetClass /deep/ .el-form-item__content {
margin-left: 72px !important;
}
}
}
</style>
登录注册页面写完了
-----------------------------------分割线----------------------------------
添加axios 根据官方文档,双击项目空白处 终端打开,然后输入 npm install axios
。
这大佬讲解的很详细
主要是请求,后端添加axios,刚好直接引出后端代码
今天写了两个后端的请求 userCommunityLogin(社区登录) findAllBasicSkill(获取基础技能标签) 两个一起说了。
- 首先社区登录需要创建一个实体类在model层 ,基础技能表有
package bysj.bysj.model;
import lombok.Data;
import java.util.Date;
/**
* 用户:社区类
*/
@Data
public class UserCommunity {
private Integer user_id;
private String user_name;
private String password;
private String phonenumber;
private Integer age;
private String sex;
private String avatar;
private String status;
private Date register_time;
private String token;
}
- 然后再mapper层实现对应的xml
<?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.BasicSkillMapper">
<select id="findAll"
resultType="bysj.bysj.model.BasicSkill">
select * from basic_skill
</select>
</mapper>
<?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.UserCommunityMapper">
<select id="userCommunityLogin"
resultType="bysj.bysj.model.UserCommunity">
select user_id, user_name from user_community where user_name = #{user_name} and password = #{password}
</select>
</mapper>
- 然后再mapper层定义对应的接口添加对应的注解
package bysj.bysj.mapper;
import bysj.bysj.model.BasicSkill;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface BasicSkillMapper {
List<BasicSkill> findAll();
}
package bysj.bysj.mapper;
import bysj.bysj.model.UserCommunity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserCommunityMapper {
UserCommunity userCommunityLogin(UserCommunity userCommunity);
}
- 最重要的controller层 获取基本技能表,还有社区登录,不能忘记token还有axios
package bysj.bysj.controller;
import bysj.bysj.mapper.UserCommunityMapper;
import bysj.bysj.model.UserCommunity;
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 UserCommunityController {
@Autowired
private UserCommunityMapper userCommunityMapper;
@PostMapping("userCommunityLogin")
public AjaxResult userCommunityLogin (HttpServletRequest request, @RequestBody UserCommunity userCommunity) {
try {
UserCommunity loginResult = userCommunityMapper.userCommunityLogin(userCommunity);
if(loginResult != null) {
loginResult.setToken(JwtUtils.createToken(loginResult.getUser_name(), loginResult.getUser_id()));
return AjaxResult.success(200, "用户" + userCommunity .getUser_name() +"登录成功", loginResult);
} else {
return AjaxResult.error(5006, "用户" + userCommunity .getUser_name() +"登录失败");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error(5006, "用户" + userCommunity .getUser_name() +"登录失败");
}
}
}
package bysj.bysj.controller;
import bysj.bysj.mapper.BasicSkillMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(originPatterns = "*", methods = { RequestMethod.GET })
public class BasicSkillController {
@Autowired
private BasicSkillMapper basicSkillMapper;
@GetMapping("front/findAll")
public AjaxResult findAll () {
try {
return AjaxResult.success(200, "获取基础技能列表成功", basicSkillMapper.findAll());
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error(5001, "获取基础技能列表失败");
}
}
}
主要是注意token还有ajax 用postman测试请求
沟通前端前端api创建对应的js
import request from '../request'
export function userCommunityLogin (data) {
return request({
url: '/userCommunityLogin',
method: 'post',
data: data
})
}
import request from '../request'
export function findAllBasicSkill () {
return request({
url: '/front/findAll',
method: 'get'
})
}
同时在request.js里面添加拦截器
import axios from 'axios'
import store from '../store'
import { getToken } from '../utils/auth'
import { Message } from 'element-ui'
// 创建一个 axios 实例
const service = axios.create({
baseURL: 'http://localhost:8085',
timeout: 5000
})
// 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)
}
)
// 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)
}
)
export default service
同时在utils里面添加auth.js 用于发送 获取 销毁 token
import Cookies from 'js-cookie'
const TokenKey = 'BysjUser-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)
}