Files
pxdj/pxdj-merch-uni/src/pages/login/login.vue
2025-10-24 17:12:18 +08:00

249 lines
5.6 KiB
Vue

<!-- pages/login/login.vue -->
<template>
<view class="container">
<!-- 微信登录 -->
<template v-if="loginType === 'wxLogin'">
<view class="login-title">手机号快捷登录</view>
<view class="wx-login-container">
<template>
<view class="login-status">
<text>请授权您的手机号码完成登录</text>
</view>
<button
v-if="autoWxLoginFlag"
class="wx-login-btn"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
:disabled="!isAgree"
:class="{'btn-disabled': !isAgree}"
>
<text class="btn-text">授权手机号</text>
</button>
<!-- 用户协议 -->
<view class="agreement-container">
<checkbox-group @change="handleAgreeChange">
<label class="agreement-item">
<checkbox :checked="isAgree" color="#07c160" style="transform:scale(0.7)" />
<text class="agreement-text">我已阅读并同意</text>
<text class="agreement-link" @tap.stop="openUserAgreement">用户协议</text>
<text class="agreement-text"></text>
<text class="agreement-link" @tap.stop="openPrivacyPolicy">隐私政策</text>
</label>
</checkbox-group>
</view>
</template>
</view>
</template>
</view>
</template>
<script setup>
import { getWeChatOpenId } from '@/api/user/user.ts'
import useUserStore from "@/stores/user/useUserStore"
import { doWxLogin } from "./hooks/login.ts"
const userStore = useUserStore();
const isRegister = ref(false);
const inviteCode = ref("");
const userInfo = ref({});
const isAgree = ref(false);
const phoneParams = ref({});
const loginType = ref("wxLogin");
const autoWxLoginFlag = ref(false);
// 生命周期
onLoad((options) => {
isRegister.value = options.isRegister == 0;
loginType.value = options.loginType || "wxLogin";
if (options.inviteCode) {
inviteCode.value = options.inviteCode;
}
autoWxLogin();
});
const getAppId = () => {
const appId = uni.getAccountInfoSync().miniProgram.appId;
return appId;
};
const autoWxLogin = () => {
uni.login({
provider: 'weixin',
success: function (loginRes) {
// 登录成功后显示提示用户需要点击授权
uni.showModal({
title: '登录提示',
content: '请点击下方按钮授权手机号来完成登录',
showCancel: false,
success: () => {
autoWxLoginFlag.value = true;
}
});
},
fail: (err) => {
console.error('微信登录失败:', err)
uni.showToast({
title: '微信登录失败,请重试',
icon: 'none'
})
}
})
}
const getOpenID = async (code) => {
try {
const { data } = await getWeChatOpenId({ code });
phoneParams.value = {
sessionKey: data.session_key,
openid: data.openid,
unionid: data?.unionid ?? "",
};
uni.showToast({ title: "请授权手机号", icon: "none" });
} catch (error) {
console.error("获取OpenID失败:", error);
}
};
const getPhoneNumber = async (e) => {
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
uni.showToast({ title: "需要授权手机号才能登录", icon: "none" });
return
}
if (!isAgree.value) {
uni.showToast({ title: "请先同意用户协议和隐私政策", icon: "none" });
return
}
uni.login({
provider: 'weixin',
success: function (loginRes) {
const params = {
openIdCode: loginRes.code,
phoneCode: e.detail.code,
appId: getAppId()
}
uni.showLoading({
title: '登录中...',
mask: true
})
doWxLogin(params).then((res) => {
uni.hideLoading()
/* uni.reLaunch({
url: '/pages/index/index'
}) */
}).catch(err => {
uni.hideLoading()
uni.showToast({
title: err.msg || '登录失败,请重试',
icon: 'none'
})
})
}
})
}
// 同意协议变更
const handleAgreeChange = (e) => {
isAgree.value = e.detail.value.length > 0;
}
// 打开用户协议
const openUserAgreement = () => {
uni.navigateTo({
url: '/pages/agreement/user-agreement'
})
}
// 打开隐私政策
const openPrivacyPolicy = () => {
uni.navigateTo({
url: '/pages/agreement/privacy-policy'
})
}
</script>
<style scoped lang="scss">
@import "./login.scss";
.login-status {
margin: 80rpx 0 40rpx;
color: #333;
font-size: 30rpx;
text-align: center;
font-weight: 500;
}
.agreement-container {
margin-top: 40rpx;
width: 100%;
display: flex;
justify-content: center;
}
.agreement-item {
display: flex;
align-items: center;
justify-content: center;
}
.agreement-text {
font-size: 26rpx;
color: #666;
}
.agreement-link {
font-size: 26rpx;
color: #07c160;
}
.wx-login-btn {
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: rgba(255, 255, 255, 0.2);
transform: rotate(45deg);
opacity: 0;
}
&:active::after {
animation: btnRipple 0.6s ease-out;
}
.btn-text {
font-weight: 500;
letter-spacing: 4rpx;
}
}
.btn-disabled {
opacity: 0.6;
background: linear-gradient(135deg, #cccccc, #aaaaaa) !important;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1) !important;
}
@keyframes btnRipple {
0% {
opacity: 0;
transform: translate(-50%, -50%) rotate(45deg) scale(0);
}
50% {
opacity: 0.5;
}
100% {
opacity: 0;
transform: translate(-50%, -50%) rotate(45deg) scale(2);
}
}
</style>