From 9405406c4701ece012cbcff4f125fdd36128b24b Mon Sep 17 00:00:00 2001 From: congsh Date: Sat, 18 Apr 2026 01:24:12 +0800 Subject: [PATCH] fix: login persistence, username login, realtime refresh, group name uniqueness - Fix cookie path to '/' for auth persistence across page refreshes - Login field now accepts both username and email - Add 30s polling for group list and team session status refresh - Add group name uniqueness check before creation Co-Authored-By: Claude Opus 4.7 --- frontend/src/api/pocketbase.ts | 2 +- .../src/components/group/CreateGroupDialog.vue | 14 ++++++++++++++ frontend/src/views/Layout.vue | 12 ++++++++++++ frontend/src/views/Login.vue | 17 ++++++++--------- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/frontend/src/api/pocketbase.ts b/frontend/src/api/pocketbase.ts index 86018b8..982889a 100644 --- a/frontend/src/api/pocketbase.ts +++ b/frontend/src/api/pocketbase.ts @@ -10,7 +10,7 @@ pb.authStore.loadFromCookie(document.cookie) // 保存认证状态到 cookie pb.authStore.onChange(() => { - document.cookie = pb.authStore.exportToCookie({ httpOnly: false }) + document.cookie = pb.authStore.exportToCookie({ httpOnly: false, path: '/' }) }) // 获取当前用户 diff --git a/frontend/src/components/group/CreateGroupDialog.vue b/frontend/src/components/group/CreateGroupDialog.vue index 9e122a2..b453ed8 100644 --- a/frontend/src/components/group/CreateGroupDialog.vue +++ b/frontend/src/components/group/CreateGroupDialog.vue @@ -4,6 +4,7 @@ import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { useGroupStore } from '@/stores/group' import { createGroup } from '@/api/groups' +import pb from '@/api/pocketbase' const props = defineProps<{ modelValue: boolean @@ -33,7 +34,20 @@ async function handleSubmit() { ElMessage.warning('请输入群组名称') return } + + // 检查群组名是否重复 loading.value = true + try { + const existing = await pb.collection('groups').getList(1, 1, { + filter: `name="${form.value.name.trim()}"` + }) + if (existing.items.length > 0) { + ElMessage.warning('该群组名称已存在,请换一个') + loading.value = false + return + } + } catch { /* ignore */ } + try { const group = await createGroup({ name: form.value.name.trim(), diff --git a/frontend/src/views/Layout.vue b/frontend/src/views/Layout.vue index 3586e48..3d9b3b0 100644 --- a/frontend/src/views/Layout.vue +++ b/frontend/src/views/Layout.vue @@ -23,16 +23,28 @@ const showScheduleModal = ref(false) const showCreateGroup = ref(false) const showJoinGroup = ref(false) +let refreshTimer: ReturnType | null = null + onMounted(async () => { await userStore.initUser() await groupStore.loadGroups() await teamStore.loadActiveSession() await notificationStore.loadPendingInvitations() await notificationStore.startListening() + + // 每30秒轮询刷新群组列表和临时小组状态 + refreshTimer = setInterval(async () => { + await groupStore.loadGroups() + await teamStore.loadActiveSession() + }, 30000) }) onUnmounted(() => { notificationStore.stopListening() + if (refreshTimer) { + clearInterval(refreshTimer) + refreshTimer = null + } }) function handleLogout() { diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index 02a14c6..9d975b7 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -9,19 +9,19 @@ import PasswordInput from '@/components/common/PasswordInput.vue' const router = useRouter() const userStore = useUserStore() -const email = ref('') +const identity = ref('') const password = ref('') const loading = ref(false) async function handleLogin() { - if (!email.value || !password.value) { - ElMessage.warning('请输入邮箱和密码') + if (!identity.value || !password.value) { + ElMessage.warning('请输入用户名/邮箱和密码') return } try { loading.value = true - await userStore.login(email.value, password.value) + await userStore.login(identity.value, password.value) const redirect = '/' router.push(redirect) @@ -45,12 +45,11 @@ async function handleLogin() {