2025-10-14 17:46:23 +08:00

794 lines
17 KiB
Vue

<template>
<uni-nav-bar title="登录" fixed color="#8B2316" height="160rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
<view class="login-container">
<!-- 主要内容区域 -->
<view class="main-content" >
<!-- 应用图标和名称 -->
<view class="app-info">
<view class="app-icon">
<up-image :src="logo" width="164rpx" height="167rpx" ></up-image>
</view>
<text class="app-name">专家端</text>
</view>
<!-- 手机号输入 -->
<view class="input-section">
<view class="input-group">
<up-image :src="phoneImg" width="36rpx" height="36rpx" ></up-image>
<input
class="phone-input"
type="number"
placeholder="请输入手机号"
v-model="phoneNumber"
maxlength="11"
/>
</view>
<!-- 验证码输入 -->
<view class="input-group">
<up-image :src="smsImg" width="36rpx" height="36rpx" ></up-image>
<view class="code-input-container">
<input
class="code-input"
type="number"
placeholder="请输入验证码"
v-model="smsCode"
maxlength="6"
/>
<view class="send-code-btn" @click="sendSmsCode" :class="{ disabled: isCountingDown }">
<text class="send-code-text">{{ countdownText }}</text>
</view>
</view>
</view>
</view>
<!-- 登录按钮 -->
<view class="login-button" @click="onSmsLogin" :class="{ disabled: !canLogin }">
<text class="button-text">登录</text>
</view>
<view class="sms-login" @click="goPwdLogin">
<text class="sms-text">账号密码登录</text>
</view>
<!-- 其他登录方式 -->
<view class="other-login">
<!-- 微信登录 -->
<view class="wechat-login" @click="onWechatLogin">
<view class="wechat-icon">
<up-image :src="wxImg" width="100rpx" height="100rpx" ></up-image>
</view>
<text class="wechat-text">微信登录</text>
</view>
</view>
</view>
<!-- 底部协议 -->
<view class="agreement-section">
<view class="agreement-content">
<view class="checkbox" @click="toggleAgreement">
<up-image :src="checkImg" width="45rpx" height="45rpx" v-if="!isAgreed"></up-image>
<up-image :src="checkOnImg" width="45rpx" height="45rpx" v-else></up-image>
</view>
<view class="agreement-text">
<text class="text-content">我已阅读并同意</text>
<text class="link-text" @click="onServiceTerms">中国移动认证服务条款</text>
<text class="text-content"></text>
<text class="link-text" @click="onPrivacyPolicy">肝胆相照隐私政策</text>
<text class="text-content"></text>
<text class="link-text" @click="onUserAgreement">肝胆相照用户服务协议</text>
<text class="text-content">并使用本机号码登录并注册</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { onShow, onLoad } from "@dcloudio/uni-app";
import api from "@/api/api.js"
import sortObj from "@/utils/sort"
import version from "@/utils/version.js"
import client_type from "@/utils/client_type.js"
import logo from "@/static/icon_login_logo.png"
import wxImg from "@/static/weixin_login.png"
import checkImg from "@/static/login_new_unselect.png"
import checkOnImg from "@/static/login_new_select.png"
import phoneImg from "@/static/phone.png"
import smsImg from "@/static/sms.png"
import pwdImg from "@/static/pwd.png"
import { Base64 } from 'js-base64';
import { md5 } from 'js-md5';
import BASE_URL from "@/utils/config.js";
import my_api from "@/api/my_api.js";
import { createSignedPostData } from '@/utils/sign.js'
import navTo from "@/utils/navTo"
const customStyle = reactive({
height: "100rpx",
fontSize: "36rpx",
backgroundColor:'#8B2316',
borderRadius: '50rpx'
});
// 协议同意状态
const isAgreed = ref(false);
// 手机号和验证码
const phoneNumber = ref('');
const smsCode = ref('');
// 倒计时相关
const isCountingDown = ref(false);
const countdown = ref(0);
const countdownText = computed(() => {
if (isCountingDown.value) {
return `${countdown.value}s后重发`;
}
return '获取验证码';
});
// 是否可以登录
const canLogin = computed(() => {
return isAgreed.value && phoneNumber.value.length === 11 && smsCode.value.length === 6;
});
const goPwdLogin=()=>{
uni.redirectTo({
url:'/pages_app/pwdLogin/pwdLogin'
})
}
const alertAgree=()=>{
uni.showToast({
title: '请先同意用户协议',
icon:'none'
})
};
// 发送验证码
const sendSmsCode = async () => {
if (isCountingDown.value) {
return;
}
if (!phoneNumber.value) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
});
return;
}
if (!/^1[3-9]\d{9}$/.test(phoneNumber.value)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
});
return;
}
try {
const timeResult = await api.getTimeStamp();
let timestamp=timeResult.data.timestamp.toString();
let postData={
mobile: phoneNumber.value,
version:version,
user_uuid:'',
client_type:'A', //client_type,
timestamp:timestamp,
type:2,
}
let obj=sortObj(postData)
let str1 = formatString(obj).replace(" ",'');
let str2=str1+md5(timestamp);
let str3=md5(str2);
let sign=Base64.encode(str3).replace('+', '-').replace('/', '_').replaceAll("=", "");
// 调用真实API获取验证码
const result = await api.getCode(postData,{
sign:sign
});
console.log(result);
if (result.code === 200) {
uni.showToast({
title: '验证码已发送',
icon: 'none'
});
// 开始倒计时
isCountingDown.value = true;
countdown.value = 60;
const timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(timer);
isCountingDown.value = false;
}
}, 1000);
} else {
uni.showToast({
title: result.data.message || '发送失败',
icon: 'none'
});
}
} catch (error) {
console.error('发送验证码失败:', error);
uni.showToast({
title: '发送验证码失败,请重试',
icon: 'none'
});
}
};
const formatString=(obj)=>{
let str=''
for (var key in obj) {
if(str){
str+=key+obj[key]
}else{
str=key+obj[key]
}
}
console.log(str);
return str
}
// 短信验证码登录
const onSmsLogin = async () => {
if (!isAgreed.value) {
uni.showToast({
title: '请先同意相关协议',
icon: 'none'
});
return;
}
if (!phoneNumber.value) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
});
return;
}
if (!/^1[3-9]\d{9}$/.test(phoneNumber.value)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
});
return;
}
if (!smsCode.value) {
uni.showToast({
title: '请输入验证码',
icon: 'none'
});
return;
}
if (smsCode.value.length !== 6) {
uni.showToast({
title: '请输入6位验证码',
icon: 'none'
});
return;
}
try {
uni.showLoading({
title: '登录中...'
});
const res = uni.getSystemInfoSync();
const timeResult = await api.getTimeStamp();
let timestamp=timeResult.data.timestamp.toString();
let postData={
mobile: phoneNumber.value,
version:version,
user_uuid:'',
current_spec:res.model,
sms:smsCode.value,
timestamp:timestamp,
client_type:'A'
}
let obj=sortObj(postData)
let str1 = formatString(obj).replace(" ",'');
console.log("str1:"+str1)
let str2=str1+md5(timestamp);
console.log("str2:"+str2)
let str3=md5(str2);
console.log("str3:"+str3)
let sign=Base64.encode(str3).replace('+', '-').replace('/', '_').replaceAll("=", "");
console.log("sign:"+sign)
// 调用真实API进行短信登录
const result = await api.smsLogin(postData,{
sign:sign
});
uni.hideLoading();
console.log(result)
if (result.code == 1) {
// 保存用户信息到本地存储
if (process.env.UNI_PLATFORM == "h5") {
if (window.location.href.indexOf('dev') > -1) {
uni.setStorageSync('AUTH_TOKEN_App',result.access_token);
uni.setStorageSync('AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
} else {
uni.setStorageSync('DEV_AUTH_TOKEN_App', result.access_token);
uni.setStorageSync('DEV_AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('DEV_AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
}
} else if(process.env.UNI_PLATFORM == "mp-weixin") {
const {
envVersion
} = uni.getAccountInfoSync().miniProgram;
if (envVersion == "release") {
uni.setStorageSync('AUTH_TOKEN_App',result.access_token);
uni.setStorageSync('AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
} else {
uni.setStorageSync('DEV_AUTH_TOKEN_App', result.access_token);
uni.setStorageSync('DEV_AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('DEV_AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
}
}else{
if (BASE_URL.indexOf('dev') == -1) {
uni.setStorageSync('AUTH_TOKEN_App',result.access_token);
uni.setStorageSync('AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
} else {
uni.setStorageSync('DEV_AUTH_TOKEN_App', result.access_token);
uni.setStorageSync('DEV_AUTH_YX_ACCID_App', result.YX_accid);
uni.setStorageSync('DEV_AUTH_YX_TOKEN_App', result.YX_token);
uni.setStorageSync('userInfo', result.data);
}
}
const app = getApp()
app.initNim({ account:result.YX_accid, token: result.YX_token })
uni.showToast({
title: '登录成功',
icon: 'none'
});
// 跳转到首页
setTimeout(() => {
uni.redirectTo({
url: '/pages/index/index'
});
},0);
} else {
uni.showToast({
title: result.data.message || '登录失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('登录失败:', error);
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
});
}
};
// 微信登录
const onWechatLogin = () => {
if (!isAgreed.value) {
uni.showToast({
title: '请先同意相关协议',
icon: 'none'
});
return;
}
console.log('微信登录 11');
uni.login({
provider: 'weixin',
success: function (loginRes) {
// 登录成功
uni.getUserInfo({
provider: 'weixin',
success: async function(info) {
// 获取用户信息成功, info.authResult保存用户信息
const wechat_user_info = {
openid:info.userInfo.openId,
unionid:info.userInfo.unionId,
nickname:info.userInfo.nickName,
}
const { postData, sign } = await createSignedPostData({
wechat_user_info: JSON.stringify(wechat_user_info),
current_spec: "uniapp"
})
my_api.expertWxLogin(postData,{
sign:sign
}).then(res=>{
uni.showToast({
title: '登录成功',
icon: 'success'
})
if (BASE_URL.indexOf('dev') == -1) {
uni.setStorageSync('AUTH_TOKEN_App',res.data.access_token);
uni.setStorageSync('AUTH_YX_ACCID_App', res.data.YX_accid);
uni.setStorageSync('AUTH_YX_TOKEN_App', res.data.YX_token);
uni.setStorageSync('userInfo', res.data);
} else {
uni.setStorageSync('DEV_AUTH_TOKEN_App', res.data.access_token);
uni.setStorageSync('DEV_AUTH_YX_ACCID_App', res.data.YX_accid);
uni.setStorageSync('DEV_AUTH_YX_TOKEN_App', res.data.YX_token);
uni.setStorageSync('userInfo', res.data);
}
// 跳转到首页
navTo({
url: '/pages/index/index'
})
}).catch(err=>{
console.log(err)
uni.showToast({
title: res.data.msg || '登录失败',
icon: 'none'
})
})
}
})
},
fail: function (err) {
// 登录授权失败
// err.code是错误码
console.log(err)
}
});
};
// 切换协议同意状态
const toggleAgreement = () => {
isAgreed.value = !isAgreed.value;
};
// 查看服务条款
const onServiceTerms = () => {
console.log('查看服务条款');
uni.navigateTo({
url: '/pages_app/agreement/service-terms'
});
};
// 查看隐私政策
const onPrivacyPolicy = () => {
console.log('查看隐私政策');
uni.navigateTo({
url: '/pages_app/agreement/privacy-policy'
});
};
// 查看用户协议
const onUserAgreement = () => {
console.log('查看用户协议');
uni.navigateTo({
url: '/pages_app/agreement/user-agreement'
});
};
// 页面加载
onLoad(() => {
console.log('短信登录页面加载完成');
});
// 页面显示
onShow(() => {
console.log('短信登录页面显示');
});
// 组件挂载后
onMounted(() => {
console.log('短信登录页面组件已挂载');
});
</script>
<style>
.login-container {
min-height: calc(100vh - 160rpx);
background-color: #ffffff;
display: flex;
flex-direction: column;
}
/* 主要内容区域 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 100rpx 60rpx 0;
}
/* 应用信息 */
.app-info {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 80rpx;
}
.app-icon {
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
position: relative;
}
.app-name {
font-size: 32rpx;
font-weight: bold;
color: #8B2316;
}
/* 输入区域 */
.input-section {
width: 100%;
margin-bottom: 60rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
.input-group {
display: flex;
padding-left: 20rpx;
align-items: center;
}
.input-group:first-child{
border-bottom: 2rpx solid #e5e5e5;
}
.input-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
}
.phone-input {
width: 100%;
height: 80rpx;
padding: 0 40rpx;
font-size: 32rpx;
background-color: #fff;
box-sizing: border-box;
}
.code-input-container {
display: flex;
align-items: center;
gap: 20rpx;
}
.code-input {
flex: 1;
height: 80rpx;
border-radius: 50rpx;
padding: 0 40rpx;
font-size: 32rpx;
background-color: #fff;
box-sizing: border-box;
}
.send-code-btn {
width: 200rpx;
height: 44rpx;
background-color: #fff;
border-radius: 50rpx;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 2rpx solid #8B2316;
transition: background-color 0.2s ease;
}
.send-code-btn.disabled {
background-color: #ccc;
cursor: not-allowed;
}
.send-code-text {
font-size: 25rpx;
color:#8B2316;
}
/* 登录按钮 */
.login-button {
width: 100%;
height: 80rpx;
background-color: #8B2316;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10rpx;
cursor: pointer;
transition: background-color 0.2s ease;
}
.login-button.disabled {
background-color: #ccc;
cursor: not-allowed;
}
.login-button:active:not(.disabled) {
background-color: #6B1A0F;
}
.button-text {
font-size: 32rpx;
color: white;
font-weight: bold;
}
/* 其他登录方式 */
.other-login {
width: 100%;
margin-bottom: 40rpx;
}
.divider {
display: flex;
align-items: center;
margin-bottom: 60rpx;
}
.line {
flex: 1;
height: 2rpx;
background-color: #e5e5e5;
}
.divider-text {
padding: 0 30rpx;
font-size: 26rpx;
color: #999;
}
/* 微信登录 */
.wechat-login {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
cursor: pointer;
}
.wechat-icon {
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.wechat-text {
font-size: 28rpx;
color: #8B2316;
}
/* 底部协议 */
.agreement-section {
padding: 40rpx 60rpx 60rpx;
}
.agreement-content {
display: flex;
align-items: flex-start;
gap: 20rpx;
}
.checkbox {
width: 45rpx;
height:45rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 4rpx;
cursor: pointer;
transition: all 0.3s ease-in-out;
}
.agreement-text {
flex: 1;
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
.text-content {
color: #666;
}
.link-text {
color: #8B2316;
}
/* 响应式调整 */
@media (max-width: 750rpx) {
.main-content {
padding: 80rpx 40rpx 0;
}
.app-icon {
width: 100rpx;
height: 100rpx;
}
.phone-input,
.code-input,
.send-code-btn {
height: 90rpx;
}
.login-button {
height: 90rpx;
}
.button-text {
font-size: 30rpx;
}
.agreement-section {
padding: 30rpx 40rpx 50rpx;
}
.agreement-text {
font-size: 22rpx;
}
}
.sms-login {
width:100%;
margin-bottom:0rpx;
cursor: pointer;
}
.sms-text {
font-size: 28rpx;
color: #8B2316;
}
</style>