205 lines
4.8 KiB
Vue
205 lines
4.8 KiB
Vue
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import { User } from '@element-plus/icons-vue'
|
||
import { authApi } from '../../api/auth'
|
||
|
||
interface Props {
|
||
modelValue: boolean
|
||
}
|
||
|
||
interface Emits {
|
||
(e: 'update:modelValue', value: boolean): void
|
||
(e: 'loginSuccess', data: { token: string; user: any }): void
|
||
(e: 'backToLogin'): void
|
||
}
|
||
|
||
const props = defineProps<Props>()
|
||
const emit = defineEmits<Emits>()
|
||
|
||
const registerForm = ref({ username: '', password: '', confirmPassword: '' })
|
||
const registerLoading = ref(false)
|
||
const usernameCheckResult = ref<boolean | null>(null)
|
||
|
||
const visible = computed({
|
||
get: () => props.modelValue,
|
||
set: (value) => emit('update:modelValue', value)
|
||
})
|
||
|
||
const canRegister = computed(() => {
|
||
const { username, password, confirmPassword } = registerForm.value
|
||
return username &&
|
||
password.length >= 6 &&
|
||
password === confirmPassword &&
|
||
usernameCheckResult.value === true
|
||
})
|
||
|
||
async function checkUsernameAvailability() {
|
||
if (!registerForm.value.username) {
|
||
usernameCheckResult.value = null
|
||
return
|
||
}
|
||
|
||
try {
|
||
const data = await authApi.checkUsername(registerForm.value.username)
|
||
usernameCheckResult.value = data.available
|
||
} catch {
|
||
usernameCheckResult.value = null
|
||
}
|
||
}
|
||
|
||
async function handleRegister() {
|
||
if (!canRegister.value) return
|
||
|
||
registerLoading.value = true
|
||
try {
|
||
// 1. 注册
|
||
await authApi.register({
|
||
username: registerForm.value.username,
|
||
password: registerForm.value.password
|
||
})
|
||
|
||
// 2. 注册成功后直接登录
|
||
const loginData = await authApi.login({
|
||
username: registerForm.value.username,
|
||
password: registerForm.value.password
|
||
})
|
||
|
||
emit('loginSuccess', {
|
||
token: loginData.token,
|
||
user: {
|
||
username: loginData.username,
|
||
permissions: loginData.permissions
|
||
}
|
||
})
|
||
resetForm()
|
||
} catch (err) {
|
||
ElMessage.error((err as Error).message)
|
||
} finally {
|
||
registerLoading.value = false
|
||
}
|
||
}
|
||
|
||
function cancelRegister() {
|
||
visible.value = false
|
||
resetForm()
|
||
}
|
||
|
||
function resetForm() {
|
||
registerForm.value = { username: '', password: '', confirmPassword: '' }
|
||
usernameCheckResult.value = null
|
||
}
|
||
|
||
function backToLogin() {
|
||
emit('backToLogin')
|
||
resetForm()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<el-dialog
|
||
title=""
|
||
v-model="visible"
|
||
:close-on-click-modal="false"
|
||
width="380px"
|
||
center
|
||
class="auth-dialog">
|
||
<div style="padding: 20px 0;">
|
||
<div style="text-align: center; margin-bottom: 16px; color: #666;">
|
||
<img src="/icon/image.png" alt="logo" class="auth-logo" />
|
||
</div>
|
||
<div class="auth-title-wrap">
|
||
<h3 class="auth-title">创建账号</h3>
|
||
<p class="auth-subtitle">创建账号以获取完整服务体验</p>
|
||
</div>
|
||
|
||
<el-input
|
||
v-model="registerForm.username"
|
||
placeholder="请输入用户名"
|
||
size="large"
|
||
style="margin-bottom: 15px;"
|
||
:disabled="registerLoading"
|
||
@blur="checkUsernameAvailability">
|
||
</el-input>
|
||
|
||
<div v-if="usernameCheckResult !== null" style="margin-bottom: 15px; text-align: left;">
|
||
<span v-if="usernameCheckResult" style="color: #67C23A; font-size: 12px;">
|
||
✓ 用户名可用
|
||
</span>
|
||
<span v-else style="color: #F56C6C; font-size: 12px;">
|
||
✗ 用户名已存在
|
||
</span>
|
||
</div>
|
||
|
||
<el-input
|
||
v-model="registerForm.password"
|
||
placeholder="请输入密码(至少6位)"
|
||
type="password"
|
||
size="large"
|
||
style="margin-bottom: 15px;"
|
||
:disabled="registerLoading">
|
||
</el-input>
|
||
|
||
<el-input
|
||
v-model="registerForm.confirmPassword"
|
||
placeholder="请再次输入密码"
|
||
type="password"
|
||
size="large"
|
||
style="margin-bottom: 20px;"
|
||
:disabled="registerLoading">
|
||
</el-input>
|
||
|
||
<div>
|
||
<el-button
|
||
type="primary"
|
||
size="large"
|
||
:loading="registerLoading"
|
||
:disabled="!canRegister || registerLoading"
|
||
@click="handleRegister"
|
||
style="width: 100%;">
|
||
注册
|
||
</el-button>
|
||
</div>
|
||
|
||
<div style="margin-top: 20px; text-align: center;">
|
||
<el-button type="text" @click="backToLogin" :disabled="registerLoading">
|
||
已有账号?返回登录
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
</template>
|
||
<style scoped>
|
||
.auth-logo {
|
||
width: 160px;
|
||
height: auto;
|
||
}
|
||
|
||
.auth-dialog {
|
||
--el-color-primary: #1677FF;
|
||
}
|
||
|
||
.auth-dialog :deep(.el-button--primary) {
|
||
background-color: #1677FF;
|
||
border-color: #1677FF;
|
||
}
|
||
|
||
.auth-title-wrap {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.auth-title {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #1f1f1f;
|
||
text-align: left;
|
||
}
|
||
|
||
.auth-subtitle {
|
||
margin: 6px 0 0;
|
||
font-size: 12px;
|
||
color: #8c8c8c;
|
||
text-align: left;
|
||
}
|
||
</style> |