添加扫码登录功能,新增登录API接口,更新前端页面和样式,删除不必要的文件。
This commit is contained in:
@ -1 +0,0 @@
|
||||
:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}.card{padding:2em}#app{max-width:1280px;margin:0 auto;padding:2rem;text-align:center}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}.read-the-docs[data-v-830e400e]{color:#888}.logo[data-v-962047bb]{height:6em;padding:1.5em;will-change:filter;transition:filter .3s}.logo[data-v-962047bb]:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.vue[data-v-962047bb]:hover{filter:drop-shadow(0 0 2em #42b883aa)}
|
File diff suppressed because one or more lines are too long
108
static/css/style.css
Normal file
108
static/css/style.css
Normal file
@ -0,0 +1,108 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 30px;
|
||||
color: #1e88e5;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.scan-area {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
#camera-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#scan-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#scan-placeholder img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #1e88e5;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 12px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
#stop-button {
|
||||
background-color: #e53935;
|
||||
}
|
||||
|
||||
#stop-button:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
#scan-result {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#result-content {
|
||||
margin-top: 10px;
|
||||
word-break: break-all;
|
||||
font-size: 14px;
|
||||
color: #1e88e5;
|
||||
}
|
260
static/js/app.js
Normal file
260
static/js/app.js
Normal file
@ -0,0 +1,260 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const video = document.getElementById('video');
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const scanButton = document.getElementById('scan-button');
|
||||
const stopButton = document.getElementById('stop-button');
|
||||
const cameraContainer = document.getElementById('camera-container');
|
||||
const scanPlaceholder = document.getElementById('scan-placeholder');
|
||||
const scanResult = document.getElementById('scan-result');
|
||||
const resultContent = document.getElementById('result-content');
|
||||
|
||||
// 固定的sign_wg和kps_wg值,实际应用中可能需要动态生成
|
||||
const sign_wg = "AAQHaE4ww2nnIPvofH2SfMv3N6OplcPRjxlgScTZozm/ZCMfQP74bsMLyKW883hZCGY=";
|
||||
const kps_wg = "AARWcp9UM71t5VzV9i5pBJ4dLXjJ7EZL5a9qz2QVVQtkkmcqS4wQGYtk38CRzW6HH4+5c7qsB9/EtUgkWcd8x/k7h9+PmAHUDvxKHUWnX7iL3h2fH86XJ4cEqwvUnQ77QGs=";
|
||||
|
||||
let scanning = false;
|
||||
let stream = null;
|
||||
let qrCodeToken = null;
|
||||
|
||||
// 创建一个显示消息的函数,使用Element UI的Message组件
|
||||
function showMessage(message, type = 'info') {
|
||||
//确保ELEMENT已定义
|
||||
// 使用tailwindcss实现消息提示
|
||||
// 获取或创建消息容器
|
||||
let messageContainer = document.getElementById('message-container');
|
||||
if (!messageContainer) {
|
||||
messageContainer = document.createElement('div');
|
||||
messageContainer.id = 'message-container';
|
||||
messageContainer.className = 'fixed top-0 left-0 right-0 flex flex-col items-center mt-16 z-20 pointer-events-none';
|
||||
document.body.appendChild(messageContainer);
|
||||
}
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.textContent = message;
|
||||
messageDiv.className = `px-4 py-2 rounded-md shadow-md mb-2 max-w-xs sm:max-w-sm md:max-w-md break-words ${
|
||||
type === 'success' ? 'bg-green-500 text-white' :
|
||||
type === 'error' ? 'bg-red-500 text-white' :
|
||||
type === 'warning' ? 'bg-yellow-500 text-white' :
|
||||
'bg-blue-500 text-white'
|
||||
} transition-opacity duration-300 opacity-100`;
|
||||
|
||||
messageContainer.appendChild(messageDiv);
|
||||
|
||||
// 3秒后自动消失
|
||||
setTimeout(() => {
|
||||
messageDiv.classList.replace('opacity-100', 'opacity-0');
|
||||
setTimeout(() => {
|
||||
messageContainer.removeChild(messageDiv);
|
||||
// 如果没有消息了,移除容器
|
||||
if (messageContainer.children.length === 0) {
|
||||
document.body.removeChild(messageContainer);
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
//同时保留console.log以便调试
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
// 开始扫码
|
||||
scanButton.addEventListener('click', startScanning);
|
||||
|
||||
// 停止扫码
|
||||
stopButton.addEventListener('click', stopScanning);
|
||||
|
||||
function startScanning() {
|
||||
if (scanning) return;
|
||||
|
||||
// 显示摄像头区域,隐藏占位符
|
||||
cameraContainer.style.display = 'block';
|
||||
scanPlaceholder.style.display = 'none';
|
||||
scanResult.style.display = 'none';
|
||||
scanButton.style.display = 'none';
|
||||
stopButton.style.display = 'block';
|
||||
|
||||
// 请求摄像头权限
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
facingMode: 'environment',
|
||||
width: { ideal: 1280 },
|
||||
height: { ideal: 720 }
|
||||
}
|
||||
})
|
||||
.then(function(mediaStream) {
|
||||
stream = mediaStream;
|
||||
video.srcObject = mediaStream;
|
||||
video.setAttribute('playsinline', true);
|
||||
video.play();
|
||||
scanning = true;
|
||||
requestAnimationFrame(tick);
|
||||
showMessage('摄像头已启动,请对准二维码', 'success');
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('无法访问摄像头: ', error);
|
||||
showMessage('无法访问摄像头,请确保已授予摄像头权限', 'error');
|
||||
resetScanUI();
|
||||
});
|
||||
}
|
||||
|
||||
function stopScanning() {
|
||||
if (!scanning) return;
|
||||
|
||||
if (stream) {
|
||||
stream.getTracks().forEach(track => {
|
||||
track.stop();
|
||||
});
|
||||
}
|
||||
|
||||
scanning = false;
|
||||
resetScanUI();
|
||||
showMessage('已停止扫码', 'warning');
|
||||
}
|
||||
|
||||
function resetScanUI() {
|
||||
cameraContainer.style.display = 'none';
|
||||
scanPlaceholder.style.display = 'flex';
|
||||
stopButton.style.display = 'none';
|
||||
scanButton.style.display = 'block';
|
||||
}
|
||||
|
||||
function tick() {
|
||||
if (!scanning) return;
|
||||
|
||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
||||
// 设置canvas大小与视频一致
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
|
||||
// 在canvas上绘制当前视频帧
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 获取图像数据
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 使用jsQR库解析二维码
|
||||
const code = jsQR(imageData.data, imageData.width, imageData.height, {
|
||||
inversionAttempts: "dontInvert",
|
||||
});
|
||||
// 如果检测到二维码
|
||||
if (code) {
|
||||
// 使用Element UI的Message组件显示消息
|
||||
showMessage("检测到二维码: " + code.data, 'success');
|
||||
|
||||
// 处理扫码结果
|
||||
handleScanResult(code.data);
|
||||
|
||||
// 停止扫描
|
||||
stopScanning();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 继续扫描
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function handleScanResult(data) {
|
||||
// 显示扫码结果
|
||||
scanResult.style.display = 'block';
|
||||
resultContent.textContent = "扫码成功,正在处理...";
|
||||
|
||||
try {
|
||||
// 从URL中提取token
|
||||
const tokenMatch = data.match(/token=([^&]+)/);
|
||||
if (tokenMatch && tokenMatch[1]) {
|
||||
qrCodeToken = tokenMatch[1];
|
||||
showMessage("提取到token: " + qrCodeToken, 'success');
|
||||
|
||||
// 调用登录API
|
||||
showMessage(qrCodeToken,'success');
|
||||
callLoginAPI(qrCodeToken);
|
||||
} else {
|
||||
console.error("无法从二维码中提取token");
|
||||
showMessage("无法从二维码中提取token,请重试", 'error');
|
||||
resultContent.textContent = "无法从二维码中提取token,请重试";
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("处理二维码内容失败:", e);
|
||||
showMessage("处理二维码内容失败: " + e.toString(), 'error');
|
||||
resultContent.textContent = "扫码内容处理失败: " + e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// 调用夸克网盘登录API
|
||||
function callLoginAPI(token) {
|
||||
showMessage(token);
|
||||
resultContent.textContent = "正在登录...";
|
||||
showMessage("正在使用token登录...", 'info');
|
||||
// 生成时间戳作为vcode
|
||||
const vcode = new Date().getTime();
|
||||
showMessage(vcode, 'info');
|
||||
// request_id为vcode+5
|
||||
const request_id = vcode + 5;
|
||||
|
||||
// 构建请求参数
|
||||
const formData = new FormData();
|
||||
formData.append('client_id', '532');
|
||||
formData.append('v', '1.2');
|
||||
formData.append('request_id', request_id.toString());
|
||||
formData.append('sign_wg', sign_wg);
|
||||
formData.append('kps_wg', kps_wg);
|
||||
formData.append('vcode', vcode.toString());
|
||||
formData.append('token', token);
|
||||
|
||||
// 构建完整的URL
|
||||
const url = '/login';
|
||||
// 发送请求
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8"
|
||||
},
|
||||
body: JSON.stringify({"token":token}),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.status) {
|
||||
throw new Error('网络请求失败: ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
showMessage("登录API响应: " + JSON.stringify(data), 'info');
|
||||
|
||||
if (data && data.status) {
|
||||
// 登录成功,设置Cookie
|
||||
resultContent.textContent = "登录成功";
|
||||
showMessage("登录成功!", 'success');
|
||||
sendCookieToPCPage(data.data);
|
||||
} else {
|
||||
// 登录失败
|
||||
resultContent.textContent = "登录失败: " + (data.msg || "未知错误");
|
||||
showMessage("登录失败: " + (data.msg || "未知错误"), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("登录请求失败:", error);
|
||||
resultContent.textContent = "登录请求失败: " + error.toString();
|
||||
showMessage("登录请求失败: " + error.toString(), 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// 向PC网页传入Cookie的函数
|
||||
function sendCookieToPCPage(loginData) {
|
||||
// 这里需要实现sendCookieToPCPage函数
|
||||
showMessage("正在设置Cookie...", 'info');
|
||||
}
|
||||
|
||||
// 监听来自iframe的消息
|
||||
// window.addEventListener('message', function(event) {
|
||||
// console.log("收到消息:", event.data);
|
||||
// if (event.data && event.data.status) {
|
||||
// if (event.data.status === 'success') {
|
||||
// resultContent.textContent = "Cookie设置成功!请返回夸克网盘页面查看";
|
||||
// showMessage("Cookie设置成功!请返回夸克网盘页面查看", 'success');
|
||||
// } else {
|
||||
// resultContent.textContent = "Cookie设置失败: " + event.data.message;
|
||||
// showMessage("Cookie设置失败: " + event.data.message, 'error');
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
});
|
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
Reference in New Issue
Block a user