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-22 11:36:25 +08:00
|
|
|
|
<!-- 恢复v-model,同时保留防错乱逻辑 -->
|
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 {
|
2026-05-22 11:36:25 +08:00
|
|
|
|
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 {
|
|
|
|
|
|
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-22 11:36:25 +08:00
|
|
|
|
// 🔥 关键:登录瞬间快照当前类型,防异步错乱
|
|
|
|
|
|
const currentType = this.loginType;
|
|
|
|
|
|
|
2026-05-20 14:57:38 +08:00
|
|
|
|
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-05-22 11:36:25 +08:00
|
|
|
|
loginType: currentType
|
2026-01-30 09:01:38 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-20 14:57:38 +08:00
|
|
|
|
} catch (err) {
|
|
|
|
|
|
this.$mytip.toast("授权失败,请重试");
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async doLogin(data) {
|
|
|
|
|
|
uni.showLoading({ title: "登录中...", mask: true });
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const authRes = await this.$u.post("/login/weChatLogin", data);
|
|
|
|
|
|
const token = authRes.data;
|
|
|
|
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
this.$mytip.toast("登录失败");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.$u.vuex("vuex_token", token);
|
|
|
|
|
|
|
2026-05-22 11:36:25 +08:00
|
|
|
|
// 🔥 用快照的类型,不用this.loginType,防切换错乱
|
|
|
|
|
|
await this.getUserOtherInfo(data.loginType, token);
|
2026-05-20 14:57:38 +08:00
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
|
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>
|