156 lines
7.2 KiB
Vue
156 lines
7.2 KiB
Vue
<template>
|
||
<view class="auth-page">
|
||
<view class="tabs">
|
||
<view :class="['tab', tab==='login'?'active':'']" @click="tab='login'">登录</view>
|
||
<view :class="['tab', tab==='register'?'active':'']" @click="tab='register'">注册</view>
|
||
<view :class="['tab', tab==='reset'?'active':'']" @click="tab='reset'">忘记密码</view>
|
||
</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>
|
||
|
||
<view v-else-if="tab==='register'" class="panel">
|
||
<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 v-else class="panel">
|
||
<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'
|
||
|
||
export default {
|
||
data(){
|
||
return {
|
||
loading: false,
|
||
tab: 'login',
|
||
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: {
|
||
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">
|
||
.auth-page{ padding: 32rpx; display:flex; flex-direction: column; gap: 24rpx; }
|
||
.tabs{ display:flex; gap: 24rpx; }
|
||
.tab{ padding: 12rpx 20rpx; border-radius: 999rpx; background:#f2f4f8; color:#5b6b80; font-weight:700; }
|
||
.tab.active{ background:#2d6be6; color:#fff; }
|
||
.panel{ display:flex; flex-direction: column; gap: 16rpx; background:#fff; padding: 24rpx; border-radius: 16rpx; border:2rpx solid #eef2f9; }
|
||
.input{ background:#f7f9ff; border:2rpx solid rgba(45,107,230,0.12); 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; }
|
||
.btn.ghost{ background:#eef3ff; color:#2d6be6; }
|
||
</style>
|