feat(passport): 实现统一扫码功能并迁移二维码登录页面 将原有的扫码登录和扫码核销功能合并为统一扫码功能,支持智能识别二维码类型, 自动执行相应操作。同时将二维码登录相关页面迁移到 /passport 路径下,优化用户体验与代码结构。 主要变更: - 新增统一扫码 Hook (useUnifiedQRScan) 和按钮组件 (UnifiedQRButton)- 新增统一扫码页面 /passport/unified-qr/index - 迁移二维码登录页面路径:/pages/qr-login → /passport/qr-login - 更新管理员面板及用户卡片中的扫码入口为统一扫码- 移除旧的 QRLoginDemo 和 qr-test 测试页面- 补充统一扫码使用指南文档```
285 lines
6.3 KiB
Markdown
285 lines
6.3 KiB
Markdown
# 微信小程序扫码登录使用指南
|
||
|
||
## 概述
|
||
|
||
本项目已完整实现微信小程序扫码登录功能,支持用户通过小程序扫描网页端二维码快速登录。
|
||
|
||
## 功能特性
|
||
|
||
- ✅ **多种集成方式** - 按钮组件、弹窗组件、专门页面
|
||
- ✅ **自动解析二维码** - 支持多种二维码格式(URL、JSON、纯token)
|
||
- ✅ **安全可靠** - Token有效期控制,防重复使用
|
||
- ✅ **用户体验好** - 实时状态反馈,错误处理完善
|
||
- ✅ **微信集成** - 自动获取微信用户信息
|
||
|
||
## 后端接口
|
||
|
||
### 1. 生成扫码token
|
||
```http
|
||
POST /api/qr-login/generate
|
||
```
|
||
|
||
### 2. 检查登录状态
|
||
```http
|
||
GET /api/qr-login/status/{token}
|
||
```
|
||
|
||
### 3. 确认登录
|
||
```http
|
||
POST /api/qr-login/confirm
|
||
```
|
||
|
||
### 4. 扫码操作(可选)
|
||
```http
|
||
POST /api/qr-login/scan/{token}
|
||
```
|
||
|
||
## 前端使用方式
|
||
|
||
### 方式1: 直接扫码按钮
|
||
|
||
最简单的使用方式,点击按钮直接调用扫码API:
|
||
|
||
```tsx
|
||
import QRLoginButton from '@/components/QRLoginButton';
|
||
|
||
// 基础使用
|
||
<QRLoginButton />
|
||
|
||
// 自定义配置
|
||
<QRLoginButton
|
||
text="扫码登录"
|
||
type="primary"
|
||
size="large"
|
||
onSuccess={(result) => console.log('登录成功', result)}
|
||
onError={(error) => console.log('登录失败', error)}
|
||
/>
|
||
```
|
||
|
||
### 方式2: 弹窗扫码
|
||
|
||
在当前页面弹出扫码弹窗:
|
||
|
||
```tsx
|
||
import { useState } from 'react';
|
||
import QRScanModal from '@/components/QRScanModal';
|
||
|
||
const MyComponent = () => {
|
||
const [showScan, setShowScan] = useState(false);
|
||
|
||
return (
|
||
<>
|
||
<Button onClick={() => setShowScan(true)}>
|
||
扫码登录
|
||
</Button>
|
||
|
||
<QRScanModal
|
||
visible={showScan}
|
||
onClose={() => setShowScan(false)}
|
||
onSuccess={(result) => {
|
||
console.log('登录成功', result);
|
||
setShowScan(false);
|
||
}}
|
||
onError={(error) => {
|
||
console.log('登录失败', error);
|
||
}}
|
||
/>
|
||
</>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 方式3: 跳转到专门页面
|
||
|
||
跳转到专门的扫码登录页面:
|
||
|
||
```tsx
|
||
import QRLoginButton from '@/components/QRLoginButton';
|
||
|
||
// 使用页面模式
|
||
<QRLoginButton
|
||
text="进入扫码页面"
|
||
usePageMode={true}
|
||
/>
|
||
|
||
// 或者自定义跳转
|
||
<Button onClick={() => {
|
||
Taro.navigateTo({
|
||
url: '/passport/qr-login/index'
|
||
});
|
||
}}>
|
||
扫码登录
|
||
</Button>
|
||
```
|
||
|
||
### 方式4: 使用Hook
|
||
|
||
直接使用Hook进行更灵活的控制:
|
||
|
||
```tsx
|
||
import { useQRLogin } from '@/hooks/useQRLogin';
|
||
|
||
const MyComponent = () => {
|
||
const {
|
||
startScan,
|
||
isLoading,
|
||
isSuccess,
|
||
isError,
|
||
error,
|
||
result,
|
||
canScan
|
||
} = useQRLogin();
|
||
|
||
return (
|
||
<Button
|
||
loading={isLoading}
|
||
disabled={!canScan()}
|
||
onClick={startScan}
|
||
>
|
||
{isLoading ? '扫码中...' : '扫码登录'}
|
||
</Button>
|
||
);
|
||
};
|
||
```
|
||
|
||
## 二维码格式支持
|
||
|
||
系统支持多种二维码格式:
|
||
|
||
### 1. URL格式
|
||
```
|
||
https://mp.websoft.top/qr-confirm?qrCodeKey=02278c578d3e4aad87dece6aab2f0296
|
||
https://example.com/login?token=abc123
|
||
```
|
||
|
||
### 2. JSON格式
|
||
```json
|
||
{
|
||
"token": "02278c578d3e4aad87dece6aab2f0296",
|
||
"type": "qr-login"
|
||
}
|
||
```
|
||
|
||
### 3. 简单格式
|
||
```
|
||
qr-login:02278c578d3e4aad87dece6aab2f0296
|
||
02278c578d3e4aad87dece6aab2f0296
|
||
```
|
||
|
||
## 页面配置
|
||
|
||
确保在 `app.config.ts` 中添加了相关页面:
|
||
|
||
```typescript
|
||
export default {
|
||
pages: [
|
||
// ... 其他页面
|
||
'passport/qr-login/index', // 扫码登录页面
|
||
'passport/qr-confirm/index', // 登录确认页面
|
||
],
|
||
// ...
|
||
}
|
||
```
|
||
|
||
## 使用示例
|
||
|
||
### 完整示例组件
|
||
|
||
```tsx
|
||
import React, { useState } from 'react';
|
||
import { View } from '@tarojs/components';
|
||
import { Button } from '@nutui/nutui-react-taro';
|
||
import QRLoginButton from '@/components/QRLoginButton';
|
||
import QRScanModal from '@/components/QRScanModal';
|
||
|
||
const LoginPage = () => {
|
||
const [showModal, setShowModal] = useState(false);
|
||
|
||
const handleSuccess = (result) => {
|
||
console.log('登录成功:', result);
|
||
// 处理登录成功逻辑
|
||
};
|
||
|
||
const handleError = (error) => {
|
||
console.error('登录失败:', error);
|
||
// 处理登录失败逻辑
|
||
};
|
||
|
||
return (
|
||
<View className="p-4">
|
||
{/* 方式1: 直接扫码 */}
|
||
<QRLoginButton
|
||
text="扫码登录"
|
||
onSuccess={handleSuccess}
|
||
onError={handleError}
|
||
/>
|
||
|
||
{/* 方式2: 弹窗扫码 */}
|
||
<Button onClick={() => setShowModal(true)}>
|
||
弹窗扫码
|
||
</Button>
|
||
|
||
{/* 方式3: 跳转页面 */}
|
||
<QRLoginButton
|
||
text="进入扫码页面"
|
||
usePageMode={true}
|
||
/>
|
||
|
||
<QRScanModal
|
||
visible={showModal}
|
||
onClose={() => setShowModal(false)}
|
||
onSuccess={handleSuccess}
|
||
onError={handleError}
|
||
/>
|
||
</View>
|
||
);
|
||
};
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. **用户登录状态**: 使用扫码登录功能前,用户必须已在小程序中登录
|
||
2. **权限申请**: 确保小程序已申请摄像头权限
|
||
3. **网络环境**: 确保网络连接正常,API接口可访问
|
||
4. **二维码有效期**: 二维码有5分钟有效期,过期需重新生成
|
||
5. **安全性**: 只扫描来自官方网站的登录二维码
|
||
|
||
## 错误处理
|
||
|
||
常见错误及解决方案:
|
||
|
||
- `请先登录小程序`: 用户未登录,需要先完成小程序登录
|
||
- `无效的登录二维码`: 二维码格式不正确或已过期
|
||
- `登录确认失败`: 网络问题或服务器错误,可重试
|
||
- `扫码失败`: 摄像头权限问题或二维码不清晰
|
||
|
||
## API接口详情
|
||
|
||
详细的API接口文档请参考:`src/api/qr-login/index.ts`
|
||
|
||
## 组件API
|
||
|
||
### QRLoginButton Props
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| type | string | 'primary' | 按钮类型 |
|
||
| size | string | 'normal' | 按钮大小 |
|
||
| text | string | '扫码登录' | 按钮文本 |
|
||
| showIcon | boolean | true | 是否显示图标 |
|
||
| usePageMode | boolean | false | 是否使用页面模式 |
|
||
| onSuccess | function | - | 成功回调 |
|
||
| onError | function | - | 失败回调 |
|
||
|
||
### QRScanModal Props
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| visible | boolean | false | 是否显示弹窗 |
|
||
| title | string | '扫描登录二维码' | 弹窗标题 |
|
||
| description | string | '扫描网页端显示的登录二维码' | 描述文本 |
|
||
| autoConfirm | boolean | true | 是否自动确认登录 |
|
||
| onClose | function | - | 关闭回调 |
|
||
| onSuccess | function | - | 成功回调 |
|
||
| onError | function | - | 失败回调 |
|