forked from gxwebsoft/mp-10550
feat(admin): 实现管理员模式切换和扫码登录功能
- 新增管理员模式切换方案,统一管理所有管理员功能 - 实现扫码登录功能,支持用户通过小程序扫描网页端二维码快速登录 - 添加管理员面板组件,集中展示所有管理员功能 - 开发扫码登录按钮和扫描器组件,方便集成到不同页面 - 优化用户界面设计,提高管理员用户的使用体验
This commit is contained in:
248
src/api/qr-login/index.ts
Normal file
248
src/api/qr-login/index.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import request from '@/utils/request';
|
||||
import type { ApiResult } from '@/api';
|
||||
import Taro from '@tarojs/taro';
|
||||
|
||||
/**
|
||||
* 扫码登录相关接口
|
||||
*/
|
||||
|
||||
// 生成扫码token请求参数
|
||||
export interface GenerateQRTokenParam {
|
||||
// 客户端类型:web, app, wechat
|
||||
clientType?: string;
|
||||
// 设备信息
|
||||
deviceInfo?: string;
|
||||
// 过期时间(分钟)
|
||||
expireMinutes?: number;
|
||||
}
|
||||
|
||||
// 生成扫码token响应
|
||||
export interface GenerateQRTokenResult {
|
||||
// 扫码token
|
||||
token: string;
|
||||
// 二维码内容(通常是包含token的URL或JSON)
|
||||
qrContent: string;
|
||||
// 过期时间戳
|
||||
expireTime: number;
|
||||
// 二维码图片URL(可选)
|
||||
qrImageUrl?: string;
|
||||
}
|
||||
|
||||
// 扫码状态枚举
|
||||
export enum QRLoginStatus {
|
||||
PENDING = 'pending', // 等待扫码
|
||||
SCANNED = 'scanned', // 已扫码,等待确认
|
||||
CONFIRMED = 'confirmed', // 已确认登录
|
||||
EXPIRED = 'expired', // 已过期
|
||||
CANCELLED = 'cancelled' // 已取消
|
||||
}
|
||||
|
||||
// 检查扫码状态响应
|
||||
export interface QRLoginStatusResult {
|
||||
// 当前状态
|
||||
status: QRLoginStatus;
|
||||
// 状态描述
|
||||
message?: string;
|
||||
// 如果已确认登录,返回JWT token
|
||||
accessToken?: string;
|
||||
// 用户信息
|
||||
userInfo?: {
|
||||
userId: number;
|
||||
nickname?: string;
|
||||
avatar?: string;
|
||||
phone?: string;
|
||||
};
|
||||
// 剩余有效时间(秒)
|
||||
remainingTime?: number;
|
||||
}
|
||||
|
||||
// 确认登录请求参数
|
||||
export interface ConfirmLoginParam {
|
||||
// 扫码token
|
||||
token: string;
|
||||
// 用户ID
|
||||
userId: number;
|
||||
// 登录平台:web, app, wechat
|
||||
platform?: string;
|
||||
// 微信用户信息(当platform为wechat时)
|
||||
wechatInfo?: {
|
||||
openid?: string;
|
||||
unionid?: string;
|
||||
nickname?: string;
|
||||
avatar?: string;
|
||||
gender?: number;
|
||||
};
|
||||
// 设备信息
|
||||
deviceInfo?: string;
|
||||
}
|
||||
|
||||
// 确认登录响应
|
||||
export interface ConfirmLoginResult {
|
||||
// 是否成功
|
||||
success: boolean;
|
||||
// 消息
|
||||
message: string;
|
||||
// 登录用户信息
|
||||
userInfo?: {
|
||||
userId: number;
|
||||
nickname?: string;
|
||||
avatar?: string;
|
||||
phone?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成扫码登录token
|
||||
*/
|
||||
export async function generateQRToken(data?: GenerateQRTokenParam) {
|
||||
const res = await request.post<ApiResult<GenerateQRTokenResult>>(
|
||||
'http://127.0.0.1:9200/api/qr-login/generate',
|
||||
{
|
||||
clientType: 'wechat',
|
||||
expireMinutes: 5,
|
||||
...data
|
||||
}
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查扫码登录状态
|
||||
*/
|
||||
export async function checkQRLoginStatus(token: string) {
|
||||
const res = await request.get<ApiResult<QRLoginStatusResult>>(
|
||||
`http://127.0.0.1:9200/api/qr-login/status/${token}`
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认扫码登录(通用接口)
|
||||
*/
|
||||
export async function confirmQRLogin(data: ConfirmLoginParam) {
|
||||
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
||||
'http://127.0.0.1:9200/api/qr-login/confirm',
|
||||
data
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序扫码登录确认(便捷接口)
|
||||
*/
|
||||
export async function confirmWechatQRLogin(token: string, userId: number) {
|
||||
try {
|
||||
// 获取微信用户信息
|
||||
const userInfo = await getUserProfile();
|
||||
|
||||
const data: ConfirmLoginParam = {
|
||||
token,
|
||||
userId,
|
||||
platform: 'wechat',
|
||||
wechatInfo: {
|
||||
nickname: userInfo?.nickName,
|
||||
avatar: userInfo?.avatarUrl,
|
||||
gender: userInfo?.gender
|
||||
},
|
||||
deviceInfo: await getDeviceInfo()
|
||||
};
|
||||
|
||||
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
||||
'http://127.0.0.1:9200/api/qr-login/wechat-confirm',
|
||||
data
|
||||
);
|
||||
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.message));
|
||||
} catch (error: any) {
|
||||
return Promise.reject(new Error(error.message || '确认登录失败'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信用户信息
|
||||
*/
|
||||
async function getUserProfile() {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
Taro.getUserProfile({
|
||||
desc: '用于扫码登录身份确认',
|
||||
success: (res) => {
|
||||
resolve(res.userInfo);
|
||||
},
|
||||
fail: (err) => {
|
||||
// 如果获取失败,尝试使用已存储的用户信息
|
||||
const storedUser = Taro.getStorageSync('User');
|
||||
if (storedUser) {
|
||||
resolve({
|
||||
nickName: storedUser.nickname,
|
||||
avatarUrl: storedUser.avatar,
|
||||
gender: storedUser.gender
|
||||
});
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备信息
|
||||
*/
|
||||
async function getDeviceInfo() {
|
||||
return new Promise<string>((resolve) => {
|
||||
Taro.getSystemInfo({
|
||||
success: (res) => {
|
||||
const deviceInfo = {
|
||||
platform: res.platform,
|
||||
system: res.system,
|
||||
version: res.version,
|
||||
model: res.model,
|
||||
brand: res.brand,
|
||||
screenWidth: res.screenWidth,
|
||||
screenHeight: res.screenHeight
|
||||
};
|
||||
resolve(JSON.stringify(deviceInfo));
|
||||
},
|
||||
fail: () => {
|
||||
resolve('unknown');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析二维码内容,提取token
|
||||
*/
|
||||
export function parseQRContent(qrContent: string): string | null {
|
||||
try {
|
||||
// 尝试解析JSON格式
|
||||
if (qrContent.startsWith('{')) {
|
||||
const parsed = JSON.parse(qrContent);
|
||||
return parsed.token || null;
|
||||
}
|
||||
|
||||
// 尝试解析URL格式
|
||||
if (qrContent.includes('token=')) {
|
||||
const url = new URL(qrContent);
|
||||
return url.searchParams.get('token');
|
||||
}
|
||||
|
||||
// 直接返回内容作为token
|
||||
return qrContent;
|
||||
} catch (error) {
|
||||
console.error('解析二维码内容失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user