Files
zgty-mas-m/pages/login/index.vue
2025-09-30 00:08:23 +08:00

343 lines
9.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<s-layout class="login-container" title="登录/注册" :bgStyle="{
background: '#fff'
}">
<view class="login-wrap">
<!-- 1. 统一登录组件 (整合账号密码登录和短信登录) -->
<unified-login
v-if="authType === 'accountLogin' || authType === 'smsLogin'"
:agreeStatus="state.protocol"
@onConfirm="onConfirm"
/>
<!-- 3. 忘记密码 resetPassword-->
<!-- <reset-password v-if="authType === 'resetPassword'" /> -->
<!-- 4. 绑定手机号 changeMobile -->
<change-mobile v-if="authType === 'changeMobile'" />
<!-- 5. 修改密码 changePassword-->
<changePassword v-if="authType === 'changePassword'" />
<!-- 6. 微信小程序授权 -->
<mp-authorization v-if="authType === 'mpAuthorization'" />
<!-- 7. 第三方登录 -->
<view
v-if="['accountLogin', 'smsLogin'].includes(authType)"
class="auto-login-box ss-flex ss-flex-col ss-row-center ss-col-center"
>
<!-- 7.1 微信小程序的快捷登录 -->
<view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
<view class="register-title">还没有账号?</view>
<button
class="ss-reset-button login-btn"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
style="color: var(--ui-BG-Main, #0055A2) !important"
>
快捷登录
</button>
<view class="circle" />
</view>
<!-- 7.2 微信的公众号App小程序的登录基于 openid + code -->
<button
v-if="
['WechatOfficialAccount', 'WechatMiniProgram', 'App'].includes(sheep.$platform.name) &&
sheep.$platform.isWechatInstalled
"
@tap="thirdLogin('wechat')"
class="ss-reset-button auto-login-btn"
>
<image
class="auto-login-img"
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
/>
</button>
<!-- 7.3 iOS 登录 TODO 芋艿:等后面搞 App 再弄 -->
<button
v-if="sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
@tap="thirdLogin('apple')"
class="ss-reset-button auto-login-btn"
>
<image
class="auto-login-img"
:src="sheep.$url.static('/static/img/shop/platform/apple.png')"
/>
</button>
</view>
<!-- 用户协议的勾选 -->
<view
v-if="['accountLogin', 'smsLogin'].includes(authType)"
class="agreement-box ss-flex ss-flex-col ss-col-center"
:class="{ shake: currentProtocol }"
>
<view class="agreement-title ss-m-b-20">
请阅读并同意以下协议:
</view>
<view class="agreement-options-container">
<view class="agreement-option" @tap="onAgree">
<view class="radio-container ss-flex ss-col-center">
<view
class="custom-radio"
:class="{ 'custom-radio-checked': state.protocol === true }"
>
<view v-if="state.protocol === true" class="radio-dot"></view>
</view>
<view class="agreement-text ss-flex ss-col-center ss-m-l-8">
我已阅读并同意遵守
<view class="tcp-text" @tap.stop="onProtocol('用户协议')"> 《用户协议》 </view>
<view class="agreement-text">与</view>
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')"> 《隐私协议》 </view>
</view>
</view>
</view>
</view>
</view>
<view class="safe-box" />
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive, ref } from 'vue';
import sheep from '@/sheep';
import unifiedLogin from '@/sheep/components/s-auth-modal/components/unified-login.vue';
import changeMobile from '@/sheep/components/s-auth-modal/components/change-mobile.vue';
import changePassword from '@/sheep/components/s-auth-modal/components/change-password.vue';
import mpAuthorization from '@/sheep/components/s-auth-modal/components/mp-authorization.vue';
import { onLoad } from '@dcloudio/uni-app';
import { navigateAfterLogin } from '@/sheep/helper/login-redirect';
const authType = ref('accountLogin');
onLoad((options) => {
if (options.authType) {
authType.value = options.authType;
}
});
const state = reactive({
protocol: false, // false 表示未勾选true 表示已同意
});
const currentProtocol = ref(false);
// 同意协议
function onAgree() {
state.protocol = !state.protocol;
uni.showToast({
title: state.protocol ? '已勾选协议' : '已取消勾选',
icon: state.protocol ? 'success' : 'none',
duration: 1000
});
}
// 查看协议
function onProtocol(title) {
sheep.$router.go('/pages/public/richtext', {
title,
});
}
// 点击登录 / 注册事件
function onConfirm(e) {
currentProtocol.value = e;
setTimeout(() => {
currentProtocol.value = false;
}, 1000);
}
// 第三方授权登陆微信小程序、Apple
const thirdLogin = async (provider) => {
if (state.protocol !== true) {
currentProtocol.value = true;
setTimeout(() => {
currentProtocol.value = false;
}, 1000);
sheep.$helper.toast('请先勾选协议');
return;
}
const loginRes = await sheep.$platform.useProvider(provider).login();
if (loginRes) {
const userInfo = await sheep.$store('user').getInfo();
// 如果用户已经有头像和昵称,不需要再次授权
if (userInfo.avatar && userInfo.nickname) {
// 登录成功后跳转到首页
navigateAfterLogin();
return;
}
// 触发小程序授权信息弹框
// #ifdef MP-WEIXIN
authType.value = 'mpAuthorization';
// #endif
}
};
// 微信小程序的“手机号快速验证”https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
const getPhoneNumber = async (e) => {
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
sheep.$helper.toast('快捷登录失败');
return;
}
let result = await sheep.$platform.useProvider().mobileLogin(e.detail);
if (result) {
// 登录成功后跳转到首页
navigateAfterLogin();
}
};
</script>
<style lang="scss" scoped>
@import '@/sheep/components/s-auth-modal/index.scss';
.login-container {
.login-wrap {
padding-top: 100rpx;
}
}
.shake {
animation: shake 0.05s linear 4 alternate;
}
@keyframes shake {
from {
transform: translateX(-10rpx);
}
to {
transform: translateX(10rpx);
}
}
.register-box {
position: relative;
justify-content: center;
.register-btn {
color: #999999;
font-size: 30rpx;
font-weight: 500;
}
.register-title {
color: #999999;
font-size: 30rpx;
font-weight: 400;
margin-right: 24rpx;
}
.or-title {
margin: 0 16rpx;
color: #999999;
font-size: 30rpx;
font-weight: 400;
}
.login-btn {
color: var(--ui-BG-Main, #0055A2) !important;
font-size: 30rpx;
font-weight: 500;
}
.circle {
position: absolute;
right: 0rpx;
top: 18rpx;
width: 8rpx;
height: 8rpx;
border-radius: 8rpx;
background: var(--ui-BG-Main, #0055A2) !important;
}
}
.safe-box {
height: calc(constant(safe-area-inset-bottom) / 5 * 3);
height: calc(env(safe-area-inset-bottom) / 5 * 3);
}
.tcp-text {
color: var(--ui-BG-Main, #0055A2) !important;
}
.agreement-text {
color: $dark-9;
}
.agreement-title {
font-size: 28rpx;
color: $dark-9;
text-align: left;
width: 100%;
padding-left: 60rpx;
position: relative;
}
.protocol-status {
font-size: 24rpx;
font-weight: bold;
margin-top: 8rpx;
padding: 4rpx 12rpx;
border-radius: 16rpx;
display: inline-block;
&.protocol-agreed {
color: #52c41a;
background-color: rgba(82, 196, 26, 0.1);
}
&.protocol-refused {
color: #ff4d4f;
background-color: rgba(255, 77, 79, 0.1);
}
}
.agreement-options-container {
width: 100%;
padding-left: 100rpx;
}
.agreement-option {
width: 100%;
display: flex;
justify-content: flex-start;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
opacity: 0.8;
}
}
.radio-container {
display: flex;
align-items: center;
width: 100%;
}
/* 自定义radio样式 - 同意 */
.custom-radio {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
flex-shrink: 0;
&.custom-radio-checked {
border-color: var(--ui-BG-Main, #409eff);
background-color: var(--ui-BG-Main, #409eff);
}
}
.radio-dot {
width: 16rpx;
height: 16rpx;
background-color: #fff;
border-radius: 50%;
}
</style>