183 lines
8.6 KiB
Vue
183 lines
8.6 KiB
Vue
<template>
|
||
<view class="auth-page">
|
||
<view class="login-hero">
|
||
<image class="login-hero-img" :src="authLoginTopImage" mode="widthFix" />
|
||
</view>
|
||
<view class="header"><text class="title">邮箱密码登录</text></view>
|
||
|
||
<view v-if="tab==='login'" class="panel">
|
||
<input class="input" type="text" v-model.trim="loginForm.email" placeholder="输入邮箱" />
|
||
<input class="input" type="password" v-model="loginForm.password" placeholder="输入密码" />
|
||
<button class="btn primary" :disabled="loading" @click="onLogin">登录</button>
|
||
<view class="quick-inline">
|
||
<button class="quick-link" @click="gotoRegister">注册</button>
|
||
<button class="quick-link" @click="gotoReset">忘记密码</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="panel minor" v-if="tab==='register'">
|
||
<input class="input" type="text" v-model.trim="regForm.name" placeholder="输入用户名" />
|
||
<input class="input" type="text" v-model.trim="regForm.email" placeholder="输入邮箱" />
|
||
<view class="row">
|
||
<input class="input flex1" type="text" v-model.trim="regForm.code" placeholder="邮箱验证码" />
|
||
<button class="btn ghost" :disabled="regCountdown>0 || loading" @click="sendRegCode">{{ regCountdown>0? (regCountdown+'s') : '获取验证码' }}</button>
|
||
</view>
|
||
<input class="input" type="password" v-model="regForm.password" placeholder="输入密码(≥6位)" />
|
||
<input class="input" type="password" v-model="regForm.password2" placeholder="再次输入密码" />
|
||
<button class="btn primary" :disabled="loading" @click="onRegister">注册新用户</button>
|
||
</view>
|
||
|
||
<view class="panel minor" v-if="tab==='reset'">
|
||
<input class="input" type="text" v-model.trim="resetForm.email" placeholder="输入邮箱" />
|
||
<view class="row">
|
||
<input class="input flex1" type="text" v-model.trim="resetForm.code" placeholder="邮箱验证码" />
|
||
<button class="btn ghost" :disabled="resetCountdown>0 || loading" @click="sendResetCode">{{ resetCountdown>0? (resetCountdown+'s') : '获取验证码' }}</button>
|
||
</view>
|
||
<input class="input" type="password" v-model="resetForm.password" placeholder="新密码(≥6位)" />
|
||
<input class="input" type="password" v-model="resetForm.password2" placeholder="再次输入新密码" />
|
||
<button class="btn primary" :disabled="loading" @click="onReset">重置密码</button>
|
||
</view>
|
||
</view>
|
||
|
||
</template>
|
||
|
||
<script>
|
||
import { get, post } from '../../common/http.js'
|
||
import { AUTH_LOGIN_TOP_IMAGE } from '../../common/config.js'
|
||
|
||
export default {
|
||
data(){
|
||
return {
|
||
loading: false,
|
||
tab: 'login',
|
||
authLoginTopImage: AUTH_LOGIN_TOP_IMAGE,
|
||
loginForm: { email: '', password: '' },
|
||
regForm: { name: '', email: '', code: '', password: '', password2: '' },
|
||
resetForm: { email: '', code: '', password: '', password2: '' },
|
||
regCountdown: 0,
|
||
resetCountdown: 0,
|
||
_timers: []
|
||
}
|
||
},
|
||
beforeUnmount(){ this._timers.forEach(t=>clearInterval(t)) },
|
||
methods: {
|
||
gotoRegister(){ this.tab='register' },
|
||
gotoReset(){ this.tab='reset' },
|
||
toast(msg){ try{ uni.showToast({ title: String(msg||'操作失败'), icon: 'none' }) } catch(_){} },
|
||
validateEmail(v){ return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(String(v||'').trim()) },
|
||
startCountdown(key){
|
||
if (this[key] > 0) return
|
||
this[key] = 60
|
||
const timer = setInterval(()=>{ this[key] = Math.max(0, this[key]-1); if (this[key]===0) clearInterval(timer) }, 1000)
|
||
this._timers.push(timer)
|
||
},
|
||
async onLogin(){
|
||
const { email, password } = this.loginForm
|
||
if (!this.validateEmail(email)) return this.toast('请输入正确邮箱')
|
||
if (!password || password.length<6) return this.toast('请输入至少6位密码')
|
||
this.loading = true
|
||
try{
|
||
const data = await post('/api/auth/password/login', { email, password })
|
||
this.afterLogin(data)
|
||
}catch(e){ this.toast(e.message) }
|
||
finally{ this.loading=false }
|
||
},
|
||
afterLogin(data){
|
||
try{
|
||
if (data && data.token) {
|
||
uni.setStorageSync('TOKEN', data.token)
|
||
if (data.user && data.user.shopId) uni.setStorageSync('SHOP_ID', data.user.shopId)
|
||
uni.setStorageSync('ENABLE_DEFAULT_USER', 'false')
|
||
uni.removeStorageSync('DEFAULT_USER_ID')
|
||
this.toast('登录成功')
|
||
setTimeout(()=>{ uni.reLaunch({ url: '/pages/index/index' }) }, 300)
|
||
}else{
|
||
this.toast('登录失败')
|
||
}
|
||
}catch(_){ this.toast('登录失败') }
|
||
},
|
||
async sendRegCode(){
|
||
if (!this.validateEmail(this.regForm.email)) return this.toast('请输入正确邮箱')
|
||
this.loading = true
|
||
try{
|
||
const r = await post('/api/auth/email/send', { email: this.regForm.email, scene: 'register' })
|
||
if (r && r.ok) this.startCountdown('regCountdown')
|
||
this.toast(r && r.ok ? '验证码已发送' : '发送过于频繁')
|
||
}catch(e){ this.toast(e.message) }
|
||
finally{ this.loading=false }
|
||
},
|
||
async onRegister(){
|
||
const f = this.regForm
|
||
if (!f.name || f.name.trim().length<1) return this.toast('请输入用户名')
|
||
if (!this.validateEmail(f.email)) return this.toast('请输入正确邮箱')
|
||
if (!f.code) return this.toast('请输入验证码')
|
||
if (!f.password || f.password.length<6) return this.toast('密码至少6位')
|
||
if (f.password !== f.password2) return this.toast('两次密码不一致')
|
||
this.loading = true
|
||
try{
|
||
const data = await post('/api/auth/email/register', { name: f.name.trim(), email: f.email.trim(), code: f.code.trim(), password: f.password })
|
||
this.afterLogin(data)
|
||
}catch(e){ this.toast(e.message) }
|
||
finally{ this.loading=false }
|
||
},
|
||
async sendResetCode(){
|
||
if (!this.validateEmail(this.resetForm.email)) return this.toast('请输入正确邮箱')
|
||
this.loading = true
|
||
try{
|
||
const r = await post('/api/auth/email/send', { email: this.resetForm.email, scene: 'reset' })
|
||
if (r && r.ok) this.startCountdown('resetCountdown')
|
||
this.toast(r && r.ok ? '验证码已发送' : '发送过于频繁')
|
||
}catch(e){ this.toast(e.message) }
|
||
finally{ this.loading=false }
|
||
},
|
||
async onReset(){
|
||
const f = this.resetForm
|
||
if (!this.validateEmail(f.email)) return this.toast('请输入正确邮箱')
|
||
if (!f.code) return this.toast('请输入验证码')
|
||
if (!f.password || f.password.length<6) return this.toast('新密码至少6位')
|
||
if (f.password !== f.password2) return this.toast('两次密码不一致')
|
||
this.loading = true
|
||
try{
|
||
const r = await post('/api/auth/email/reset-password', { email: f.email.trim(), code: f.code.trim(), newPassword: f.password, confirmPassword: f.password2 })
|
||
if (r && r.ok) { this.toast('已重置,请使用新密码登录'); this.tab='login'; this.loginForm.email=f.email; }
|
||
else this.toast('重置失败')
|
||
}catch(e){ this.toast(e.message) }
|
||
finally{ this.loading=false }
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
@import '../../uni.scss';
|
||
.auth-page{ padding: 32rpx; display:flex; flex-direction: column; gap: 24rpx; position: relative; min-height: 100vh; }
|
||
.header{ display:flex; align-items:center; justify-content:center; padding: 8rpx 0 0; }
|
||
.title{ font-size: 34rpx; font-weight: 800; color: $uni-text-color; }
|
||
.login-hero{ display:flex; justify-content:center; padding: 16rpx 0 0; }
|
||
.login-hero-img{ width: 72%; max-width: 560rpx; border-radius: 8rpx; }
|
||
.panel{ display:flex; flex-direction: column; gap: 16rpx; background: transparent; padding: 0; border-radius: 0; border: none; }
|
||
.panel.minor{ margin-top: 12rpx; }
|
||
.input{ background:#ffffff; border:2rpx solid #e5e7eb; border-radius: 12rpx; padding: 22rpx 20rpx; font-size: 28rpx; }
|
||
.row{ display:flex; gap: 12rpx; align-items:center; }
|
||
.flex1{ flex:1; }
|
||
.btn{ padding: 22rpx 20rpx; border-radius: 12rpx; font-weight: 800; text-align:center; }
|
||
.btn.primary{ background: linear-gradient(135deg, #4788ff 0%, #2d6be6 100%); color:#fff; border: 1rpx solid rgba(45,107,230,0.25); width: 72%; margin: 0 auto; padding: 14rpx 16rpx; }
|
||
.btn.ghost{ background:#eef3ff; color:#2d6be6; }
|
||
|
||
/* 右下角快捷入口:贴着登录功能,无边框、无背景 */
|
||
.quick-inline{ display:flex; gap: 28rpx; justify-content:flex-end; align-items:center; margin-top: 10rpx; }
|
||
.quick-link{ background: transparent !important; color: #2d6be6; border: none !important; outline: none; padding: 0; font-size: 26rpx; font-weight: 700; box-shadow: none; line-height: 1.2; }
|
||
.quick-link::after{ border: none !important; }
|
||
|
||
/* 注册/重置页:验证码按钮与左侧输入框等高,且更紧凑 */
|
||
.panel.minor .row > .input{ height: $app-form-control-height; padding: 0 $app-form-control-padding-x; }
|
||
.panel.minor .row > .btn.ghost{
|
||
height: $app-form-control-height;
|
||
padding: 0 $app-form-control-padding-x;
|
||
border-radius: $app-form-control-border-radius;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
</style>
|