diff --git a/apps/web/CLAUDE.md b/apps/web/CLAUDE.md index 48bb69e..8db7a27 100644 --- a/apps/web/CLAUDE.md +++ b/apps/web/CLAUDE.md @@ -58,7 +58,7 @@ API 调用不按模块分:学生端全在 `oj/api.ts`、后台全在 `admin/ap 跨端的(登录、资料、标签、验证码)在 `shared/api.ts`。 Shared logic lives in `shared/`: -- `store/` — Pinia stores: `user` (auth/roles), `config` (site-wide settings), `authModal` (login/signup form state), `collab` (help-request queue + collab room), `achievement` (解锁弹窗队列), `myFlowchart` (题面「你画的流程图」那张 A/S 图的 mermaid 源码) +- `store/` — Pinia stores: `user` (auth/roles), `config` (site-wide settings), `authModal` (登录框、注册框开没开), `collab` (help-request queue + collab room), `achievement` (解锁弹窗队列), `myFlowchart` (题面「你画的流程图」那张 A/S 图的 mermaid 源码) - `composables/` — `aiStream`, `pagination` (URL-synced), `websocket` (reconnect + heartbeat), `collabDoc` (Yjs binding for the collab channel), `configUpdate` (WS-pushed config sync), `useMermaid` (lazy Mermaid render), `darkTransition` (View Transitions,111 以下走降级分支), `leaveStudents` (课堂看板的「请假」), `problemJump` (按题号直达), `teacherHelp` (老师接单 / 打开学生编辑器), `chartTheme`, `breakpoints`, `rarity` - `layout/` — `default.vue` and `admin.vue` layout wrappers - `api.ts` — shared API calls (auth, profile, tags, captcha) diff --git a/apps/web/src/shared/components/Login.vue b/apps/web/src/shared/components/Login.vue index 97549df..03f5df5 100644 --- a/apps/web/src/shared/components/Login.vue +++ b/apps/web/src/shared/components/Login.vue @@ -1,34 +1,83 @@ - - - - - - - - - - - - - - - {{ msg }} - - - - 登录 - - - 没有账号?立即注册 - - - - - 往届的班级、列表里找不到的班级、以及自己注册的账号,选【没有我所在的班级】,用户名要写完整,比如 - 23 计算机 1 班张三写 ks231张三(姓名可以用拼音或拼音缩写快速查找)。 - - - + + + + 登录 + + + + + + - - - + + + + + + - - - - - {{ msg }} - - 登录 - - - 管理员和老师从这里登录,学生请走【学生登录】页签。 - - - - + {{ error }} + + + 登录 + + + 不是我,换一个名字 + + + + + + + + + + 这个班还没有名单 + 没有叫这个的 + + + {{ name }} + + + + + + + + + {{ group.label }} + + + {{ item.label }} + + + + + + + 名单里没有我 + + 老师登录 + + + 注册 + + + + + + + + 用户名 + + + + 密码 + + {{ error }} + + + 登录 + + + + 按班级选名字 + + + + 注册 + + + + diff --git a/apps/web/src/shared/components/Signup.vue b/apps/web/src/shared/components/Signup.vue index 750a30d..2a9e225 100644 --- a/apps/web/src/shared/components/Signup.vue +++ b/apps/web/src/shared/components/Signup.vue @@ -1,130 +1,287 @@ - - - - - - - - - - - - - - - {{ msg }} - - - 注册 - 已经注册?现在登录 - - - + + + + 注册 + + + + + + + + 用户名 + + {{ errors.username }} + + + 邮箱 + + {{ errors.email }} + + + 密码 + + {{ errors.password }} + + + 再输一遍密码 + + {{ errors.again }} + + {{ general }} + + 注册并登录 + + + + 已经有账号了,去登录 + + + + + + diff --git a/apps/web/src/shared/store/authModal.ts b/apps/web/src/shared/store/authModal.ts index 6016e2d..1332057 100644 --- a/apps/web/src/shared/store/authModal.ts +++ b/apps/web/src/shared/store/authModal.ts @@ -1,157 +1,47 @@ import { defineStore } from "pinia" /** - * 认证状态管理 Store - * 统一管理登录、注册相关的模态框状态和表单状态 + * 登录框、注册框开没开。表单本身在两个组件里:每次打开都从干净的表单开始 + * (机房一台电脑轮流坐人,别把上一个人的名字和密码留在框里),不需要跨组件共享。 */ export const useAuthModalStore = defineStore("authModal", () => { - // ==================== 模态框状态 ==================== const loginModalOpen = ref(false) const signupModalOpen = ref(false) - // ==================== 登录表单状态 ==================== - // class 为 null 表示还没选,别用空串 —— 空串是「没有我所在的班级」那一项的值。 - // username 同理用 null:空串在 n-select 里是「选了一个空值」,占位符不显示。 - const loginForm = reactive({ - class: null as string | null, - username: null as string | null, - password: "", - }) - - const loginLoading = ref(false) - const loginError = ref("") - - // ==================== 注册表单状态 ==================== - const signupForm = reactive({ - username: "", - email: "", - password: "", - passwordAgain: "", - }) - - const signupLoading = ref(false) - const signupError = ref("") - - // ==================== 模态框操作 ==================== - /** - * 打开登录模态框 - */ function openLoginModal() { loginModalOpen.value = true } - /** - * 关闭登录模态框 - */ function closeLoginModal() { loginModalOpen.value = false } - /** - * 打开注册模态框 - */ function openSignupModal() { signupModalOpen.value = true } - /** - * 关闭注册模态框 - */ function closeSignupModal() { signupModalOpen.value = false } - /** - * 从登录切换到注册 - */ function switchToSignup() { closeLoginModal() openSignupModal() } - /** - * 从注册切换到登录 - */ function switchToLogin() { closeSignupModal() openLoginModal() } - // ==================== 登录表单操作 ==================== - /** - * 设置登录加载状态 - */ - function setLoginLoading(loading: boolean) { - loginLoading.value = loading - } - - /** - * 设置登录错误信息 - */ - function setLoginError(error: string) { - loginError.value = error - } - - /** - * 清空登录错误 - */ - function clearLoginError() { - loginError.value = "" - } - - // ==================== 注册表单操作 ==================== - /** - * 设置注册加载状态 - */ - function setSignupLoading(loading: boolean) { - signupLoading.value = loading - } - - /** - * 设置注册错误信息 - */ - function setSignupError(error: string) { - signupError.value = error - } - - /** - * 清空注册错误 - */ - function clearSignupError() { - signupError.value = "" - } - return { - // 模态框状态 loginModalOpen, signupModalOpen, - - // 登录表单状态 - loginForm, - loginLoading, - loginError, - - // 注册表单状态 - signupForm, - signupLoading, - signupError, - - // 模态框操作 openLoginModal, closeLoginModal, openSignupModal, closeSignupModal, switchToSignup, switchToLogin, - - // 登录表单操作 - setLoginLoading, - setLoginError, - clearLoginError, - - // 注册表单操作 - setSignupLoading, - setSignupError, - clearSignupError, } }) diff --git a/apps/web/src/shared/store/user.ts b/apps/web/src/shared/store/user.ts index 04d96fc..f99d6d9 100644 --- a/apps/web/src/shared/store/user.ts +++ b/apps/web/src/shared/store/user.ts @@ -95,11 +95,14 @@ export const useUserStore = defineStore("user", () => { function clearProfile() { profile.value = null demoSwitch.value = false - // 登录框记的班级要跨退登活下来:机房一台机器对一个班,下课登出、下节课再来 - // 还是同一个班,清掉的话每个人都得重新选一遍。它是机器的属性,不是谁的隐私。 - const loginClass = storage.get(STORAGE_KEY.LOGIN_CLASS) + // 登录框记的班级和登法要跨退登活下来:机房一台机器对一个班,下课登出、下节课再来 + // 还是同一个班,清掉的话每个人都得重新选一遍;老师的电脑同理,下次还是写用户名。 + // 它们是机器的属性,不是谁的隐私。 + const keep = [STORAGE_KEY.LOGIN_CLASS, STORAGE_KEY.LOGIN_MODE].map( + (key) => [key, storage.get(key)] as const, + ) storage.clear() - if (loginClass !== null) storage.set(STORAGE_KEY.LOGIN_CLASS, loginClass) + for (const [key, value] of keep) if (value !== null) storage.set(key, value) } // 退登的两步(吊销服务端会话、清本地状态)绑在一起:只清本地会留一个还活着 diff --git a/apps/web/src/utils/constants.ts b/apps/web/src/utils/constants.ts index abd5a46..d951a86 100644 --- a/apps/web/src/utils/constants.ts +++ b/apps/web/src/utils/constants.ts @@ -157,6 +157,7 @@ export const STORAGE_KEY = { ADMIN_SIDER_COLLAPSED: "adminSiderCollapsed", DEMO_MODE: "demoMode", LOGIN_CLASS: "loginClass", + LOGIN_MODE: "loginMode", RANK_CLASS: "rankClass", }