Files
frontend-app/pages/login_package/login/login.vue
T
2026-08-29 13:02:46 +08:00

845 lines
24 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
<view class="login_warp" @touchmove.stop.prevent>
<u-icon size="30rpx" name="arrow-left" color="#333" @click="onBack"></u-icon>
<view class="login_logo_warp">
<view style="text-align: center">
<up-image src="/static/logo.png" width="90" height="118" bgColor="#f1f6ff00" radius="10"></up-image>
</view>
<!-- <view style="margin-top: 20rpx;">
<up-image src="/static/logo_z.png" width="100" height="40" bgColor="#f1f6ff00"></up-image>
</view> -->
</view>
<view class="login_info">
<view class="login_info_item">
<up-input placeholder="请输入手机号/账户名称" border="surround" v-model="form.name" :customStyle="inputCss"
:adjust-position="false" @change="checkNameParam"></up-input>
<qiaobao-assistant page-key="pages/login_package/login/login" title="账户登录" />
</view>
<!-- 密码登录 -->
<view class="login_info_item" v-if="loginWay === 'password'">
<up-input placeholder="请输入密码" border="surround" v-model="form.password" :customStyle="inputCss"
:type="isShow ? 'text' : 'password'" :adjust-position="false" @change="checkParam">
<template #suffix style="margin-right: 10rpx">
<up-image v-if="isShow" src="/pages/login_package/static/login/show.png" width="20" height="20"
bgColor="#f1f6ff00" @click="isShow = false"></up-image>
<up-image v-else src="/pages/login_package/static/login/hidden.png" width="20" height="20"
bgColor="#f1f6ff00" @click="isShow = true"></up-image>
</template>
</up-input>
</view>
<!-- 验证码登录 -->
<view class="login_info_item" v-else>
<up-input placeholder="请输入验证码" border="surround" v-model="form.code" :customStyle="inputCss"
:adjust-position="false">
<template #suffix style="margin-right: 10rpx">
<view v-if="countdown" class="code-warp">
<up-count-down :time="60 * 1000" format="ss" @finish="countdown = false"></up-count-down>
<text style="margin-left: 10rpx">s</text>
</view>
<view v-else @click="getMobileCode" class="code_msg">
获取验证码
</view>
</template>
</up-input>
</view>
<!-- 协议勾选 -->
<view class="policy_warp">
<up-checkbox name="isCheck" usedAlone v-model:checked="isPolicy" shape="circle" activeColor="#7934F6" size="19"
iconSize="18">
<template #label>
<text>已阅读并同意</text>
<text class="policy_a_warp" @click="jumpUserAgreement">《服务协议》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpPrivacyAgreement">《隐私政策》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpProductAgreement">《售后保障协议》</text>
</template>
</up-checkbox>
</view>
<view class="login_comm_but login_comm_but_yes" @click="loginFun">
<text style="margin-left: 10rpx">登录</text>
</view>
<view class="cut_way_warp">
<view v-if="loginWay === 'password'" @click="loginWay = 'code'">验证码登录</view>
<view v-if="loginWay === 'code'" @click="loginWay = 'password'">密码登录登录</view>
<view @click="jumpForgot">忘记密码</view>
<view @click="jumpRegister">注册</view>
</view>
</view>
<view>
<!-- #ifdef APP-PLUS -->
<view class="other_login">其他登录方式</view>
<view class="login_other_icon">
<view class="login_other_item" v-if="(isIOS && isWechatInstalled) || !isIOS" @click="wechatOneLogin(2)">
<up-image src="https://static.tbmall.xin/static/home/wechat.png" width="44" height="44"
bgColor="#f1f6ff00"></up-image>
</view>
<view v-if="!isIOS" class="login_other_item" @click="alipayOneLogin">
<up-image src="https://static.tbmall.xin/static/home/alipay.png" width="44" height="44"
bgColor="#f1f6ff00"></up-image>
</view>
<view v-if="isIOS" class="login_other_item" @click="appleOneLogin">
<up-image src="https://static.tbmall.xin/static/home/apple.png" width="44" height="44"
bgColor="#f1f6ff00"></up-image>
</view>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="other_login">其他登录方式</view>
<view class="login_other_icon">
<view v-if="!isPolicy" class="login_other_item" @click="handleUnpolicyWXLogin">
<up-image src="https://static.tbmall.xin/static/home/wechat.png" width="44" height="44"
bgColor="#f1f6ff00"></up-image>
</view>
<button v-else class="login_other_item" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
<up-image src="https://static.tbmall.xin/static/home/wechat.png" width="44" height="44"
bgColor="#f1f6ff00"></up-image>
</button>
</view>
<view class="login_other_icon">
<view class="login_other_item_you" @click="jumpHome">
游客模式
</view>
</view>
<!-- #endif -->
</view>
<up-toast ref="uToastRef"></up-toast>
<!-- 自定义协议弹窗 -->
<view v-if="showPolicyModal" class="modal-overlay" @click="showPolicyModal = false">
<view class="modal-content" @click.stop>
<view class="modal-title">温馨提示</view>
<view class="modal-message">请您阅读并同意《服务协议》与《隐私政策》</view>
<view class="modal-buttons">
<view class="modal-btn modal-btn-cancel" @click="showPolicyModal = false">取消</view>
<view class="modal-btn modal-btn-confirm" @click="handlePolicyConfirm">同意并登录</view>
</view>
</view>
</view>
</view>
</template>
<script>
import InputCodeComm from "@/pages/login_package/components/code-comm/code-comm.vue";
import {
SMS_TYPE
} from "@/utils/enumUtils.js";
import {
getUserList
} from "@/api/common.js";
import {
loginbysms,
loginbypw,
wechatLogin,
alipayLogin,
appleLogin
} from "@/api/auth.js";
import {
setStorageFun,
TOKEN_NAME,
USER_KEY,
USER_DATA,
USER_LOGIN_TIME,
} from "@/utils/auth.js";
import CryptoJS from "crypto-js";
import { loginFn, getPhoneNumberFn } from "@/utils/login.js";
// #ifdef APP-PLUS
const hyyAlipay = uni.requireNativePlugin("Hyy-Alipay");
// #endif
export default {
onLaunch: function () {
console.log("App Launch");
const res = uni.getSystemInfoSync();
uni.setStorageSync("platform", res.platform);
// #ifdef APP-PLUS
// 获取应用版本信息
plus.runtime.getProperty(plus.runtime.appid, function (inf) {
console.log("版本信息", inf.versionCode);
uni.setStorageSync("version-1", inf.version); // 大版本号
uni.setStorageSync("versionCode", inf.versionCode); // 小版本号
});
// #endif
},
onLoad() {
// #ifdef APP-PLUS
// 判断微信是否安装 (iOS 的 scheme 为 weixin://)
this.isWechatInstalled = plus.runtime.isApplicationExist({
action: 'weixin://'
});
// #endif
},
components: {
InputCodeComm,
},
data() {
return {
form: {
name: null,
password: null,
code: null,
},
inputCss: {
height: "80rpx",
background: "#F3F3F3FF",
"border-radius": "30rpx",
border: "0rpx",
"padding-left": "16px",
},
loginWay: "password",
isShow: false, // 是否显示
isPolicy: false, // 是否同意
countdown: false, // 清空倒计时
loading: false,
showPolicyModal: false,
isWechatInstalled: false,
};
},
methods: {
onBack() {
console.log('onBack',)
uni.switchTab({
url: '/pages/home/home'
})
},
jumpHome() {
uni.switchTab({
url: "/pages/home/home",
});
},
async loginFun(val = {}) {
const then = this;
const form = this.form;
const params = {};
if (!this.isPolicy) {
this.showPolicyModal = true;
return;
}
let response = val;
if (this.loginWay === "password") {
params.mobile = form.name;
params.password = CryptoJS.MD5(form.password).toString();
response = await loginbypw(params);
}
if (this.loginWay === "code") {
params.mobile = form.name;
params.smsCode = form.code;
response = await loginbysms(params);
}
const data = response.data;
setStorageFun(TOKEN_NAME, data.token);
// 用于无感登录
setStorageFun(USER_KEY, "");
const userInfo = {
avatar: data.avatarUrl,
name: data.name,
userId: data.userId,
grade: data.grade,
inviteCode: data.inviteCode,
};
setStorageFun(USER_DATA, userInfo);
// 当前登录得时间戳
let currentTimeStamp = new Date().getTime();
setStorageFun(USER_LOGIN_TIME, currentTimeStamp);
const res = uni.getSystemInfoSync();
uni.setStorageSync('platform', res.platform);
// #ifdef APP-PLUS         
// 获取应用版本信息
plus.runtime.getProperty(plus.runtime.appid, function (inf) {
console.log("版本信息", inf.versionCode)
uni.setStorageSync('version-1', inf.version); // 大版本号
uni.setStorageSync('versionCode', inf.versionCode); // 小版本号
});
// #endif
this.$refs.uToastRef.show({
type: "success",
message: response.msg,
});
setTimeout(() => {
then.jumpHome();
}, 2000);
},
checkNameParam(val) {
// console.log("val",val);
},
checkParam(val) {
// console.log("val",val);
},
/**
* 获取手机验证码
*/
async getMobileCode() {
const form = this.form;
if (!form.name) {
this.$refs.uToastRef.show({
type: "error",
message: "请输入账号",
});
return;
}
this.countdown = true;
const params = {
mobile: form.name,
type: SMS_TYPE.LOGIN,
};
const response = await getUserList(params);
console.log("responseresponseresponse", response);
if (response && response.bizcode === 100) {
this.$refs.uToastRef.show({
type: "success",
message: response.msg,
});
} else {
this.countdown = false;
}
},
jumpForgot() {
uni.navigateTo({
url: "/pages/rwa_package/setting/forgot-pass?type=3",
});
},
jumpRegister() {
uni.navigateTo({
url: "/pages/login_package/register/register",
});
},
// 用户协议
jumpUserAgreement() {
uni.navigateTo({
url: "/pages/mine_package/mine_user_agreement/mine_user_agreement?backPath=login",
});
},
// 隐私协议
jumpPrivacyAgreement() {
uni.navigateTo({
url: "/pages/mine_package/mine_privacy_agreement/mine_privacy_agreement?backPath=login",
});
},
// 售后保障协议
jumpProductAgreement() {
uni.navigateTo({
url: "/pages/mine_package/mine_product_agreement/mine_product_agreement?backPath=login",
});
},
// 微信登录
async wechatOneLogin(type) {
const then = this;
this.loading = true;
console.log("微信登录--1", this.isPolicy)
// 检查是否同意协议
if (!this.isPolicy) {
uni.showModal({
title: '温馨提示',
content: '请您阅读并同意《服务协议》与《隐私政策》',
confirmText: "同意登录",
success: (res) => {
if (res.confirm) {
this.isPolicy = true;
this.wechatOneLogin(type);
} else {
this.loading = false;
}
},
fail: (err) => {
this.loading = false;
console.log('showModal error:', err);
}
});
return;
}
try {
// 1. 调用微信登录API
uni.login({
provider: "weixin",
onlyAuthorize: true,
success: async (loginRes) => {
console.log("微信登录成功:", loginRes);
// 2. 发送code到后端换取token
const params = {
isValid: false,
token: loginRes.code,
type
// 可以根据需要添加其他参数
};
console.log("微信登录接口返回-1:", params);
const response = await wechatLogin(params);
console.log(
"微信登录接口返回-2:",
response,
response.data,
response.data.token
);
if (response && response.bizcode === 100) {
if (response.data.userId) {
// 登录
this.loginWay = "wechat";
this.loginFun(response);
} else {
// 去微信注册
uni.navigateTo({
url: `/pages/login_package/bind-phone/bind-phone?token=${response.data.token}&type=1`,
});
}
} else {
this.$refs.uToastRef.show({
type: "error",
message: response.msg || "微信登录失败",
});
}
},
fail: (err) => {
this.loading = false;
console.error("微信登录失败:", err);
this.$refs.uToastRef.show({
type: "error",
message: "微信登录失败,请重试",
});
},
});
} catch (error) {
this.loading = false;
console.error("微信登录异常:", error);
this.$refs.uToastRef.show({
type: "error",
message: "微信登录异常,请重试",
});
}
},
// 支付宝一键登录
async alipayOneLogin() {
const then = this;
this.loading = true;
console.log("支付宝登录", hyyAlipay);
// 检查是否同意协议
if (!this.isPolicy) {
uni.showModal({
title: '温馨提示',
content: '请您阅读并同意《服务协议》与《隐私政策》',
confirmText: "同意登录",
success: (res) => {
if (res.confirm) {
this.isPolicy = true;
this.alipayOneLogin();
} else {
this.loading = false;
}
},
fail: (err) => {
this.loading = false;
console.log('showModal error:', err);
}
});
return;
}
hyyAlipay.openAuthScheme({
appId: "2021005183625053", // 支付宝分配给开发者的应用 ID
scheme: "trustbridgeapp", // manifest.json -> "App常用其他设置" -> "UrlSchemes"里设置
},
(res) => {
console.log("支付宝回调", res);
alipayLogin({
isValid: false,
token: res.auth_code,
}).then((response) => {
console.log("支付宝登录接口返回:", response, !response.data.userId);
if (response && response.bizcode === 100) {
if (response.data.userId) {
// 登录
this.loginWay = "alipay";
this.loginFun(response);
} else {
// 去微信注册
uni.navigateTo({
url: `/pages/login_package/bind-phone/bind-phone?token=${response.data.token}&type=2`,
});
}
} else {
this.$refs.uToastRef.show({
type: "error",
message: response.msg || "微信登录失败",
});
}
});
}
);
},
handlePolicyConfirm() {
this.showPolicyModal = false;
this.isPolicy = true;
this.loginFun();
},
// Apple 登录 (仅 App iOS 端)
async appleOneLogin() {
this.loading = true;
// 检查是否同意协议
if (!this.isPolicy) {
uni.showModal({
title: '温馨提示',
content: '请您阅读并同意《服务协议》与《隐私政策》',
confirmText: "同意登录",
success: (res) => {
if (res.confirm) {
this.isPolicy = true;
this.appleOneLogin();
} else {
this.loading = false;
}
},
fail: (err) => {
this.loading = false;
console.log('showModal error:', err);
}
});
return;
}
// #ifdef APP-PLUS
uni.getProvider({
service: 'oauth',
success: (provRes) => {
if (!provRes.provider || !provRes.provider.includes('apple')) {
this.loading = false;
this.$refs.uToastRef.show({
type: "error",
message: "当前运行环境或自定义基座未支持 Apple 登录",
});
return;
}
uni.login({
provider: 'apple',
success: async (loginRes) => {
const authResult = loginRes.appleInfo || {};
const params = {
identityToken: authResult.identityToken,
};
try {
const response = await appleLogin(params);
if (response && response.bizcode === 100) {
if (response.data && response.data.userId) {
this.loginWay = "apple";
this.loginFun(response);
} else {
uni.navigateTo({
url: `/pages/login_package/bind-phone/bind-phone?token=${response.data.token}&type=3`,
});
}
} else {
this.$refs.uToastRef.show({
type: "error",
message: response?.msg || "Apple登录失败",
});
}
} catch (error) {
this.$refs.uToastRef.show({
type: "error",
message: "Apple登录异常,请重试",
});
} finally {
this.loading = false;
}
},
fail: (err) => {
this.loading = false;
console.log("Apple登录授权失败 err:", JSON.stringify(err), err);
// 判断是否为用户取消授权(Apple 登录取消错误码通常为 1001 或 code 1001)
if (err && (err.code === 1001 || err.errCode === 1001 || (err.errMsg && err.errMsg.includes("1001")))) {
this.$refs.uToastRef.show({
type: "error",
message: "已取消Apple登录",
});
} else {
this.$refs.uToastRef.show({
type: "error",
message: (err && (err.errMsg || err.msg)) ? `Apple登录失败: ${err.errMsg || err.msg}` : "Apple登录授权失败",
});
}
}
});
},
});
// #endif
},
handleUnpolicyWXLogin() {
this.showPolicyModal = true;
},
handlePolicyConfirm() {
this.isPolicy = true;
this.showPolicyModal = false;
// #ifdef MP-WEIXIN
this.$refs.uToastRef.show({
type: "success",
message: "已同意协议,请再次点击微信登录",
});
// #endif
// #ifndef MP-WEIXIN
this.loginFun();
// #endif
},
getPhoneNumber(e) { // 在bindgetphonenumber回调中获取code动态令牌
if (!e || !e.detail || !e.detail.code) {
console.log('用户拒绝授权手机号或获取失败:', e);
return;
}
loginFn().then(res => { // 微信登录&服务端获取code
console.log("微信登录", res.code, e.detail.code)
// return
getPhoneNumberFn(res.code, e.detail.code).then(response => { // 服务端获取手机号
if (response && response.bizcode === 100) {
const data = response.data;
setStorageFun(TOKEN_NAME, data.token);
// 用于无感登录
setStorageFun(USER_KEY, "");
const userInfo = {
avatar: data.avatarUrl,
name: data.name,
userId: data.userId,
grade: data.grade,
inviteCode: data.inviteCode,
};
setStorageFun(USER_DATA, userInfo);
// 当前登录得时间戳
let currentTimeStamp = new Date().getTime();
setStorageFun(USER_LOGIN_TIME, currentTimeStamp);
const sysInfo = uni.getSystemInfoSync();
uni.setStorageSync("platform", sysInfo.platform);
// #ifdef APP-PLUS
// 获取应用版本信息
plus.runtime.getProperty(plus.runtime.appid, function (inf) {
console.log("版本信息", inf.versionCode);
uni.setStorageSync("version-1", inf.version); // 大版本号
uni.setStorageSync("versionCode", inf.versionCode); // 小版本号
});
// #endif
this.$refs.uToastRef.show({
type: "success",
message: response.msg,
});
setTimeout(() => {
uni.switchTab({
url: "/pages/home/home",
});
}, 1200);
} else if (response && response.msg) {
this.$refs.uToastRef.show({
type: "error",
message: response.msg,
});
}
});
});
},
},
computed: {
isIOS() {
// #ifdef APP-PLUS
return uni.getSystemInfoSync().platform === "ios";
// #endif
return false;
},
},
};
</script>
<style lang="scss" scoped>
.login_warp {
height: 100vh;
overflow: hidden;
box-sizing: border-box;
padding: 120rpx 50rpx 40rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.policy_warp {
font-weight: 500;
font-size: 26rpx;
color: #999999;
position: relative !important;
bottom: auto !important;
left: auto !important;
width: 100% !important;
margin-top: 20rpx;
margin-bottom: 28rpx;
.policy_a_warp {
color: $app-policy-color;
}
}
.login_logo_warp {
display: grid;
justify-content: center;
}
.login_info {
margin-top: 60rpx;
.login_info_item {
margin-bottom: 40rpx;
}
.code_msg {
font-size: 36rpx;
color: $app-bt-bj;
}
}
.login_comm_but {
background: #cccccc;
border-radius: 20rpx;
width: 100%;
text-align: center;
font-weight: 500;
font-size: 32rpx;
color: #999999;
padding: 28rpx 0;
display: flex;
justify-content: center;
}
.login_comm_but_yes {
background: $app-bt-bj;
color: #ffffff;
}
.cut_way_warp {
display: flex;
justify-content: space-between;
font-weight: 500;
font-size: 28rpx;
color: #999999;
margin-top: 20rpx;
}
.other_login {
font-size: 28rpx;
text-align: center;
margin-top: 80rpx;
margin-bottom: 32rpx;
color: #999999;
}
.login_other_icon {
display: flex;
padding: 0 100rpx;
justify-content: center;
.login_other_item {
display: flex;
flex: 50%;
font-size: 28rpx;
flex-direction: column;
align-items: center;
margin-bottom: 80rpx;
background: transparent;
border: none;
padding: 0;
line-height: normal;
color: inherit;
&::after {
border: none;
}
}
.login_other_item_you {
width: 500rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #7934F6;
color: #7934F6;
border-radius: 10rpx;
}
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
width: 500rpx;
background: #fff;
border-radius: 20rpx;
padding: 24rpx;
text-align: center;
}
.modal-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.modal-message {
font-size: 28rpx;
color: #666;
line-height: 1.6;
margin-bottom: 40rpx;
}
.modal-buttons {
display: flex;
}
.modal-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
}
.modal-btn-cancel {
color: #999;
border-right: 1rpx solid #eee;
}
.modal-btn-confirm {
color: #7934F6;
}
</style>