278 lines
8.3 KiB
HTML
278 lines
8.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Title</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||
<script src="js/socket.io.js"></script>
|
||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
text-decoration: none;
|
||
}
|
||
html,
|
||
body,
|
||
#app {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
#app {
|
||
margin: 0 auto;
|
||
text-align: center;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
}
|
||
.title {
|
||
margin-top: 10px;
|
||
}
|
||
.QRcode {
|
||
margin-top: 20px;
|
||
width: 240px;
|
||
height: 240px;
|
||
position: relative;
|
||
}
|
||
.QRcode_overtime {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
left: 0;
|
||
top: 0;
|
||
background: rgba(0, 0, 0, 0.68);
|
||
color: #fff;
|
||
text-align: center;
|
||
z-index: 2;
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
}
|
||
.QRcode_overtime img {
|
||
width: 40px;
|
||
height: 40px;
|
||
margin-top: 15px;
|
||
}
|
||
.QRcode_success {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
left: 0;
|
||
top: 0;
|
||
background: rgba(0, 0, 0, 0.68);
|
||
color: #fff;
|
||
text-align: center;
|
||
z-index: 3;
|
||
font-size: 18px;
|
||
}
|
||
.QRcode_success img {
|
||
width: 40px;
|
||
height: 40px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.QRcode_img {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: absolute;
|
||
z-index: 1;
|
||
left: 0;
|
||
top: 0;
|
||
}
|
||
.info {
|
||
color: #8d8d8d;
|
||
margin-top: 10px;
|
||
}
|
||
.tips {
|
||
margin-top: 10px;
|
||
background: #8d8d8d;
|
||
border-radius: 24px;
|
||
padding: 4px 14px;
|
||
font-size: 13px;
|
||
color: #fff;
|
||
}
|
||
.WapEmpower button {
|
||
margin-top: 20px;
|
||
}
|
||
.tips {
|
||
margin-top: 20px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div id="app">
|
||
|
||
<a href='https://gitee.com/bmycode/ScanCodeLogin'>
|
||
<img src='https://gitee.com/bmycode/ScanCodeLogin/widgets/widget_3.svg' alt='Fork me on Gitee'></img>
|
||
</a>
|
||
|
||
<h2 class="title">扫码登录 案例</h2>
|
||
<p class="info">Nodejs + Redis + Socket + Mysql + Vue + Uni-app</p>
|
||
<br>
|
||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||
<el-tab-pane label="移动端扫码登录" name="ScanCode">
|
||
<div class="QRcode">
|
||
<!--扫码成功的样式-->
|
||
<div class="QRcode_success" v-if="successShow">
|
||
<img src="./img/success.png" alt="success">
|
||
<p>扫码成功</p>
|
||
</div>
|
||
<!--二维码失效的样式-->
|
||
<div class="QRcode_overtime" @click="AgainScanCode" v-if="overtimeShow">
|
||
<p>二维码失效,点击重新获取</p>
|
||
<img src="./img/reload.png" alt="reload.png">
|
||
</div>
|
||
<!--真正让用户扫的二维码-->
|
||
<img class="QRcode_img" :src="base64QRcode.QRcode">
|
||
|
||
</div>
|
||
<p class="tips">{{ tipsText }}</p>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="移动端授权登录" name="WapEmpower">
|
||
<div class="WapEmpower">
|
||
<p>请点击【登录】按钮</p>
|
||
<p>在移动端弹窗的页面中授权本次登录</p>
|
||
<p class="tips" v-if="WapSuccess.length != 0">{{WapSuccess}}</p>
|
||
<el-button type="success" @click="RequestLogin"> 登 录 </el-button>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
</div>
|
||
|
||
</body>
|
||
|
||
<script>
|
||
var app = new Vue({
|
||
el: '#app',
|
||
data: {
|
||
socket: null, // 保存 socket链接
|
||
socketURL: 'http://192.168.0.133:9080', // socket 服务的地址
|
||
base64QRcode: '', // 保存后端给的二维码和uuid
|
||
timerCheck: null, // 保存定时器
|
||
tipsText: '请扫码', // 提示文字
|
||
overtimeShow: false, // 是否显示二维码失效
|
||
successShow: false, // 是否显示扫码成功
|
||
ScanCodeShow: false, // 如果用户扫码了,那么不显示 等待用户扫码中 的提示文字
|
||
activeName: 'ScanCode',// 保存当前tab的类型
|
||
WapSuccess: '' // 授权登录成功后的提示信息
|
||
},
|
||
created() {
|
||
// 创建 Socket 链接
|
||
this.socket = io.connect(this.socketURL);
|
||
this.handleClick();
|
||
},
|
||
methods: {
|
||
// 判断点击的是哪个tabs,默认激活调用 【移动端扫码登录】
|
||
handleClick() {
|
||
switch (this.activeName) {
|
||
case "ScanCode":
|
||
this.getQRcode();
|
||
this.ScanCodeOvertime();
|
||
this.ScanCodeing();
|
||
break;
|
||
case "WapEmpower":
|
||
this.DoNotRepeatRequest();
|
||
this.AuthorizedWebSuccess()
|
||
break;
|
||
}
|
||
},
|
||
// 请求移动端登录按钮被点击,检查用户是否已经扫码登录,有发送emit,没有提示
|
||
RequestLogin() {
|
||
localStorage.getItem("id") !== null
|
||
? this.socket.emit('WebRequestLogin', localStorage.getItem("id"))
|
||
: this.$notify.error({ title: '提示', message: '请先扫码登录哦!' });
|
||
},
|
||
// 监听后端 emit, 重复点击 登录 按钮的提示
|
||
DoNotRepeatRequest() {
|
||
this.socket.on('DoNotRepeatRequest', res => {
|
||
this.$notify.error({
|
||
title: '提示',
|
||
message: res
|
||
});
|
||
});
|
||
},
|
||
// 移动端成功授权web端登录的监听
|
||
AuthorizedWebSuccess() {
|
||
this.socket.on('AuthorizedWebSuccess', res => {
|
||
this.WapSuccess = res
|
||
console.log("移动端授权登录成功,用户信息为:", res)
|
||
});
|
||
},
|
||
getQRcode() {
|
||
// 向服务器要uuid和二维码
|
||
this.socket.emit('getQRcode');
|
||
this.sendQRcode();
|
||
},
|
||
// 接受服务器给的uuid和二维码
|
||
sendQRcode() {
|
||
this.socket.on('sendQRcode', res => this.base64QRcode = res);
|
||
this.checkScanCode();
|
||
},
|
||
checkScanCode() {
|
||
|
||
// 等待扫码
|
||
this.socket.on('waitScanCode', res => {
|
||
if (!this.ScanCodeShow) {
|
||
this.tipsText = '等待用户扫码'
|
||
}
|
||
});
|
||
|
||
// 扫码成功
|
||
this.socket.on('SuccessScanCode', (res) => {
|
||
localStorage.setItem("id", JSON.parse(res).id);
|
||
//this.tipsText = `扫码成功用户信息为:${res}`; // 提示信息
|
||
this.successShow = true; // 显示扫码成功的样式
|
||
clearInterval(this.timerCheck); // 扫码成功,清除定时器,不再询问服务器
|
||
});
|
||
|
||
// 每1秒询问一次服务器,用户App端是否扫码
|
||
this.timerCheck = setInterval(() => this.socket.emit("checkScanCode", this.base64QRcode.uuid),1000);
|
||
|
||
},
|
||
|
||
// 如果二维码超时
|
||
ScanCodeOvertime() {
|
||
this.socket.on('ScanCodeOvertime', () => {
|
||
// 如果二维码已经失效,并且用户还未扫码的话,那么才去显示二维码失效的样式
|
||
// 之所以在这判断一次的原因,因为如果用户扫码成功之后,二维码才失效
|
||
// 页面上会显示二维码失效的样式,体验不好。
|
||
if(!this.successShow) {
|
||
console.log("【Vue端】:二维码超时");
|
||
clearInterval(this.timerCheck); // 二维码失效,不再询问服务器
|
||
this.overtimeShow = true; // 显示二维码失效的样式
|
||
this.tipsText = "二维码失效"; // 清空等待扫码文字
|
||
}
|
||
})
|
||
},
|
||
|
||
// 已扫码但未确认
|
||
ScanCodeing() {
|
||
this.socket.on('ScanCodeing', () => {
|
||
this.ScanCodeShow = true;
|
||
this.tipsText = "您已扫码,请在手机上确认登录!"
|
||
})
|
||
},
|
||
|
||
// 二维码失效后,用户点击二维码重新获取二维码的事件
|
||
AgainScanCode() {
|
||
this.overtimeShow = false;
|
||
this.ScanCodeShow = false;
|
||
this.socket.emit('getQRcode');
|
||
this.sendQRcode();
|
||
}
|
||
|
||
}
|
||
})
|
||
</script>
|
||
</html> |