| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- <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 oa-btn-loading"
- :class="{ 'is-loading': loading }"
- @click="onPasswordLogin"
- >
- <text class="oa-btn-loading__label">登录</text>
- <view v-if="loading" class="oa-btn-loading__spin" aria-hidden="true" />
- </view>
- <view class="login-agree" @click="toggleAgree">
- <view class="login-agree__box" :class="{ 'is-on': agreed }">
- <text v-if="agreed" class="login-agree__check">✓</text>
- </view>
- <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 oa-btn-loading"
- :class="{ 'is-loading': loading }"
- open-type="getPhoneNumber"
- :disabled="loading"
- @getphonenumber="onGetPhoneNumber"
- >
- <view class="oa-btn-loading__label login-wechat__inner">
- <text>手机号快捷登录</text>
- </view>
- <view v-if="loading" class="oa-btn-loading__spin" aria-hidden="true" />
- </button>
- <view
- v-else
- class="login-wechat oa-btn-loading"
- :class="{ 'is-loading': loading }"
- @click="onWechatGate"
- >
- <view class="oa-btn-loading__label login-wechat__inner">
- <text>手机号快捷登录</text>
- </view>
- <view v-if="loading" class="oa-btn-loading__spin" aria-hidden="true" />
- </view>
- <ConfirmDialog
- :show="agreeDialogVisible"
- title="请先同意协议"
- cancel-text="取消"
- confirm-text="同意并登录"
- :confirm-open-type="agreeDialogForWechat ? 'getPhoneNumber' : ''"
- @update:show="agreeDialogVisible = $event"
- @confirm="onAgreeConfirm"
- @cancel="onAgreeCancel"
- @getphonenumber="onAgreeGetPhoneNumber"
- >
- <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
- v-if="envBanner.show"
- class="env-banner"
- :class="'env-banner--' + envBanner.tone"
- >
- <text class="env-banner__text">{{ envBanner.label }}</text>
- </view>
- </view>
- </template>
- <script>
- import { ENV_BANNER } from '../../utils/config.js'
- import {
- isLoggedIn,
- passwordLogin,
- wechatLogin,
- clearLegacyMockSessionIfNeeded,
- takeReturnUrl
- } from '../../utils/auth.js'
- import { validateCredentials } from '../../utils/validate-credentials.js'
- import ConfirmDialog from '../../components/ConfirmDialog.vue'
- export default {
- components: {
- ConfirmDialog
- },
- data() {
- return {
- envBanner: ENV_BANNER,
- username: '',
- password: '',
- showPwd: false,
- agreed: false,
- agreeDialogVisible: false,
- /** 微信登录入口打开的协议弹窗:确认键需带 getPhoneNumber */
- agreeDialogForWechat: false,
- loading: false,
- focusUser: false,
- focusPwd: false,
- errors: {
- username: '',
- password: ''
- }
- }
- },
- onShow() {
- clearLegacyMockSessionIfNeeded()
- if (isLoggedIn()) {
- this.goAfterLogin()
- }
- },
- 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.agreeDialogForWechat = false
- 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
- this.agreeDialogForWechat = false
- if (this._agreeResolver) {
- this._agreeResolver(false)
- this._agreeResolver = null
- }
- },
- /** 协议弹窗确认键直接拉起手机号授权(与「同意」同一次点击) */
- onAgreeGetPhoneNumber(e) {
- this.agreed = true
- this.agreeDialogVisible = false
- this.agreeDialogForWechat = false
- this.onGetPhoneNumber(e)
- },
- openProtocol(type) {
- uni.navigateTo({ url: `/pages/protocol/index?type=${type}` })
- },
- goAfterLogin() {
- const back = takeReturnUrl()
- if (back) {
- const url = back.startsWith('/') ? back : '/' + back
- uni.reLaunch({ url })
- return
- }
- uni.reLaunch({ url: '/pages/index/index' })
- },
- goHome() {
- this.goAfterLogin()
- },
- 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.goAfterLogin(), 400)
- } catch (e) {
- uni.showToast({ title: e.message || '登录失败', icon: 'none' })
- } finally {
- this.loading = false
- }
- },
- onWechatGate() {
- if (this.loading) return
- this.agreeDialogForWechat = true
- this.agreeDialogVisible = true
- },
- async onGetPhoneNumber(e) {
- if (this.loading) return
- if (!this.agreed) {
- // 理论上微信按钮仅在已同意时渲染;防御
- this.agreeDialogForWechat = true
- this.agreeDialogVisible = true
- 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.goAfterLogin(), 400)
- } catch (err) {
- uni.showToast({
- title: (err && err.message) || '登录失败',
- icon: 'none'
- })
- } finally {
- this.loading = false
- }
- }
- }
- }
- </script>
- <style scoped>
- .login-ph {
- color: #9aaba3;
- }
- </style>
|