273 lines
7.8 KiB
Vue
273 lines
7.8 KiB
Vue
<template>
|
||
<view class="login-page">
|
||
<view class="login-brand">
|
||
<image
|
||
class="login-brand__logo"
|
||
src="/static/logo.png"
|
||
mode="widthFix"
|
||
/>
|
||
<view class="login-brand__sub">员工工作台登录</view>
|
||
</view>
|
||
|
||
<view class="login-card">
|
||
<view class="login-field">
|
||
<text class="login-field__label">账号</text>
|
||
<view
|
||
class="login-field__box"
|
||
:class="{ 'is-focus': focusUser, 'is-error': !!errors.username }"
|
||
>
|
||
<input
|
||
class="login-field__input"
|
||
type="text"
|
||
:value="username"
|
||
placeholder="请输入用户名 / 工号"
|
||
placeholder-class="login-ph"
|
||
@input="onUserInput"
|
||
@focus="focusUser = true"
|
||
@blur="focusUser = false"
|
||
/>
|
||
</view>
|
||
<view v-if="errors.username" class="login-field__error">{{ errors.username }}</view>
|
||
</view>
|
||
|
||
<view class="login-field">
|
||
<text class="login-field__label">密码</text>
|
||
<view
|
||
class="login-field__box"
|
||
:class="{ 'is-focus': focusPwd, 'is-error': !!errors.password }"
|
||
>
|
||
<input
|
||
class="login-field__input"
|
||
:password="!showPwd"
|
||
:value="password"
|
||
placeholder="请输入密码"
|
||
placeholder-class="login-ph"
|
||
@input="onPwdInput"
|
||
@focus="focusPwd = true"
|
||
@blur="focusPwd = false"
|
||
/>
|
||
<text class="login-field__action" @click="showPwd = !showPwd">
|
||
{{ showPwd ? '隐藏' : '显示' }}
|
||
</text>
|
||
</view>
|
||
<view v-if="errors.password" class="login-field__error">{{ errors.password }}</view>
|
||
</view>
|
||
|
||
<view
|
||
class="login-submit"
|
||
:class="{ 'is-disabled': loading }"
|
||
@click="onPasswordLogin"
|
||
>
|
||
{{ loading ? '登录中…' : '登录' }}
|
||
</view>
|
||
|
||
<view class="login-agree" @click="toggleAgree">
|
||
<view class="login-agree__box" :class="{ 'is-on': agreed }" />
|
||
<view class="login-agree__text">
|
||
已阅读并同意
|
||
<text class="login-agree__link" @click.stop="openProtocol('user')">《用户协议》</text>
|
||
和
|
||
<text class="login-agree__link" @click.stop="openProtocol('privacy')">《隐私政策》</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="login-divider">或</view>
|
||
|
||
<button
|
||
v-if="agreed"
|
||
class="login-wechat"
|
||
:class="{ 'is-disabled': loading }"
|
||
open-type="getPhoneNumber"
|
||
:disabled="loading"
|
||
@getphonenumber="onGetPhoneNumber"
|
||
>
|
||
<image class="login-wechat__icon" src="/static/wechat-logo.png" mode="aspectFit" />
|
||
<text>微信授权登录</text>
|
||
</button>
|
||
<view
|
||
v-else
|
||
class="login-wechat"
|
||
:class="{ 'is-disabled': loading }"
|
||
@click="onWechatGate"
|
||
>
|
||
<image class="login-wechat__icon" src="/static/wechat-logo.png" mode="aspectFit" />
|
||
<text>微信授权登录</text>
|
||
</view>
|
||
|
||
<ConfirmDialog
|
||
:show="agreeDialogVisible"
|
||
title="请先同意协议"
|
||
cancel-text="取消"
|
||
confirm-text="同意并登录"
|
||
@update:show="agreeDialogVisible = $event"
|
||
@confirm="onAgreeConfirm"
|
||
@cancel="onAgreeCancel"
|
||
>
|
||
<text>登录前需阅读并同意</text>
|
||
<text class="confirm-dialog__link" @click.stop="openProtocol('user')">
|
||
《用户协议》
|
||
</text>
|
||
<text>和</text>
|
||
<text class="confirm-dialog__link" @click.stop="openProtocol('privacy')">
|
||
《隐私政策》
|
||
</text>
|
||
<text>。是否同意并继续登录?</text>
|
||
</ConfirmDialog>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
isLoggedIn,
|
||
passwordLogin,
|
||
wechatLogin,
|
||
clearLegacyMockSessionIfNeeded
|
||
} from '../../utils/auth.js'
|
||
import { validateCredentials } from '../../utils/validate-credentials.js'
|
||
import ConfirmDialog from '../../components/ConfirmDialog.vue'
|
||
|
||
export default {
|
||
components: {
|
||
ConfirmDialog
|
||
},
|
||
data() {
|
||
return {
|
||
username: '',
|
||
password: '',
|
||
showPwd: false,
|
||
agreed: false,
|
||
agreeDialogVisible: false,
|
||
loading: false,
|
||
focusUser: false,
|
||
focusPwd: false,
|
||
errors: {
|
||
username: '',
|
||
password: ''
|
||
}
|
||
}
|
||
},
|
||
onShow() {
|
||
clearLegacyMockSessionIfNeeded()
|
||
if (isLoggedIn()) {
|
||
uni.reLaunch({ url: '/pages/index/index' })
|
||
}
|
||
},
|
||
methods: {
|
||
onUserInput(e) {
|
||
this.username = e.detail.value
|
||
this.errors.username = ''
|
||
},
|
||
onPwdInput(e) {
|
||
this.password = e.detail.value
|
||
this.errors.password = ''
|
||
},
|
||
toggleAgree() {
|
||
this.agreed = !this.agreed
|
||
},
|
||
ensureAgree() {
|
||
if (this.agreed) return Promise.resolve(true)
|
||
this.agreeDialogVisible = true
|
||
return new Promise((resolve) => {
|
||
this._agreeResolver = resolve
|
||
})
|
||
},
|
||
onAgreeConfirm() {
|
||
this.agreed = true
|
||
this.agreeDialogVisible = false
|
||
if (this._agreeResolver) {
|
||
this._agreeResolver(true)
|
||
this._agreeResolver = null
|
||
}
|
||
},
|
||
onAgreeCancel() {
|
||
this.agreeDialogVisible = false
|
||
if (this._agreeResolver) {
|
||
this._agreeResolver(false)
|
||
this._agreeResolver = null
|
||
}
|
||
},
|
||
openProtocol(type) {
|
||
uni.navigateTo({ url: `/pages/protocol/index?type=${type}` })
|
||
},
|
||
goHome() {
|
||
uni.reLaunch({ url: '/pages/index/index' })
|
||
},
|
||
async onPasswordLogin() {
|
||
if (this.loading) return
|
||
this.errors = { username: '', password: '' }
|
||
const v = validateCredentials(this.username, this.password)
|
||
if (!v.ok) {
|
||
if (v.code === 'EMPTY_USERNAME') this.errors.username = v.message
|
||
if (v.code === 'EMPTY_PASSWORD') this.errors.password = v.message
|
||
return
|
||
}
|
||
if (!(await this.ensureAgree())) return
|
||
this.loading = true
|
||
try {
|
||
await passwordLogin({
|
||
username: this.username,
|
||
password: this.password
|
||
})
|
||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||
setTimeout(() => this.goHome(), 400)
|
||
} catch (e) {
|
||
uni.showToast({ title: e.message || '登录失败', icon: 'none' })
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
async onWechatGate() {
|
||
if (this.loading) return
|
||
await this.ensureAgree()
|
||
},
|
||
async onGetPhoneNumber(e) {
|
||
if (this.loading) return
|
||
if (!(await this.ensureAgree())) return
|
||
const detail = (e && e.detail) || {}
|
||
const errMsg = detail.errMsg || ''
|
||
if (errMsg && errMsg.indexOf('ok') === -1) {
|
||
uni.showToast({
|
||
title: '需要授权手机号,或请使用账号密码登录',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
const phoneCode = detail.code
|
||
if (!phoneCode) {
|
||
uni.showToast({
|
||
title: '需要授权手机号,或请使用账号密码登录',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
this.loading = true
|
||
try {
|
||
const loginRes = await new Promise((resolve, reject) => {
|
||
uni.login({ provider: 'weixin', success: resolve, fail: reject })
|
||
})
|
||
await wechatLogin({
|
||
loginCode: loginRes.code,
|
||
phoneCode
|
||
})
|
||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||
setTimeout(() => this.goHome(), 400)
|
||
} catch (err) {
|
||
uni.showToast({
|
||
title: (err && err.message) || '登录失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.login-ph {
|
||
color: #9aaba3;
|
||
}
|
||
</style>
|