508 lines
12 KiB
Vue
508 lines
12 KiB
Vue
<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>
|