327 lines
6.8 KiB
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>
|