500 lines
15 KiB
Vue
500 lines
15 KiB
Vue
<template>
|
||
<view class="login-page">
|
||
<!-- 头部区域 -->
|
||
<view class="header">
|
||
<view class="logo">
|
||
<text class="logo-text">华锐绿能燃料调度</text>
|
||
</view>
|
||
<text class="welcome-text">欢迎登录</text>
|
||
</view>
|
||
|
||
<!-- 登录方式切换 -->
|
||
<!-- <view class="login-mode-switch">
|
||
<view class="switch-tabs">
|
||
<view class="tab-item" :class="{ active: loginMode === 'password' }"
|
||
@click="switchLoginMode('password')">
|
||
<text class="tab-text">账号密码登录</text>
|
||
</view>
|
||
<view class="tab-item" :class="{ active: loginMode === 'phone' }" @click="switchLoginMode('phone')">
|
||
<text class="tab-text">手机号登录</text>
|
||
</view>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 登录表单 -->
|
||
<view class="login-form">
|
||
<!-- 账号密码登录 -->
|
||
<view class="password-login">
|
||
<view class="form-item">
|
||
<view class="input-label">
|
||
<My size="32" color="#999" />
|
||
<text class="label-text">手机号</text>
|
||
</view>
|
||
<nut-input v-model="loginForm.username" placeholder="请输入手机号" type="text" class="form-input" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<view class="input-label">
|
||
<Setting size="32" color="#999" />
|
||
<text class="label-text">密码</text>
|
||
</view>
|
||
<nut-input v-model="loginForm.password" placeholder="请输入密码" type="password" class="form-input" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 忘记密码和手机验证码登录 -->
|
||
<view class="form-options">
|
||
<!-- <text class="forgot-password" @click="forgotPassword">忘记密码?</text> -->
|
||
<!-- <text class="phone-login-link" @click="switchToPhoneLogin">手机验证码登录</text> -->
|
||
</view>
|
||
|
||
<!-- 登录按钮 -->
|
||
<button class="login-btn" @click="handleLogin" :disabled="isLoading">
|
||
{{ isLoading ? '登录中...' : '登录' }}
|
||
</button>
|
||
|
||
<!-- 注册链接 - 已关闭注册入口 -->
|
||
<!-- <view class="register-link" @click="goToRegister">
|
||
<text class="register-text">还没有账号?</text>
|
||
<text class="register-btn">立即注册</text>
|
||
</view> -->
|
||
</view>
|
||
|
||
<!-- Toast 提示 -->
|
||
<nut-toast v-model:visible="showToast" :msg="toastMsg" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue'
|
||
import { Toast } from '@nutui/nutui-taro'
|
||
import { My, Setting } from '@nutui/icons-vue'
|
||
import Taro from '@tarojs/taro'
|
||
import { aesEncrypt } from '../../utils/crypto.js'
|
||
import { saveUserInfo } from '../../utils/auth.js'
|
||
import { authAPI, driverAPI } from '../../api/index.js'
|
||
|
||
// 账号密码登录表单数据
|
||
const loginForm = reactive({
|
||
username: '',
|
||
password: ''
|
||
})
|
||
|
||
// 状态管理
|
||
const isLoading = ref(false)
|
||
const showToast = ref(false)
|
||
const toastMsg = ref('')
|
||
|
||
// 显示提示信息
|
||
const showMessage = (msg) => {
|
||
toastMsg.value = msg
|
||
showToast.value = true
|
||
}
|
||
|
||
// 忘记密码
|
||
const forgotPassword = () => {
|
||
showMessage('忘记密码功能开发中...')
|
||
}
|
||
|
||
// 处理登录
|
||
const handleLogin = async () => {
|
||
// 账号密码登录验证
|
||
if (!loginForm.username.trim()) {
|
||
showMessage('请输入用户名')
|
||
return
|
||
}
|
||
|
||
if (!loginForm.password.trim()) {
|
||
showMessage('请输入密码')
|
||
return
|
||
}
|
||
|
||
if (loginForm.password.length < 6) {
|
||
showMessage('密码长度不能少于6位')
|
||
return
|
||
}
|
||
|
||
isLoading.value = true
|
||
|
||
try {
|
||
// 调用登录接口
|
||
// 对密码进行加密
|
||
const encryptedPassword = aesEncrypt(loginForm.password);
|
||
|
||
const loginData = {
|
||
username: loginForm.username,
|
||
password: encryptedPassword
|
||
}
|
||
|
||
|
||
const response = await authAPI.login(loginData)
|
||
|
||
|
||
if (response.statusCode === 200 && response.data) {
|
||
// 构建用户信息
|
||
const userInfo = {
|
||
isLogin: true,
|
||
token: response.data.access_token || response.data.token,
|
||
refreshToken: response.data.refresh_token,
|
||
expiresIn: response.data.expires_in,
|
||
tokenType: response.data.token_type || 'Bearer',
|
||
userId: response.data.userId || response.data.user_id,
|
||
username: loginForm.username,
|
||
avatar: response.data.avatar || '',
|
||
loginTime: new Date().getTime()
|
||
}
|
||
|
||
// 先保存token到本地存储,供后续API调用使用
|
||
saveUserInfo(userInfo)
|
||
|
||
// 等待更长时间确保token完全生效
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
// 检查用户是否为司机
|
||
try {
|
||
// 添加重试机制,避免token未生效的问题
|
||
let driverResponse = null
|
||
let retryCount = 0
|
||
const maxRetries = 3
|
||
|
||
while (retryCount < maxRetries) {
|
||
try {
|
||
driverResponse = await driverAPI.getDriverInfo(userInfo.userId)
|
||
break // 成功获取,跳出循环
|
||
} catch (error) {
|
||
retryCount++
|
||
console.warn(`获取司机信息失败,重试第${retryCount}次:`, error)
|
||
|
||
if (retryCount < maxRetries) {
|
||
// 等待一段时间后重试
|
||
await new Promise(resolve => setTimeout(resolve, 500))
|
||
} else {
|
||
throw error // 重试次数用完,抛出错误
|
||
}
|
||
}
|
||
}
|
||
|
||
if (driverResponse.statusCode === 200 && driverResponse.data) {
|
||
// 检查是否包含司机ID
|
||
const hasDriverId = driverResponse.data.id || driverResponse.data.driverId ||
|
||
(driverResponse.data.data && (driverResponse.data.data.id || driverResponse.data.data.driverId))
|
||
|
||
if (hasDriverId) {
|
||
// 用户是司机且有司机ID,登录成功
|
||
const updatedUserInfo = {
|
||
...userInfo,
|
||
isDriver: true,
|
||
driverInfo: driverResponse.data,
|
||
driverId: driverResponse.data.id || driverResponse.data.driverId ||
|
||
(driverResponse.data.data && (driverResponse.data.data.id || driverResponse.data.data.driverId))
|
||
}
|
||
saveUserInfo(updatedUserInfo)
|
||
|
||
showMessage('登录成功')
|
||
|
||
// 检查用户是否已完成实名认证
|
||
const driverStatus = driverResponse.data?.driverStatus ||
|
||
driverResponse.data?.data?.driverStatus
|
||
const isIdentityVerified = driverResponse.data?.identityVerified ||
|
||
driverResponse.data?.data?.identityVerified ||
|
||
updatedUserInfo.identityVerified
|
||
|
||
// 如果司机状态为1(正常)或者已经完成实名认证,则跳转到首页
|
||
if (driverStatus === '1' || isIdentityVerified) {
|
||
// 用户已完成实名认证,跳转到首页
|
||
setTimeout(() => {
|
||
Taro.switchTab({ url: '/pages/index/index' })
|
||
}, 1000)
|
||
} else {
|
||
// 用户未完成实名认证,跳转到实名认证页面
|
||
setTimeout(() => {
|
||
Taro.navigateTo({ url: '/pages/realname/index?from=login' })
|
||
}, 1000)
|
||
}
|
||
} else {
|
||
// 用户不是司机或没有司机ID,登录失败
|
||
showMessage('登录失败:您不是司机用户')
|
||
}
|
||
} else {
|
||
// 获取司机信息失败,登录失败
|
||
showMessage('登录失败:无法获取司机信息')
|
||
}
|
||
} catch (error) {
|
||
console.error('获取司机信息失败:', error)
|
||
|
||
// 检查是否是token相关错误
|
||
if (error.message && error.message.includes('用户凭证已过期')) {
|
||
// 如果是token过期错误,可能是token还未完全生效,重试一次
|
||
console.log('检测到token过期错误,尝试重新获取司机信息...')
|
||
try {
|
||
// 再次等待并重试
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
const retryResponse = await driverAPI.getDriverInfo(userInfo.userId)
|
||
|
||
if (retryResponse.statusCode === 200 && retryResponse.data) {
|
||
// 处理重试成功的响应
|
||
const hasDriverId = retryResponse.data.id || retryResponse.data.driverId ||
|
||
(retryResponse.data.data && (retryResponse.data.data.id || retryResponse.data.data.driverId))
|
||
|
||
if (hasDriverId) {
|
||
const updatedUserInfo = {
|
||
...userInfo,
|
||
isDriver: true,
|
||
driverInfo: retryResponse.data,
|
||
driverId: retryResponse.data.id || retryResponse.data.driverId ||
|
||
(retryResponse.data.data && (retryResponse.data.data.id || retryResponse.data.data.driverId))
|
||
}
|
||
saveUserInfo(updatedUserInfo)
|
||
|
||
showMessage('登录成功')
|
||
|
||
// 检查认证状态并跳转
|
||
const driverStatus = retryResponse.data?.driverStatus ||
|
||
retryResponse.data?.data?.driverStatus
|
||
const isIdentityVerified = retryResponse.data?.identityVerified ||
|
||
retryResponse.data?.data?.identityVerified ||
|
||
updatedUserInfo.identityVerified
|
||
|
||
if (driverStatus === '1' || isIdentityVerified) {
|
||
setTimeout(() => {
|
||
Taro.switchTab({ url: '/pages/index/index' })
|
||
}, 1000)
|
||
} else {
|
||
setTimeout(() => {
|
||
Taro.navigateTo({ url: '/pages/realname/index?from=login' })
|
||
}, 1000)
|
||
}
|
||
return
|
||
}
|
||
}
|
||
} catch (retryError) {
|
||
console.error('重试获取司机信息仍然失败:', retryError)
|
||
}
|
||
}
|
||
|
||
showMessage('登录失败:无法验证司机身份')
|
||
}
|
||
} else {
|
||
// 登录失败
|
||
const errorMsg = response.data?.msg || response.data?.message || '登录失败'
|
||
showMessage(errorMsg)
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('登录失败:', error)
|
||
showMessage('登录失败,请重试')
|
||
} finally {
|
||
isLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 跳转到注册页面 - 已关闭注册入口
|
||
// const goToRegister = () => {
|
||
// Taro.navigateTo({ url: '/pages/register/index' })
|
||
// }
|
||
|
||
// 页面初始化完成,不再设置默认账号密码
|
||
|
||
</script>
|
||
|
||
<style>
|
||
.login-page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
padding: 0 40rpx;
|
||
}
|
||
|
||
/* 头部区域 */
|
||
.header {
|
||
text-align: center;
|
||
padding: 120rpx 0 60rpx;
|
||
}
|
||
|
||
.logo {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.logo-text {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: white;
|
||
}
|
||
|
||
.welcome-text {
|
||
font-size: 32rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
/* 登录方式切换 */
|
||
.login-mode-switch {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.switch-tabs {
|
||
display: flex;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 16rpx;
|
||
padding: 8rpx;
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 20rpx;
|
||
border-radius: 12rpx;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.tab-item.active {
|
||
background: white;
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 28rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tab-item.active .tab-text {
|
||
color: #667eea;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 登录表单 */
|
||
.login-form {
|
||
background: white;
|
||
border-radius: 24rpx;
|
||
padding: 60rpx 40rpx;
|
||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.input-label {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.label-text {
|
||
margin-left: 16rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
border: 2rpx solid #f0f0f0;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx 24rpx;
|
||
font-size: 28rpx;
|
||
line-height: 1.4;
|
||
background: #fff;
|
||
box-sizing: border-box;
|
||
|
||
/* 修复真机显示问题 */
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
outline: none;
|
||
|
||
/* 修复字体渲染 */
|
||
font-family: inherit;
|
||
color: #333;
|
||
|
||
}
|
||
|
||
.form-input::placeholder {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.form-input:focus {
|
||
border-color: #667eea;
|
||
outline: none;
|
||
}
|
||
|
||
/* 验证码输入 */
|
||
.verification-input {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
align-items: center;
|
||
}
|
||
|
||
.code-input {
|
||
flex: 1;
|
||
}
|
||
|
||
.send-code-btn {
|
||
padding: 24rpx 32rpx;
|
||
background: #667eea;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 16rpx;
|
||
font-size: 26rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.send-code-btn:disabled {
|
||
background: #ccc;
|
||
color: #999;
|
||
}
|
||
|
||
/* 表单选项 */
|
||
.form-options {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 60rpx;
|
||
}
|
||
|
||
.forgot-password {
|
||
font-size: 28rpx;
|
||
color: #667eea;
|
||
}
|
||
|
||
.phone-login-link {
|
||
font-size: 28rpx;
|
||
color: #667eea;
|
||
}
|
||
|
||
/* 登录按钮 */
|
||
.login-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: #667eea;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 16rpx;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.login-btn:disabled {
|
||
background: #ccc;
|
||
color: #999;
|
||
}
|
||
|
||
/* 注册链接 */
|
||
.register-link {
|
||
text-align: center;
|
||
}
|
||
|
||
.register-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.register-btn {
|
||
font-size: 28rpx;
|
||
color: #667eea;
|
||
font-weight: bold;
|
||
margin-left: 8rpx;
|
||
}
|
||
</style>
|