一、用户的登陆认证
1.前端显示登陆页面
登录页组件
Login.vue
<template>
<div class="login box">
<img src="../../static/img/Loginbg.3377d0c.jpg" alt="">
<div class="login">
<div class="login-title">
<img src="../../static/img/Logotitle.1ba5466.png" alt="">
<p>帮助有志向的年轻人通过努力学习获得体面的工作和生活!</p>
</div>
<div class="login_box">
<div class="title">
<span @click="login_type=0">密码登录</span>
<span @click="login_type=1">短信登录</span>
</div>
<div class="inp" v-if="login_type==0">
<input v-model = "username" type="text" placeholder="用户名 / 手机号码" class="user">
<input v-model = "password" type="password" name="" class="pwd" placeholder="密码">
<div id="geetest1"></div>
<div class="rember">
<p>
<input type="checkbox" class="no" name="a" v-model="remember"/>
<span>记住密码</span>
</p>
<p>忘记密码</p>
</div>
<button class="login_btn" @click="loginHandle">登录</button>
<p class="go_login" >没有账号 <span>立即注册</span></p>
</div>
<div class="inp" v-show="login_type==1">
<input v-model = "username" type="text" placeholder="手机号码" class="user">
<input v-model = "password" type="text" class="pwd" placeholder="短信验证码">
<button id="get_code">获取验证码</button>
<button class="login_btn">登录</button>
<p class="go_login" >没有账号 <span>立即注册</span></p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Login',
data(){
return {
login_type: 0,
username:"",
password:"",
remember:false,
}
},
methods:{
loginHandle(){
this.$axios.post(`${this.$settings.Host}/users/login/`,{
username:this.username,
password:this.password,
}).then((res)=>{
console.log(res);
// console.log(this.remember);
if (this.remember){
localStorage.token = res.data.token;
localStorage.username = res.data.username;
localStorage.id = res.data.id;
sessionStorage.removeItem('token');
sessionStorage.removeItem('username');
sessionStorage.removeItem('id');
}else {
sessionStorage.token = res.data.token;
sessionStorage.username = res.data.username;
sessionStorage.id = res.data.id;
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('id');
}
this.$router.push('/');
}).catch((error)=>{
this.$alert('用户名password error', 'error msg', {
confirmButtonText: '确定',
});
})
}
},
};
</script>
<style scoped>
.box{
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.box img{
width: 100%;
min-height: 100%;
}
.box .login {
position: absolute;
width: 500px;
height: 400px;
top: 0;
left: 0;
margin: auto;
right: 0;
bottom: 0;
top: -338px;
}
.login .login-title{
width: 100%;
text-align: center;
}
.login-title img{
width: 190px;
height: auto;
}
.login-title p{
font-family: PingFangSC-Regular;
font-size: 18px;
color: #fff;
letter-spacing: .29px;
padding-top: 10px;
padding-bottom: 50px;
}
.login_box{
width: 400px;
height: auto;
background: #fff;
box-shadow: 0 2px 4px 0 rgba(0,0,0,.5);
border-radius: 4px;
margin: 0 auto;
padding-bottom: 40px;
}
.login_box .title{
font-size: 20px;
color: #9b9b9b;
letter-spacing: .32px;
border-bottom: 1px solid #e6e6e6;
display: flex;
justify-content: space-around;
padding: 50px 60px 0 60px;
margin-bottom: 20px;
cursor: pointer;
}
.login_box .title span:nth-of-type(1){
color: #4a4a4a;
border-bottom: 2px solid #84cc39;
}
.inp{
width: 350px;
margin: 0 auto;
}
.inp input{
border: 0;
outline: 0;
width: 100%;
height: 45px;
border-radius: 4px;
border: 1px solid #d9d9d9;
text-indent: 20px;
font-size: 14px;
background: #fff !important;
}
.inp input.user{
margin-bottom: 16px;
}
.inp .rember{
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
margin-top: 10px;
}
.inp .rember p:first-of-type{
font-size: 12px;
color: #4a4a4a;
letter-spacing: .19px;
margin-left: 22px;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
/*position: relative;*/
}
.inp .rember p:nth-of-type(2){
font-size: 14px;
color: #9b9b9b;
letter-spacing: .19px;
cursor: pointer;
}
.inp .rember input{
outline: 0;
width: 30px;
height: 45px;
border-radius: 4px;
border: 1px solid #d9d9d9;
text-indent: 20px;
font-size: 14px;
background: #fff !important;
}
.inp .rember p span{
display: inline-block;
font-size: 12px;
width: 100px;
/*position: absolute;*/
/*left: 20px;*/
}
#geetest{
margin-top: 20px;
}
.login_btn{
width: 100%;
height: 45px;
background: #84cc39;
border-radius: 5px;
font-size: 16px;
color: #fff;
letter-spacing: .26px;
margin-top: 30px;
}
.inp .go_login{
text-align: center;
font-size: 14px;
color: #9b9b9b;
letter-spacing: .26px;
padding-top: 20px;
}
.inp .go_login span{
color: #84cc39;
cursor: pointer;
}
</style>
绑定登陆页面路由地址
src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Login from "@/components/Login";
Vue.use(Router)
export default new Router({
mode:'history',
routes: [
{
path: '/',
component: Home
},
{
path: '/home',
component: Home
},
{
path: '/user/login',
component: Login
}
]
})
调整首页头部子组件中登陆按钮的链接信息
VHeader.vue
<router-link to="/user/login">
<button class="signin">登录</button>
</router-link>
2.后端实现登陆认证
Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给提供的表。认证系统包含:
- 用户管理
- 权限
- 用户组
- 密码哈希系统
- 用户登录或内容显示的表单和视图
- 一个可插拔的后台系统 admin
Django默认用户的认证机制依赖Session机制,我们在项目中将引入JWT认证机制,将用户的身份凭据存放在Token中,然后对接Django的认证系统,帮助我们来实现:
- 用户的数据模型
- 用户密码的加密与验证
- 用户的权限系统
Django用户模型类
Django认证系统中提供了用户模型类User保存用户的数据,默认的User包含以下常见的基本字段:
字段名 | 字段描述 |
---|---|
username | 必选。150个字符以内。 用户名可能包含字母数字,_ ,@ ,+ . 和- 个字符。 |
first_name | 可选(blank=True )。 少于等于30个字符。 |
last_name | 可选(blank=True )。 少于等于30个字符。 |
email | 可选(blank=True )。 邮箱地址。 |
password | 必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。 |
groups | 与Group 之间的多对多关系。 |
user_permissions | 与Permission 之间的多对多关系。 |
is_staff | 布尔值。 设置用户是否可以访问Admin 站点。 |
is_active | 布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。 |
is_superuser | 是否是超级用户。超级用户具有所有权限。 |
last_login | 用户最后一次登录的时间。 |
date_joined | 账户创建的时间。 当账号创建时,默认设置为当前的date/time。 |
上面缺少一些字段,所以后面我们会对它进行改造,比如说它里面没有手机号字段,后面我们需要加上。
常用方法:
-
set_password
(raw_password)设置用户的密码为给定的原始字符串,并负责密码的。 不会保存
User
对象。当None
为raw_password
时,密码将设置为一个不可用的密码。 -
check_password
(raw_password)如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。
管理器方法:
管理器方法即可以通过User.objects.
进行调用的方法。
-
create_user
(username, email=None, password=None, **extra_fields)创建、保存并返回一个
User
对象。 -
create_superuser
(username, email, password, **extra_fields)与
create_user()
相同,但是设置is_staff
和is_superuser
为True
。
3.创建用户模块的子应用
E:\axiangmu\luffcc\lufapi\lufapi\apps>python ../../manage.py startapp users
在settings.py文件中注册子应用
INSTALLED_APPS = [
...
'users',
]
4.创建自定义的用户模型类
Django认证系统中提供的用户模型类及方法很方便,我们可以使用这个模型类,但是字段有些无法满足项目需求,如本项目中需要保存用户的手机号,需要给模型类添加额外的字段。
Django提供了django.contrib.auth.models.AbstractUser
用户抽象模型类允许我们继承,扩展字段来使用Django认证系统的用户模型类。
我们可以在apps中创建Django应用users,并在配置文件中注册users应用。
在创建好的应用models.py
中定义用户的用户模型类。目前我们是将所有用户都放到一个表里面的,比如管理员,老师(上传课程),客户等等,以后我们通过用户组进行用户划分。
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
phone = models.CharField(max_length=16,null=True,blank=True)
wechat = models.CharField(max_length=16,null=True,blank=True)
class Meta:
db_table = 'ly_user'
verbose_name = '用户表'
verbose_name_plural = verbose_name
我们自定义的用户模型类还不能直接被Django的认证系统所识别,需要在配置文件中告知Django认证系统使用我们自定义的模型类。
在配置文件中进行设置
#注册自定义用户模型,格式:“应用名.模型类名”
AUTH_USER_MODEL = 'users.User'
AUTH_USER_MODEL
参数的设置以点.
来分隔,表示应用名.模型类名
。
注意:Django建议我们对于AUTH_USER_MODEL参数的设置一定要在第一次数据库迁移之前就设置好,否则后续使用可能出现未知错误。
执行数据库迁移
python manage.py makemigrations
python manage.py migrate
如果在第一次数据迁移以后,才设置AUTH_USER_MODEL自定义用户模型,则会报错。解决方案如下:
0. 先把现有的数据库导出备份,然后清掉数据库中所有的数据表。
1. 把开发者创建的所有子应用下面的migrations目录下除了__init__.py以外的所有迁移文件,只要涉及到用户的,一律删除,并将django-migrations表中的数据全部删除。
2. 把django.contrib.admin.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
3. 把django.contrib.auth.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
4. 把reversion.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。这个不在django目录里面,在site-packages里面,是xadmin安装的时候带的,它会记录用户信息,也需要删除
5. 把xadmin.migrations目录下除了__init__.py以外的所有迁移文件,全部删除。
6. 删除我们当前数据库中的所有表
7. 接下来,执行数据迁移(makemigrations和migrate),回顾第0步中的数据,将数据导入就可以了,以后如果要修改用户相关数据,不需要重复本次操作,直接数据迁移即可。
二、Django REST framework JWT
1.JWT的构成
JWT就一段字符串,由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
header
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将头部进行base64.b64encode()加密(该加密是可以对称解密的),构成了第一部分.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
python中base64加密解密
import base64
str1 = 'admin'
str2 = str1.encode()
b1 = base64.b64encode(str2) #数据越多,加密后的字符串越长
b2 = base64.b64decode(b1) #admin
各个语言中都有base64加密解密的功能,所以我们jwt为了安全,需要配合第三段加密
payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息可以存放下面三个部分信息。
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
-
iss: jwt签发者
-
sub: jwt所面向的用户
-
aud: 接收jwt的一方
-
exp: jwt的过期时间,这个过期时间必须要大于签发时间
-
nbf: 定义在什么时间之前,该jwt都是不可用的.
-
iat: jwt的签发时间
-
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
以上是JWT 规定的7个官方字段,供选用
公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload,json格式的数据:
{
"sub": "1234567890",
"exp": "3422335555", #时间戳形式
"name": "John Doe",
"admin": true
}
然后将其进行base64.b64encode() 加密,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret密钥
这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); //xxxx // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.
连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
认证流程图
![](https://img-blog.csdnimg.cn/20201028204716131.png?type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTU3NTgw,size_16,color_FFFFFF,t_70#pic_center)
关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。
2.安装配置JWT
安装
pip install djangorestframework-jwt -i https://mirrors.aliyun.com/pypi/simple/
配置(github网址:https://github.com/jpadilla/django-rest-framework-jwt)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
import datetime
JWT_AUTH = {
# 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
JWT_EXPIRATION_DELTA 指明token的有效期
django创建项目的时候,在settings配置文件中直接就给生成了一个serect_key,直接可以使用它作为我们jwt的serect_key,其实djangorestframework-jwt默认配置中就使用的它。
3.手动生成jwt(暂时用不到)
Django REST framework JWT 扩展的说明文档中提供了手动签发JWT的方法
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
4.后端实现登陆认证接口
Django REST framework JWT提供了登录获取token的视图,可以直接使用
在子应用users路由urls.py中
from rest_framework_jwt.views import obtain_jwt_token
from django.urls import path
urlpatterns = [
path(r'login/', obtain_jwt_token),
]
在主路由中,引入当前子应用的路由文件
path('users/', include("users.urls")),
-------待添加------
接下来,我们可以通过postman来测试下功能,但是jwt是通过username和password来进行登录认证处理的,所以我们要给真实数据,jwt会去我们配置的user表中去查询用户数据的。
三、前端实现登陆功能
在登陆组件中找到登陆按钮,绑定点击事件
<button class="login_btn" @click="loginHandle">登录</button>
在methods中请求后端
export default {
name: 'Login',
data(){
return {
login_type: 0,
username:"",
password:"",
remember:false,
}
},
methods:{
loginHandle(){
this.$axios.post(`${this.$settings.Host}/users/login/`,{
username:this.username,
password:this.password,
}).then((res)=>{
console.log(res);
// console.log(this.remember);
if (this.remember){
localStorage.token = res.data.token;
localStorage.username = res.data.username;
localStorage.id = res.data.id;
sessionStorage.removeItem('token');
sessionStorage.removeItem('username');
sessionStorage.removeItem('id');
}else {
sessionStorage.token = res.data.token;
sessionStorage.username = res.data.username;
sessionStorage.id = res.data.id;
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('id');
}
this.$router.push('/');
}).catch((error)=>{
this.$alert('用户名password error', 'error msg', {
confirmButtonText: '确定',
});
})
}
},
};
1.前端保存jwt
可将JWT保存在cookie中,也可以保存在浏览器的本地存储里,我们保存在浏览器本地存储中
浏览器的本地存储提供了sessionStorage 和 localStorage 两种,从属于window对象:
- sessionStorage 浏览器关闭即失效
- localStorage 长期有效
使用方法
sessionStorage.变量名 = 变量值 // 保存数据
sessionStorage.setItem("变量名","变量值") // 保存数据
sessionStorage.变量名 // 读取数据
sessionStorage.getItem("变量名") // 读取数据
sessionStorage.removeItem("变量名") // 清除单个数据
sessionStorage.clear() // 清除所有sessionStorage保存的数据
localStorage.变量名 = 变量值 // 保存数据
localStorage.setItem("变量名","变量值") // 保存数据
localStorage.变量名 // 读取数据
localStorage.getItem("变量名") // 读取数据
localStorage.removeItem("变量名") // 清除单个数据
localStorage.clear() // 清除所有sessionStorage保存的数据
登陆组件代码Login.vue
if (this.remember){
localStorage.token = res.data.token;
localStorage.username = res.data.username;
localStorage.id = res.data.id;
sessionStorage.removeItem('token');
sessionStorage.removeItem('username');
sessionStorage.removeItem('id');
}else {
sessionStorage.token = res.data.token;
sessionStorage.username = res.data.username;
sessionStorage.id = res.data.id;
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('id');
}
this.$router.push('/');
默认的返回值仅有token,还需在返回值中增加username和id,方便在客户端页面中显示当前登陆用户
通过修改该视图的返回值可以完成我们的需求。
在user/utils.py 中,
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
# print('>>>>>',user,type(user))
return {
'token': token,
'username': user.username,
'id':user.id
}
修改settings/dev.py配置文件
JWT_AUTH = {
# 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
登陆组件代码Login.vue
if (this.remember){
localStorage.token = res.data.token;
localStorage.username = res.data.username;
localStorage.id = res.data.id;
sessionStorage.removeItem('token');
sessionStorage.removeItem('username');
sessionStorage.removeItem('id');
}else {
sessionStorage.token = res.data.token;
sessionStorage.username = res.data.username;
sessionStorage.id = res.data.id;
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('id');
}
this.$router.push('/');
2.多条件登录
JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。
可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。
官方说:修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend,并重写authenticate方法。
authenticate(self, request, username=None, password=None, **kwargs)
方法的参数说明:
- request 本次认证的请求对象
- username 本次认证提供的用户账号
- password 本次认证提供的密码
想要让用户既可以以用户名登录,也可以以手机号登录,那么对于authenticate方法而言,username参数即表示用户名或者手机号。
重写authenticate方法的思路:
- 根据username参数查找用户User对象,username参数可能是用户名,也可能是手机号
- 若查找到User对象,调用User对象的check_password方法检查密码是否正确
在users应用下创建一个utils.py中编写:
from users import models
from django.db.models import Q
def get_user_obj(accout): #666
try:
user_obj = models.User.objects.get(Q(username=accout)|Q(phone=accout))
except:
return None
return user_obj
from django.contrib.auth.backends import ModelBackend
class CustomeModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
user_obj = get_user_obj(username)
if user_obj:
if user_obj.check_password(password):
return user_obj
else:
return None
在配置文件settings/dev.py中告知Django使用自定义的认证后端
AUTHENTICATION_BACKENDS = [
'users.utils.CustomeModelBackend',
]
以上就实现了我们通过用户名或者手机号的一个多条件登录。
跳转到首页之后,需要显示的不再是登录注册按钮,而是显示购物车,个人中心等内容,所以需要调增一下VHeader.vue组件。
3.前端首页实现登陆状态的判断
common/Vheader.vue组件代码:
<template>
<div class="total-header">
<div class="header">
<el-container>
<el-header height="80px" class="header-cont">
<el-row>
<el-col class="logo" :span="3">
<a href="/">
<img src="@/assets/header-logo.svg" alt="">
</a>
</el-col>
<el-col class="nav" :span="10">
<el-row>
<el-col :span="3" v-for="(value,index) in nav_data_list" :key="index">
<a :href="value.link" class="active" v-if="value.is_site">{{value.title}}</a>
<router-link :to="value.link" v-else>{{value.title}}</router-link>
</el-col>
</el-row>
</el-col>
<el-col :span="11" class="header-right-box">
<div class="search">
<input type="text" id="Input" placeholder="请输入想搜索的课程" style="" @blur="inputShowHandler" ref="Input"
v-show="!s_status">
<ul @click="ulShowHandler" v-show="s_status" class="search-ul" ref="xx">
<span>Python</span>
<span>Linux</span>
</ul>
<p>
<img class="icon" src="@/assets/sousuo1.png" alt="" v-show="s_status">
<img class="icon" src="@/assets/sousuo2.png" alt="" v-show="!s_status">
<img class="new" src="@/assets/new.png" alt="">
</p>
</div>
<div class="register" v-show="!token">
<router-link to="/user/login">
<button class="signin">登录</button>
</router-link>
|
<a target="_blank" href="https://www.luffycity.com/signup">
<router-link to="/register">
<button class="signup">注册</button>
</router-link>
</a>
</div>
<div class="shop-car" v-show="token">
<router-link to="/cart">
<b>6</b>
<img src="@/assets/shopcart.png" alt="">
<span>购物车 </span>
</router-link>
</div>
<div class="nav-right-box" v-show="token">
<div class="nav-right">
<router-link to="/myclass">
<div class="nav-study">我的教室</div>
</router-link>
<div class="nav-img" @mouseover="personInfoList" @mouseout="personInfoOut">
<img src="@/assets/touxiang.png" alt="" style="border: 1px solid rgb(243, 243, 243);">
<ul class="home-my-account" v-show="list_status" @mouseover="personInfoList">
<li>
我的账户
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
<li>
我的订单
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
<li>
贝里小卖铺
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
<li>
我的优惠券
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
<li>
<span>
我的消息
<b>(26)</b>
</span>
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
<li @click="logout">
退出
<img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
alt="">
</li>
</ul>
</div>
</div>
</div>
</el-col>
</el-row>
</el-header>
</el-container>
</div>
</div>
</template>
<script>
export default {
name: "Vheader",
data() {
return {
token: false, // 登录成功与否的标记
s_status: true, // 放大镜效果切换控制,默认input标签不显示
list_status: false, // 个人中心下拉菜单是否显示
nav_data_list:[],
}
},
created(){
this.get_nav_data();
this.check_login();
},
methods: {
ulShowHandler() {
// console.log(this);
this.s_status = false;
console.log(this.$refs);
this.$nextTick(function () {
console.log(this);
this.$refs.Input.focus();
});
},
inputShowHandler() {
console.log('xxxxx')
this.s_status = true;
},
personInfoList() {
this.list_status = true;
},
personInfoOut() {
this.list_status = false;
},
get_nav_data(){
this.$axios.get(`${this.$settings.Host}/home/nav/top/`)
.then((res)=>{
this.nav_data_list = res.data;
})
},
check_login(){
this.token = localStorage.token || sessionStorage.token;
//console.log(this.token);
},
// 退出登录
logout(){
sessionStorage.removeItem('token');
sessionStorage.removeItem('username');
sessionStorage.removeItem('id');
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('id');
console.log('xxxxxx')
this.check_login();
// this.token = false;
}
}
}
</script>
<style scoped>
.header-cont .nav .active {
color: #f5a623;
font-weight: 500;
border-bottom: 2px solid #f5a623;
}
.total-header {
min-width: 1200px;
z-index: 100;
box-shadow: 0 4px 8px 0 hsla(0, 0%, 59%, .1);
}
.header {
width: 1200px;
margin: 0 auto;
}
.header .el-header {
padding: 0;
}
.logo {
height: 80px;
/*line-height: 80px;*/
/*text-align: center;*/
display: flex; /* css3里面的弹性布局,高度设定好之后,设置这个属性就能让里面的内容居中 */
align-items: center;
}
.nav .el-row .el-col {
height: 80px;
line-height: 80px;
text-align: center;
}
.nav a {
font-size: 15px;
font-weight: 400;
cursor: pointer;
color: #4a4a4a;
text-decoration: none;
}
.nav .el-row .el-col a:hover {
border-bottom: 2px solid #f5a623
}
.header-cont {
position: relative;
}
.search input {
width: 185px;
height: 26px;
font-size: 14px;
color: #4a4a4a;
border: none;
border-bottom: 1px solid #ffc210;
outline: none;
}
.search ul {
width: 185px;
height: 26px;
display: flex;
align-items: center;
padding: 0;
padding-bottom: 3px;
border-bottom: 1px solid hsla(0, 0%, 59%, .25);
cursor: text;
margin: 0;
font-family: Helvetica Neue, Helvetica, Microsoft YaHei, Arial, sans-serif;
}
.search .search-ul, .search #Input {
padding-top: 10px;
}
.search ul span {
color: #545c63;
font-size: 12px;
padding: 3px 12px;
background: #eeeeef;
cursor: pointer;
margin-right: 3px;
border-radius: 11px;
}
.hide {
display: none;
}
.search {
height: auto;
display: flex;
}
.search p {
position: relative;
margin-right: 20px;
margin-left: 4px;
}
.search p .icon {
width: 16px;
height: 16px;
cursor: pointer;
}
.search p .new {
width: 18px;
height: 10px;
position: absolute;
left: 15px;
top: 0;
}
.register {
height: 36px;
display: flex;
align-items: center;
line-height: 36px;
}
.register .signin, .register .signup {
font-size: 14px;
color: #5e5e5e;
white-space: nowrap;
}
.register button {
outline: none;
cursor: pointer;
border: none;
background: transparent;
}
.register a {
color: #000;
outline: none;
}
.header-right-box {
height: 100%;
display: flex;
align-items: center;
font-size: 15px;
color: #4a4a4a;
position: absolute;
right: 0;
top: 0;
}
.shop-car {
width: 99px;
height: 28px;
border-radius: 15px;
margin-right: 20px;
background: #f7f7f7;
display: flex;
align-items: center;
justify-content: center;
position: relative;
cursor: pointer;
}
.shop-car b {
position: absolute;
left: 28px;
top: -1px;
width: 18px;
height: 16px;
color: #fff;
font-size: 12px;
font-weight: 350;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background: #ff0826;
overflow: hidden;
transform: scale(.8);
}
.shop-car img {
width: 20px;
height: 20px;
margin-right: 7px;
}
.nav-right-box {
position: relative;
}
.nav-right-box .nav-right {
float: right;
display: flex;
height: 100%;
line-height: 60px;
position: relative;
}
.nav-right .nav-study {
font-size: 15px;
font-weight: 300;
color: #5e5e5e;
margin-right: 20px;
cursor: pointer;
}
.nav-right .nav-study:hover {
color: #000;
}
.nav-img img {
width: 26px;
height: 26px;
border-radius: 50%;
display: inline-block;
cursor: pointer;
}
.home-my-account {
position: absolute;
right: 0;
top: 60px;
z-index: 101;
width: 190px;
height: auto;
background: #fff;
border-radius: 4px;
box-shadow: 0 4px 8px 0 #d0d0d0;
}
li {
list-style: none;
}
.home-my-account li {
height: 40px;
font-size: 14px;
font-weight: 300;
color: #5e5e5e;
padding-left: 20px;
padding-right: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
.home-my-account li img {
cursor: pointer;
width: 5px;
height: 10px;
}
.home-my-account li span {
height: 40px;
display: flex;
align-items: center;
}
.home-my-account li span b {
font-weight: 300;
margin-top: -2px;
}
</style>
以上也包括退出功能。