Files
xk-doctor-wx/components/doctor-login/doctor-login.vue

508 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
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="container safe-area-inset-bottom">
<view class="title">欢迎登录</view>
<view class="box">
<view class="info">
<view class="pwd">
<d-input
:maxlength="11"
height="96"
v-model="form.mobile"
:prefixIcon="require('../../static/image/act.png')"
borderRadius="96rpx"
prefixIconSize="40rpx"
placeholder="请输入手机号"
:placeholder-style="{ fontSize: '32rpx' }"
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
/>
</view>
<view class="pwd">
<d-input
height="96"
v-model="form.password"
type="password"
:prefixIcon="require('../../static/image/pwd.png')"
password
borderRadius="96rpx"
prefixIconSize="40rpx"
placeholder="请输入登录密码"
:placeholder-style="{ fontSize: '32rpx' }"
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
/>
</view>
<view class="pwd">
<u-field v-model="smsCode"
type="number"
:maxlength="6"
placeholder="请填写验证码"
:prefixIcon="require('../../static/image/pwd.png')"
:placeholder-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
>
<template slot="icon">
<!-- <image src="../../../../static/image/pwd.png"></image>-->
<u-icon
size="40"
name="chat"
style="margin-left: 15rpx"
@click="form.check = !form.check"
></u-icon>
</template>
<u-button size="mini" slot="right" @click="getCode">{{ codeText }}</u-button>
</u-field>
<u-verification-code ref="uCode" @change="codeChange" class="code"></u-verification-code>
</view>
<view class="flex-row" style="margin-top: 88rpx;">
<u-icon
size="35"
:color="form.check ? '#6ACDBB' : '#e0fdff'"
name="checkmark-circle-fill"
@click="form.check = !form.check"
></u-icon>
<view class="agreement">
我已阅读并同意
<text @click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">用户服务协议</text>
<text @click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">隐私权政策</text>
</view>
</view>
<view class="btn">
<u-button :throttle-time="20" shape="circle" @click="submitLogin" :ripple="true" :loading="loading">
登录
</u-button>
</view>
<view class="btn wx-btn" v-if="wxLoginBtn">
<u-button
:throttle-time="20"
shape="circle"
@click="submitWxLogin"
:ripple="true"
:loading="wxLoading"
:custom-style="{ background: '#07C160', color: '#fff' }"
>
一键登录
</u-button>
</view>
</view>
</view>
<u-popup v-model="showAgreementConfirm" mode="center" border-radius="24" width="600" z-index="10075">
<view class="confirm-modal">
<view class="confirm-title">服务协议和隐私政策</view>
<view class="confirm-desc">
请您在使用前仔细阅读并同意
<text class="link" @click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">用户服务协议</text>
<text class="link" @click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">隐私权政策</text>
</view>
<view class="confirm-footer">
<button class="cancel-btn" @click="showAgreementConfirm = false">不同意</button>
<button class="agree-btn" @click="agreeAndLogin">同意并登录</button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import { sendDoctorWxCode, doctorWxLogin, getDoctorWxAuthConfig, doctorWxLoginByWechat } from '@/api/doctorAuth.js';
import {
saveLoginSession,
fetchProfileAfterLogin,
navigateAfterLogin,
} from '@/utils/loginSession.js';
import { buildDoctorWxAuthExtra } from '@/utils/doctorWxAuth.js';
export default {
name: 'DoctorLogin',
data() {
return {
form: {
mobile: '',
password: '',
check: false,
},
smsCode: '',
codeText: '',
loading: false,
wxLoading: false,
wxLoginBtn: false,
pendingLoginPayload: null,
loginMode: 'password',
showAgreementConfirm: false,
pendingLoginAction: null,
};
},
mounted() {
this.loadWxConfig();
},
methods: {
loadWxConfig() {
return getDoctorWxAuthConfig().then((res) => {
const result = (res && res.code === 0 && res.result) ? res.result : {};
this.wxLoginBtn = !!result.wx_login_btn;
}).catch(() => {});
},
codeChange(text) {
this.codeText = text;
},
getCode() {
if (!this.$refs.uCode.canGetCode) {
this.$u.toast('倒计时结束后再发送');
return;
}
if (!this.form.mobile) {
this.$toast('请输入手机号');
return;
}
if (!this.form.password) {
this.$toast('请输入密码');
return;
}
uni.showLoading({ title: '正在获取验证码' });
sendDoctorWxCode(this.form.mobile, this.form.password)
.then((res) => {
// HTTP 200 但业务 code 非 0 时 reject统一走 catch 弹 toast
if (res.code !== 0) {
return Promise.reject(new Error(res.message || res.msg || '获取验证码失败'));
}
this.$refs.uCode.start();
if (res.result && res.result.code) {
this.smsCode = res.result.code;
}
})
.catch((err) => {
this.$toast(err.message || err.msg || '获取验证码失败');
})
.finally(() => {
uni.hideLoading();
});
},
ensureAgreement(action) {
if (this.form.check) {
return true;
}
this.pendingLoginAction = action;
this.showAgreementConfirm = true;
return false;
},
agreeAndLogin() {
this.form.check = true;
this.showAgreementConfirm = false;
if (this.pendingLoginAction === 'wx') {
this.proceedWxLogin();
} else {
this.doLogin({});
}
this.pendingLoginAction = null;
},
proceedWxLogin() {
// #ifdef MP-WEIXIN
this.wxLoading = true;
uni.login({
provider: 'weixin',
success: (loginRes) => {
if (!loginRes.code) {
this.$toast('获取微信授权失败');
this.wxLoading = false;
return;
}
this.doWxLogin(loginRes.code, {});
},
fail: () => {
this.$toast('微信授权失败');
this.wxLoading = false;
},
});
// #endif
},
submitLogin() {
if (!this.form.mobile) {
this.$toast('请输入手机号');
return;
}
if (!this.form.password) {
this.$toast('请输入密码');
return;
}
if (!this.smsCode) {
this.$toast('请输入验证码');
return;
}
if (!this.ensureAgreement('password')) {
return;
}
this.doLogin({});
},
submitWxLogin() {
if (!this.ensureAgreement('wx')) {
return;
}
this.proceedWxLogin();
},
doWxLogin(code, extra = {}) {
this.loginMode = 'wx';
this.wxLoading = true;
doctorWxLoginByWechat({ code, ...extra })
.then((res) => {
if (res.code !== 0 || !res.result) {
return Promise.reject(new Error(res.message || res.msg || '登录失败'));
}
const result = res.result;
if (result.need_select) {
const accounts = Array.isArray(result.accounts) ? result.accounts : [];
if (!accounts.length) {
return Promise.reject(new Error('请选择登录账号'));
}
this.wxLoading = false;
return new Promise((resolve) => {
this.$nextTick(() => {
this.$emit('need-select', {
accounts,
defaultAccountId: result.default_account_id,
defaultAccountType: result.default_account_type,
mode: 'wx',
});
resolve(null);
});
});
}
return this.finishLogin(result);
})
.catch((err) => {
this.$toast(err.message || '登录失败');
})
.finally(() => {
this.wxLoading = false;
});
},
doLogin(extra = {}) {
this.loginMode = 'password';
this.loading = true;
const payload = {
phone: this.form.mobile,
password: this.form.password,
code: this.smsCode,
...extra,
};
doctorWxLogin(payload)
.then((res) => {
if (res.code !== 0 || !res.result) {
return Promise.reject(new Error(res.message || res.msg || '登录失败'));
}
const result = res.result;
if (result.need_select) {
const accounts = Array.isArray(result.accounts) ? result.accounts : [];
if (!accounts.length) {
return Promise.reject(new Error('请选择登录账号'));
}
this.pendingLoginPayload = payload;
this.loading = false;
return new Promise((resolve) => {
this.$nextTick(() => {
this.$emit('need-select', {
accounts,
defaultAccountId: result.default_account_id,
defaultAccountType: result.default_account_type,
});
resolve(null);
});
});
}
return this.finishLogin(result);
})
.catch((err) => {
this.$toast(err.message || '登录失败');
})
.finally(() => {
this.loading = false;
});
},
onAccountPicked(account) {
if (this.loginMode === 'wx') {
this.wxLoading = true;
uni.login({
provider: 'weixin',
success: (loginRes) => {
if (!loginRes.code) {
this.$toast('获取微信授权失败');
this.wxLoading = false;
return;
}
this.doWxLogin(loginRes.code, buildDoctorWxAuthExtra(account));
},
fail: () => {
this.$toast('微信授权失败');
this.wxLoading = false;
},
});
return;
}
this.doLogin(buildDoctorWxAuthExtra(account));
},
async finishLogin(result) {
saveLoginSession(result);
await fetchProfileAfterLogin(result);
this.$toast('登录成功');
if (result.account_type !== 'admin') {
uni.$emit('user-login');
}
setTimeout(() => {
navigateAfterLogin(result);
}, 400);
},
},
};
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
}
.title {
width: 750rpx;
padding-top: 200rpx;
text-align: center;
font-size: 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #0D111A;
background: linear-gradient(180deg, #EAF2FF 0%, #FFFFFF 100%);
margin-bottom: 60rpx;
}
.flex-row {
display: flex;
flex-direction: row;
margin-bottom: 34rpx;
.agreement {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-left: 16rpx;
text {
color: #2979FF;
size: 24rpx;
}
}
}
.box {
box-sizing: border-box;
padding: 0 52rpx;
}
.code {
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #1777FF;
background: #F9FAFB;
}
.pwd {
width: 646rpx;
height: 96rpx;
background: #F9FAFB;
border-radius: 48rpx 48rpx 48rpx 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #CCCCCC;
margin: 0rpx 0 30rpx;
::v-deep button {
color: #1777FF;
border: 0;
}
}
.btn {
::v-deep button {
width: 648rpx;
height: 100rpx;
line-height: 100rpx;
background: #6ACDBB;
border: 0;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
}
.wx-btn {
margin-top: 24rpx;
::v-deep button {
background: #07C160 !important;
}
}
.confirm-modal {
padding: 40rpx 30rpx;
background: #fff;
.confirm-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
}
.confirm-desc {
font-size: 28rpx;
color: #666;
line-height: 1.6;
margin-bottom: 40rpx;
text-align: center;
.link {
color: #2979FF;
}
}
.confirm-footer {
display: flex;
justify-content: space-between;
gap: 20rpx;
button {
flex: 1;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
font-size: 30rpx;
border: none;
&::after {
border: none;
}
}
.cancel-btn {
background: #F5F7FA;
color: #909399;
}
.agree-btn {
background: #6ACDBB;
color: #FFFFFF;
}
}
}
</style>