consumer-app/pages/login/login.vue

524 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="login-page">
<!-- 状态栏占位 -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 头部区域 -->
<view class="header">
<view class="back-btn" @click="handleBack" v-if="showBack">
<text class="back-icon"></text>
</view>
<view class="header-title">登录</view>
</view>
<!-- 登录内容区 -->
<view class="login-content">
<!-- Logo区域 -->
<!-- <view class="logo-section">
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
</view> -->
<!-- 登录表单 -->
<view class="login-form">
<!-- 手机号输入 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">手机号</text>
<input
class="input-field"
type="number"
v-model="formData.mobile"
placeholder="请输入手机号"
maxlength="11"
/>
</view>
</view>
<!-- 密码输入 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">密码</text>
<input
class="input-field"
:type="showPassword ? 'text' : 'password'"
v-model="formData.password"
placeholder="请输入密码"
/>
<view class="password-toggle" @click="togglePassword">
<text class="toggle-icon">{{ showPassword ? '👁️' : '👁️‍🗨️' }}</text>
</view>
</view>
</view>
<!-- 登录按钮 -->
<button
class="login-btn"
:class="{ disabled: !canLogin }"
:disabled="!canLogin || loading"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
</button>
<!-- 分割线 -->
<view class="divider">
<view class="divider-line"></view>
<text class="divider-text">或</text>
<view class="divider-line"></view>
</view>
<!-- 小程序一键授权登录 -->
<!-- #ifdef MP-WEIXIN -->
<button
class="wechat-login-btn"
open-type="getPhoneNumber"
@getphonenumber="handleGetPhoneNumber"
:disabled="loading"
>
<text>一键授权手机号快速登录</text>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="wechat-login-tip">
<text>小程序一键授权登录仅支持微信小程序环境</text>
</view>
<!-- #endif -->
</view>
</view>
</view>
</template>
<script>
import { login, loginByPhone } from '@/api/auth.js'
import { getUserInfo } from '@/api/profile.js'
export default {
data() {
return {
statusBarHeight: 0,
showBack: false,
loading: false,
showPassword: false,
formData: {
mobile: '',
password: ''
}
}
},
computed: {
// 判断是否可以登录
canLogin() {
return (
this.formData.mobile.length === 11 &&
this.formData.password.length > 0
)
}
},
onLoad(options) {
// 检查是否已登录
const token = uni.getStorageSync('token')
if (token) {
// 已登录,跳转到首页
uni.switchTab({
url: '/pages/index/index',
fail: () => {
uni.reLaunch({
url: '/pages/index/index'
})
}
})
return
}
// 判断是否显示返回按钮(从其他页面跳转过来时显示)
const pages = getCurrentPages()
if (pages.length > 1) {
this.showBack = true
}
this.getSystemInfo()
},
methods: {
// 获取系统信息
getSystemInfo() {
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 0
},
// 返回上一页
handleBack() {
uni.navigateBack()
},
// 切换密码显示/隐藏
togglePassword() {
this.showPassword = !this.showPassword
},
// 账户密码登录
async handleLogin() {
if (!this.canLogin) {
uni.showToast({
title: '请填写完整的登录信息',
icon: 'none'
})
return
}
// 验证手机号格式
const mobileReg = /^1[3-9]\d{9}$/
if (!mobileReg.test(this.formData.mobile)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
this.loading = true
try {
const res = await login({
mobile: this.formData.mobile,
password: this.formData.password
})
// 登录成功保存token兼容不同的响应格式
const token = res?.accessToken || res?.token || res?.data?.accessToken || res?.data?.token
if (token) {
uni.setStorageSync('token', token)
}
// 保存refreshToken用于刷新accessToken
const refreshToken = res?.refreshToken || res?.data?.refreshToken
if (refreshToken) {
uni.setStorageSync('refreshToken', refreshToken)
}
// 保存token过期时间
const expiresTime = res?.expiresTime || res?.data?.expiresTime
if (expiresTime) {
uni.setStorageSync('tokenExpiresTime', expiresTime)
}
// 保存用户ID
const userId = res?.userId || res?.data?.userId
if (userId) {
uni.setStorageSync('userId', userId)
}
// 保存用户信息(如果有)
const userInfo = res?.userInfo || res?.data?.userInfo || res?.user || res?.data?.user
if (userInfo) {
uni.setStorageSync('userInfo', userInfo)
}
// 登录成功后,调用个人信息接口获取完整用户信息
try {
const userInfoRes = await getUserInfo()
if (userInfoRes && userInfoRes.data) {
// 根据接口返回的数据结构,保存 res.data
uni.setStorageSync('userInfo', userInfoRes.data)
} else if (userInfoRes) {
// 兼容直接返回数据的情况
uni.setStorageSync('userInfo', userInfoRes)
}
} catch (error) {
console.error('获取用户信息失败:', error)
// 获取用户信息失败不影响登录流程,继续执行
}
uni.showToast({
title: '登录成功',
icon: 'success'
})
// 延迟跳转,让用户看到成功提示
setTimeout(() => {
// 检查是否有返回路径
const pages = getCurrentPages()
if (pages.length > 1) {
// 有上一页,返回上一页
uni.navigateBack()
} else {
// 没有上一页,跳转到首页
uni.switchTab({
url: '/pages/index/index'
})
}
}, 1500)
} catch (error) {
console.error('登录失败:', error)
// 错误信息已在request中统一处理这里不需要再次提示
} finally {
this.loading = false
}
},
// 小程序一键授权手机号登录
async handleGetPhoneNumber(e) {
console.log('获取手机号授权结果:', e)
if (e.detail.errMsg === 'getPhoneNumber:ok') {
this.loading = true
try {
// 获取微信登录code用于后端解密手机号
const loginRes = await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: resolve,
fail: reject
})
})
// 调用登录接口
// 注意:这里需要根据后端实际接口要求传递参数
// 通常需要传递 code、encryptedData、iv 等参数
const res = await loginByPhone({
code: loginRes.code,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv
})
// 登录成功保存token兼容不同的响应格式
const token = res?.token || res?.data?.token || res?.accessToken || res?.access_token
if (token) {
uni.setStorageSync('token', token)
}
// 保存refreshToken用于刷新accessToken
const refreshToken = res?.refreshToken || res?.data?.refreshToken
if (refreshToken) {
uni.setStorageSync('refreshToken', refreshToken)
}
// 保存token过期时间
const expiresTime = res?.expiresTime || res?.data?.expiresTime
if (expiresTime) {
uni.setStorageSync('tokenExpiresTime', expiresTime)
}
// 保存用户ID
const userId = res?.userId || res?.data?.userId
if (userId) {
uni.setStorageSync('userId', userId)
}
// 保存用户信息(如果有)
const userInfo = res?.userInfo || res?.data?.userInfo || res?.user || res?.data?.user
if (userInfo) {
uni.setStorageSync('userInfo', userInfo)
}
// 登录成功后,调用个人信息接口获取完整用户信息
try {
const userInfoRes = await getUserInfo()
uni.setStorageSync('userInfo', userInfoRes)
} catch (error) {
console.error('获取用户信息失败:', error)
}
uni.showToast({
title: '登录成功',
icon: 'success'
})
// 延迟跳转
setTimeout(() => {
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.switchTab({
url: '/pages/index/index'
})
}
}, 1500)
} catch (error) {
console.error('一键登录失败:', error)
// 错误信息已在request中统一处理
} finally {
this.loading = false
}
} else {
uni.showToast({
title: '授权失败,请重试',
icon: 'none'
})
}
}
}
}
</script>
<style lang="scss" scoped>
.login-page {
min-height: 100vh;
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
}
.status-bar {
background-color: transparent;
}
.header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 20rpx 0;
background-color: transparent;
.back-btn {
position: absolute;
left: 30rpx;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.back-icon {
font-size: 60rpx;
color: #333333;
line-height: 1;
}
}
.header-title {
font-size: 36rpx;
font-weight: 500;
color: #1a1819;
}
}
.login-content {
padding: 60rpx 60rpx 0;
}
.logo-section {
display: flex;
justify-content: center;
margin-bottom: 80rpx;
.logo {
width: 200rpx;
height: 200rpx;
}
}
.login-form {
.form-item {
margin-bottom: 40rpx;
.input-wrapper {
position: relative;
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx 24rpx;
display: flex;
align-items: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
.input-label {
width: 120rpx;
font-size: 28rpx;
color: #333333;
font-weight: 500;
flex-shrink: 0;
}
.input-field {
flex: 1;
font-size: 28rpx;
color: #1a1819;
}
.password-toggle {
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.toggle-icon {
font-size: 32rpx;
}
}
}
}
.login-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #004294 0%, #0066cc 100%);
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none;
margin-top: 40rpx;
display: flex;
align-items: center;
justify-content: center;
&::after {
border: none;
}
&.disabled {
background: #cccccc;
color: #999999;
}
}
.divider {
display: flex;
align-items: center;
margin: 60rpx 0 40rpx;
padding: 0 20rpx;
.divider-line {
flex: 1;
height: 1rpx;
background-color: #e0e0e0;
}
.divider-text {
margin: 0 20rpx;
font-size: 24rpx;
color: #999999;
}
}
.wechat-login-btn {
width: 100%;
height: 88rpx;
background-color: #07c160;
border-radius: 44rpx;
color: #ffffff;
font-size: 28rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
&::after {
border: none;
}
.wechat-icon {
font-size: 36rpx;
}
}
.wechat-login-tip {
text-align: center;
padding: 40rpx 0;
font-size: 24rpx;
color: #999999;
}
}
</style>