仿写网易云-项目初始化-扫描二维码登录

2 篇文章 0 订阅

1. 项目初始化

在这里插入图片描述

2. 安装使用 Element UI

这里使用的是全部引入,比较方便

  • 安装 element ui :npm i element-ui -S

    全部引入 在 main.js 中添加下面的代码

    • //  element ui
      import ElementUI from 'element-ui'
      import 'element-ui/lib/theme-chalk/index.css'
      Vue.use(ElementUI)
      

    按需引入

  • 安装 babel-plugin-componentnpm install babel-plugin-component -D

  • 修改 babel.config.js 文件:

    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset',
        ['@babel/preset-env', { modules: false }]
      ],
      plugins: [
        [
          'component',
          {
            libraryName: 'element-ui',
            styleLibraryName: 'theme-chalk'
          }
        ]
      ]
    }
    
    
  • 按需引入使用案例,在 main.js

    //  element ui
    import { Button, Select } from 'element-ui'
    
    Vue.component(Button)
    Vue.component(Select)
    

3. 设置一下页面的布局

  • Header
  • Footer
  • Side
  • Main

4. 开始写 Header

头部导航

  • 左边的 HeaderLogo
  • 中间的搜索框 HeaderSearch
  • 右边的头像昵称(登录状态)或显示未登录 HeaderRight

5. 点击未登录弹出登录窗口

5.0 添加代理解决跨域问题

在写扫码登录的时候发现及时二维码状态返回扫码登录成功并且收到cookie的情况下,再次访问接口查询当前的状态的时候还是一个未登录的状态,原因是因为本地node运行在3000的端口,而vue项目占用8080的端口,端口号不一致导致跨域问题,8080端口的cookie再次访问3000的端口时没有被携带,所以这里使用代理的方式来解决跨域问题。
在 vue.config.js 中加入 devServer 节点,并加入下面的配置:

  • target:接口域名
  • changeOrigin:表示是否跨域
devServer: {
    proxy: {
      '/': {
        target: 'http://192.168.3.16:3000',
        ws: false,
        changeOrigin: true
      }
    }
  }

5.1 原理分析

主要用到 Element 组件的 Dialog对话框

参考了这篇博客: vue之登录弹框Dialog对话框实现

但是这里与此篇博客不同在于,这个弹窗有三个页面

(所以需要用到路由Router来进行切换):

  • 扫码登录
  • 电话号码登录
  • 注册

在这里插入图片描述

  • Dialog 的使用

    • 官方使用文档:
    • el-dialog 设置一个居中和宽度
    • 在弹出框 el-dialog 中加入一个占位符 router-view 根据不同路径渲染不同组件
    • 未登录字眼链接默认跳转 /logincode,也就是扫描二维码登录
     <el-button class="noLogin" type="text" @click="loginVisible = true">
         <router-link class="noLoginA" to="/logincode">未登录 </router-link>
         <i class="el-icon-caret-bottom"></i>
    </el-button>
    <el-dialog :visible.sync="loginVisible" center :append-to-body='true' :lock-scroll="false" width="350px">
        <router-view></router-view>
    </el-dialog>
    
    data () {
        return {
            loginVisible: false
        }
    }
    
  • 路由使用步骤

    1. 占位符 <router-view></router-view>

    2. router/index.js 引入组件,配置跳转路径对应的组件

      import LoginByScanCode from '@/components/Header/User/LoginByScanCode.vue'
      import LoginByPhoneNumber from '@/components/Header/User/LoginByPhoneNumber.vue'
      import RegisterByPhoneNumber from '@/components/Header/User/RegisterByPhoneNumber.vue'
      
      const routes = [
        {path: '/logincode',component: LoginByScanCode},
        {path: '/loginphone',component: LoginByPhoneNumber},
        {path: '/register',component: RegisterByPhoneNumber}
      ]
      
    3. 页面中根据需要使用 <router-link to="/xxx"></router-link>

  • /logincode 通过扫描二维码登录 LoginByScanCode

  • /loginphone 通过验证手机和手机密码登录 LoginByPhoneNumber

  • /register 注册新账号 RegisterByPhoneNumber

5.2 扫码登录 LoginByScanCode 组件

5.2.1 组件UI 样式

在这里插入图片描述

在这里插入图片描述

HTML 结构

<template>
  <div id="login-code-container">
    <p class="title">扫码登录</p>
    <div class="main-outer">
      <div class="left-img-outer">
        <img id="left-img" src="@/assets/images/left-img.png">
      </div>
      <div class="right-code-outer">
        <div id="right-code">
          <img :src="QRBase64">
          <div class="code-invalid" v-if="codeIsValid === false">
            <p>二维码已失效</p>
            <h5><el-button type="primary" size="mini" @click="afreshGetQR()">点击刷新</el-button></h5>
          </div>
          <div class="code-loading" v-if="codeIsLoading === true">
            <p><i class="el-icon-loading"></i></p>
            <p>登录中...... </p>
          </div>
        </div>
        <p>使用<a href="https://music.163.com/#/download">PT音乐</a>APP版本扫码登录</p>
      </div>
    </div>
    <router-link to="/loginphone" class="other-way">选择其他登录模式 ></router-link>
  </div>
</template>

CSS 样式

<style lang="less" scoped>
// 扫码登录 标题
#login-code-container .title{
  margin-top: 30px;
  font-size: 28px;
  text-align: center;
  color: #3c3c3c;
}
// 选择其他登录模式 样式
#login-code-container .other-way{
  display: inline-block;
  margin: 30px 0 25px 0;
  width: 100%;
  text-align: center;
  padding-right: 0;
  font-size: 12px;
  line-height: 28px;
  color: rgba(0,0,0,0.80);
  text-decoration: none;
}
// 选择其他登录模式 hover 样式
#login-code-container .other-way:hover{
  text-decoration-line:underline;
}
// 中间主体扫码样式
#login-code-container .main-outer{
  height: 250px;
  margin-top: 40px;
  // background-color: rgb(209, 169, 169);
}
// 左边图片外框 右边二维码外框
.left-img-outer,.right-code-outer{
  display: inline-block;
  width: 45%;
}
.left-img-outer{
  // background-color: rgb(174, 218, 138);
}
.right-code-outer{
  float: right;
  padding: 20px 0;
  // background-color: rgb(218, 210, 147);
}
.right-code-outer>p{
  text-align: center;
  line-height: 20px;
}
.right-code-outer a{
  text-decoration: none;
  color: #0c73c2;
}
#left-img{
  width: 100%;
}
#right-code{
  width: 100%;
  height: 135px;
  // background-color: rgb(238, 234, 175);
  margin: 10px 0;
  position: relative;
}
#right-code img{
  width: 100%;
}
#right-code .code-invalid{
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  background-color: rgba(5, 5, 5, 0.7);
}

.code-invalid p{
  text-align: center;
  color: #fff;
  font-size: 14px;
  margin: 30% 0 10%;
}
.code-invalid h5{
  text-align: center;
}
#right-code .code-loading{
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  background-color: rgba(253, 253, 253, 0.9);
}
.code-loading i{
  margin-top: 35%;
  font-size: 20px;
}
.code-loading p{
  line-height: 20px;
  text-align: center;
  color: #3c3c3c;
}
</style>

5.2.2 函数的封装

因为需要使用 axios ,在 src 目录下 utils/request.js 引入 axios,并且设置请求根路径

import axios from 'axios'

// 调用 axios.create() 函数,创建一个 axios 的实例对象,用 request 来接收
const request = axios.create({
  // 指定请求的根路径
  baseURL: 'http://127.0.0.1:3000'
})

export default request

在在 src 目录下创建 api ,用来封装函数

  • api/LoginAndReguster/loginByCode.js 放扫码登录相关的请求
  • api/user/user.js 放与用户账户相关的请求
// 注册相关的 API 接口
import request from '@/utils/request.js'

// 每次请求都带上时间戳 timestamp 参数  防止缓存
// withCredentials 请求为跨域类型时是否在请求中协带cookie

// 获得 QR 的 key
export const getLoginQRKey = function () {
  return request.get('/login/qr/key', {
    params: {
      timestamp: new Date().getTime(),
      withCredentials: true
    }
  })
}

// 传入 key 生成二维码图片的 base64 和二维码信息
export const getLoginQR = function (key) {
  return request.get('/login/qr/create', {
    params: {
      key: key,
      qrimg: true,
      timestamp: new Date().getTime(),
      withCredentials: true
    }
  })
}

// 获取登录信息
export const getLoginStatus = function () {
  return request.get('/login/status', {
    params: {
      timestamp: new Date().getTime(),
      withCredentials: true
    }
  })
}

// 带上key 检查二维码是否过期
export const checkStatus = function (key) {
  return request.get('/login/qr/check', {
    params: {
      key: key,
      timestamp: new Date().getTime(),
      withCredentials: true
    }
  })
}

import request from '@/utils/request.js'

// 获取用户信息
export const getUserAccount = function (cookier) {
  return request.get('/user/account', {
    params: {
      cookie: cookier,
      timestamp: new Date().getTime(),
      withCredentials: true
    }
  })
}

5.2.3 页面功能逻辑

写在前面:感觉整个页面逻辑确实没有什么问题,但是在扫描成功确认登录后再次去访问登录状态时还是没有登录,根据登录成功返回的 cookie 带着再去访问账号信息时还是得不到账号信息,对比官方文档给出的案例感觉并无差别,但是官方的案例登陆之后给浏览器设置了cookie,而我的登录成功之后浏览器并没有被设置cookie,导致检查状态一直是未登录。不知道该如何解决。如果有哪位大佬知道原因,还麻烦指点我一下。

getLoginQRImg 函数

  • getLoginQRKey 获取二维码的 key

  • getLoginQR 获取二维码的信息和 base64 格式的编码,可以直接作为 img 的src渲染到页面上

  • 使用 setInterval创建定时器 timer,间隔 3000ms 便访问一次二维码的状态,根据不同的状态渲染页面样式

    • code === 800 二维码已经失效,渲染失效样式的DOM,并且清理 timer
    • code === 802 正在等待确认中,渲染登录中的样式
    • code === 803登录成功,返回 cookie,,并且清理 timer
  • afreshGetQR 函数 当二维码失效的时候点击点击刷新的时候重新调用 getLoginQRImg函数

  • getLoginQRImg 函数放在 created组件周期时

  • 但是这里有一个问题,点击 Dialog 的关闭按钮的时候,timer 没有被清除,所以需要监听 Dialog 的状态,父组件中有一个 loginVisible,当Dialog 关闭时为 false,打开时为 true 。需要将此变量传递给子组件:

    • 父组件中的占位符bind绑定变量给子组件:

      <router-view :loginVisible="loginVisible"></router-view>
      
    • 子组件使用 props 接收,设置一个默认值 false:

      props: {
          loginVisible: {
              type: Boolean,
              default: false
          }
      }
      
  • 在进入定时器 timer 的时候判断该值,如果位false则清除定时器,达到关闭Dialog 同时清除timer的作用。

// 导入获取 key 和 QR 的 API
import { getLoginQRKey, getLoginQR, getLoginStatus, checkStatus } from '@/api/LoginAndRegister/loginByQR.js'
// 导入 user.js
import { getUserAccount } from '@/api/user/user.js'

export default {
  props: {
    loginVisible: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      codeIsValid: true,
      codeIsLoading: false,
      QRkey: '',
      QRBase64: '',
      cookier: ''
    }
  },
  methods: {
    // 获取登录二维码
    async getLoginQRImg () {
      // 获取 key
      const { data: dataKeyObj } = await getLoginQRKey()
      const keyObj = dataKeyObj.data
      this.QRkey = keyObj.unikey
      console.log('二维码的key:', keyObj.unikey)

      // 获取 二维码
      const { data: dataQRObj } = await getLoginQR(this.QRkey)
      const QRObj = dataQRObj.data
      this.QRBase64 = QRObj.qrimg
      console.log('二维码base64格式:', QRObj.qrimg)

      // 循环判断二维码是否过期  是否已经登录
      const timer = setInterval(async () => {
        // loginVisible 为 true 时定时器才有效
        // 当 dialog 被删除时 定时器被消除
        if (this.loginVisible === false) {
          clearInterval(timer)
        }
        const { data: keyStatus } = await checkStatus(this.QRkey)
        console.log(keyStatus)
        if (keyStatus.code === 800) {
          // 二维码过期
          console.log('二维码已失效')
          this.codeIsLoading = false
          clearInterval(timer)
          this.codeIsValid = false
        } else if (keyStatus.code === 802) {
          // 授权登录中
          console.log('授权登录中...')
          this.codeIsLoading = true
        } else if (keyStatus.code === 803) {
          this.codeIsLoading = false
          // 登录成功 会返回 cookie
          console.log('登录成功')
          clearInterval(timer)
          const { data: loginStatus } = await getLoginStatus()
          console.log(loginStatus)
          this.cookier = keyStatus.cookie
          const { data: userAccount } = await getUserAccount(this.cookier)
          console.log(userAccount)
        }
      }, 3000)
    },
    // 重新获取二维码
    afreshGetQR () {
      console.log('重新获取二维码')
      this.getLoginQRImg()
      this.codeIsValid = true
    }
  },
  created () {
    this.getLoginQRImg()
  }
}
  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值