feat: support nickname login and nickname uniqueness check v0.0.3
- Login: query user by name field, authenticate with username - Register: add blur-triggered nickname uniqueness validation - Requires PocketBase users collection listRule/viewRule set to public Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { pb } from '@/api/pocketbase'
|
||||
import PasswordInput from '@/components/common/PasswordInput.vue'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -15,16 +16,29 @@ const loading = ref(false)
|
||||
|
||||
async function handleLogin() {
|
||||
if (!identity.value || !password.value) {
|
||||
ElMessage.warning('请输入邮箱和密码')
|
||||
ElMessage.warning('请输入昵称/邮箱和密码')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
await userStore.login(identity.value, password.value)
|
||||
let loginIdentity = identity.value.trim()
|
||||
|
||||
const redirect = '/'
|
||||
router.push(redirect)
|
||||
// 如果不包含 @,按昵称或用户名查找对应 username 用于认证
|
||||
if (!loginIdentity.includes('@')) {
|
||||
const result = await pb.collection('users').getList(1, 1, {
|
||||
filter: `name="${loginIdentity}" || username="${loginIdentity}"`,
|
||||
$autoCancel: false
|
||||
})
|
||||
if (result.items.length === 0) {
|
||||
ElMessage.error('用户不存在')
|
||||
return
|
||||
}
|
||||
loginIdentity = (result.items[0] as any).username
|
||||
}
|
||||
|
||||
await userStore.login(loginIdentity, password.value)
|
||||
router.push('/')
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '登录失败')
|
||||
} finally {
|
||||
@@ -45,12 +59,11 @@ async function handleLogin() {
|
||||
|
||||
<form class="login-form" @submit.prevent="handleLogin">
|
||||
<div class="form-group">
|
||||
<label for="identity">邮箱</label>
|
||||
<label for="identity">昵称 / 邮箱</label>
|
||||
<el-input
|
||||
id="identity"
|
||||
v-model="identity"
|
||||
type="email"
|
||||
placeholder="请输入注册时的邮箱"
|
||||
placeholder="请输入昵称或邮箱"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { pb } from '@/api/pocketbase'
|
||||
import PasswordInput from '@/components/common/PasswordInput.vue'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -17,6 +18,21 @@ const formError = ref('')
|
||||
|
||||
// ── 实时校验 ──
|
||||
const nicknameOk = computed(() => nickname.value.length >= 2 && nickname.value.length <= 16)
|
||||
const nicknameTaken = ref(false)
|
||||
const nicknameChecking = ref(false)
|
||||
|
||||
async function checkNickname() {
|
||||
const val = nickname.value.trim()
|
||||
if (!val || val.length < 2) { nicknameTaken.value = false; return }
|
||||
try {
|
||||
nicknameChecking.value = true
|
||||
const result = await pb.collection('users').getList(1, 1, {
|
||||
filter: `name="${val}"`,
|
||||
$autoCancel: false
|
||||
})
|
||||
nicknameTaken.value = result.items.length > 0
|
||||
} catch { /* ignore */ } finally { nicknameChecking.value = false }
|
||||
}
|
||||
|
||||
const hasLetter = computed(() => /[a-zA-Z]/.test(password.value))
|
||||
const hasDigit = computed(() => /[0-9]/.test(password.value))
|
||||
@@ -46,6 +62,10 @@ async function handleRegister() {
|
||||
formError.value = '昵称需 2-16 个字符'
|
||||
return
|
||||
}
|
||||
if (nicknameTaken.value) {
|
||||
formError.value = '该昵称已被使用'
|
||||
return
|
||||
}
|
||||
if (!passwordOk.value) {
|
||||
formError.value = '密码不符合要求'
|
||||
return
|
||||
@@ -89,7 +109,10 @@ async function handleRegister() {
|
||||
<!-- 昵称 -->
|
||||
<div class="form-group">
|
||||
<label for="nickname">昵称</label>
|
||||
<el-input id="nickname" v-model="nickname" placeholder="2-16 个字符,支持中文" required />
|
||||
<el-input id="nickname" v-model="nickname" placeholder="2-16 个字符,支持中文" required @blur="checkNickname" />
|
||||
<span v-if="nicknameChecking" class="field-hint">检查中...</span>
|
||||
<span v-else-if="nicknameTaken && nickname.length >= 2" class="field-hint error">该昵称已被使用</span>
|
||||
<span v-else-if="nicknameOk && nickname" class="field-hint ok">昵称可用</span>
|
||||
<div class="field-rules">
|
||||
<span class="rule" :class="{ ok: nicknameOk && nickname }">2-16 个字符,支持中英文</span>
|
||||
</div>
|
||||
@@ -259,6 +282,10 @@ async function handleRegister() {
|
||||
color: var(--gg-danger);
|
||||
}
|
||||
|
||||
.field-hint.ok {
|
||||
color: var(--gg-primary);
|
||||
}
|
||||
|
||||
/* ── 密码强度 ── */
|
||||
.password-strength {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user