This commit is contained in:
2026-02-07 16:11:40 +08:00
parent 7998e702a1
commit 71314c5b12
63 changed files with 33553 additions and 0 deletions

499
src/pages/login/index.vue Normal file
View File

@@ -0,0 +1,499 @@
<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>