upload project
This commit is contained in:
commit
06961cae04
422 changed files with 110626 additions and 0 deletions
466
subpackages/settings/phone-binding/phone-binding.js
Normal file
466
subpackages/settings/phone-binding/phone-binding.js
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
// 手机号绑定页面
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
5
subpackages/settings/phone-binding/phone-binding.json
Normal file
5
subpackages/settings/phone-binding/phone-binding.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"navigationStyle": "custom",
|
||||
"backgroundColor": "#667eea",
|
||||
"backgroundTextStyle": "light"
|
||||
}
|
||||
141
subpackages/settings/phone-binding/phone-binding.wxml
Normal file
141
subpackages/settings/phone-binding/phone-binding.wxml
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<!--手机号绑定页面-->
|
||||
<view class="container" style="height: {{windowHeight}}px;">
|
||||
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" style="height: {{navBarHeight}}px; padding-top: {{statusBarHeight}}px;">
|
||||
<view class="navbar-content">
|
||||
<view class="navbar-left" bindtap="goBack">
|
||||
<text class="back-icon">←</text>
|
||||
</view>
|
||||
<view class="navbar-title">绑定手机号</view>
|
||||
<view class="navbar-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<view class="main-content">
|
||||
|
||||
<!-- 头部说明 -->
|
||||
<view class="header-section">
|
||||
<view class="header-icon">📱</view>
|
||||
<view class="header-title">绑定手机号</view>
|
||||
<view class="header-desc">为了更好的使用体验和账号安全,请绑定您的手机号</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
|
||||
<!-- 手机号输入 -->
|
||||
<view class="input-group">
|
||||
<text class="input-label">手机号</text>
|
||||
<view class="input-container">
|
||||
<view class="input-icon">📱</view>
|
||||
<input
|
||||
class="phone-input"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
value="{{phoneNumber}}"
|
||||
bindinput="onPhoneInput"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 验证码输入 -->
|
||||
<view class="input-group">
|
||||
<text class="input-label">验证码</text>
|
||||
<view class="input-container code-container">
|
||||
<view class="input-icon">🔐</view>
|
||||
<input
|
||||
class="code-input"
|
||||
type="number"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="6"
|
||||
value="{{verifyCode}}"
|
||||
bindinput="onCodeInput"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
<view
|
||||
class="code-button {{canSendCode ? 'active' : 'disabled'}}"
|
||||
bindtap="sendVerifyCode"
|
||||
>
|
||||
<text class="code-button-text">{{codeButtonText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 绑定按钮 -->
|
||||
<view
|
||||
class="bind-button {{canBind ? 'active' : 'disabled'}}"
|
||||
bindtap="handleBind"
|
||||
>
|
||||
<view class="bind-content">
|
||||
<view class="bind-icon" wx:if="{{isBinding}}">⏳</view>
|
||||
<view class="bind-icon" wx:else>🔗</view>
|
||||
<text class="bind-text">{{isBinding ? '绑定中...' : '立即绑定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<view class="tips-section">
|
||||
<view class="tips-title">温馨提示</view>
|
||||
<view class="tips-item">• 绑定手机号后可以更安全地管理您的账号</view>
|
||||
<view class="tips-item">• 如果该手机号已关联其他账号,系统会提示您进行账号合并</view>
|
||||
<view class="tips-item">• 账号合并后,所有数据将统一到当前账号</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 账号合并对话框 -->
|
||||
<view class="merge-dialog-mask" wx:if="{{showMergeDialog}}" bindtap="closeMergeDialog">
|
||||
<view class="merge-dialog" catchtap="">
|
||||
|
||||
<view class="dialog-header">
|
||||
<view class="dialog-title">发现可合并的账号</view>
|
||||
<view class="dialog-close" bindtap="closeMergeDialog">×</view>
|
||||
</view>
|
||||
|
||||
<view class="dialog-content">
|
||||
<view class="dialog-desc">该手机号已关联以下账号,请选择处理方式:</view>
|
||||
|
||||
<!-- 候选账号列表 -->
|
||||
<view class="candidates-list">
|
||||
<view
|
||||
class="candidate-item"
|
||||
wx:for="{{mergeCandidates}}"
|
||||
wx:key="customId"
|
||||
>
|
||||
<view class="candidate-info">
|
||||
<view class="candidate-name">{{item.nickname || '未知用户'}}</view>
|
||||
<view class="candidate-detail">
|
||||
<text class="detail-item">注册时间:{{item.registerTime}}</text>
|
||||
<text class="detail-item">匹配原因:{{item.matchReason}}</text>
|
||||
<text class="detail-item">可信度:{{item.confidence}}%</text>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="merge-btn"
|
||||
data-custom-id="{{item.customId}}"
|
||||
bindtap="mergeSpecificAccount"
|
||||
>
|
||||
合并此账号
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dialog-actions">
|
||||
<view class="action-btn cancel-btn" bindtap="closeMergeDialog">取消</view>
|
||||
<view class="action-btn auto-btn" bindtap="autoMergeAll">自动合并所有</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 安全区域占位 -->
|
||||
<view style="height: {{safeAreaBottom}}px;"></view>
|
||||
|
||||
</view>
|
||||
384
subpackages/settings/phone-binding/phone-binding.wxss
Normal file
384
subpackages/settings/phone-binding/phone-binding.wxss
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/* 手机号绑定页面样式 */
|
||||
@import "../../../styles/design-system.wxss";
|
||||
@import "../../../styles/components.wxss";
|
||||
|
||||
.container {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.navbar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 44px;
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.navbar-left {
|
||||
width: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
width: 80rpx;
|
||||
}
|
||||
|
||||
/* 主要内容 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 120rpx 60rpx 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部区域 */
|
||||
.header-section {
|
||||
text-align: center;
|
||||
margin-bottom: 80rpx;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
line-height: 1.6;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 24rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
margin-bottom: 60rpx;
|
||||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border-radius: 16rpx;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.input-container:focus-within {
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
padding: 0 20rpx;
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.phone-input,
|
||||
.code-input {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 验证码容器 */
|
||||
.code-container {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.code-button {
|
||||
height: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 0 16rpx 16rpx 0;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 2rpx solid #eee;
|
||||
}
|
||||
|
||||
.code-button.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.code-button.disabled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.code-button-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 绑定按钮 */
|
||||
.bind-button {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.bind-button.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.bind-button.disabled {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
.bind-button.active:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.bind-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.bind-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.bind-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bind-button.active .bind-text {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bind-button.disabled .bind-text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 温馨提示 */
|
||||
.tips-section {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.tips-item {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
/* 合并对话框 */
|
||||
.merge-dialog-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.merge-dialog {
|
||||
background: white;
|
||||
border-radius: 24rpx;
|
||||
width: 100%;
|
||||
max-width: 600rpx;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 40rpx 40rpx 20rpx;
|
||||
border-bottom: 2rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.dialog-close {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
flex: 1;
|
||||
padding: 20rpx 40rpx;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 30rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.candidates-list {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.candidate-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 20rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.candidate-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.candidate-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.candidate-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.merge-btn {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 16rpx 24rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
padding: 20rpx 40rpx 40rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #f8f9fa;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.auto-btn {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue