SEC_Warehouse/pages/login/index.vue

327 lines
6.8 KiB
Vue

<template>
<view class="page">
<view class="hero">
<text class="title">欢迎使用智慧养老</text>
<text class="sub">登录后可管理设备与查看事件</text>
</view>
<view class="card">
<up-form :model="form" label-position="top" :border-bottom="false">
<up-form-item label="手机号" :required="true">
<view class="mobile-wrap">
<text class="prefix">+86</text>
<up-input
v-model="form.mobile"
type="number"
maxlength="11"
clearable
placeholder="请输入手机号"
border="none"
:custom-style="inputInnerStyle"
/>
</view>
</up-form-item>
<up-form-item label="验证码" :required="true">
<view class="code-wrap">
<up-input
v-model="form.code"
type="number"
maxlength="6"
clearable
placeholder="请输入验证码"
border="none"
:custom-style="inputInnerStyle"
/>
<up-button
class="code-btn"
size="mini"
type="primary"
:plain="countdown > 0"
:disabled="countdown > 0"
:text="codeBtnText"
@click="sendCode"
/>
</view>
</up-form-item>
</up-form>
<up-button class="login-btn" type="success" shape="circle" text="手机号验证码登录" @click="loginByMobileSubmit" />
<view class="split">
<view class="line"></view>
<text class="split-text">或</text>
<view class="line"></view>
</view>
<up-button class="wx-btn" type="success" shape="circle" text="微信一键登录" @click="loginByWechatSubmit" />
</view>
</view>
</template>
<script>
import { loginByMobile, loginByWechat, sendSmsCode } from '@/api/user'
import { createCountdown } from '@/hooks/useCountdown'
import { goAfterLogin, isLoggedIn, setAuth } from '@/utils/auth'
import { showToast } from '@/utils/toast'
import { isValidMobile, isValidSmsCode } from '@/utils/validators'
export default {
data() {
return {
form: {
mobile: '',
code: ''
},
countdown: 0,
redirect: '',
countdownController: null,
inputInnerStyle: {
padding: '0'
}
}
},
computed: {
codeBtnText() {
return this.countdown > 0 ? `${this.countdown}s后重试` : '获取验证码'
}
},
created() {
this.countdownController = createCountdown({
duration: 60,
onChange: (value) => {
this.countdown = value
}
})
},
onLoad(query) {
this.redirect = query.redirect || ''
if (isLoggedIn()) {
goAfterLogin(this.redirect)
}
},
onUnload() {
this.clearCountdown()
},
methods: {
clearCountdown() {
if (!this.countdownController) {
return
}
this.countdownController.clear()
},
validateMobileOrToast() {
if (isValidMobile(this.form.mobile)) {
return true
}
showToast('请输入正确手机号')
return false
},
finishLogin(payload = {}) {
const token = payload.token || `mock-token-${Date.now()}`
const user = payload.user || {
name: '微信用户',
mobile: '',
loginType: 'wechat'
}
setAuth({ token, user })
goAfterLogin(this.redirect)
},
async sendCode() {
if (!this.validateMobileOrToast()) {
return
}
try {
await sendSmsCode(this.form.mobile)
} catch (error) {
return
}
showToast('验证码已发送')
this.countdownController.start()
},
async loginByMobileSubmit() {
if (!this.validateMobileOrToast()) {
return
}
if (!isValidSmsCode(this.form.code)) {
showToast('请输入6位验证码')
return
}
uni.showLoading({
title: '登录中'
})
try {
const data = await loginByMobile(this.form.mobile, this.form.code)
this.finishLogin({
token: data?.token,
user: data?.user || {
name: `用户${this.form.mobile.slice(-4)}`,
mobile: this.form.mobile,
loginType: 'mobile'
}
})
} catch (error) {
} finally {
uni.hideLoading()
}
},
async loginByWechatSubmit() {
// #ifdef MP-WEIXIN
uni.showLoading({
title: '微信登录中'
})
uni.login({
provider: 'weixin',
success: async (res) => {
try {
const data = await loginByWechat(res.code || '')
this.finishLogin({
token: data?.token,
user: data?.user || {
name: '微信用户',
mobile: '',
loginType: 'wechat'
}
})
} catch (error) {
} finally {
uni.hideLoading()
}
},
fail: () => {
uni.hideLoading()
showToast('微信登录失败,请重试')
}
})
// #endif
// #ifndef MP-WEIXIN
uni.showLoading({
title: '微信登录中'
})
try {
const data = await loginByWechat('')
this.finishLogin({
token: data?.token,
user: data?.user || {
name: '微信用户',
mobile: '',
loginType: 'wechat'
}
})
} catch (error) {
} finally {
uni.hideLoading()
}
// #endif
}
}
}
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: linear-gradient(180deg, #ecf9f5 0%, #f5f6f8 48%, #f1f1f4 100%);
padding: 80rpx 32rpx;
box-sizing: border-box;
}
.hero {
margin: 30rpx 8rpx 46rpx;
}
.title {
display: block;
font-size: 54rpx;
color: #18242f;
font-weight: 700;
}
.sub {
display: block;
margin-top: 16rpx;
font-size: 30rpx;
color: #6f7782;
}
.card {
background: #ffffff;
border-radius: 24rpx;
padding: 30rpx;
box-shadow: 0 16rpx 40rpx rgba(10, 63, 48, 0.08);
}
.mobile-wrap,
.code-wrap {
min-height: 98rpx;
border: 1rpx solid #e8eaef;
border-radius: 16rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
box-sizing: border-box;
}
.mobile-wrap {
margin-top: 10rpx;
}
.code-wrap {
margin-top: 10rpx;
}
.prefix {
width: 86rpx;
color: #1f2b37;
font-size: 30rpx;
font-weight: 600;
}
.code-btn {
margin-left: 12rpx;
}
.login-btn {
margin-top: 28rpx;
}
.wx-btn {
margin-top: 22rpx;
}
.split {
margin: 34rpx 0 8rpx;
display: flex;
align-items: center;
}
.line {
flex: 1;
height: 1rpx;
background: #eceef1;
}
.split-text {
padding: 0 20rpx;
font-size: 26rpx;
color: #9aa2ad;
}
</style>