commit
This commit is contained in:
@@ -254,11 +254,38 @@ Page({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.data.canLogin) {
|
||||
const { phone, code, password, loginType, countdown } = this.data;
|
||||
|
||||
// 检查手机号
|
||||
if (phone.length !== 11) {
|
||||
wx.showToast({
|
||||
title: '请输入正确的手机号',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { phone, code, password, loginType } = this.data;
|
||||
|
||||
// 检查验证码或密码
|
||||
if (loginType === 'code') {
|
||||
if (!code || code.length < 4) {
|
||||
wx.showToast({
|
||||
title: '请输入验证码',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!password || password.length < 6) {
|
||||
wx.showToast({
|
||||
title: '请输入密码(至少6位)',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
wx.showLoading({
|
||||
|
||||
@@ -17,6 +17,7 @@ Page({
|
||||
countryCodeIndex: 0,
|
||||
pollTimer: null as any, // 轮询定时器
|
||||
pollCount: 0, // 轮询次数
|
||||
socketTask: null as any, // WebSocket连接
|
||||
// 验证码相关
|
||||
needCaptcha: false, // 是否需要验证码
|
||||
captchaType: '', // 验证码类型
|
||||
@@ -38,6 +39,15 @@ Page({
|
||||
|
||||
onLoad() {
|
||||
console.log('小红书绑定页面加载');
|
||||
|
||||
// 页面加载时就生成session_id并建立WebSocket连接
|
||||
const sessionId = `xhs_login_${Date.now().toString(36)}${Math.random().toString(36).substr(2, 9)}`;
|
||||
console.log('[页面加载] 生成session_id:', sessionId);
|
||||
this.setData({ sessionId });
|
||||
|
||||
// 建立WebSocket连接
|
||||
console.log('[页面加载] 建立WebSocket连接...');
|
||||
this.connectWebSocket(sessionId);
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
@@ -48,6 +58,10 @@ Page({
|
||||
if (this.data.pollTimer) {
|
||||
clearInterval(this.data.pollTimer);
|
||||
}
|
||||
// 关闭WebSocket连接
|
||||
if (this.data.socketTask) {
|
||||
this.data.socketTask.close();
|
||||
}
|
||||
},
|
||||
|
||||
// 区号选择
|
||||
@@ -77,7 +91,7 @@ Page({
|
||||
return;
|
||||
}
|
||||
|
||||
const { phone, countryCodes, countryCodeIndex } = this.data;
|
||||
const { phone, countryCodes, countryCodeIndex, sessionId, socketTask } = this.data;
|
||||
if (phone.length !== 11) {
|
||||
wx.showToast({
|
||||
title: '请输入正确的手机号',
|
||||
@@ -87,43 +101,52 @@ Page({
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载
|
||||
this.setData({
|
||||
showLoading: true
|
||||
});
|
||||
|
||||
try {
|
||||
// 调用后端API发送验证码
|
||||
const countryCode = countryCodes[countryCodeIndex];
|
||||
console.log('发送验证码到:', phone, '区号:', countryCode);
|
||||
console.log('[发送验证码] 开始,手机号:', phone, '区号:', countryCode);
|
||||
console.log('[发送验证码] 使用现有session_id:', sessionId);
|
||||
|
||||
const result = await EmployeeService.sendXHSCode(phone);
|
||||
|
||||
// 保存session_id用于后续复用浏览器
|
||||
if (result.data && result.data.session_id) {
|
||||
this.setData({
|
||||
sessionId: result.data.session_id
|
||||
});
|
||||
console.log('已保存session_id:', result.data.session_id);
|
||||
// 检查WebSocket连接
|
||||
if (!socketTask) {
|
||||
console.error('[发送验证码] WebSocket连接不存在,重新建立...');
|
||||
this.connectWebSocket(sessionId);
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
this.setData({
|
||||
showLoading: false
|
||||
// 通过WebSocket发送send_code消息
|
||||
console.log('[发送验证码] 通过WebSocket发送请求...');
|
||||
const currentSocketTask = this.data.socketTask;
|
||||
if (!currentSocketTask) {
|
||||
throw new Error('WebSocket连接未建立');
|
||||
}
|
||||
|
||||
currentSocketTask.send({
|
||||
data: JSON.stringify({
|
||||
type: 'send_code',
|
||||
phone: phone,
|
||||
country_code: countryCode,
|
||||
login_page: 'home' // 使用小红书首页登录
|
||||
}),
|
||||
success: () => {
|
||||
console.log('[发送验证码] WebSocket消息发送成功');
|
||||
wx.showToast({
|
||||
title: '正在发送验证码...',
|
||||
icon: 'loading',
|
||||
duration: 20000
|
||||
});
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error('[发送验证码] WebSocket消息发送失败:', err);
|
||||
wx.showToast({
|
||||
title: '发送失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
title: '验证码已发送,请在小红书APP中查看',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 开始倒计时
|
||||
this.startCountdown();
|
||||
|
||||
} catch (error: any) {
|
||||
this.setData({
|
||||
showLoading: false
|
||||
});
|
||||
|
||||
console.error('[发送验证码] 异常:', error);
|
||||
wx.showToast({
|
||||
title: error.message || '发送失败,请重试',
|
||||
icon: 'none',
|
||||
@@ -164,7 +187,12 @@ Page({
|
||||
|
||||
// 绑定账号
|
||||
async bindAccount() {
|
||||
const { phone, code, sessionId } = this.data;
|
||||
const { phone, code, sessionId, countryCodes, countryCodeIndex } = this.data;
|
||||
|
||||
console.log('[绑定账号] 开始绑定');
|
||||
console.log('[绑定账号] phone:', phone);
|
||||
console.log('[绑定账号] code:', code);
|
||||
console.log('[绑定账号] sessionId:', sessionId);
|
||||
|
||||
// 验证手机号
|
||||
if (!phone || phone.length !== 11) {
|
||||
@@ -196,42 +224,50 @@ Page({
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载
|
||||
this.setData({
|
||||
showLoading: true,
|
||||
loadingText: '正在验证...',
|
||||
pollCount: 0
|
||||
});
|
||||
|
||||
try {
|
||||
// 调用后端API进行绑定(异步处理)
|
||||
console.log('绑定小红书账号:', { phone, code, sessionId });
|
||||
console.log('[绑定账号] 通过WebSocket发送验证码验证请求...');
|
||||
|
||||
const result = await EmployeeService.bindXHS(phone, code, sessionId);
|
||||
const socketTask = this.data.socketTask;
|
||||
if (!socketTask) {
|
||||
throw new Error('WebSocket连接已断开,请重新发送验证码');
|
||||
}
|
||||
|
||||
// 后端立即返回,开始轮询绑定状态
|
||||
this.setData({
|
||||
loadingText: '正在登录小红书...'
|
||||
const countryCode = countryCodes[countryCodeIndex];
|
||||
|
||||
wx.showToast({
|
||||
title: '正在验证...',
|
||||
icon: 'loading',
|
||||
duration: 60000
|
||||
});
|
||||
|
||||
// 开始轮询绑定状态
|
||||
this.startPollingBindStatus();
|
||||
socketTask.send({
|
||||
data: JSON.stringify({
|
||||
type: 'verify_code',
|
||||
phone: phone,
|
||||
code: code,
|
||||
country_code: countryCode,
|
||||
login_page: 'home' // 使用小红书首页登录
|
||||
}),
|
||||
success: () => {
|
||||
console.log('[绑定账号] WebSocket消息发送成功,等待后端响应...');
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error('[绑定账号] WebSocket消息发送失败:', err);
|
||||
wx.showToast({
|
||||
title: '发送失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
this.setData({
|
||||
showLoading: false
|
||||
console.error('[绑定账号] 异常:', error);
|
||||
wx.showToast({
|
||||
title: error.message || '绑定失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 绑定失败
|
||||
this.setData({
|
||||
showFail: true
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
showFail: false
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -242,6 +278,11 @@ Page({
|
||||
clearInterval(this.data.pollTimer);
|
||||
}
|
||||
|
||||
// 初始化轮询计数
|
||||
this.setData({
|
||||
pollCount: 0
|
||||
});
|
||||
|
||||
// 立即查询一次
|
||||
this.checkBindStatus();
|
||||
|
||||
@@ -264,17 +305,12 @@ Page({
|
||||
// 超过60次轮询(60秒),停止轮询
|
||||
if (pollCount > 60) {
|
||||
this.stopPolling();
|
||||
this.setData({
|
||||
showLoading: false,
|
||||
showFail: true
|
||||
wx.hideToast();
|
||||
wx.showToast({
|
||||
title: '登录超时,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
showFail: false
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -293,10 +329,12 @@ Page({
|
||||
if (status.status === 'success') {
|
||||
// 绑定成功
|
||||
this.stopPolling();
|
||||
wx.hideToast();
|
||||
|
||||
this.setData({
|
||||
showLoading: false,
|
||||
showSuccess: true
|
||||
wx.showToast({
|
||||
title: '绑定成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 更新本地绑定状态缓存
|
||||
@@ -312,10 +350,6 @@ Page({
|
||||
console.log('[手机号登录] 绑定成功,已更新本地缓存');
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
showSuccess: false
|
||||
});
|
||||
|
||||
// 绑定成功后返回个人中心
|
||||
wx.navigateBack();
|
||||
}, 2000);
|
||||
@@ -323,43 +357,36 @@ Page({
|
||||
} else if (status.status === 'failed') {
|
||||
// 绑定失败
|
||||
this.stopPolling();
|
||||
wx.hideToast();
|
||||
|
||||
this.setData({
|
||||
showLoading: false,
|
||||
showFail: true
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
title: status.error || '绑定失败',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
showFail: false
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
} else if (status.status === 'need_captcha') {
|
||||
// 需要验证码验证
|
||||
this.stopPolling();
|
||||
wx.hideToast();
|
||||
|
||||
console.log('需要验证码验证:', status.captcha_type);
|
||||
|
||||
wx.showToast({
|
||||
title: status.message || '需要验证码验证',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
this.setData({
|
||||
showLoading: false,
|
||||
needCaptcha: true,
|
||||
captchaType: status.captcha_type || 'unknown',
|
||||
qrcodeImage: status.qrcode_image || '',
|
||||
loadingText: status.message || '需要验证码验证'
|
||||
qrcodeImage: status.qrcode_image || ''
|
||||
});
|
||||
|
||||
} else if (status.status === 'processing') {
|
||||
// 仍在处理中,继续轮询
|
||||
this.setData({
|
||||
loadingText: status.message || '正在登录小红书...'
|
||||
});
|
||||
console.log('登录处理中:', status.message);
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
@@ -380,9 +407,22 @@ Page({
|
||||
|
||||
// 切换到手机号登录
|
||||
switchToPhone() {
|
||||
console.log('[切换登录] 切换到手机号登录');
|
||||
|
||||
// 停止二维码轮询
|
||||
this.stopQRCodePolling();
|
||||
|
||||
// 关闭二维码弹窗(如果正在显示)
|
||||
if (this.data.needCaptcha) {
|
||||
console.log('[切换登录] 检测到风控弹窗,关闭...');
|
||||
this.setData({
|
||||
needCaptcha: false
|
||||
});
|
||||
|
||||
// 关闭WebSocket连接
|
||||
this.closeWebSocket();
|
||||
}
|
||||
|
||||
// 清空所有扫码登录相关数据
|
||||
this.setData({
|
||||
loginType: 'phone',
|
||||
@@ -401,6 +441,8 @@ Page({
|
||||
countdown: 0,
|
||||
isCounting: false
|
||||
});
|
||||
|
||||
console.log('[切换登录] 切换完成');
|
||||
},
|
||||
|
||||
// 切换到扫码登录
|
||||
@@ -848,5 +890,536 @@ Page({
|
||||
this.stopQRCodePolling();
|
||||
this.switchToPhone();
|
||||
}
|
||||
},
|
||||
|
||||
// 建立WebSocket连接
|
||||
connectWebSocket(sessionId: string) {
|
||||
// 关闭旧连接
|
||||
if (this.data.socketTask) {
|
||||
try {
|
||||
this.data.socketTask.close();
|
||||
} catch (e) {
|
||||
console.log('[WebSocket] 关闭旧连接失败(可能已关闭):', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取Python服务地址(WebSocket端点在Python后端)
|
||||
const pythonURL = API.pythonURL || API.baseURL;
|
||||
// 将http/https转为ws/wss
|
||||
const wsURL = pythonURL.replace('http://', 'ws://').replace('https://', 'wss://');
|
||||
const url = `${wsURL}/ws/login/${sessionId}`;
|
||||
|
||||
console.log('[WebSocket] 开始连接:', url);
|
||||
console.log('[WebSocket] Python服务地址:', pythonURL);
|
||||
|
||||
// 小程序环境检查
|
||||
if (url.includes('localhost') || url.includes('127.0.0.1')) {
|
||||
console.warn('[WebSocket] 检测到localhost地址,请确保开发工具中已勾选"不校验合法域名"');
|
||||
console.warn('[WebSocket] 另外需要安装websockets库: pip install websockets');
|
||||
}
|
||||
|
||||
let socketConnected = false; // 标记连接是否成功建立
|
||||
|
||||
const socketTask = wx.connectSocket({
|
||||
url: url,
|
||||
success: () => {
|
||||
console.log('[WebSocket] 连接请求发送成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[WebSocket] 连接请求失败:', err);
|
||||
socketConnected = false;
|
||||
|
||||
// 检查是否是localhost问题
|
||||
if (url.includes('localhost') || url.includes('127.0.0.1')) {
|
||||
wx.showToast({
|
||||
title: '请在开发工具中勾选"不校验合法域名"并确保Python服务已安装websockets库',
|
||||
icon: 'none',
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 监听连接打开
|
||||
socketTask.onOpen(() => {
|
||||
console.log('[WebSocket] 连接已打开');
|
||||
socketConnected = true;
|
||||
|
||||
// 重置重连计数
|
||||
this.setData({ reconnectCount: 0 } as any);
|
||||
|
||||
// 启动ping/pong保持连接
|
||||
const pingTimer = setInterval(() => {
|
||||
if (socketTask && socketConnected) {
|
||||
socketTask.send({
|
||||
data: 'ping',
|
||||
success: () => console.log('[WebSocket] Ping发送成功'),
|
||||
fail: (err) => console.error('[WebSocket] Ping发送失败:', err)
|
||||
});
|
||||
}
|
||||
}, 30000); // 每30秒ping一次
|
||||
|
||||
this.setData({ pingTimer } as any);
|
||||
});
|
||||
|
||||
// 监听消息
|
||||
socketTask.onMessage((res) => {
|
||||
console.log('[WebSocket] 收到消息:', res.data);
|
||||
|
||||
// 过滤pong消息(服务端的心跳响应)
|
||||
if (res.data === 'pong') {
|
||||
console.log('[WebSocket] 收到pong响应');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(res.data as string);
|
||||
|
||||
// 处理扫码成功消息(发送验证码阶段的风控)
|
||||
if (data.type === 'qrcode_scan_success') {
|
||||
console.log('✅ 扫码验证完成!', data.message);
|
||||
|
||||
// 关闭验证码弹窗
|
||||
this.setData({
|
||||
needCaptcha: false
|
||||
});
|
||||
|
||||
// 显示提示:扫码成功,请重新发送验证码
|
||||
wx.showToast({
|
||||
title: data.message || '扫码成功,请重新发送验证码',
|
||||
icon: 'success',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
console.log('[WebSocket] 扫码验证完成,提示用户重新发送验证码');
|
||||
console.log('[WebSocket] 保持连接,等待后续操作');
|
||||
|
||||
// 不关闭WebSocket,保持连接用于后续登录流程
|
||||
}
|
||||
// 处理二维码失效消息
|
||||
else if (data.type === 'qrcode_expired') {
|
||||
console.log('⚠️ 二维码已失效!', data.message);
|
||||
|
||||
// 关闭验证码弹窗
|
||||
this.setData({
|
||||
needCaptcha: false
|
||||
});
|
||||
|
||||
// 显示提示:二维码已失效
|
||||
wx.showToast({
|
||||
title: data.message || '二维码已失效,请重新发送验证码',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
console.log('[WebSocket] 二维码已失效,关闭弹窗');
|
||||
console.log('[WebSocket] 保持连接,等待用户重新操作');
|
||||
|
||||
// 不关闭WebSocket,保持连接用于重新发送验证码
|
||||
}
|
||||
// 处理登录成功消息(点击登录按钮阶段的风控)
|
||||
else if (data.type === 'login_success') {
|
||||
// 判断是扫码验证成功还是真正的登录成功
|
||||
if (data.storage_state) {
|
||||
// 真正的登录成功,包含 storage_state
|
||||
console.log('✅ 登录成功!', data);
|
||||
|
||||
wx.hideToast();
|
||||
|
||||
// 关闭验证码弹窗
|
||||
this.setData({
|
||||
needCaptcha: false
|
||||
});
|
||||
|
||||
// 关闭WebSocket
|
||||
this.closeWebSocket();
|
||||
|
||||
// 显示绑定成功动画
|
||||
this.setData({
|
||||
showSuccess: true
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
showSuccess: false
|
||||
});
|
||||
|
||||
// 跳转回上一页
|
||||
wx.navigateBack({
|
||||
delta: 1,
|
||||
success: () => {
|
||||
console.log('✅ 跳转回上一页');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('⚠️ 跳转失败:', err);
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
} else {
|
||||
// 扫码验证成功,但还需要继续登录
|
||||
console.log('✅ 扫码验证成功!', data.user_info);
|
||||
|
||||
// 关闭验证码弹窗
|
||||
this.setData({
|
||||
needCaptcha: false
|
||||
});
|
||||
|
||||
// 显示提示:扫码成功,请重新发送验证码
|
||||
wx.showToast({
|
||||
title: '扫码成功,请重新发送验证码',
|
||||
icon: 'success',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
console.log('[WebSocket] 扫码验证完成,提示用户重新发送验证码');
|
||||
console.log('[WebSocket] 保持连接,等待后续登录操作');
|
||||
|
||||
// 不关闭WebSocket,保持连接用于后续登录流程
|
||||
}
|
||||
}
|
||||
// 处理need_captcha消息(发送验证码或登录时触发风控)
|
||||
else if (data.type === 'need_captcha') {
|
||||
console.log('⚠️ 需要扫码验证:', data.captcha_type);
|
||||
|
||||
// 显示二维码弹窗
|
||||
this.setData({
|
||||
needCaptcha: true,
|
||||
captchaType: data.captcha_type || 'unknown',
|
||||
qrcodeImage: data.qrcode_image || ''
|
||||
});
|
||||
|
||||
wx.hideToast();
|
||||
wx.showToast({
|
||||
title: data.message || '需要扫码验证',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
console.log('[WebSocket] 已显示风控二维码');
|
||||
}
|
||||
// 处理code_sent消息(验证码发送结果)
|
||||
else if (data.type === 'code_sent') {
|
||||
console.log('[WebSocket] 验证码发送结果:', data);
|
||||
|
||||
wx.hideToast();
|
||||
|
||||
if (data.success) {
|
||||
wx.showToast({
|
||||
title: data.message || '验证码已发送',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 开始倒计时
|
||||
this.startCountdown();
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: data.message || '发送失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
// 处理login_result消息(登录结果)
|
||||
else if (data.type === 'login_result') {
|
||||
console.log('[WebSocket] 登录结果:', data);
|
||||
|
||||
wx.hideToast();
|
||||
|
||||
if (!data.success) {
|
||||
wx.showToast({
|
||||
title: data.message || '登录失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[WebSocket] 解析消息失败:', e, '原始数据:', res.data);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听错误
|
||||
socketTask.onError((err) => {
|
||||
console.error('[WebSocket] 连接错误:', err);
|
||||
socketConnected = false;
|
||||
|
||||
// 记录错误,准备重连
|
||||
console.log('[WebSocket] 将在关闭后尝试重连');
|
||||
});
|
||||
|
||||
// 监听关闭
|
||||
socketTask.onClose(() => {
|
||||
console.log('[WebSocket] 连接关闭');
|
||||
socketConnected = false;
|
||||
|
||||
// 清理ping定时器
|
||||
if ((this.data as any).pingTimer) {
|
||||
clearInterval((this.data as any).pingTimer);
|
||||
}
|
||||
|
||||
// 检查是否需要重连(只有弹窗还在时才重连)
|
||||
if (this.data.needCaptcha && sessionId) {
|
||||
// 增加重连计数
|
||||
const reconnectCount = (this.data as any).reconnectCount || 0;
|
||||
|
||||
// 最多重连3次
|
||||
if (reconnectCount < 3) {
|
||||
const delay = Math.min(1000 * Math.pow(2, reconnectCount), 5000); // 指数退避: 1s, 2s, 4s
|
||||
console.log(`[WebSocket] 将在${delay}ms后进行第${reconnectCount + 1}次重连`);
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.data.needCaptcha) { // 再次检查弹窗是否还在
|
||||
console.log('[WebSocket] 开始重连...');
|
||||
this.setData({ reconnectCount: reconnectCount + 1 } as any);
|
||||
this.connectWebSocket(sessionId);
|
||||
}
|
||||
}, delay);
|
||||
} else {
|
||||
console.log('[WebSocket] 已达到最大重连次数,停止重连');
|
||||
wx.showToast({
|
||||
title: 'WebSocket连接失败,请重新发送验证码',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.setData({ socketTask });
|
||||
},
|
||||
|
||||
// 关闭WebSocket连接
|
||||
closeWebSocket() {
|
||||
console.log('[WebSocket] 开始关闭连接');
|
||||
|
||||
// 重置重连计数(主动关闭不需要重连)
|
||||
this.setData({ reconnectCount: 999 } as any); // 设置为很大的数阻止重连
|
||||
|
||||
// 清理ping定时器
|
||||
if ((this.data as any).pingTimer) {
|
||||
clearInterval((this.data as any).pingTimer);
|
||||
console.log('[WebSocket] 已清理ping定时器');
|
||||
}
|
||||
|
||||
// 关闭WebSocket连接
|
||||
const socketTask = this.data.socketTask;
|
||||
if (socketTask) {
|
||||
try {
|
||||
// 检查WebSocket连接状态
|
||||
// readyState: 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED
|
||||
const readyState = (socketTask as any).readyState;
|
||||
|
||||
if (readyState === 0 || readyState === 1) {
|
||||
// 只有连接中或已打开的连接才关闭
|
||||
console.log(`[WebSocket] 连接状态: ${readyState}, 执行关闭`);
|
||||
socketTask.close({
|
||||
success: () => {
|
||||
console.log('[WebSocket] 连接关闭成功');
|
||||
},
|
||||
fail: (err: any) => {
|
||||
// 忽略关闭失败错误,可能已经关闭
|
||||
console.log('[WebSocket] 连接关闭失败(忽略):', err.errMsg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(`[WebSocket] 连接已关闭或正在关闭, readyState: ${readyState}`);
|
||||
}
|
||||
|
||||
// 清空socketTask引用
|
||||
this.setData({ socketTask: null });
|
||||
} catch (e) {
|
||||
console.log('[WebSocket] 关闭连接异常(忽略):', e);
|
||||
// 仍然清空socketTask引用
|
||||
this.setData({ socketTask: null });
|
||||
}
|
||||
} else {
|
||||
console.log('[WebSocket] 没有活跃的WebSocket连接');
|
||||
}
|
||||
},
|
||||
|
||||
// 保存二维码
|
||||
saveQRCode() {
|
||||
if (!this.data.qrcodeImage) {
|
||||
wx.showToast({
|
||||
title: '二维码不存在',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[保存二维码] 开始保存');
|
||||
|
||||
// base64转为临时文件
|
||||
const base64Data = this.data.qrcodeImage.replace(/^data:image\/\w+;base64,/, '');
|
||||
const filePath = `${wx.env.USER_DATA_PATH}/qrcode_${Date.now()}.png`;
|
||||
|
||||
// 写入临时文件
|
||||
const fs = wx.getFileSystemManager();
|
||||
fs.writeFile({
|
||||
filePath: filePath,
|
||||
data: base64Data,
|
||||
encoding: 'base64',
|
||||
success: () => {
|
||||
console.log('[保存二维码] 临时文件写入成功:', filePath);
|
||||
|
||||
// 保存到相册
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath: filePath,
|
||||
success: () => {
|
||||
console.log('[保存二维码] 保存成功');
|
||||
wx.showToast({
|
||||
title: '二维码已保存到相册',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[保存二维码] 保存到相册失败:', err);
|
||||
|
||||
// 检查是否是权限问题
|
||||
if (err.errMsg.includes('auth')) {
|
||||
wx.showModal({
|
||||
title: '需要授权',
|
||||
content: '请允许保存图片到相册',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.openSetting({
|
||||
success: (settingRes) => {
|
||||
if (settingRes.authSetting['scope.writePhotosAlbum']) {
|
||||
// 获得授权后重试
|
||||
this.saveQRCode();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: '保存失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[保存二维码] 临时文件写入失败:', err);
|
||||
wx.showToast({
|
||||
title: '保存失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 测试WebSocket连接
|
||||
testWebSocket() {
|
||||
console.log('[测试] 开始测试WebSocket...');
|
||||
|
||||
// 生成测试session_id
|
||||
const testSessionId = `test_${Date.now()}`;
|
||||
console.log('[测试] 测试session_id:', testSessionId);
|
||||
|
||||
// 关闭旧连接
|
||||
if (this.data.socketTask) {
|
||||
try {
|
||||
this.data.socketTask.close();
|
||||
} catch (e) {
|
||||
console.log('[测试] 关闭旧连接失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取WebSocket地址
|
||||
const pythonURL = API.pythonURL || API.baseURL;
|
||||
const wsURL = pythonURL.replace('http://', 'ws://').replace('https://', 'wss://');
|
||||
const url = `${wsURL}/ws/login/${testSessionId}`;
|
||||
|
||||
console.log('[测试] WebSocket地址:', url);
|
||||
|
||||
const socketTask = wx.connectSocket({
|
||||
url: url,
|
||||
success: () => {
|
||||
console.log('[测试] 连接请求成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[测试] 连接请求失败:', err);
|
||||
wx.showToast({
|
||||
title: '连接失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 监听连接打开
|
||||
socketTask.onOpen(() => {
|
||||
console.log('[测试] 连接已打开');
|
||||
wx.showToast({
|
||||
title: 'WebSocket连接成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 3秒后发送测试消息
|
||||
setTimeout(() => {
|
||||
console.log('[测试] 发送测试消息...');
|
||||
socketTask.send({
|
||||
data: JSON.stringify({
|
||||
type: 'test',
|
||||
message: 'This is a test message from frontend'
|
||||
}),
|
||||
success: () => {
|
||||
console.log('[测试] 测试消息发送成功');
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error('[测试] 测试消息发送失败:', err);
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// 监听消息
|
||||
socketTask.onMessage((res) => {
|
||||
console.log('[测试] 收到消息:', res.data);
|
||||
|
||||
// 过滤pong消息
|
||||
if (res.data === 'pong') {
|
||||
console.log('[测试] 收到pong响应');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(res.data as string);
|
||||
console.log('[测试] 解析后的消息:', data);
|
||||
|
||||
// 显示消息
|
||||
wx.showModal({
|
||||
title: '收到WebSocket消息',
|
||||
content: `类型: ${data.type}\n内容: ${data.message || JSON.stringify(data)}`,
|
||||
showCancel: false
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('[测试] 解析消息失败:', e);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听错误
|
||||
socketTask.onError((err) => {
|
||||
console.error('[测试] 连接错误:', err);
|
||||
wx.showToast({
|
||||
title: '连接错误',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
|
||||
// 监听关闭
|
||||
socketTask.onClose(() => {
|
||||
console.log('[测试] 连接关闭');
|
||||
});
|
||||
|
||||
this.setData({ socketTask });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<!--pages/profile/platform-bind/platform-bind.wxml-->
|
||||
<view class="container">
|
||||
<!-- WebSocket测试按钮 -->
|
||||
<view style="position: fixed; top: 20rpx; right: 20rpx; z-index: 9999;">
|
||||
<button style="padding: 10rpx 20rpx; font-size: 24rpx; background: #4CAF50; color: white; border-radius: 8rpx;" bindtap="testWebSocket">测试WebSocket</button>
|
||||
</view>
|
||||
|
||||
<!-- 绑定内容 -->
|
||||
<view class="bind-content">
|
||||
<!-- 小红书Logo -->
|
||||
@@ -69,11 +74,18 @@
|
||||
|
||||
<!-- 手机号登录区域 -->
|
||||
<view class="phone-login-section" wx:if="{{loginType === 'phone'}}">
|
||||
<!-- 加载中 -->
|
||||
<view class="inline-loading" wx:if="{{showLoading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">{{loadingText}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 二维码验证区域(验证码验证时出现) -->
|
||||
<view class="qrcode-section" wx:if="{{needCaptcha && qrcodeImage}}">
|
||||
<text class="qrcode-title">请使用小红书APP扫描二维码</text>
|
||||
<image class="qrcode" src="{{qrcodeImage}}" mode="aspectFit"></image>
|
||||
<text class="qrcode-hint">扫码后即可继续绑定流程</text>
|
||||
<button class="save-qrcode-btn" bindtap="saveQRCode">保存二维码</button>
|
||||
</view>
|
||||
|
||||
<view class="bind-form" wx:if="{{!needCaptcha}}">
|
||||
@@ -127,13 +139,6 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="toast-overlay" wx:if="{{showLoading && loginType === 'phone'}}">
|
||||
<view class="toast">
|
||||
<view class="toast-loading"></view>
|
||||
<text class="toast-text">{{loadingText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="toast-overlay" wx:if="{{showFail}}">
|
||||
<view class="toast">
|
||||
<view class="toast-icon info">
|
||||
|
||||
@@ -248,6 +248,33 @@ page {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 保存二维码按钮 */
|
||||
.save-qrcode-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #FF2442 0%, #FF4F6A 100%);
|
||||
border-radius: 12rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 16rpx rgba(255, 36, 66, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.save-qrcode-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.save-qrcode-btn:active {
|
||||
opacity: 0.8;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.bind-form {
|
||||
@@ -465,6 +492,17 @@ page {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 内联加载提示(手机号登录区域) */
|
||||
.inline-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
padding: 80rpx 0;
|
||||
min-height: 300rpx;
|
||||
}
|
||||
|
||||
/* 二维码错误提示 */
|
||||
.qrcode-error {
|
||||
position: absolute;
|
||||
|
||||
@@ -74,9 +74,10 @@ export class EmployeeService {
|
||||
* 发送小红书验证码
|
||||
* 返回 session_id 用于后续复用浏览器
|
||||
*/
|
||||
static async sendXHSCode(xhsPhone: string, showLoading = true) {
|
||||
static async sendXHSCode(xhsPhone: string, showLoading = true, sessionId?: string) {
|
||||
return post<{ sent_at: string; session_id: string }>(API.xhs.sendCode, {
|
||||
xhs_phone: xhsPhone
|
||||
xhs_phone: xhsPhone,
|
||||
session_id: sessionId // 传递session_id给后端
|
||||
}, showLoading);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user