Files
RentWeAppFront/pages-biz/login/login.vue

239 lines
5.1 KiB
Vue
Raw Normal View History

2026-01-15 17:18:24 +08:00
<template>
<view class="login-container">
2026-05-20 14:57:38 +08:00
<customNavbar opacity="0" :show-home="true" :show-back="false" :is-transparent="true" />
2026-01-15 17:18:24 +08:00
<view class="content">
<view class="title">登录</view>
<view class="illustration">
<image :src="staticHost + '/public/static/login/bg.png'" mode="aspectFit" />
</view>
<button class="login-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhone">
2026-05-14 14:42:51 +08:00
手机号快捷登录
2026-01-15 17:18:24 +08:00
</button>
2026-05-20 14:57:38 +08:00
<view class="user-type">
2026-01-15 17:18:24 +08:00
<u-radio-group v-model="loginType" active-color="#EA414A" shape="square">
<u-radio name="0">个人</u-radio>
<u-radio name="1">企业</u-radio>
</u-radio-group>
</view>
<view class="agreement">
<u-checkbox v-model="agreeProtocol" shape="square" active-color="#EA414A">
<text class="agreement-text">
我已阅读并同意
<text class="link" @tap.stop="goPrivacy('user')">用户协议</text>
<text class="link" @tap.stop="goPrivacy('privacy')">隐私政策</text>
</text>
</u-checkbox>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loginType: "0",
2026-05-20 14:57:38 +08:00
agreeProtocol: false
};
2026-01-15 17:18:24 +08:00
},
computed: {
staticHost() {
2026-05-20 14:57:38 +08:00
return this.$config.staticUrl;
2026-01-15 17:18:24 +08:00
}
},
methods: {
2026-05-20 14:57:38 +08:00
// 微信获取手机号(修复版)
async onGetPhone(e) {
// 用户拒绝
if (e.detail.errMsg !== "getPhoneNumber:ok") {
this.$mytip.toast("已取消授权");
2026-01-15 17:18:24 +08:00
return;
}
2026-05-20 14:57:38 +08:00
// 必须勾选协议
2026-01-15 17:18:24 +08:00
if (!this.agreeProtocol) {
2026-05-20 14:57:38 +08:00
this.$mytip.toast("请先同意用户协议与隐私政策");
return;
2026-01-15 17:18:24 +08:00
}
try {
2026-05-20 14:57:38 +08:00
// 1. 先拿微信登录 code
2026-01-15 17:18:24 +08:00
const loginRes = await new Promise((resolve, reject) => {
uni.login({
2026-05-20 14:57:38 +08:00
provider: "weixin",
2026-01-15 17:18:24 +08:00
success: resolve,
fail: reject
});
});
2026-05-20 14:57:38 +08:00
// 2. 传给后端登录
await this.doLogin({
2026-01-30 09:01:38 +08:00
loginCode: loginRes.code,
2026-05-20 14:57:38 +08:00
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
2026-01-30 09:01:38 +08:00
loginType: this.loginType
});
2026-05-20 14:57:38 +08:00
} catch (err) {
this.$mytip.toast("授权失败,请重试");
}
},
// 登录接口(修复:用户信息 + 全局刷新)
async doLogin(data) {
uni.showLoading({ title: "登录中...", mask: true });
try {
// 1. 登录拿 token
const authRes = await this.$u.post("/login/weChatLogin", data);
const token = authRes.data;
if (!token) {
uni.hideLoading();
this.$mytip.toast("登录失败");
return;
}
// 2. 保存 token全局
this.$u.vuex("vuex_token", token);
// 3. 获取并保存用户信息(修复:必须 await 等它完成)
await this.getUserOtherInfo(this.loginType, token);
// 4. 登录成功 → 强制刷新全局状态 + 跳首页
2026-01-15 17:18:24 +08:00
uni.hideLoading();
2026-05-20 14:57:38 +08:00
this.$mytip.toast("登录成功");
// 🔥 关键:强制刷新页面,解决用户信息不显示
setTimeout(() => {
uni.switchTab({
url: "/pages/index/index",
success() {
const page = getCurrentPages().pop();
if (page) page.onLoad();
}
});
}, 500);
2026-01-15 17:18:24 +08:00
} catch (err) {
uni.hideLoading();
2026-05-20 14:57:38 +08:00
this.$mytip.toast("登录失败,请稍后重试");
2026-01-15 17:18:24 +08:00
}
},
2026-05-20 14:57:38 +08:00
// 获取用户信息(修复:必须返回 Promise
2026-01-15 17:18:24 +08:00
getUserOtherInfo(loginType, token) {
2026-05-20 14:57:38 +08:00
return new Promise((resolve, reject) => {
this.$u.get("/login/userInfo", {}, {
"WT": token,
"USERTYPE": loginType
}).then(obj => {
if (obj?.flag) {
// 🔥 保存用户信息到缓存
uni.setStorageSync('userInfo', obj.data);
// 🔥 同时存入 vuex 全局状态(首页就能实时刷新)
this.$u.vuex('vuex_userInfo', obj.data);
}
resolve();
}).catch(() => {
resolve();
});
2026-01-15 17:18:24 +08:00
});
},
2026-05-20 14:57:38 +08:00
// 返回
goBack() {
uni.navigateBack({ delta: 1 });
2026-01-15 17:18:24 +08:00
},
2026-05-20 14:57:38 +08:00
// 协议
goPrivacy(type) {
const url = type === "user"
? "/pages-biz/privacy/userAgreement"
: "/pages-biz/privacy/privacyPolicy";
uni.navigateTo({ url });
2026-01-15 17:18:24 +08:00
}
}
2026-05-20 14:57:38 +08:00
};
2026-01-15 17:18:24 +08:00
</script>
<style lang="scss" scoped>
.login-container {
height: 100vh;
2026-05-20 14:57:38 +08:00
background: #fff;
padding-top: var(--status-bar-height);
}
2026-01-30 09:01:38 +08:00
2026-05-20 14:57:38 +08:00
.content {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 40rpx;
margin-top: 200rpx;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.title {
font-size: 48rpx;
font-weight: 500;
color: #222;
margin-bottom: 60rpx;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.illustration {
width: 100%;
height: 400rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 60rpx;
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
image {
width: 80%;
2026-01-15 17:18:24 +08:00
height: 100%;
}
}
2026-05-20 14:57:38 +08:00
.login-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(90deg, #FF2F31 0%, #FF9379 100%);
border-radius: 49rpx;
font-size: 36rpx;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40rpx;
border: none;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.user-type {
display: flex;
justify-content: center;
gap: 40rpx;
margin-bottom: 40rpx;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.agreement {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.agreement-text {
font-size: 26rpx;
color: #cecece;
}
2026-01-15 17:18:24 +08:00
2026-05-20 14:57:38 +08:00
.link {
color: #334254;
2026-01-15 17:18:24 +08:00
}
</style>