467 lines
11 KiB
JavaScript
467 lines
11 KiB
JavaScript
|
|
// 手机号绑定页面
|
|||
|
|
const app = getApp();
|
|||
|
|
const apiClient = require('../../../utils/api-client.js');
|
|||
|
|
const accountSyncManager = require('../../../utils/account-sync.js');
|
|||
|
|
const authManager = require('../../../utils/auth.js');
|
|||
|
|
|
|||
|
|
Page({
|
|||
|
|
data: {
|
|||
|
|
// 表单数据
|
|||
|
|
phoneNumber: '',
|
|||
|
|
verifyCode: '',
|
|||
|
|
|
|||
|
|
// 状态控制
|
|||
|
|
canSendCode: false,
|
|||
|
|
canBind: false,
|
|||
|
|
isBinding: false,
|
|||
|
|
|
|||
|
|
// 验证码倒计时
|
|||
|
|
codeButtonText: '获取验证码',
|
|||
|
|
codeCountdown: 0,
|
|||
|
|
codeTimer: null,
|
|||
|
|
|
|||
|
|
// 合并相关
|
|||
|
|
showMergeDialog: false,
|
|||
|
|
mergeCandidates: [],
|
|||
|
|
|
|||
|
|
// 系统适配
|
|||
|
|
statusBarHeight: 44,
|
|||
|
|
navBarHeight: 88,
|
|||
|
|
windowHeight: 667,
|
|||
|
|
safeAreaBottom: 0,
|
|||
|
|
|
|||
|
|
// 用户信息
|
|||
|
|
userInfo: null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onLoad: function (options) {
|
|||
|
|
|
|||
|
|
this.initSystemInfo();
|
|||
|
|
this.initUserInfo();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onUnload: function () {
|
|||
|
|
// 清理定时器
|
|||
|
|
if (this.data.codeTimer) {
|
|||
|
|
clearInterval(this.data.codeTimer);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 初始化系统信息
|
|||
|
|
initSystemInfo() {
|
|||
|
|
try {
|
|||
|
|
const systemInfo = wx.getSystemInfoSync();
|
|||
|
|
const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
|
|||
|
|
|
|||
|
|
const statusBarHeight = systemInfo.statusBarHeight;
|
|||
|
|
const navBarHeight = menuButtonInfo.bottom + menuButtonInfo.top - statusBarHeight;
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
statusBarHeight,
|
|||
|
|
navBarHeight,
|
|||
|
|
windowHeight: systemInfo.windowHeight,
|
|||
|
|
safeAreaBottom: systemInfo.safeArea ? systemInfo.screenHeight - systemInfo.safeArea.bottom : 0
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('初始化系统信息失败:', error);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 初始化用户信息
|
|||
|
|
initUserInfo() {
|
|||
|
|
const userInfo = authManager.getUserDisplayInfo();
|
|||
|
|
if (userInfo) {
|
|||
|
|
this.setData({ userInfo });
|
|||
|
|
} else {
|
|||
|
|
console.error('无法获取用户信息,返回上一页');
|
|||
|
|
wx.navigateBack();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 手机号输入处理
|
|||
|
|
onPhoneInput: function (e) {
|
|||
|
|
const value = e.detail.value;
|
|||
|
|
this.setData({
|
|||
|
|
phoneNumber: value
|
|||
|
|
});
|
|||
|
|
this.validateForm();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 验证码输入处理
|
|||
|
|
onCodeInput: function (e) {
|
|||
|
|
const value = e.detail.value;
|
|||
|
|
this.setData({
|
|||
|
|
verifyCode: value
|
|||
|
|
});
|
|||
|
|
this.validateForm();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
validateForm: function () {
|
|||
|
|
const { phoneNumber, verifyCode, codeCountdown } = this.data;
|
|||
|
|
|
|||
|
|
// 手机号格式验证
|
|||
|
|
const phoneRegex = /^1[3-9]\d{9}$/;
|
|||
|
|
const isPhoneValid = phoneRegex.test(phoneNumber);
|
|||
|
|
|
|||
|
|
// 验证码验证
|
|||
|
|
const isCodeValid = verifyCode.length === 6;
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
canSendCode: isPhoneValid && codeCountdown === 0,
|
|||
|
|
canBind: isPhoneValid && isCodeValid
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 发送验证码
|
|||
|
|
sendVerifyCode: function () {
|
|||
|
|
if (!this.data.canSendCode) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const { phoneNumber } = this.data;
|
|||
|
|
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '发送中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
apiClient.sendVerifyCode(phoneNumber)
|
|||
|
|
.then(response => {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
this.startCodeCountdown();
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '验证码已发送',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
console.error('验证码发送失败:', error);
|
|||
|
|
|
|||
|
|
let errorMessage = '发送失败,请重试';
|
|||
|
|
if (error.message) {
|
|||
|
|
errorMessage = error.message;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: errorMessage,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 开始验证码倒计时
|
|||
|
|
startCodeCountdown: function () {
|
|||
|
|
let countdown = 60;
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
codeCountdown: countdown,
|
|||
|
|
codeButtonText: `${countdown}s后重发`
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const timer = setInterval(() => {
|
|||
|
|
countdown--;
|
|||
|
|
|
|||
|
|
if (countdown <= 0) {
|
|||
|
|
clearInterval(timer);
|
|||
|
|
this.setData({
|
|||
|
|
codeCountdown: 0,
|
|||
|
|
codeButtonText: '重新发送',
|
|||
|
|
codeTimer: null
|
|||
|
|
});
|
|||
|
|
this.validateForm();
|
|||
|
|
} else {
|
|||
|
|
this.setData({
|
|||
|
|
codeCountdown: countdown,
|
|||
|
|
codeButtonText: `${countdown}s后重发`
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}, 1000);
|
|||
|
|
|
|||
|
|
this.setData({
|
|||
|
|
codeTimer: timer
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 绑定手机号
|
|||
|
|
handleBind: function () {
|
|||
|
|
if (!this.data.canBind || this.data.isBinding) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const { phoneNumber, verifyCode } = this.data;
|
|||
|
|
|
|||
|
|
this.setData({ isBinding: true });
|
|||
|
|
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '绑定中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 🔥 使用新的分步绑定逻辑
|
|||
|
|
this.attemptBinding(phoneNumber, verifyCode);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 🔥 分步绑定逻辑
|
|||
|
|
async attemptBinding(phoneNumber, verifyCode) {
|
|||
|
|
try {
|
|||
|
|
// 第一步:尝试不自动合并的绑定
|
|||
|
|
|
|||
|
|
const result = await accountSyncManager.bindPhone(phoneNumber, verifyCode, false);
|
|||
|
|
|
|||
|
|
// 绑定成功
|
|||
|
|
this.handleBindingSuccess(result);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
|
|||
|
|
if (error.message && error.message.includes('已关联其他账号')) {
|
|||
|
|
// 发现冲突,询问用户是否合并
|
|||
|
|
this.showMergeConfirmDialog(phoneNumber, verifyCode);
|
|||
|
|
} else {
|
|||
|
|
// 其他错误,直接显示
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '绑定失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
this.setData({ isBinding: false });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示合并确认对话框
|
|||
|
|
showMergeConfirmDialog(phoneNumber, verifyCode) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '发现账号冲突',
|
|||
|
|
content: `手机号 ${phoneNumber} 已被其他账号使用。\n\n是否同意自动合并账号?合并后将保留当前账号的数据。`,
|
|||
|
|
cancelText: '取消绑定',
|
|||
|
|
confirmText: '同意合并',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
// 用户同意合并
|
|||
|
|
this.performAutoMergeBinding(phoneNumber, verifyCode);
|
|||
|
|
} else {
|
|||
|
|
// 用户取消
|
|||
|
|
this.setData({ isBinding: false });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 执行自动合并绑定
|
|||
|
|
async performAutoMergeBinding(phoneNumber, verifyCode) {
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '合并账号中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
|
|||
|
|
const result = await accountSyncManager.bindPhone(phoneNumber, verifyCode, true);
|
|||
|
|
|
|||
|
|
// 合并成功
|
|||
|
|
this.handleBindingSuccess(result);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('🔄 自动合并绑定失败:', error);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '合并失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
this.setData({ isBinding: false });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理绑定成功
|
|||
|
|
handleBindingSuccess(result) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
// 重置绑定状态
|
|||
|
|
this.setData({ isBinding: false });
|
|||
|
|
|
|||
|
|
// 根据文档,处理不同的绑定结果
|
|||
|
|
if (result.hasMerged && result.mergeCount > 0) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: `绑定成功!已自动合并 ${result.mergeCount} 个账号`,
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '绑定成功!',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 延迟返回上一页,让用户看到成功提示
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
}, 2000);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理账号冲突
|
|||
|
|
async handleAccountConflict() {
|
|||
|
|
try {
|
|||
|
|
const userInfo = this.data.userInfo;
|
|||
|
|
if (!userInfo || !userInfo.customId) {
|
|||
|
|
throw new Error('无法获取用户ID');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const mergeInfo = await accountSyncManager.detectMerge(userInfo.customId);
|
|||
|
|
|
|||
|
|
if (mergeInfo.hasMergeCandidates && mergeInfo.candidates.length > 0) {
|
|||
|
|
this.setData({
|
|||
|
|
mergeCandidates: mergeInfo.candidates,
|
|||
|
|
showMergeDialog: true
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 🚨 异常情况:绑定失败但检测不到冲突账号
|
|||
|
|
console.error('🚨 系统异常:绑定失败但检测不到冲突账号');
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '系统检测异常',
|
|||
|
|
content: `手机号 ${this.data.phoneNumber} 绑定失败,提示已被其他账号使用,但系统检测不到冲突账号。\n\n可能原因:\n1. 数据库状态异常\n2. 已删除账号的残留数据\n3. 账号状态不一致\n\n建议联系技术支持处理。`,
|
|||
|
|
showCancel: true,
|
|||
|
|
cancelText: '使用其他手机号',
|
|||
|
|
confirmText: '联系客服',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '请联系客服处理此问题',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('检测可合并账号失败:', error);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '检测账号失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 关闭合并对话框
|
|||
|
|
closeMergeDialog: function () {
|
|||
|
|
this.setData({
|
|||
|
|
showMergeDialog: false,
|
|||
|
|
mergeCandidates: []
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 手动合并指定账号
|
|||
|
|
mergeSpecificAccount: function (e) {
|
|||
|
|
const candidateCustomId = e.currentTarget.dataset.customId;
|
|||
|
|
this.performMerge(candidateCustomId, '用户手动选择合并');
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 自动合并所有账号
|
|||
|
|
autoMergeAll: function () {
|
|||
|
|
const { phoneNumber, verifyCode } = this.data;
|
|||
|
|
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '自动合并中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 根据文档,使用autoMerge=true参数进行自动合并
|
|||
|
|
accountSyncManager.bindPhone(phoneNumber, verifyCode, true)
|
|||
|
|
.then(result => {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
const mergeCount = result.mergeCount || 0;
|
|||
|
|
const message = result.hasMerged ?
|
|||
|
|
`绑定成功!已自动合并 ${mergeCount} 个账号` :
|
|||
|
|
'绑定成功!';
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: message,
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.setData({ showMergeDialog: false });
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
}, 2000);
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
console.error('自动合并失败:', error);
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '自动合并失败',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 执行合并操作
|
|||
|
|
async performMerge(secondaryCustomId, mergeReason) {
|
|||
|
|
try {
|
|||
|
|
const userInfo = this.data.userInfo;
|
|||
|
|
if (!userInfo || !userInfo.customId) {
|
|||
|
|
throw new Error('无法获取用户ID');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '合并中...',
|
|||
|
|
mask: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await accountSyncManager.mergeAccount(
|
|||
|
|
userInfo.customId,
|
|||
|
|
secondaryCustomId,
|
|||
|
|
mergeReason
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '账号合并成功!',
|
|||
|
|
icon: 'success',
|
|||
|
|
duration: 2000
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.setData({ showMergeDialog: false });
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
}, 2000);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
console.error('账号合并失败:', error);
|
|||
|
|
|
|||
|
|
wx.showToast({
|
|||
|
|
title: error.message || '合并失败,请重试',
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 返回上一页
|
|||
|
|
goBack: function () {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|