feat: email register and login

This commit is contained in:
tim
2025-07-05 02:06:46 +08:00
parent d540c995a9
commit 3568e54984
4 changed files with 71 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
:root {
--primary-color-hover: rgb(9, 95, 105);
--primary-color: rgb(10, 110, 120);
--primary-color-disabled: rgba(93, 152, 156, 0.5);
--header-height: 60px;
--header-background-color: white;
--header-border-color: lightgray;

View File

@@ -4,13 +4,13 @@ import router from './router'
import './assets/global.css'
import Toast, { POSITION } from 'vue-toastification'
import 'vue-toastification/dist/index.css'
import { useToast } from 'vue-toastification'
// Configurable API domain and port
export const API_DOMAIN =
process.env.VUE_APP_API_DOMAIN ||
`${window.location.protocol}//${window.location.hostname}`
export const API_PORT = process.env.VUE_APP_API_PORT || window.location.port
export const API_DOMAIN = 'http://127.0.0.1'
export const API_PORT = 8081
export const API_BASE_URL = API_PORT ? `${API_DOMAIN}:${API_PORT}` : API_DOMAIN
export const toast = useToast()
const app = createApp(App)
app.use(router)

View File

@@ -29,10 +29,17 @@
</div>
<div class="login-page-button-primary" @click="submitLogin">
<div v-if="!isWaitingForLogin" class="login-page-button-primary" @click="submitLogin">
<div class="login-page-button-text">登录</div>
</div>
<div v-else class="login-page-button-primary disabled">
<div class="login-page-button-text">
<i class="fas fa-spinner fa-spin"></i>
登录中...
</div>
</div>
<div class="login-page-button-secondary">没有账号 <a class="login-page-button-secondary-link" href="/signup">注册</a>
</div>
</div>
@@ -48,33 +55,36 @@
</template>
<script>
import { API_BASE_URL } from '../main'
import { API_BASE_URL, toast } from '../main'
export default {
name: 'LoginPageView',
data() {
return {
username: '',
password: ''
password: '',
isWaitingForLogin: false
}
},
methods: {
async submitLogin() {
try {
this.isWaitingForLogin = true
const res = await fetch(`${API_BASE_URL}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: this.username, password: this.password })
})
this.isWaitingForLogin = false
const data = await res.json()
if (res.ok && data.token) {
localStorage.setItem('token', data.token)
this.$toast.success('登录成功')
toast.success('登录成功')
this.$router.push('/')
} else {
this.$toast.error(data.error || '登录失败')
toast.error(data.error || '登录失败')
}
} catch (e) {
this.$toast.error('登录失败')
toast.error('登录失败')
}
}
}
@@ -178,6 +188,16 @@ export default {
background-color: var(--primary-color-hover);
}
.login-page-button-primary.disabled {
background-color: var(--primary-color-disabled);
opacity: 0.5;
cursor: not-allowed;
}
.login-page-button-primary.disabled:hover {
background-color: var(--primary-color-disabled);
}
.login-page-button {
display: flex;
flex-direction: row;

View File

@@ -54,9 +54,15 @@
>
</div>
<div class="signup-page-button-primary" @click="sendVerification">
<div v-if="!isWaitingForEmailSent" class="signup-page-button-primary" @click="sendVerification">
<div class="signup-page-button-text">验证邮箱</div>
</div>
<div v-else class="signup-page-button-primary disabled">
<div class="signup-page-button-text">
<i class="fas fa-spinner fa-spin"></i>
发送中...
</div>
</div>
<div class="signup-page-button-secondary">已经有账号 <a class="signup-page-button-secondary-link"
href="/login">登录</a></div>
@@ -72,9 +78,15 @@
placeholder="邮箱验证码"
>
</div>
<div class="signup-page-button-primary" @click="verifyCode">
<div v-if="!isWaitingForEmailVerified" class="signup-page-button-primary" @click="verifyCode">
<div class="signup-page-button-text">注册</div>
</div>
<div v-else class="signup-page-button-primary disabled">
<div class="signup-page-button-text">
<i class="fas fa-spinner fa-spin"></i>
验证中...
</div>
</div>
</div>
</div>
@@ -88,7 +100,7 @@
</template>
<script>
import { API_BASE_URL } from '../main'
import { API_BASE_URL, toast } from '../main'
export default {
name: 'SignupPageView',
@@ -102,7 +114,9 @@ export default {
usernameError: '',
passwordError: '',
nickname: '',
code: ''
code: '',
isWaitingForEmailSent: false,
isWaitingForEmailVerified: false
}
},
methods: {
@@ -127,46 +141,51 @@ export default {
return
}
try {
console.log('base url: ', API_BASE_URL)
this.isWaitingForEmailSent = true
const res = await fetch(`${API_BASE_URL}/api/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: this.username,
email: this.email,
password: this.password
password: this.password,
})
})
this.isWaitingForEmailSent = false
const data = await res.json()
if (res.ok) {
this.emailStep = 1
this.$toast.success('验证码已发送,请查看邮箱')
toast.success('验证码已发送,请查看邮箱')
} else if (data.field) {
if (data.field === 'username') this.usernameError = data.error
if (data.field === 'email') this.emailError = data.error
if (data.field === 'password') this.passwordError = data.error
} else {
this.$toast.error(data.error || '发送失败')
toast.error(data.error || '发送失败')
}
} catch (e) {
this.$toast.error('发送失败')
toast.error('发送失败')
}
},
async verifyCode() {
try {
this.isWaitingForEmailVerified = true
const res = await fetch(`${API_BASE_URL}/api/auth/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: this.username, code: this.code })
})
this.isWaitingForEmailVerified = false
const data = await res.json()
if (res.ok) {
this.$toast.success('注册成功,请登录')
toast.success('注册成功,请登录')
this.$router.push('/login')
} else {
this.$toast.error(data.error || '注册失败')
toast.error(data.error || '注册失败')
}
} catch (e) {
this.$toast.error('注册失败')
toast.error('注册失败')
}
}
}
@@ -266,6 +285,16 @@ export default {
gap: 10px;
}
.signup-page-button-primary.disabled {
background-color: var(--primary-color-disabled);
opacity: 0.5;
cursor: not-allowed;
}
.signup-page-button-primary.disabled:hover {
background-color: var(--primary-color-disabled);
}
.signup-page-button-primary:hover {
background-color: var(--primary-color-hover);
}