From 4654615f899d95b7550367d60c25fc86a41b49ff Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Tue, 1 Sep 2026 07:25:21 -0600 Subject: [PATCH] =?UTF-8?q?feat(=E7=99=BB=E5=BD=95):=20=E5=A7=93=E5=90=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8B=BC=E9=9F=B3=E5=92=8C=E6=8B=BC=E9=9F=B3?= =?UTF-8?q?=E7=BC=A9=E5=86=99=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 机房电脑打中文要切输入法,选姓名前先切一次法太别扭。现在 zhangsan、zs、 xiaoxian 都能搜到张三/曾小贤,中文照旧能打。 用 pinyin-pro 的 match 而不是自己拼一份拼音串比对:多音字它两个读音都认, 单田芳搜 stf 和 dtf 都出得来,曾小贤搜 zxx 和 cengxiaoxian 也都出得来 —— 自己拼只会留下默认读音那一个,姓氏恰恰是多音字重灾区。 词典有 85 KB(gzip),静态引入会压进首屏,所以改成打开登录框才 import。 已登录的人一次都不会下载(构建产物里它是独立 chunk,只被 default 布局 动态引用;实测页面加载 0 个请求,打开登录框才有 1 个)。拉不下来时退回 纯中文匹配,不至于让人登不上。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01YQCLjAmz6pPXMNGoZmvAmC --- apps/web/package.json | 1 + apps/web/src/shared/components/Login.vue | 27 +++++++++++++++++++++++- bun.lock | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/apps/web/package.json b/apps/web/package.json index e596a16..ae8f54e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -48,6 +48,7 @@ "nanoid": "^6.0.1", "normalize.css": "^8.0.1", "pinia": "^4.0.3", + "pinyin-pro": "^3.29.3", "skulpt": "^1.2.0", "vue": "^3.5.41", "vue-chartjs": "^5.3.4", diff --git a/apps/web/src/shared/components/Login.vue b/apps/web/src/shared/components/Login.vue index ac19ba9..e6b1847 100644 --- a/apps/web/src/shared/components/Login.vue +++ b/apps/web/src/shared/components/Login.vue @@ -26,6 +26,28 @@ const adminRef = useTemplateRef("adminRef") const studentPasswordRef = useTemplateRef("studentPasswordRef") const classUserOptions = ref([]) const classUserLoading = ref(false) +// 姓名支持拼音和拼音缩写搜索(张三 ← zhangsan / zs)。pinyin-pro 带着一份几百 K 的 +// 词典,静态引入会压进首屏 —— 改成打开登录框才拉,已登录的人一次都不会下载。 +// 用 match 而不是自己拼拼音串:多音字它两个读音都认(单田芳 ← stf 和 dtf 都能搜到)。 +type MatchFn = typeof import("pinyin-pro").match +const pinyinMatch = shallowRef(null) +async function ensurePinyin() { + if (pinyinMatch.value) return + try { + pinyinMatch.value = (await import("pinyin-pro")).match + } catch { + // 拉不下来就只剩中文匹配,不至于让人登不上 + } +} +function filterClassUser(pattern: string, option: SelectOption) { + const keyword = pattern.trim() + if (!keyword) return true + const label = String(option.label ?? "") + if (label.includes(keyword)) return true + return pinyinMatch.value + ? pinyinMatch.value(label, keyword, { continuous: true }) !== null + : false +} // 空串是「没有我所在的班级」,null 才是没选 —— 往届的班级毕业后会从后台配置里删掉, // 这一项是他们唯一的入口,写完整用户名登录 const OTHER_CLASS = "" @@ -179,6 +201,7 @@ watch(classList, restoreLastClass) // 每次打开都从干净的表单开始,别把上一个人的姓名和密码留在框里 watch(loginModalOpen, (open) => { if (!open) return + ensurePinyin() resetForm() studentRef.value?.restoreValidation() adminRef.value?.restoreValidation() @@ -188,6 +211,7 @@ watch(loginModalOpen, (open) => { onMounted(() => { authStore.clearLoginError() restoreLastClass() + if (loginModalOpen.value) ensurePinyin() }) @@ -228,9 +252,10 @@ onMounted(() => { :options="classUserOptions" :loading="classUserLoading" filterable + :filter="filterClassUser" name="class-username" id="login-class-username" - placeholder="选择姓名" + placeholder="选择姓名,也可以打拼音" @update:value="onUsernamePicked" /> =5.6.0", "vue": "^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA=="], + "pinyin-pro": ["pinyin-pro@3.29.3", "", {}, "sha512-+UU9bx6vfDw8amOJGHm0TE0rdQl8VPylsDWviQ5OOQ3e+on1xRP4OqDbiDuMT5OISgvfl/Y6ez1BBRaIP80GLQ=="], + "pkg-types": ["pkg-types@2.3.1", "https://registry.npmjs.com/pkg-types/-/pkg-types-2.3.1.tgz", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], "points-on-curve": ["points-on-curve@0.2.0", "https://registry.npmjs.com/points-on-curve/-/points-on-curve-0.2.0.tgz", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="],