126 lines
3.0 KiB
Vue
126 lines
3.0 KiB
Vue
<template>
|
|
<main class="auth-page">
|
|
<section class="auth-card">
|
|
<h1 class="auth-title">创建账号</h1>
|
|
<p class="auth-subtitle">完成注册后即可登录使用</p>
|
|
|
|
<div class="auth-form-group">
|
|
<label class="auth-label" for="username">用户名</label>
|
|
<input
|
|
id="username"
|
|
v-model.trim="username"
|
|
type="text"
|
|
class="auth-input"
|
|
placeholder="仅限小写字母/数字/下划线"
|
|
autocomplete="username"
|
|
/>
|
|
</div>
|
|
|
|
<div class="auth-form-group">
|
|
<label class="auth-label" for="email">邮箱</label>
|
|
<input
|
|
id="email"
|
|
v-model.trim="email"
|
|
type="email"
|
|
class="auth-input"
|
|
autocomplete="email"
|
|
/>
|
|
</div>
|
|
|
|
<div class="auth-form-group">
|
|
<label class="auth-label" for="password">密码</label>
|
|
<input
|
|
id="password"
|
|
v-model="password"
|
|
type="password"
|
|
class="auth-input"
|
|
autocomplete="new-password"
|
|
@keydown.enter="register"
|
|
/>
|
|
</div>
|
|
|
|
<div class="auth-form-group">
|
|
<label class="auth-label" for="invite">邀请码</label>
|
|
<input
|
|
id="invite"
|
|
v-model.trim="inviteCode"
|
|
type="text"
|
|
class="auth-input"
|
|
placeholder="必填"
|
|
autocomplete="off"
|
|
@keydown.enter="register"
|
|
/>
|
|
</div>
|
|
|
|
<button class="auth-button" :disabled="submitting" @click="register">注册</button>
|
|
<div class="auth-error">{{ error }}</div>
|
|
<div class="auth-link">已有账号?<a href="/login">返回登录</a></div>
|
|
</section>
|
|
</main>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, ref } from 'vue';
|
|
import { applyTheme, loadTheme } from './theme';
|
|
|
|
declare global {
|
|
interface Window {
|
|
ensureCsrfToken?: () => Promise<unknown>;
|
|
}
|
|
}
|
|
|
|
const username = ref('');
|
|
const email = ref('');
|
|
const password = ref('');
|
|
const inviteCode = ref('');
|
|
const error = ref('');
|
|
const submitting = ref(false);
|
|
|
|
const register = async () => {
|
|
if (!username.value || !email.value || !password.value || !inviteCode.value) {
|
|
error.value = '请完整填写所有字段';
|
|
return;
|
|
}
|
|
|
|
submitting.value = true;
|
|
error.value = '';
|
|
|
|
try {
|
|
const resp = await fetch('/register', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: username.value,
|
|
email: email.value,
|
|
password: password.value,
|
|
invite_code: inviteCode.value
|
|
})
|
|
});
|
|
const data = await resp.json();
|
|
|
|
if (data.success) {
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
|
|
error.value = data.error || '注册失败';
|
|
} catch (_err) {
|
|
error.value = '网络错误,请稍后再试';
|
|
} finally {
|
|
submitting.value = false;
|
|
}
|
|
};
|
|
|
|
onMounted(async () => {
|
|
applyTheme(loadTheme());
|
|
|
|
if (window.ensureCsrfToken) {
|
|
try {
|
|
await window.ensureCsrfToken();
|
|
} catch (_err) {
|
|
// ignore
|
|
}
|
|
}
|
|
});
|
|
</script>
|