初始化移动端提交

This commit is contained in:
chenbowen
2025-09-30 00:08:23 +08:00
parent 08784ca8f3
commit f2ffc65094
406 changed files with 55626 additions and 93 deletions

342
pages/login/index.vue Normal file
View File

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