SEC_Warehouse/pages/login/index.vue

347 lines
6.6 KiB
Vue

<template>
<view class="page">
<view class="hero">
<text class="title">欢迎使用智慧养老</text>
<text class="sub">登录后可管理设备与查看事件</text>
</view>
<view class="card">
<view class="form-row">
<text class="prefix">+86</text>
<input
v-model="mobile"
class="input"
type="number"
maxlength="11"
placeholder="请输入手机号"
/>
</view>
<view class="form-row code-row">
<input
v-model="code"
class="input"
type="number"
maxlength="6"
placeholder="请输入验证码"
/>
<button class="code-btn" :disabled="countdown > 0" @click="sendCode">
{{ codeBtnText }}
</button>
</view>
<button class="login-btn" @click="loginByMobileSubmit">手机号验证码登录</button>
<view class="split">
<view class="line"></view>
<text class="split-text">或</text>
<view class="line"></view>
</view>
<button class="wx-btn" @click="loginByWechatSubmit">微信一键登录</button>
</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 {
mobile: '',
code: '',
countdown: 0,
redirect: '',
countdownController: null
}
},
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.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.mobile)
} catch (error) {
return
}
showToast('验证码已发送')
this.countdownController.start()
},
async loginByMobileSubmit() {
if (!this.validateMobileOrToast()) {
return
}
if (!isValidSmsCode(this.code)) {
showToast('请输入6位验证码')
return
}
uni.showLoading({
title: '登录中'
})
try {
const data = await loginByMobile(this.mobile, this.code)
this.finishLogin({
token: data?.token,
user: data?.user || {
name: `用户${this.mobile.slice(-4)}`,
mobile: this.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>
.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: 38rpx 30rpx 42rpx;
box-shadow: 0 16rpx 40rpx rgba(10, 63, 48, 0.08);
}
.form-row {
min-height: 98rpx;
border: 1rpx solid #e8eaef;
border-radius: 16rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
box-sizing: border-box;
}
.form-row + .form-row {
margin-top: 22rpx;
}
.prefix {
width: 86rpx;
color: #1f2b37;
font-size: 30rpx;
font-weight: 600;
}
.input {
flex: 1;
height: 98rpx;
font-size: 30rpx;
color: #1f2b37;
}
.code-row {
padding-right: 12rpx;
}
.code-btn {
width: 190rpx;
height: 70rpx;
line-height: 70rpx;
border-radius: 36rpx;
background: #e9faf4;
color: #15b880;
font-size: 26rpx;
border: none;
}
.code-btn::after {
border: none;
}
.code-btn[disabled] {
color: #8f98a3;
background: #f1f3f6;
}
.login-btn,
.wx-btn {
margin-top: 26rpx;
width: 100%;
height: 92rpx;
line-height: 92rpx;
border-radius: 16rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 600;
border: none;
}
.login-btn {
background: linear-gradient(90deg, #23c797, #11b880);
}
.login-btn::after,
.wx-btn::after {
border: none;
}
.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;
}
.wx-btn {
margin-top: 24rpx;
background: #07c160;
}
</style>