<template>
|
<view>
|
<view class="login-page">
|
<image class="login-page__bg" src="/static/image/login_bg@2x.png" mode="aspectFill"></image>
|
<view></view>
|
|
<view class="login-page__content">
|
<view class="login-card">
|
<view class="login-card__tabs">
|
<view
|
v-for="item in tabs"
|
:key="item.value"
|
class="login-card__tab"
|
:style="{
|
backgroundImage: returnBackgroundImg(item.value, activeTab === item.value)
|
}"
|
:class="{ 'login-card__tab--active': activeTab === item.value }"
|
@click="activeTab = item.value"
|
>
|
<text class="login-card__tab-text">{{ item.label }}</text>
|
<view v-if="activeTab === item.value" class="login-card__tab-line"></view>
|
</view>
|
</view>
|
|
<view class="login-card__form">
|
<view v-if="activeTab === 'account'" class="login-card__group">
|
<view class="login-card__field">
|
<input v-model="form.telephone" class="login-card__input" type="number" placeholder="请输入手机号" placeholder-style="color: #c5cad4;" />
|
</view>
|
<view class="login-card__field">
|
<input v-model="form.password" class="login-card__input" password placeholder="请输入密码" placeholder-style="color: #c5cad4;" />
|
</view>
|
</view>
|
|
<view v-else class="login-card__group">
|
<view class="login-card__field">
|
<input v-model="form.telephone" class="login-card__input" type="number" placeholder="请输入手机号" placeholder-style="color: #c5cad4;" />
|
</view>
|
<view class="login-card__field login-card__field--code">
|
<input v-model="form.code" class="login-card__input" type="number" placeholder="请输入验证码" placeholder-style="color: #c5cad4;" />
|
<text class="login-card__code-btn" @click="sendCode">{{ countdown > 0 ? countdown + 's' : '获取验证码' }}</text>
|
</view>
|
</view>
|
|
<button class="login-card__submit" hover-class="login-card__submit--hover" @click="login">登录</button>
|
</view>
|
|
<view class="login-card__agreement" @click="toggleAgreement">
|
<view class="login-card__agreement-icon" :class="{ 'login-card__agreement-icon--checked': isAgreed }">
|
<text v-if="isAgreed" class="login-card__check">✓</text>
|
</view>
|
<view class="login-card__agreement-text">
|
<text>登录即代表您同意</text>
|
<text class="login-card__agreement-link" @click.stop="openAgreement('driverPrivacyPolicy', '司机隐私政策')">《用户隐私政策》</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
<u-popup :show="showPrivacyPopup" mode="center" :mask-click="false" round="24">
|
<view class="privacy-popup">
|
<text class="privacy-popup__title">隐私政策</text>
|
<view class="privacy-popup__content">
|
<text>本应用尊重并保护所有用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照隐私政策的规定使用和披露您的个人信息。可阅读</text>
|
<text class="privacy-popup__link" @click="openAgreement('driverPrivacyPolicy', '司机隐私政策')">《隐私政策》</text>
|
<text>。</text>
|
</view>
|
<view class="privacy-popup__actions">
|
<button class="privacy-popup__button privacy-popup__button--ghost" @click="rejectPrivacyPolicy">不同意并退出APP</button>
|
<button class="privacy-popup__button privacy-popup__button--primary" @click="agreePrivacyPolicy">同意</button>
|
</view>
|
</view>
|
</u-popup>
|
</view>
|
</template>
|
|
<script>
|
import { mapState } from 'vuex';
|
|
export default {
|
data() {
|
return {
|
activeTab: 'account',
|
tabs: [
|
{ label: '账号登录', value: 'account' },
|
{ label: '验证码登录', value: 'code' }
|
],
|
form: {
|
telephone: '',
|
password: '',
|
code: ''
|
},
|
isAgreed: false,
|
showPrivacyPopup: false,
|
countdown: 0,
|
countdownTimer: null
|
};
|
},
|
computed: {
|
...mapState(['token'])
|
},
|
onLoad() {
|
if (this.token) {
|
uni.switchTab({ url: '/pages/index/index' })
|
return
|
}
|
this.showPrivacyPopup = !uni.getStorageSync('login_privacy_policy_agreed')
|
},
|
methods: {
|
returnBackgroundImg(value, active) {
|
if (value === 'account' && !active) {
|
return "url('/static/image/login_ic_account@2x.png')"
|
} else if (value === 'account' && active) {
|
return "url('/static/image/login_ic_account_sel@2x.png')"
|
} else if (value === 'code' && !active) {
|
return "url('/static/image/login_ic_code@2x.png')"
|
} else if (value === 'code' && active) {
|
return "url('/static/image/login_ic_code_sel@2x.png')"
|
}
|
},
|
openAgreement(type, title) {
|
uni.navigateTo({
|
url: `/pages/agreement/agreement?type=${type}&title=${encodeURIComponent(title)}`
|
});
|
},
|
agreePrivacyPolicy() {
|
uni.setStorageSync('login_privacy_policy_agreed', true)
|
this.showPrivacyPopup = false
|
},
|
rejectPrivacyPolicy() {
|
if (typeof plus !== 'undefined' && plus.runtime) {
|
plus.runtime.quit()
|
return
|
}
|
uni.exitProgram && uni.exitProgram()
|
},
|
toggleAgreement() {
|
this.isAgreed = !this.isAgreed;
|
},
|
sendCode() {
|
if (this.countdown > 0) return;
|
|
if (!this.form.telephone) {
|
uni.showToast({ title: '请输入手机号', icon: 'none' });
|
return;
|
}
|
if (!/^1[3-9]\d{9}$/.test(this.form.telephone)) {
|
uni.showToast({ title: '手机号格式不正确', icon: 'none' });
|
return;
|
}
|
|
this.$u.api.sendCode({ telephone: this.form.telephone }).then(res => {
|
if (res.code === 200) {
|
uni.showToast({ title: '验证码已发送', icon: 'success' });
|
this.countdown = 60;
|
this.countdownTimer = setInterval(() => {
|
this.countdown--;
|
if (this.countdown <= 0) {
|
clearInterval(this.countdownTimer);
|
this.countdownTimer = null;
|
}
|
}, 1000);
|
} else {
|
uni.showToast({ title: res.msg || '发送失败', icon: 'none' });
|
}
|
})
|
},
|
login() {
|
if (!this.isAgreed) {
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' });
|
return;
|
}
|
if (this.activeTab === 'account') {
|
if (!this.form.telephone) {
|
uni.showToast({ title: '请输入手机号', icon: 'none' });
|
return;
|
}
|
if (!/^1[3-9]\d{9}$/.test(this.form.telephone)) {
|
uni.showToast({ title: '手机号格式不正确', icon: 'none' });
|
return;
|
}
|
if (!this.form.password) {
|
uni.showToast({ title: '请输入密码', icon: 'none' });
|
return;
|
}
|
uni.showLoading({ title: '登录中...' })
|
this.$u.api.login(this.form).then(res => {
|
uni.hideLoading()
|
if (res.code === 200) {
|
this.$store.commit('setToken', res.data.token);
|
this.$u.api.verifyDetail().then(user => {
|
if (user.code === 200) {
|
console.log('verifyDetail success:', user)
|
uni.showToast({ title: '登录成功', icon: 'success' });
|
uni.$emit('loginSuccessStartLocationPolling');
|
this.$store.commit('setUserInfo', user.data);
|
setTimeout(() => {
|
uni.switchTab({ url: '/pages/index/index' });
|
}, 1500);
|
}
|
})
|
}
|
})
|
} else {
|
if (!this.form.telephone) {
|
uni.showToast({ title: '请输入手机号', icon: 'none' });
|
return;
|
}
|
if (!/^1[3-9]\d{9}$/.test(this.form.telephone)) {
|
uni.showToast({ title: '手机号格式不正确', icon: 'none' });
|
return;
|
}
|
if (!this.form.code) {
|
uni.showToast({ title: '请输入验证码', icon: 'none' });
|
return;
|
}
|
|
uni.showLoading({ title: '登录中...' })
|
this.$u.api.register({ telephone: this.form.telephone, code: this.form.code }).then(res => {
|
uni.hideLoading()
|
if (res.code === 200) {
|
this.$store.commit('setToken', res.data.token);
|
this.$u.api.verifyDetail().then(user => {
|
if (user.code === 200) {
|
console.log('verifyDetail success:', user)
|
uni.showToast({ title: '登录成功', icon: 'success' });
|
uni.$emit('loginSuccessStartLocationPolling');
|
this.$store.commit('setUserInfo', user.data);
|
setTimeout(() => {
|
uni.switchTab({ url: '/pages/index/index' });
|
}, 1500);
|
}
|
})
|
}
|
}).catch(() => {
|
uni.hideLoading()
|
})
|
}
|
}
|
},
|
beforeDestroy() {
|
if (this.countdownTimer) {
|
clearInterval(this.countdownTimer);
|
this.countdownTimer = null;
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.login-page {
|
position: relative;
|
height: 100vh;
|
background: linear-gradient(180deg, #cfe6ff 0%, #a7c6ff 100%);
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
&__bg {
|
position: absolute;
|
left: 0;
|
top: 0;
|
right: 0;
|
bottom: 0;
|
width: 100vw;
|
height: 100vh;
|
}
|
|
&__content {
|
position: relative;
|
z-index: 1;
|
height: 88vh;
|
box-sizing: border-box;
|
padding: 180rpx 40rpx 20rpx 40rpx;
|
}
|
}
|
|
.login-card {
|
position: relative;
|
height: calc(100% - 15rpx);
|
box-sizing: border-box;
|
border-radius: 40rpx;
|
background: rgba(255, 255, 255, 0.95);
|
box-shadow: 0 24rpx 70rpx rgba(69, 124, 219, 0.16);
|
overflow: hidden;
|
|
&__tabs {
|
display: flex;
|
align-items: stretch;
|
background: rgba(228, 236, 247, 0.8);
|
}
|
|
&__tab {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
height: 100rpx;
|
border-bottom-left-radius: 28rpx;
|
border-bottom-right-radius: 28rpx;
|
background-repeat: no-repeat;
|
background-size: 100% 100%;
|
position: relative;
|
|
&--active {
|
flex: 1.3;
|
// background: #ffffff;
|
}
|
|
.login-card__tab-line {
|
position: absolute;
|
bottom: 10rpx;
|
left: 50%;
|
transform: translate(0, -55%);
|
width: 36rpx;
|
height: 8rpx;
|
margin-top: 10rpx;
|
border-radius: 999rpx;
|
background: #1f74ff;
|
z-index: 999;
|
}
|
}
|
|
&__tab-text {
|
font-size: 34rpx;
|
font-weight: 400;
|
color: #666666;
|
|
.login-card__tab--active & {
|
color: #222222;
|
}
|
}
|
|
&__tab-line {
|
width: 36rpx;
|
height: 8rpx;
|
margin-top: 10rpx;
|
border-radius: 999rpx;
|
background: #1f74ff;
|
}
|
|
&__form {
|
padding: 46rpx 36rpx 0;
|
}
|
|
&__group {
|
display: flex;
|
flex-direction: column;
|
gap: 26rpx;
|
}
|
|
&__field {
|
display: flex;
|
align-items: center;
|
height: 96rpx;
|
padding: 0 34rpx;
|
border-radius: 48rpx;
|
background: #f3f6fb;
|
|
&--code {
|
justify-content: space-between;
|
gap: 20rpx;
|
}
|
}
|
|
&__input {
|
flex: 1;
|
height: 98rpx;
|
font-size: 30rpx;
|
color: #31343a;
|
background: transparent;
|
}
|
|
&__code-btn {
|
flex-shrink: 0;
|
font-size: 26rpx;
|
font-weight: 600;
|
color: #1f74ff;
|
}
|
|
&__submit {
|
margin-top: 40rpx;
|
width: 100%;
|
height: 98rpx;
|
line-height: 98rpx;
|
border: 0;
|
border-radius: 50rpx;
|
background: #106EFA;
|
font-size: 32rpx;
|
font-weight: 600;
|
color: #ffffff;
|
padding: 0;
|
|
&::after {
|
border: 0;
|
}
|
|
&--hover {
|
opacity: 0.94;
|
}
|
}
|
|
&__agreement {
|
position: absolute;
|
left: 36rpx;
|
right: 36rpx;
|
bottom: calc(env(safe-area-inset-bottom) + 40rpx);
|
display: flex;
|
align-items: flex-start;
|
gap: 10rpx;
|
}
|
|
&__agreement-icon {
|
position: relative;
|
flex-shrink: 0;
|
width: 28rpx;
|
height: 28rpx;
|
margin-top: 8rpx;
|
border-radius: 50%;
|
background: #ffffff;
|
border: 2rpx solid #cccccc;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
|
&--checked {
|
background: #1f74ff;
|
border-color: #1f74ff;
|
}
|
}
|
|
&__check {
|
font-size: 20rpx;
|
color: #ffffff;
|
font-weight: bold;
|
}
|
|
&__agreement-text {
|
display: flex;
|
flex-wrap: wrap;
|
font-size: 24rpx;
|
line-height: 1.6;
|
color: #555555;
|
}
|
|
&__agreement-link {
|
color: #106EFA;
|
}
|
}
|
|
.privacy-popup {
|
width: 620rpx;
|
padding: 48rpx 36rpx 36rpx;
|
background: #ffffff;
|
border-radius: 24rpx;
|
box-sizing: border-box;
|
|
&__title {
|
display: block;
|
text-align: center;
|
font-size: 34rpx;
|
font-weight: 600;
|
color: #222222;
|
}
|
|
&__content {
|
margin-top: 28rpx;
|
font-size: 28rpx;
|
line-height: 1.7;
|
color: #555555;
|
}
|
|
&__link {
|
color: #106efa;
|
}
|
|
&__actions {
|
margin-top: 40rpx;
|
display: flex;
|
gap: 20rpx;
|
}
|
|
&__button {
|
flex: 1;
|
height: 88rpx;
|
line-height: 88rpx;
|
border-radius: 44rpx;
|
font-size: 28rpx;
|
padding: 0;
|
|
&::after {
|
border: 0;
|
}
|
|
&--ghost {
|
background: #f3f6fb;
|
color: #5d6675;
|
}
|
|
&--primary {
|
background: #106efa;
|
color: #ffffff;
|
}
|
}
|
}
|
</style>
|