mrr.sj.front/pages/blogPopup/blogPopup.vue

1029 lines
29 KiB
Vue
Raw Normal View History

2026-03-24 11:45:13 +08:00
<template>
<!-- 底部弹窗蒙层 -->
<view class="login-mask" :style="{background:maskShow?`rgba(0, 0, 0, 0.5)`:`transparent`}" @tap="closeWindow">
<!-- 底部弹窗内容 -->
<view class="login-popup" @tap.stop.prevent v-show="maskShow">
<!-- 弹窗头部标题+关闭按钮 -->
<view class="popup-header">
<text class="popup-title">登录享更多服务</text>
</view>
<image class="popup-close" src="/static/images/icons/close.png" @click="closeWindow" />
<!-- 登录选项卡 -->
<view class="login-tabs">
<view class="tab-item" :class="{active: loginType === 'code'}" @click="loginType = 'code'">验证码</view>
<view class="tab-line"></view>
<view class="tab-item" :class="{active: loginType === 'account'}" @click="loginType = 'account'">账号密码
</view>
<view class="tab-line"></view>
<view class="tab-item" :class="{active: loginType === 'onekey'}" @click="handleOnekeyTab">一键登录</view>
</view>
<!-- 输入区域根据选项卡切换 -->
<view class="input-area">
<!-- 验证码登录 -->
<template v-if="loginType === 'code'">
<view class="input-item">
<uv-album></uv-album>
<image class="input-icon-phone" mode="widthFix" src="/static/images/icons/log_phone.png" />
<input type="number" v-model="phone" placeholder="请输入手机号码" :adjust-position="false"
class="input-text" @input="handlePhoneInput" />
</view>
<!-- 手机号错误提示 -->
<text class="input-error" v-if="phoneError">{{phoneError}}</text>
<view class="input-item">
<image class="input-icon-password" mode="widthFix"
src="/static/images/icons/log_password.png" />
<input type="number" v-model="code" placeholder="请输入验证码" :adjust-position="false"
class="input-text" maxlength="6" />
<uv-code keepRunning :seconds="seconds" @end="end" @start="start" ref="code"
@change="codeChange"></uv-code>
<text class="get-code-btn" :class="{running:runningCode,noPhone:!phone}"
@tap="getCode">{{tips}}</text>
</view>
</template>
<!-- 账号密码登录 -->
<template v-else-if="loginType === 'account'">
<view class="input-item">
<image class="input-icon-phone" mode="widthFix" src="/static/images/icons/log_phone.png" />
<input v-model="phone" placeholder="请输入账号" class="input-text" :adjust-position="false"
@input="handlePhoneInput" />
</view>
<!-- 手机号错误提示 -->
<text class="input-error" v-if="phoneError">请输入正确的账号</text>
<view class="input-item">
<image class="input-icon-password" mode="widthFix"
src="/static/images/icons/log_password.png" />
<input type="text" v-model="password" placeholder="请输入密码" :adjust-position="false"
:password="!showPassword" class="input-text" />
<image src="/static/images/icons/noLook.png" mode="widthFix" class="look-icon"
v-if="!showPassword" @click.stop="showPassword = true"></image>
<image src="/static/images//icons/look.png" mode="widthFix" class="look-icon"
@click.stop="showPassword = false" v-else></image>
</view>
</template>
</view>
<!-- 登录按钮根据选项卡/输入状态切换样式 -->
<view class="login-btn" :class="{active: canLogin}" @click="handleLogin">
立即登录
</view>
<!-- 协议勾选 -->
<view class="agree">
<agree-radio :isAgree="isAgree" @change-agree="changeAgree" class="agree-btn"></agree-radio>
<view class="agree-card">
<text class="agree-text">未注册手机号登录后将自动生成账号且代表您已阅读并同意</text>
2026-03-25 13:29:04 +08:00
<text class="agreement" @click="goAgreement(28)">用户注册协议</text>
<text class="agreement" @click="goAgreement(26)">隐私政策</text>
2026-03-24 11:45:13 +08:00
</view>
</view>
</view>
</view>
</template>
<script>
2026-03-25 13:34:16 +08:00
import {
isNotificationsEnabled,
permissions,
registPush
} from '@/utils/AS-NotifyTheAuthority.js'
2026-03-24 11:45:13 +08:00
import AgreeRadio from '@/components/agree-radio/agree-radio.vue'
2026-03-25 13:29:04 +08:00
import request from '@/utils/request';
2026-03-24 11:45:13 +08:00
import uiConfig from "@/utils/ui-config.js"
const aLiSDKModule = uni.requireNativePlugin('AliCloud-NirvanaPns');
export default {
components: {
AgreeRadio
},
data() {
return {
2026-03-25 13:29:04 +08:00
isFirst:false,//是否第一次打开
2026-03-24 11:45:13 +08:00
showPassword: false, // 是否展示密码
2026-03-25 13:34:16 +08:00
maskShow: true,
2026-03-24 11:45:13 +08:00
loginType: 'code', // 当前选项卡code/account/onekey
phone: '', // 验证码-手机号
code: '', // 验证码-验证码
password: '', // 账号密码-密码
account: '', // 账号密码-账号
isAgree: false, // 协议勾选状态
tips: '',
seconds: 60,
phoneError: '', // 手机号错误提示
runningCode: false, //是否在倒计时
}
},
onLoad() {
// this.checkAndroidPermission()
this.isFirst = true
this.handleOnekeyTab()
},
computed: {
// 判断是否可登录(匹配示例按钮状态)
canLogin() {
if (this.loginType === 'code') {
return this.isAgree && this.phone && this.code && this.code.length >= 4 && !this.phoneError;
} else if (this.loginType === 'account') {
return this.isAgree && this.phone && this.password && !this.phoneError;
} else if (this.loginType === 'onekey') {
return this.isAgree;
}
return false;
}
},
methods: {
/**
* 检查安卓系统权限设备信息/短信
*/
checkAndroidPermission() {
2026-03-25 13:29:04 +08:00
// #ifdef APP-PLUS
const permissions = [
'android.permission.READ_PHONE_STATE'
];
plus.android.requestPermissions(permissions, (result) => {
const { granted, neverDenied } = result;
console.log(11111111,result)
if (neverDenied.length > 0) {
// 永久拒绝:引导用户去系统设置页开启
uni.showModal({
title: '权限提示',
content: '需要您开启设备信息和短信权限才能使用一键登录',
success: (res) => {
if (res.confirm) {
plus.android.openSettings(); // 打开安卓设置页
}
}
});
} else if (granted.length === permissions.length) {
// 已授权:继续运营商一键登录
this.checkOperatorAuth();
}
}, (err) => {
console.error('申请权限失败:', err);
});
// #endif
2026-03-24 11:45:13 +08:00
},
// 是否同意修改
changeAgree(value) {
this.isAgree = value;
},
goAgreement(type) {
uni.navigateTo({
url: "/pages/agreement/agreement?type=" + type
})
},
codeChange(text) {
this.tips = text;
},
/**
* 获取手机号今日获取验证码的次数
* @param {String} phone - 手机号
* @returns {Number} 今日获取次数
*/
getPhoneCodeCount(phone) {
// 生成今日日期YYYY-MM-DD作为本地存储key的一部分确保按自然日统计
const today = new Date().toISOString().split('T')[0];
// 定义本地存储的keycode_count_手机号_日期
const storageKey = `code_count_${phone}_${today}`;
// 从本地存储获取次数默认0
const count = uni.getStorageSync(storageKey) || 0;
return Number(count);
},
/**
* 更新手机号今日获取验证码的次数+1
* @param {String} phone - 手机号
*/
updatePhoneCodeCount(phone) {
const today = new Date().toISOString().split('T')[0];
const storageKey = `code_count_${phone}_${today}`;
const count = this.getPhoneCodeCount(phone);
// 存储新的次数(+1
uni.setStorageSync(storageKey, count + 1);
},
getCode() {
let that = this
// 验证手机号(双重校验,确保接口请求时手机号合法)
if (this.phoneError) {
uni.showToast({
title: this.phoneError,
icon: 'none'
})
return
}
if (!this.phone) {
uni.showToast({
title: "请输入手机号",
icon: 'none'
})
return
}
// ------ 单日单手机号最多获取10次开始 ------
const codeCount = this.getPhoneCodeCount(this.phone);
if (codeCount >= 10) {
uni.showToast({
title: '今日获取验证码次数已达上限,请明日再试',
icon: 'none',
duration: 2000
});
return;
}
// ------ 单日单手机号最多获取10次结束 ------
if (this.$refs.code.canGetCode) {
uni.showLoading({
title: '正在获取验证码'
})
// 请求获取验证码接口
request.post('/user/sendcode', {
account: that.phone,
type: 2
}).then(result => {
uni.hideLoading();
if (result.state == 1) {
uni.showToast({
title: result.msg,
icon: 'none'
})
this.$refs.code.start();
// 更新手机号今日获取次数(仅当接口请求成功时计数)
this.updatePhoneCodeCount(this.phone);
} else {
uni.showToast({
title: result.msg,
icon: 'none'
})
}
}).catch(err => {
uni.hideLoading();
uni.showToast({
title: err.msg,
icon: 'none'
})
})
} else {
uni.$uv.toast('倒计时结束后再发送');
}
},
end() {
this.runningCode = false
},
start() {
this.runningCode = true
},
// 关闭弹窗
closeWindow() {
uni.navigateBack({
delta: 1
});
},
// 点击“一键登录”选项卡:直接唤起原生弹窗(不处理类型切换)
handleOnekeyTab() {
// this.loginType = 'onekey';
// 延迟100ms确保选项卡切换完成后调用
try {
// 加速登录页加载
aLiSDKModule.accelerateLoginPage(5000, (result) => {
console.log('加速授权页结果:', JSON.stringify(result));
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
if (result.resultCode === '600000') {
console.log("加速授权页拉起成功");
// 延迟执行,确保配置构建完成
setTimeout(() => {
try {
// 额外的try/catch防止配置构建或登录控制器调用出错
const config = uiConfig.buildSheet();
this._presentLoginController(config);
} catch (innerError) {
this.maskShow = true
console.error("构建登录配置或调用登录控制器失败:", innerError);
// 这里可以添加配置构建失败后的备用操作
}
}, 100);
} else {
2026-03-25 13:29:04 +08:00
if(!this.isFirst){
if(result.resultCode == 600004 || result.resultCode ==600008){
2026-03-24 11:45:13 +08:00
uni.showToast({
title: "蜂窝网络未开启",
icon: 'none'
});
2026-03-25 13:29:04 +08:00
}else if(result.resultCode == 600007 || result.resultCode ==600009){
2026-03-24 11:45:13 +08:00
uni.showToast({
title: "未检测到sim卡",
icon: 'none'
});
2026-03-25 13:29:04 +08:00
}else{
2026-03-24 11:45:13 +08:00
uni.showToast({
title: "唤起授权页失败",
icon: 'none'
});
}
}
2026-03-25 13:29:04 +08:00
this.maskShow = true
this.isFirst = false
console.error("授权页加速拉起失败:", result);
2026-03-24 11:45:13 +08:00
}
});
} catch (error) {
// 捕获aLiSDKModule未定义或其他执行错误
this.maskShow = true
console.error("跳转到登录页时发生错误:", error);
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
}
// aLiSDKModule.accelerateLoginPage(5000, result => {
// console.log(JSON.stringify(result));
// if ('600000' == result.resultCode) {
// console.log("加速授权页拉起成功");
// setTimeout(() => {
// const config = uiConfig.buildSheet();
// this._presentLoginController(config);
// }, 200);
// } else {
// console.log("授权页加速拉起失败," + result.msg);
// }
// });
},
/**
* 密码登录辅助方法获取账号密码错误次数
* @param {String} account - 登录账号手机号
* @returns {Number} 错误次数默认0
*/
getPwdErrorCount(account) {
const storageKey = `pwd_error_count_${account}`;
return Number(uni.getStorageSync(storageKey)) || 0;
},
/**
* 密码登录辅助方法更新账号密码错误次数
* @param {String} account - 登录账号手机号
* @param {Number} count - 最新错误次数
*/
updatePwdErrorCount(account, count) {
const storageKey = `pwd_error_count_${account}`;
uni.setStorageSync(storageKey, count);
},
/**
* 密码登录辅助方法获取账号锁定时间戳
* @param {String} account - 登录账号手机号
* @returns {Number} 锁定时间戳毫秒默认0
*/
getPwdLockTime(account) {
const storageKey = `pwd_lock_time_${account}`;
return Number(uni.getStorageSync(storageKey)) || 0;
},
/**
* 密码登录辅助方法设置账号锁定记录锁定时间戳
* @param {String} account - 登录账号手机号
*/
setPwdLock(account) {
const storageKey = `pwd_lock_time_${account}`;
uni.setStorageSync(storageKey, Date.now()); // 存储当前时间戳
},
/**
* 密码登录辅助方法清除账号锁定缓存错误次数+锁定时间
* @param {String} account - 登录账号手机号
*/
clearPwdLockCache(account) {
uni.removeStorageSync(`pwd_error_count_${account}`);
uni.removeStorageSync(`pwd_lock_time_${account}`);
},
/**
* 密码登录辅助方法检查账号是否被锁定
* @param {String} account - 登录账号手机号
* @returns {Object} { isLocked: Boolean, remainingTime: Number }
* remainingTime: 剩余锁定时间0表示未锁定
*/
checkAccountLocked(account) {
const lockTime = this.getPwdLockTime(account);
if (!lockTime) return {
isLocked: false,
remainingTime: 0
};
const now = Date.now();
const lockDuration = 3600 * 1000; // 1小时毫秒
const remainingTime = Math.ceil((lockTime + lockDuration - now) / 1000); // 剩余锁定时间(秒)
if (remainingTime > 0) {
return {
isLocked: true,
remainingTime
};
} else {
// 锁定时间已过,自动清除缓存
this.clearPwdLockCache(account);
return {
isLocked: false,
remainingTime: 0
};
}
},
// 执行登录
handleLogin() {
let that = this;
// 手机号错误时直接返回
if (this.phoneError) {
uni.showToast({
title: this.phoneError,
icon: 'none'
})
return
}
if (this.loginType === 'code') {
// 验证码登录:调用后端接口(示例)
if (!this.phone) {
uni.showToast({
title: "请输入手机号",
icon: 'none'
})
return
} else if (!that.code) {
uni.showToast({
title: '请输入验证码',
icon: 'none'
});
return false;
} else if (this.code.length < 4) { // 修复逻辑:原代码是>=4时提示错误改为<4
uni.showToast({
title: '请输入正确验证码',
icon: 'none'
});
return false;
} else if (!that.isAgree) {
uni.showToast({
title: '请先阅读并同意《用户注册协议》与《隐私政策》',
icon: 'none'
});
return false;
} else {
2026-03-25 13:29:04 +08:00
request.post('/sj/codelogin', {
2026-03-24 11:45:13 +08:00
account: that.phone,
code: that.code,
deviceid: getApp().globalData.deviceid
}).then(result => {
2026-03-25 13:29:04 +08:00
if (result.code == 200) {
2026-03-25 13:34:16 +08:00
2026-03-25 13:29:04 +08:00
uni.setStorageSync('accessToken', result.data.access_token);
uni.setStorageSync('refreshToken', result.data.refresh_token);
uni.setStorageSync('refresh_token_expries', result.data.refresh_token_expries);
2026-03-24 11:45:13 +08:00
uni.setStorageSync('loginAccount', that.phone); // 保存登录的手机号
2026-03-25 13:34:16 +08:00
registPush();
this.messageUpudateNum()
2026-03-24 11:45:13 +08:00
this.closeWindow();
} else {
uni.showToast({
title: result.msg,
icon: 'none'
})
}
}).catch(err => {
uni.showToast({
title: err.msg,
icon: 'none'
})
})
}
} else if (this.loginType === 'account') {
// 账号密码登录:先检查账号是否锁定
const {
isLocked,
remainingTime
} = this.checkAccountLocked(this.phone);
if (isLocked) {
// 计算小时、分钟、秒,优化提示文案
const hours = Math.floor(remainingTime / 3600);
const minutes = Math.floor((remainingTime % 3600) / 60);
const seconds = remainingTime % 60;
let lockTip = '';
if (hours > 0) {
lockTip = `该账号因多次密码错误已锁定,请${hours}小时${minutes}分钟后重试`;
} else if (minutes > 0) {
lockTip = `该账号因多次密码错误已锁定,请${minutes}分钟${seconds}秒后重试`;
} else {
lockTip = `该账号因多次密码错误已锁定,请${seconds}秒后重试或找回密码`;
}
uni.showToast({
title: lockTip,
icon: 'none',
duration: 3000
});
return false;
}
// 密码格式校验
let reg = that.validatePassword(that.password);
if (!this.phone) {
uni.showToast({
title: "请输入手机号",
icon: 'none'
})
return
} else if (!that.password) {
uni.showToast({
title: '请输入密码',
icon: 'none'
});
return false;
} else if (!reg) {
uni.showToast({
title: '请输入正确的密码6-20位字母 + 数字/符号组合)',
icon: 'none'
});
return false;
} else if (!that.isAgree) {
uni.showToast({
title: '请先阅读并同意《用户注册协议》与《隐私政策》',
icon: 'none'
});
return false;
} else {
2026-03-25 13:29:04 +08:00
request.post('/sj/accountlogin', {
2026-03-24 11:45:13 +08:00
account: that.phone,
password: that.password,
deviceid: getApp().globalData.deviceid
}).then(result => {
2026-03-25 13:29:04 +08:00
console.log(result)
if (result.code == 200) {
2026-03-24 11:45:13 +08:00
// 登录成功:清除该账号的密码错误次数和锁定缓存
that.clearPwdLockCache(that.phone);
2026-03-25 13:29:04 +08:00
uni.setStorageSync('accessToken', result.data.access_token);
uni.setStorageSync('refreshToken', result.data.refresh_token);
uni.setStorageSync('refresh_token_expries', result.data.refresh_token_expries);
2026-03-24 11:45:13 +08:00
uni.setStorageSync('loginAccount', that.phone); // 保存登录的手机号
2026-03-25 13:34:16 +08:00
registPush();
that.messageUpudateNum()
2026-03-24 11:45:13 +08:00
that.closeWindow();
} else {
// 登录失败判断是否为密码错误假设后端返回msg包含“密码错误”关键词可根据实际接口调整
2026-03-25 13:29:04 +08:00
if (result.code === 502) {
2026-03-24 11:45:13 +08:00
const currentErrorCount = that.getPwdErrorCount(that.phone);
const maxErrorCount = 5;
const remainingCount = maxErrorCount - currentErrorCount - 1;
if (remainingCount > 0) {
// 未达最大错误次数:更新错误次数,提示剩余次数
that.updatePwdErrorCount(that.phone, currentErrorCount + 1);
uni.showToast({
title: `密码错误,还有${remainingCount}次机会`,
icon: 'none',
duration: 2000
});
} else {
// 达最大错误次数:锁定账号,提示锁定信息
that.updatePwdErrorCount(that.phone, maxErrorCount);
that.setPwdLock(that.phone);
uni.showToast({
title: '该账号因多次密码错误已锁定请1小时后重试或找回密码',
icon: 'none',
duration: 3000
});
}
} else {
// 其他错误(如账号不存在等),直接提示
uni.showToast({
title: result.msg,
icon: 'none'
});
}
}
}).catch(err => {
uni.showToast({
title: err.msg,
icon: 'none'
})
})
}
}
},
// 密码格式校验
validatePassword(password) {
// 正则说明:
// (?=.*[A-Za-z]):必须包含至少一个字母
// (?=.*\d):必须包含至少一个数字
// [\w\W]{6,20}匹配任意字符字母、数字、符号、中文等长度6-20位
// 若想限制仅允许常见符号(排除中文),可改用:/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*()_+\-=\[\]{}|;':",./<>?]{6,20}$/
const reg = /^(?=.*[A-Za-z])(?=.*\d)[\w\W]{6,20}$/;
return reg.test(password);
},
quitLoginPage() {
aLiSDKModule.quitLoginPage();
this.maskShow = true
},
// 阿里云一键登录原生弹窗逻辑
_presentLoginController(config) {
let that = this
uni.showLoading({
mask: true,
});
// 调用该接口首先会弹起授权页点击授权页的登录按钮获取Token
aLiSDKModule.getLoginToken(
5000,
config,
tokenResult => {
uni.hideLoading();
console.log("tokenResult:", JSON.stringify(tokenResult));
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
if ("600001" == tokenResult.resultCode) {
console.log("授权页拉起成功");
this.maskShow = false
aLiSDKModule.setCheckboxIsChecked(false);
} else if ("600000" == tokenResult.resultCode) {
console.log("获取Token成功接下来拿着结果里面的Token去服务端换取手机号码SDK服务到此结束");
2026-03-25 13:29:04 +08:00
request.post('/sj/phoneLogin', {
2026-03-24 11:45:13 +08:00
token: tokenResult.token,
}).then(result => {
if (result.code == 200) {
// 登录成功:
uni.setStorageSync('accessToken', result.data.access_token);
uni.setStorageSync('refreshToken', result.data.refresh_token);
uni.setStorageSync('refresh_token_expries', result.data
.refresh_token_expries);
uni.setStorageSync('loginAccount', result.data.account); // 保存登录的手机号
2026-03-25 13:34:16 +08:00
registPush();
that.messageUpudateNum()
2026-03-24 11:45:13 +08:00
that.closeWindow();
} else {
uni.showToast({
title: result.msg,
icon: 'none'
});
}
}).catch(err => {
uni.showToast({
title: err.msg,
icon: 'none'
})
}).finally(() => {
//手动关闭授权页
this.quitLoginPage();
})
} else {
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
if (tokenResult.msg) {
//手动关闭授权页
2026-03-25 13:29:04 +08:00
if(!this.isFirst){
2026-03-24 11:45:13 +08:00
uni.showToast({
title: tokenResult.msg,
icon: 'none'
})
}
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
}
this.maskShow = true
this.quitLoginPage();
}
this.isFirst = false
2026-03-25 13:29:04 +08:00
2026-03-24 11:45:13 +08:00
},
clickResult => {
console.log(JSON.stringify(clickResult));
switch (clickResult.resultCode) {
case "700000":
this.closeWindow()
this.quitLoginPage();
console.log("用户点击了返回");
break
case "700001":
this.quitLoginPage();
console.log("用户切换其他登录方式");
break
case "700002":
console.log("用户点击登录按钮");
if (!clickResult.result.isChecked) {
if (!config.uiConfig.setPrivacyAlertIsNeedShow) {
plus.nativeUI.toast("请同意服务条款");
}
}
break;
case "700003":
console.log("用户点击checkBox");
break
case "700004":
console.log("用户点击协议");
break
case "700010":
this.closeWindow()
console.log("用户点击返回按钮Android专用");
this.quitLoginPage();
break
case "700011":
this.closeWindow()
console.log("用户使用物理返回键Android专用");
this.quitLoginPage();
break
case "700006":
console.log("弹出二次授权弹窗");
break
case "700007":
console.log("关闭二次授权弹窗");
break
case "700008":
console.log("点击二次授权弹窗确认按钮");
break
case "700009":
console.log("点击二次授权弹窗协议");
break
}
},
customUiResult => {
console.log("点击了自定义控件 " + JSON.stringify(customUiResult));
if ("close" == customUiResult.widgetId) {
aLiSDKModule.quitLoginPage();
} else if ("closePrivacyAlert" == customUiResult.widgetId) {
aLiSDKModule.closePrivactAlertView();
} else {
plus.nativeUI.toast("点击了自定义按钮widgetId" + customUiResult.widgetId);
}
});
},
/**
* 手机号输入实时校验
* 1. 过滤非数字字符兼容部分设备type="number"仍可输入非数字的情况
* 2. 实时校验11位数字非11位显示错误提示
*/
handlePhoneInput() {
// 过滤非数字字符(账号密码登录时账号可能为手机号,统一过滤)
this.phone = this.phone.replace(/\D/g, '');
if (this.loginType === 'code' && this.phone.length > 0 && !uni.$uv.test.mobile(this.phone)) {
this.phoneError = '请输入正确的手机号';
} else if (this.loginType === 'account' && this.phone.length > 0 && this.phone.length < 6) {
this.phoneError = '请输入正确的账号';
} else {
this.phoneError = '';
}
}
}
}
</script>
<style lang="scss">
/* 蒙层:全屏半透明,底部对齐弹窗 */
page {
width: 100%;
height: 100%;
background: transparent;
}
.login-mask {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
// width: 100%;
// height: 100%;
// position: relative;
// width: 100vw;
// height: 100vh;
background: transparent;
// display: flex;
// flex-direction: column;
// justify-content: flex-end;
z-index: 999;
overflow: hidden;
}
/* 底部弹窗宽度100%,顶部圆角 */
.login-popup {
position: absolute;
left: 0;
right: 0;
bottom: 0;
// width: 100%;
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
padding: 42rpx 66rpx 100rpx 66rpx;
box-sizing: border-box;
// position: relative;
}
/* 弹窗头部:标题+关闭按钮 */
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 55rpx;
.popup-title {
width: 100%;
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
}
.popup-close {
width: 40rpx;
height: 40rpx;
position: absolute;
top: 37rpx;
right: 40rpx;
}
/* 登录选项卡 */
.login-tabs {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 55rpx;
padding: 0 96rpx;
.tab-line {
width: 2rpx;
height: 20rpx;
background: #D8D8D8;
border-radius: 1rpx;
}
.tab-item {
font-weight: 400;
font-size: 26rpx;
color: #333333;
line-height: 37rpx;
text-align: center;
font-style: normal;
position: relative;
padding-bottom: 6rpx;
&.active {
color: #f00;
&::after {
content: '';
position: absolute;
bottom: 0rpx;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
border-radius: 2rpx;
height: 4rpx;
background: #f00;
}
}
}
}
/* 输入区域 */
.input-area {
margin-bottom: 80rpx;
.input-item {
display: flex;
align-items: center;
box-sizing: border-box;
width: 618rpx;
height: 98rpx;
background: #F5F5F5;
border-radius: 49rpx;
padding: 28rpx 24rpx 28rpx 30rpx;
margin-bottom: 30rpx;
position: relative;
.look-icon {
width: 37rpx;
position: absolute;
top: 50%;
right: 30rpx;
transform: translateY(-50%);
}
.input-icon-phone {
width: 30rpx;
height: 32rpx;
margin-right: 19rpx;
}
.input-icon-password {
width: 32rpx;
height: 38rpx;
margin-right: 19rpx;
}
.input-text {
flex: 1;
font-size: 30rpx;
height: 40rpx;
color: #333;
}
.get-code-btn {
font-weight: 400;
font-size: 30rpx;
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
line-height: 42rpx;
text-align: center;
font-style: normal;
}
.running {
color: #999999;
}
.noPhone {
color: #999999;
}
.eye-icon {
font-size: 30rpx;
color: #666;
margin-left: 15rpx;
}
}
// 错误提示样式
.input-error {
display: block;
font-size: 24rpx;
2026-06-02 11:39:23 +08:00
color: #FF4767;
2026-03-24 11:45:13 +08:00
margin-left: 30rpx;
margin-top: -20rpx;
margin-bottom: 20rpx;
line-height: 1.2;
}
}
/* 登录按钮:匹配示例的粉/红切换 */
.login-btn {
width: 590rpx;
height: 98rpx;
background: rgba(232, 16, 30, 0.4);
border-radius: 50rpx;
font-weight: 500;
font-size: 30rpx;
color: #FFFFFF;
text-align: center;
font-style: normal;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
&.active {
background: rgba(232, 16, 30, 1);
}
}
.agree {
display: flex;
flex-flow: row nowrap;
flex-direction: row;
align-items: flex-start;
.agree-btn {
transform: translateY(4rpx);
margin-right: 8rpx;
}
.agree-card {
.agreement {
font-weight: 400;
font-size: 26rpx;
color: rgba(232, 16, 30, 1);
line-height: 37rpx;
margin: 0 10rpx;
}
.agree-text {
font-weight: 400;
font-size: 26rpx;
color: #898989;
line-height: 37rpx;
}
}
}
2026-03-25 13:29:04 +08:00
.uni-input-placeholder{
2026-03-24 11:45:13 +08:00
font-weight: 400;
font-size: 30rpx;
color: #BCC0C0;
line-height: 42rpx;
text-align: left;
font-style: normal;
}
</style>