This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Regular.min.css"/>
|
||||
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -10,8 +10,6 @@
|
||||
"@dcloudio/uni-h5": "3.0.0-4080720251210001",
|
||||
"clipboard": "^2.0.11",
|
||||
"dayjs": "^1.11.19",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"jsqr": "^1.4.0",
|
||||
"uview-plus": "^3.6.29",
|
||||
"vue": "^3.4.0"
|
||||
},
|
||||
@@ -4870,12 +4868,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html5-qrcode": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/html5-qrcode/-/html5-qrcode-2.3.8.tgz",
|
||||
"integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
|
||||
@@ -5104,12 +5096,6 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsqr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsqr/-/jsqr-1.4.0.tgz",
|
||||
"integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/lcid": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/lcid/-/lcid-3.1.1.tgz",
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
"@dcloudio/uni-h5": "3.0.0-4080720251210001",
|
||||
"clipboard": "^2.0.11",
|
||||
"dayjs": "^1.11.19",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"jsqr": "^1.4.0",
|
||||
"uview-plus": "^3.6.29",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
|
||||
@@ -48,35 +48,17 @@
|
||||
<button class="btn-scan" @tap="handleScanLogin">
|
||||
<image src="/static/scan-code.png" mode="aspectFit" class="scan-icon"></image>
|
||||
<text>扫一扫登录</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="agreement">
|
||||
<view class="agreement-text">
|
||||
<text class="text">登录即表示同意</text>
|
||||
<text class="link">《用户协议》</text>
|
||||
<text class="text">和</text>
|
||||
<text class="link">《隐私政策》</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showScanner" class="scanner-overlay">
|
||||
<view class="scanner-container">
|
||||
<view class="scanner-header">
|
||||
<text>扫一扫</text>
|
||||
<text class="close-btn" @tap="closeScanner">×</text>
|
||||
</view>
|
||||
<view class="scanner-body">
|
||||
<input type="file" accept="image/*" capture="environment" @change="onFileSelected" class="file-input" id="file-input" />
|
||||
<view class="scan-prompt">
|
||||
<image src="/static/scan-code.png" mode="aspectFit" class="scan-icon-large"></image>
|
||||
<text class="tip">点击下方按钮选择相机拍照</text>
|
||||
<label for="file-input" class="btn-take-photo">拍照/选择图片</label>
|
||||
<view class="agreement">
|
||||
<view class="agreement-text">
|
||||
<text class="text">登录即表示同意</text>
|
||||
<text class="link">《用户协议》</text>
|
||||
<text class="text">和</text>
|
||||
<text class="link">《隐私政策》</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="scanner-tip">将二维码放入框内即可自动扫描</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -87,7 +69,7 @@
|
||||
import { authApi } from '@/api/index.js';
|
||||
import { useUserStore } from '@/store/index.js';
|
||||
import { APP_ID, USE_WECHAT_AUTH } from '@/utils/env.js';
|
||||
import jsQR from 'jsqr';
|
||||
import { initWxSdk } from '@/utils/wxsdk.js';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
@@ -105,7 +87,6 @@
|
||||
const agreed = ref(true);
|
||||
const inputFocus = ref(false);
|
||||
const showError = ref(false);
|
||||
const showScanner = ref(false);
|
||||
|
||||
const getCode = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -126,12 +107,10 @@
|
||||
const clearInput = () => {
|
||||
identifier.value = '';
|
||||
showError.value = false;
|
||||
// 保持焦点状态
|
||||
inputFocus.value = true;
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
// 延迟失焦,给清除按钮点击留出时间
|
||||
setTimeout(() => {
|
||||
inputFocus.value = false;
|
||||
}, 200);
|
||||
@@ -146,46 +125,33 @@
|
||||
};
|
||||
|
||||
const handleScanLogin = async () => {
|
||||
showScanner.value = true;
|
||||
};
|
||||
|
||||
const onFileSelected = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const code = jsQR(imageData.data, imageData.width, imageData.height);
|
||||
|
||||
if (code) {
|
||||
const match = code.data.match(/=([^=]+)$/);
|
||||
if (match && match[1]) {
|
||||
identifier.value = match[1];
|
||||
closeScanner();
|
||||
handleLogin();
|
||||
} else {
|
||||
uni.showToast({ title: '无效的二维码', icon: 'none' });
|
||||
}
|
||||
// #ifdef H5
|
||||
const ready = await initWxSdk();
|
||||
if (!ready) {
|
||||
uni.showToast({ title: '扫一扫功能暂不可用', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.scanQRCode({
|
||||
needResult: 1,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => {
|
||||
const result = res.resultStr;
|
||||
const match = result.match(/=([^=]+)$/);
|
||||
if (match && match[1]) {
|
||||
identifier.value = match[1];
|
||||
handleLogin();
|
||||
} else {
|
||||
uni.showToast({ title: '无法识别二维码', icon: 'none' });
|
||||
uni.showToast({ title: '无效的二维码', icon: 'none' });
|
||||
}
|
||||
};
|
||||
img.src = event.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const closeScanner = async () => {
|
||||
showScanner.value = false;
|
||||
document.getElementById('file-input').value = '';
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: '扫码失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.showToast({ title: '请在微信中打开', icon: 'none' });
|
||||
// #endif
|
||||
};
|
||||
|
||||
const getPathDeviceId = () => {
|
||||
@@ -209,7 +175,6 @@
|
||||
userStore.setToken(loginData.token);
|
||||
uni.setStorageSync('identifier', userStore.state.identifier);
|
||||
|
||||
// 判断是否需要绑定手机号
|
||||
if (loginData.need_bind_phone) {
|
||||
uni.redirectTo({ url: '/pages/bind/bind?fromLogin=true' });
|
||||
} else {
|
||||
@@ -241,44 +206,18 @@
|
||||
userStore.setToken(loginData.token);
|
||||
uni.setStorageSync('identifier', userStore.state.identifier);
|
||||
|
||||
// H5 环境获取微信用户信息
|
||||
// #ifdef H5
|
||||
try {
|
||||
// 通过 uni.getUserProfile 获取用户信息(需要用户点击授权)
|
||||
// 注意:这个方法在微信公众号 H5 中不可用,需要通过后端接口获取
|
||||
// 这里暂时保存默认信息,实际应该由后端返回用户信息
|
||||
if (loginData.user_info) {
|
||||
userStore.setUserInfo({
|
||||
avatar: loginData.user_info.avatar || '',
|
||||
nickname: loginData.user_info.nickname || ''
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('获取用户信息失败', err);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序环境可以直接获取用户信息
|
||||
try {
|
||||
const { userInfo } = await uni.getUserProfile({
|
||||
desc: '用于完善用户资料'
|
||||
});
|
||||
if (loginData.user_info) {
|
||||
userStore.setUserInfo({
|
||||
avatar: userInfo.avatarUrl || '',
|
||||
nickname: userInfo.nickName || ''
|
||||
avatar: loginData.user_info.avatar || '',
|
||||
nickname: loginData.user_info.nickname || ''
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('用户取消授权');
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 判断是否需要绑定手机号
|
||||
if (loginData.need_bind_phone) {
|
||||
// 需要绑定手机号,跳转到绑定页面
|
||||
uni.redirectTo({ url: '/pages/bind/bind?fromLogin=true' });
|
||||
} else {
|
||||
// 不需要绑定,直接进入首页
|
||||
uni.redirectTo({ url: '/pages/index/index' });
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -286,10 +225,6 @@
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
handleWechatCallback();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -501,91 +436,4 @@
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.scanner-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.scanner-container {
|
||||
width: 600rpx;
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-large);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scanner-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border-bottom: 1rpx solid var(--border-light);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
font-size: 48rpx;
|
||||
color: var(--text-tertiary);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.scanner-region {
|
||||
width: 100%;
|
||||
height: 600rpx;
|
||||
}
|
||||
|
||||
.scanner-tip {
|
||||
padding: 24rpx;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.scanner-body {
|
||||
padding: 60rpx 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scan-prompt {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.scan-icon-large {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 28rpx;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.btn-take-photo {
|
||||
padding: 24rpx 48rpx;
|
||||
background: var(--primary);
|
||||
color: var(--text-inverse);
|
||||
border-radius: var(--radius-medium);
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user