Files
ys-app/src/pages/login/login.vue

482 lines
15 KiB
Vue
Raw Normal View History

2025-07-22 11:21:01 +08:00
<template>
<view class="container">
<view class="login-con">
<view :style="{height: navBarPaddingTop + 'px'}"></view>
<image mode="aspectFit" src="../../static/images/pic-logo.png" class="login-logo"></image>
<view class="login-title">欢迎来到718友晟</view>
<view class="login-tab">
<customTabs v-model="activeTab" :tabs="tabList" :modelValue="activeTab">
<!-- 验证码登录 -->
<view v-show="activeTab === 0">
<view class="login-form">
<uni-forms ref="form" :model="formData" :rules="rules" label-position="top">
<uni-forms-item label="手机号" required name="phone">
<view class="code-con">
<uni-icons custom-prefix="iconfont" color="#239FDF" type="icon-phone" size="20"></uni-icons>
<uni-easyinput type="number" :inputBorder="false"
v-model="formData.phone" placeholder="请输入手机号"
maxlength="11"
/>
</view>
</uni-forms-item>
<uni-forms-item label="验证码" required name="verifyCode">
<view class="code-con">
<uni-icons custom-prefix="iconfont" color="#239FDF" type="icon-code" size="20"></uni-icons>
<uni-easyinput type="number" :inputBorder="false"
v-model="formData.verifyCode" placeholder="请输入验证码"
maxlength="6" style="width:190rpx"
/>
<button type="primary" plain @click="getCode" size="mini"
:loading="codeLoading" :disabled="codeDisabled" class="btn-plain"
>{{codeText}}</button>
</view>
</uni-forms-item>
</uni-forms>
</view>
</view>
<!-- 账号登录 -->
<view v-show="activeTab === 1">
<view class="login-form">
<uni-forms ref="form2" :model="formData2" :rules="rules2" label-position="top">
<uni-forms-item label="用户名" required name="username">
<view class="code-con">
<uni-easyinput prefixIcon="person" :inputBorder="false"
v-model="formData2.username" placeholder="请输入用户名"
/>
</view>
</uni-forms-item>
<uni-forms-item label="密码" required name="password" class="password">
<view class="code-con">
<uni-easyinput prefixIcon="locked" type="password" :inputBorder="false"
v-model="formData2.password" placeholder="请输入登录密码"
/>
</view>
</uni-forms-item>
</uni-forms>
</view>
</view>
</customTabs>
<view class="agreen-con">
<uni-icons v-if="agreeChecked" type="checkbox-filled" color="#02C74C" size="25" @click="agreeCheck"></uni-icons>
<uni-icons v-else type="circle" color="#bfbfbf" size="25" @click="agreeCheck"></uni-icons>
<view class="agreen-xy">
我已阅读并接受<text @click="agreeVisable(1)">服务条款</text><text @click="agreeVisable(2)">隐私保护协议</text>
</view>
</view>
<button type="primary" class="btn-submit" @click="submitForm" :loading="btnLoading" :disabled="btnLoading"> </button>
</view>
</view>
<view class="login-bottom"></view>
</view>
</template>
<script setup>
import { ref,onMounted } from 'vue';
2025-08-01 18:12:36 +08:00
import { onLoad } from '@dcloudio/uni-app';
2025-07-22 11:21:01 +08:00
import customTabs from '@/components/customTabs.vue';
import {isPhoneNumber} from '@/utils/validate';
2025-07-31 18:24:41 +08:00
import { getVerifyCode,login } from '@/api/auth';
2025-07-22 11:21:01 +08:00
import cache from '@/utils/cache';
import { AGREEWELCOME_KEY } from '@/enums/cacheEnums';
import { getNavBarPaddingTop} from '@/utils/system.js'
import { useUserStore } from '@/stores/user';
2025-08-12 13:43:21 +08:00
import encryptObj from '@/utils/encrypt.js'
import { CLIENT_ID } from '@/enums/cacheEnums';
2025-08-14 20:26:26 +08:00
import {showAlert} from '@/utils/message.js'
2025-08-12 13:43:21 +08:00
2025-08-14 20:26:26 +08:00
// #ifdef APP-PLUS
// 获取 存储手机的module
const safeSave = uni.requireNativePlugin("Tm-TmSafeSaveFileModule");
// #endif
2025-07-22 11:21:01 +08:00
const userStore = useUserStore()
2025-08-01 18:12:36 +08:00
onLoad(async(opt) => {
uni.setStorageSync('page_cache',true);
})
2025-07-22 11:21:01 +08:00
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop();
})
const activeTab = ref(1);//默认账号登录
const tabList = ['验证码登录', '账号登录'];
// 验证码登录
const form = ref(null);
const formData = ref({
2025-08-12 16:19:19 +08:00
phone: '',
verifyCode: '',
2025-07-22 11:21:01 +08:00
loginType:0
});
const rules = {
phone: {
rules: [
{ required: true, errorMessage: '请输入手机号' },
{
validateFunction:function(rule,value,data,callback){
if (!isPhoneNumber(value)) {
callback('请正确输入手机号')
}
return true
}
}
]
},
verifyCode: {
rules: [
{ required: true, errorMessage: '请输入验证码' },
{ pattern: /^[0-9]{6}$/, errorMessage: '验证码必须是6位数字' }
]
}
};
let codeLoading = ref(false);
let codeDisabled = ref(false);
let codeText = ref('发送验证码');
let countdown = ref(60);
let timer = ref(null);
let backCode=ref(null)
// 验证特定字段
const validateField = async (fieldName) => {
try {
await form.value.validateField(fieldName);
return true;
} catch (e) {
return false;
}
};
// 发送验证码
let getCode=async()=> {
// 1.验证手机号
const isValid = await validateField('phone');
if (isValid) {
codeLoading.value = true;
codeDisabled.value = true;
// 2.调用获取短信验证码接口
let res = await getVerifyCode({phone:formData.value.phone})
backCode.value = res.code;
codeText.value = `发送中...`;
// 3.开始倒计时
startCountdown();
}
// showAlert("验证码已发送!",'提示',false);
}
let startCountdown=()=>{
if (timer.value) return;
timer.value = setInterval(() => {
if (countdown.value > 0) {
countdown.value--;
codeText.value = `${countdown.value}`;
} else {
resetCountdown();
}
codeLoading.value = false;
}, 1000);
}
let resetCountdown =()=>{
clearInterval(timer.value);
timer.value = null;
countdown.value = 60;
codeText.value = '发送验证码';
codeDisabled.value = false;
}
// 账号登录
const form2 = ref(null);
const formData2 = ref({
2025-08-12 16:19:19 +08:00
username: '',
password: '',
2025-07-22 11:21:01 +08:00
loginType:1
});
const rules2 = {
username: {
rules: [
{ required: true, errorMessage: '请输入用户名' },
2025-08-12 13:43:21 +08:00
// { minLength: 3, maxLength: 10, errorMessage: '用户名长度在3到10个字符之间' }
2025-07-22 11:21:01 +08:00
]
},
password: {
rules: [
{ required: true, errorMessage: '请输入密码' },
2025-08-12 13:43:21 +08:00
// { pattern: /^[a-zA-Z0-9]{6,12}$/, errorMessage: '密码必须是6-12位字母或数字' }
2025-07-22 11:21:01 +08:00
]
}
};
// 协议
let agreeChecked = ref(true);
// 勾选同意协议 存入缓存
const agreeCheck = ()=>{
agreeChecked.value = !agreeChecked.value;
if (agreeChecked.value) {
cache.set(AGREEWELCOME_KEY, agreeChecked.value)
} else {
cache.remove(AGREEWELCOME_KEY);
}
}
const btnLoading=ref(false)
// 登录提交
const submitForm = () => {
btnLoading.value = true;
console.log("submitForm=>activeTab=>",activeTab.value)
// 手机获取验证码登录
if(activeTab.value===0){
form.value.validate().then(async param => {
// 2.调用登录接口
2025-08-12 16:19:19 +08:00
param.loginType = activeTab.value;
2025-07-22 11:21:01 +08:00
let res = await login(param)
// 3.登录后存储token
userStore.login(res);
uni.switchTab({ url: '/pages/home/home' })
btnLoading.value = false;
}).catch(err => {
console.log('表单错误00:', err);
btnLoading.value = false;
});
}else if(activeTab.value===1){
// 用户名和密码登录
form2.value.validate().then(async param => {
2025-08-12 13:43:21 +08:00
param.loginType = activeTab.value;
2025-08-12 16:19:19 +08:00
let clientId = `${CLIENT_ID || "2"}`;
2025-08-14 20:26:26 +08:00
param.clientId = clientId;
2025-08-12 16:19:19 +08:00
let password = encryptObj.Encrypt(param.username + clientId + "," + param.password);
2025-08-12 13:43:21 +08:00
param.password = password;
2025-08-14 20:26:26 +08:00
// #ifdef H5
2025-08-13 18:15:04 +08:00
param.uniqCode = uni.getStorageSync('app_device_id');//先从缓存取之后处理 '7f47cfb4-59e2-4cb9-ac46-9da5e23c4de2'//
2025-07-22 11:21:01 +08:00
let res = await login(param);
userStore.login(res);
uni.switchTab({ url: '/pages/home/home' })
btnLoading.value = false;
2025-08-14 20:26:26 +08:00
// #endif
// #ifdef APP-PLUS
try {
// 授权设备存储
// let systemInfo = uni.getSystemInfoSync();
// let result = {}
// if(systemInfo.osAndroidAPILevel >=30){
// let permissions = ['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO', 'android.permission.READ_MEDIA_AUDIO'];
// result = await requestAndroidPermissionAsync(permissions);
// }else{
// let permissions = ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'];
// result = await requestAndroidPermissionAsync(permissions);
// }
// if (result.granted) {
// console.log('所有权限已授予')
safeSave.getSafeFile({ "key": "app_device_id" }, res3 => {
if (res3.code == 1) {
let deviceId = res3.data;
showAlert("读取成功=>"+deviceId);
param.uniqCode = deviceId;
login(param).then(res=>{
userStore.login(res);
uni.switchTab({ url: '/pages/home/home' })
btnLoading.value = false;
})
} else {
showAlert('读取失败:'+res3.msg)
btnLoading.value = false;
}
})
// } else {
// showAlert("您还没对设备授权,请授权!")
// btnLoading.value = false;
// }
} catch (error) {
console.error('出错:', error);
showAlert('出错:', error)
btnLoading.value = false;
}
// #endif
2025-07-22 11:21:01 +08:00
}).catch(err => {
console.log('表单错误11:', err);
btnLoading.value = false;
});
}
};
</script>
<style scoped>
.container {
height: 100vh;
}
.login-con {
position: relative;
background: url('@/static/images/login-bg.png') no-repeat;
background-size: 750rpx 633rpx;
width: 750rpx;
height: 633rpx;
margin:0 auto;
}
.login-con .login-logo {
width: 109rpx;
height: 91rpx;
margin: 84rpx 0 0 70rpx;
}
.login-con .login-title {
font-size: 56rpx;
color: #fff;
font-weight: bold;
margin: 0 0 35rpx 70rpx;
}
:deep(.login-tab .tabs-header) {
background: none !important;
border-bottom: none !important;
width: 438rpx;
margin: 0 auto;
}
:deep(.login-tab .tab-item) {
color: rgba(255, 255, 255, 0.5);
font-size: 36rpx;
font-weight: bold;
}
:deep(.tab-item.active) {
color: #fff;
}
:deep(.tab-item.active::after) {
background-color: #fff;
width: 100rpx;
height: 8rpx;
/* display: none; */
}
.login-form {
background: #fff;
border-radius: 30rpx;
padding: 30rpx 50rpx 15rpx;
width:510rpx;
margin: 20rpx auto 0;
box-shadow: 0rpx -1rpx 16rpx 0rpx rgba(0, 0, 0, 0.1);
}
.code-con{
padding:0 0 10rpx;
margin:0 auto;
display: flex;
align-items: center;
color:#919191;
border-bottom: 1px solid #E7E7E7;
}
:deep(.uni-forms-item__label){
color:#239FDF;
font-weight: bold;
font-size:28rpx;
padding:0 !important;
}
:deep(.uni-easyinput__content){
color:#333;
font-weight: bold;
font-size:32rpx;
}
:deep(.uni-easyinput__content) .uni-icons{
font-weight: normal;
font-size:24rpx;
color:#239FDF !important;
}
:deep(.uni-input-placeholder){
background:none;
font-weight:normal;
color:#BFBFBF;
font-size:28rpx;
}
:deep(.uni-forms-item__error){
padding-left:60rpx;
}
:deep(.uni-forms-item .is-required){
display: none;
}
/* 使用深度选择器 */
:deep(.uni-easyinput__content .uni-icons.uniui-clear) {
color: #BFBFBF !important;
font-weight: normal !important;
font-size:40rpx !important;
}
:deep(.uni-easyinput__content .uni-icons.uniui-clear::before){
content: "\e66c" !important;
}
.agreen-con{
margin-top:40rpx;
color:#919191;
font-size:26rpx;
display: flex;
align-items: center;
justify-content: center;
}
.agreen-con .agreen-xy{
margin-left:10rpx;
}
.agreen-con text{
color:#05A3F4;
}
.btn-plain{
color: #05A3F4 !important;
border: 1px solid #05A3F4 !important;
/* padding:0 30rpx; */
font-size:24rpx;
/* line-height: 2.1; */
/* width:230rpx; */
}
.btn-submit{
width:490rpx;
height:88rpx;
line-height: 88rpx;
background-color:#05A3F4 !important;
margin:80rpx auto 0;
font-size:36rpx;
border-radius: 44rpx;
}
.btn-submit::after{
border:none;
}
.login-bottom{
position: absolute;
background: url('../../static/images/login-txt.png');
background-size:654rpx 121rpx;
width:654rpx;
height: 121rpx;
bottom:30rpx;
left:50%;
margin-left:-327rpx;
}
2025-07-31 18:24:41 +08:00
.slider-con{
position: absolute;
top:0;
width:100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.1);
}
2025-07-22 11:21:01 +08:00
</style>