|
|
@@ -18,33 +18,45 @@
|
|
|
<text class="subtitle">专业的市场量化分析工具</text>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 登录按钮区域 -->
|
|
|
+ <!-- 登录注册区域 -->
|
|
|
<view class="login-actions">
|
|
|
- <!-- 微信授权登录按钮 -->
|
|
|
- <button
|
|
|
- v-if="!showPhoneAuth"
|
|
|
- class="login-btn primary-btn"
|
|
|
- @click="handleWxAuth"
|
|
|
- >
|
|
|
- <text class="btn-text">微信授权登录</text>
|
|
|
- </button>
|
|
|
-
|
|
|
- <!-- 手机号输入表单(新用户第二步) -->
|
|
|
- <view v-else class="phone-form">
|
|
|
+ <!-- 初始状态:显示登录和注册按钮 -->
|
|
|
+ <view v-if="currentStep === 'initial'" class="button-group">
|
|
|
+ <button class="login-btn primary-btn" @click="handleLoginClick">
|
|
|
+ <text class="btn-text">登录</text>
|
|
|
+ </button>
|
|
|
+ <button class="login-btn secondary-btn" @click="handleRegisterClick">
|
|
|
+ <text class="btn-text">注册</text>
|
|
|
+ </button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 输入手机号密码表单 -->
|
|
|
+ <view v-else-if="currentStep === 'input'" class="form-container">
|
|
|
+ <view class="form-title">{{ isLogin ? '登录账号' : '注册账号' }}</view>
|
|
|
<input
|
|
|
v-model="phoneNumber"
|
|
|
type="number"
|
|
|
placeholder="请输入手机号"
|
|
|
maxlength="11"
|
|
|
- class="phone-input"
|
|
|
+ class="form-input"
|
|
|
+ />
|
|
|
+ <input
|
|
|
+ v-model="password"
|
|
|
+ type="text"
|
|
|
+ :password="true"
|
|
|
+ placeholder="请输入密码"
|
|
|
+ class="form-input"
|
|
|
/>
|
|
|
- <button class="login-btn primary-btn" @click="handlePhoneSubmit">
|
|
|
- <text class="btn-text">完成注册</text>
|
|
|
+ <button class="login-btn primary-btn" @click="handleSubmit">
|
|
|
+ <text class="btn-text">{{ isLogin ? '登录' : '注册' }}</text>
|
|
|
+ </button>
|
|
|
+ <button class="login-btn cancel-btn" @click="handleCancel">
|
|
|
+ <text class="btn-text">取消</text>
|
|
|
</button>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 用户协议 -->
|
|
|
- <view class="agreement">
|
|
|
+ <!-- 用户协议 - 只在初始状态显示 -->
|
|
|
+ <view v-if="currentStep === 'initial'" class="agreement">
|
|
|
<checkbox-group @change="handleAgreementChange">
|
|
|
<label class="agreement-label">
|
|
|
<checkbox :checked="agreedToTerms" color="#5d55e8" />
|
|
|
@@ -62,19 +74,30 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { startH5Auth, handleH5AuthCallback } from '@/utils/auth.js'
|
|
|
+import { startH5Auth, handleH5AuthCallback, setToken } from '@/utils/auth.js'
|
|
|
+import { checkUserStatus, h5PhoneLogin } from '@/utils/api.js'
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
- showPhoneAuth: false,
|
|
|
+ currentStep: 'initial', // 'initial' | 'input'
|
|
|
+ isLogin: true, // true=登录, false=注册
|
|
|
agreedToTerms: false,
|
|
|
- tempUserData: null,
|
|
|
- phoneNumber: ''
|
|
|
+ showAgreement: false, // 是否显示协议选择框
|
|
|
+ phoneNumber: '',
|
|
|
+ password: '',
|
|
|
+ tempWxData: null // 临时存储微信授权数据
|
|
|
}
|
|
|
},
|
|
|
|
|
|
onLoad(options) {
|
|
|
+ // 恢复登录/注册状态
|
|
|
+ const savedIsLogin = uni.getStorageSync('temp_isLogin')
|
|
|
+ if (savedIsLogin !== null && savedIsLogin !== undefined && savedIsLogin !== '') {
|
|
|
+ this.isLogin = savedIsLogin === 'true'
|
|
|
+ uni.removeStorageSync('temp_isLogin')
|
|
|
+ }
|
|
|
+
|
|
|
// 检查URL中是否有微信授权回调的code参数
|
|
|
let code = options && options.code
|
|
|
|
|
|
@@ -111,7 +134,7 @@ export default {
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
- async handleWxAuth() {
|
|
|
+ async handleLoginClick() {
|
|
|
if (!this.agreedToTerms) {
|
|
|
uni.showToast({
|
|
|
title: '请先阅读并同意用户协议',
|
|
|
@@ -121,8 +144,11 @@ export default {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ this.isLogin = true
|
|
|
+ uni.setStorageSync('temp_isLogin', 'true')
|
|
|
+
|
|
|
try {
|
|
|
- uni.showLoading({ title: '跳转中...' })
|
|
|
+ uni.showLoading({ title: '授权中...' })
|
|
|
await startH5Auth()
|
|
|
uni.hideLoading()
|
|
|
} catch (error) {
|
|
|
@@ -136,37 +162,98 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ async handleRegisterClick() {
|
|
|
+ if (!this.agreedToTerms) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请先阅读并同意用户协议',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isLogin = false
|
|
|
+ uni.setStorageSync('temp_isLogin', 'false')
|
|
|
+
|
|
|
+ try {
|
|
|
+ uni.showLoading({ title: '授权中...' })
|
|
|
+ await startH5Auth()
|
|
|
+ uni.hideLoading()
|
|
|
+ } catch (error) {
|
|
|
+ uni.hideLoading()
|
|
|
+ console.error('[注册] 微信授权失败:', error)
|
|
|
+ uni.showToast({
|
|
|
+ title: error.message || '授权失败,请重试',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
async handleAuthCallback(code) {
|
|
|
try {
|
|
|
- uni.showLoading({ title: '登录中...' })
|
|
|
+ uni.showLoading({ title: '处理中...' })
|
|
|
+
|
|
|
+ // 获取微信用户信息
|
|
|
const result = await handleH5AuthCallback(code)
|
|
|
+
|
|
|
+ // 检查用户是否已注册
|
|
|
+ const statusResult = await checkUserStatus(result.openid)
|
|
|
+ const isRegistered = statusResult.data
|
|
|
+
|
|
|
uni.hideLoading()
|
|
|
|
|
|
- if (result.isRegistered) {
|
|
|
- console.log('[登录] 老用户登录成功')
|
|
|
- this.handleLoginSuccess()
|
|
|
+ // 保存微信数据
|
|
|
+ this.tempWxData = {
|
|
|
+ openid: result.openid,
|
|
|
+ unionid: result.unionid,
|
|
|
+ nickname: result.nickname,
|
|
|
+ avatarUrl: result.avatarUrl
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据用户状态和操作类型判断
|
|
|
+ if (this.isLogin) {
|
|
|
+ // 登录流程
|
|
|
+ if (isRegistered) {
|
|
|
+ // 已注册,显示输入框
|
|
|
+ this.currentStep = 'input'
|
|
|
+ } else {
|
|
|
+ // 未注册,提示先注册
|
|
|
+ uni.showToast({
|
|
|
+ title: '该微信未注册,请先注册',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ this.currentStep = 'initial'
|
|
|
+ }
|
|
|
} else {
|
|
|
- console.log('[登录] 新用户,需要手机号授权')
|
|
|
- this.tempUserData = {
|
|
|
- openid: result.openid,
|
|
|
- unionid: result.unionid,
|
|
|
- nickname: result.nickname,
|
|
|
- avatarUrl: result.avatarUrl
|
|
|
+ // 注册流程
|
|
|
+ if (!isRegistered) {
|
|
|
+ // 未注册,显示输入框
|
|
|
+ this.currentStep = 'input'
|
|
|
+ } else {
|
|
|
+ // 已注册,提示直接登录
|
|
|
+ uni.showToast({
|
|
|
+ title: '该微信已注册,请直接登录',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ this.currentStep = 'initial'
|
|
|
}
|
|
|
- this.showPhoneAuth = true
|
|
|
}
|
|
|
} catch (error) {
|
|
|
uni.hideLoading()
|
|
|
- console.error('[登录] 授权回调处理失败:', error)
|
|
|
+ console.error('[授权] 处理失败:', error)
|
|
|
uni.showToast({
|
|
|
- title: error.message || '登录失败,请重试',
|
|
|
+ title: error.message || '处理失败,请重试',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
})
|
|
|
+ this.currentStep = 'initial'
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- async handlePhoneSubmit() {
|
|
|
+ async handleSubmit() {
|
|
|
if (!this.phoneNumber) {
|
|
|
uni.showToast({
|
|
|
title: '请输入手机号',
|
|
|
@@ -185,46 +272,58 @@ export default {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ if (!this.password) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请输入密码',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
- uni.showLoading({ title: '注册中...' })
|
|
|
+ uni.showLoading({ title: this.isLogin ? '登录中...' : '注册中...' })
|
|
|
|
|
|
- const { h5PhoneLogin } = await import('@/utils/api.js')
|
|
|
- const result = await h5PhoneLogin({
|
|
|
- openid: this.tempUserData.openid,
|
|
|
- unionid: this.tempUserData.unionid,
|
|
|
+ const params = {
|
|
|
+ openid: this.tempWxData.openid,
|
|
|
+ unionid: this.tempWxData.unionid,
|
|
|
phone: this.phoneNumber,
|
|
|
- nickname: this.tempUserData.nickname,
|
|
|
- avatarUrl: this.tempUserData.avatarUrl
|
|
|
- })
|
|
|
+ password: this.password
|
|
|
+ }
|
|
|
+
|
|
|
+ // 注册时需要传递昵称和头像
|
|
|
+ if (!this.isLogin) {
|
|
|
+ params.nickname = this.tempWxData.nickname
|
|
|
+ params.avatarUrl = this.tempWxData.avatarUrl
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await h5PhoneLogin(params)
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
if (result.code === 200 && result.data.token) {
|
|
|
- const { setToken } = await import('@/utils/auth.js')
|
|
|
setToken(result.data.token)
|
|
|
- console.log('[登录] 注册成功')
|
|
|
+ console.log('[登录] 成功')
|
|
|
this.handleLoginSuccess()
|
|
|
} else {
|
|
|
- throw new Error(result.message || '注册失败')
|
|
|
+ throw new Error(result.message || (this.isLogin ? '登录失败' : '注册失败'))
|
|
|
}
|
|
|
} catch (error) {
|
|
|
uni.hideLoading()
|
|
|
- console.error('[登录] 注册失败:', error)
|
|
|
+ console.error('[提交] 失败:', error)
|
|
|
uni.showToast({
|
|
|
- title: error.message || '注册失败,请重试',
|
|
|
+ title: error.message || (this.isLogin ? '登录失败,请重试' : '注册失败,请重试'),
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- handlePhoneAuth() {
|
|
|
- // 已废弃,保留以防万一
|
|
|
- uni.showToast({
|
|
|
- title: 'H5环境暂不支持手机号快速验证,请联系管理员',
|
|
|
- icon: 'none',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
+ handleCancel() {
|
|
|
+ this.currentStep = 'initial'
|
|
|
+ this.phoneNumber = ''
|
|
|
+ this.password = ''
|
|
|
+ this.tempWxData = null
|
|
|
},
|
|
|
|
|
|
handleLoginSuccess() {
|
|
|
@@ -374,6 +473,10 @@ export default {
|
|
|
z-index: 1;
|
|
|
}
|
|
|
|
|
|
+.button-group {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
.login-btn {
|
|
|
width: 100%;
|
|
|
height: 96rpx;
|
|
|
@@ -383,7 +486,7 @@ export default {
|
|
|
justify-content: center;
|
|
|
font-size: 32rpx;
|
|
|
font-weight: 500;
|
|
|
- margin-bottom: 40rpx;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
border: none;
|
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
|
|
|
}
|
|
|
@@ -393,15 +496,44 @@ export default {
|
|
|
color: #667eea;
|
|
|
}
|
|
|
|
|
|
-.btn-icon {
|
|
|
- font-size: 40rpx;
|
|
|
- margin-right: 16rpx;
|
|
|
+.secondary-btn {
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ color: #fff;
|
|
|
+ border: 2rpx solid #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.cancel-btn {
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
|
|
|
.btn-text {
|
|
|
font-size: 32rpx;
|
|
|
}
|
|
|
|
|
|
+.form-container {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.form-title {
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #fff;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.form-input {
|
|
|
+ width: 100%;
|
|
|
+ height: 96rpx;
|
|
|
+ border-radius: 48rpx;
|
|
|
+ background: #fff;
|
|
|
+ padding: 0 40rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
.agreement {
|
|
|
margin-top: 60rpx;
|
|
|
}
|
|
|
@@ -422,19 +554,4 @@ export default {
|
|
|
color: #fff;
|
|
|
text-decoration: underline;
|
|
|
}
|
|
|
-
|
|
|
-.phone-form {
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.phone-input {
|
|
|
- width: 100%;
|
|
|
- height: 96rpx;
|
|
|
- border-radius: 48rpx;
|
|
|
- background: #fff;
|
|
|
- padding: 0 40rpx;
|
|
|
- font-size: 32rpx;
|
|
|
- margin-bottom: 40rpx;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
</style>
|