仿写网易云-项目初始化-扫描二维码登录
仿写网易云-项目初始化-扫描二维码登录
- 1. 项目初始化
- 2. 安装使用 Element UI
- 3. 设置一下页面的布局
- 4. 开始写 Header
- 5. 点击未登录弹出登录窗口
- 5.0 添加代理解决跨域问题
- 5.1 原理分析
- 5.2 扫码登录 LoginByScanCode 组件
- 5.2.1 组件UI 样式
- 5.2.2 函数的封装
- 5.2.3 页面功能逻辑
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-component
:npm 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} }
路由使用步骤
占位符
<router-view></router-view>
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} ]
页面中根据需要使用
<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
获取二维码的 keygetLoginQR
获取二维码的信息和 base64 格式的编码,可以直接作为 img 的src渲染到页面上使用
setInterval
创建定时器timer
,间隔 3000ms 便访问一次二维码的状态,根据不同的状态渲染页面样式code === 800
二维码已经失效,渲染失效样式的DOM,并且清理 timercode === 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 () {// 获取 keyconst { data: dataKeyObj } = await getLoginQRKey()const keyObj = dataKeyObj.datathis.QRkey = keyObj.unikeyconsole.log('二维码的key:', keyObj.unikey)// 获取 二维码const { data: dataQRObj } = await getLoginQR(this.QRkey)const QRObj = dataQRObj.datathis.QRBase64 = QRObj.qrimgconsole.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 = falseclearInterval(timer)this.codeIsValid = false} else if (keyStatus.code === 802) {// 授权登录中console.log('授权登录中...')this.codeIsLoading = true} else if (keyStatus.code === 803) {this.codeIsLoading = false// 登录成功 会返回 cookieconsole.log('登录成功')clearInterval(timer)const { data: loginStatus } = await getLoginStatus()console.log(loginStatus)this.cookier = keyStatus.cookieconst { data: userAccount } = await getUserAccount(this.cookier)console.log(userAccount)}}, 3000)},// 重新获取二维码afreshGetQR () {console.log('重新获取二维码')this.getLoginQRImg()this.codeIsValid = true}},created () {this.getLoginQRImg()}
}
仿写网易云-项目初始化-扫描二维码登录相关推荐
- 随机字符串解决大问题之腾讯网如何实现手机扫描二维码登录qq功能的
随机字符串解决大问题之腾讯网如何实现手机扫描二维码登录qq功能的 腾讯网(www.qq.com)有一个扫码登录功能很有意思, 点击首页一键登录按钮,就会展现一个二维码,用手机qq扫描此二维码就可以使当 ...
- 百度网盘PC端扫描二维码登录时无法加载二维码问题解决方法
问题: 今天在PC端扫描登录百度网盘时,二维码无法加载出来,具体情况如图: 解决方法: 1.打开IE浏览器 2.打开工具 3.打开Internet选项 4.打开高级选项,重置IE设置 5.点击确定,打 ...
- 浅谈扫描二维码登录微信网页版与摇一摇传图的实现原理
前言:简单体验了下微信网页版通过二维码登录和摇一摇传图功能,从技术角度看,网上专家吹捧的 [隔空取物]其实并不神秘,我先简单分析一下. 1. 微信移动端扫描二维码登录(C-S-C模式) CSC模式为: ...
- 图示扫描二维码登录原理
想要了解手机端扫描二维码登录原理,首先我们要了解二维码和token认证机制两个内容,接下来我们将用图示的方法来直观感受这个面试时候的paper tiger. (第一次用visio画图,用熟练之后就感觉 ...
- 电脑版和手机版QQ都要手机版QQ扫描二维码登录?
是的,电脑版和手机版 QQ 都需要使用手机版 QQ 扫描二维码登录.这是因为扫描二维码登录是 QQ 的安全认证方式之一.
- 微信扫描二维码登录第三方平台
嗯...... 最近做了一个微信扫码登陆第三方平台功能,说下步骤就行,反正原理你们网上直接百度,我这里写了,估计也没几个人有耐心看 第一步 生成一个链接 https://open.weixin.qq. ...
- Android安卓开发集成微信第三方扫描二维码登录-超级无敌具详细
Android安卓开发中集成微信二维码登录的步骤: 写在前面的: 该教程使用AS作为演示,使用ecplise请参照微信官方文档下载相应jar等所需参考文档和资源.在最后,我会附上这个Activity的 ...
- WebSocket实现app扫描二维码登录
后台框架采用SpringMVC,不同的框架可根据逻辑更改即可: [思路]- PC端生成二维码,二维码包含uuid(全局唯一标识符),且打通websocket通道,等待服务器返回登录成功信息:APP扫描 ...
- 网页扫描二维码登录页面的原理(通俗、易懂)
我的理解是: 二维码登录网页的基本原理是,用户进入登陆网页后,服务器生成一个uid来唯一标识一个用户,每一次刷新uid都会变换,保证一个uid绑定 一个账号和密码,同时游览器与服务器创建一个长链接,用 ...
- 扫描二维码登录的过程?
二维码登录实际上和用户名密码登录要做的事情一致,账号告诉服务器「我是谁?」,密码是向服务器证明「我是我」.扫码也是一样. 下图是一个基础的客户端账号密码认证过程: 然后想一下二维码登陆时的场景: 二维 ...
最新文章
- Qt中树形结构显示目录结构
- hadoop的hdfs文件操作实现上传文件到hdfs
- Java必备:java入门、java学习
- 浅谈LTE技术及实际应用方案
- mongdb 群集_群集文档的文本摘要
- IOT(20)---2018年有哪些值得期待的物联网应用领域?
- matlab yalmip cplex,关于 cplex+matlab+yalmip问题
- PCS7 数据库解析
- python———两个栈实现一个队列
- 病毒周报(080630至080706)
- Image Tampering Detection via Semantic Segmentation Network
- 如何系统学习Android开发?一线互联网内部整理的Android学习路线图是时候拿出来了
- 微软java虚拟机下载_微软Java虚拟机下载-Microsoft VM(Java虚拟机)5.0.3805最新版 - 维维软件园...
- Scrapy爬虫框架管道文件pipelines数据图像存储
- 在Unity中使用ComputeShader
- MODBUS-RTU数据帧格式、报文实例
- 未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”包
- 计算机网络缩写词集锦
- 取消英文google的Instant predictions
- 关于实现局域网内视频播放