feat(名字): 界面上不再显示 ks + 班号前缀,混着几个班的地方名字后面写班级
Deploy / deploy (push) Canceled after 0s

- 学生名字一律只显示名字(老号 ks232张三 显示成 张三),顶栏、首页问候、悬停提示、请假名单、比赛成绩表都一样
- 全服 / 年级排名、全部提交、几个班同时举手时,名字后面淡写班级;提交列表的名字列窄,只写班号(陈长 258)
- AI 班级分析点名「要多关心的同学」时不再带 ks 前缀
- 建号查重补漏:同班同名也比真名(加过数字的张三2、编号制老号);名字自己带数字(王芳1)也认得出被占;
  导入时后端再拦一次同班同名,建号页要等查重结果对上当前名单才能点
- 登录名单换成新接口 /classes/:c/members(名字按界面规则剥,换过班的老号不再露出全名),
  旧接口原样留给上线时还开着旧登录页的机房电脑
- 后台手写老格式 ks232张三 新建用户、没选班时按前缀补班级;PUT /me/profile 只带真名时不再 500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-02 23:41:49 +08:00
1 parent 221d264701
commit 2bda05a978
32 files changed
+229 -111

No files matched your search

+2 -1
View File
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { displayName } from '@oj2/contract'
import type { DashboardInfo } from '@oj2/contract'
import { BarElement, CategoryScale, Chart as ChartJS, LinearScale, Tooltip } from 'chart.js'
import { useThemeVars } from 'naive-ui'
@@ -146,7 +147,7 @@ onUnmounted(stopRolling)
</script>
<template>
<PageHeader :title="`你好,${userStore.user?.username ?? '管理员'}`" />
<PageHeader :title="`你好,${userStore.user ? displayName(userStore.user.username) : '管理员'}`" />
<div v-if="info" class="tiles">
<div class="tile">
+16 -5
View File
@@ -86,25 +86,36 @@ const duplicates = computed(() => {
// 这个班已经有的同名账号(老号按剥掉 ks 前缀的名字比),顺带拿回每个名字会建成的用户名:停手半秒再去查
const existing = ref<{ name: string; username: string; createTime: string | null }[]>([])
const checking = ref(false)
/** 查重结果对应的是哪一份「班级 + 名单」:和眼前的对不上(刚改过、还在防抖)就不让建 */
const inputKey = computed(() => `${klass.value}\n${names.value.join('\n')}`)
const checkedKey = ref('')
// 改得快时前一次请求可能后回来,只认最后发出去的那次
let checkSeq = 0
watchDebounced(
() => `${klass.value}\n${names.value.join('\n')}`,
async () => {
inputKey,
async (key) => {
const seq = ++checkSeq
const list = [...new Set(names.value)]
if (!list.length || !klass.value || list.length > 500) {
existing.value = []
suggested.value = new Map()
checkedKey.value = ''
return
}
checking.value = true
try {
const res = await checkUsernames(klass.value, list)
if (seq !== checkSeq) return
existing.value = res.existing
suggested.value = new Map(list.map((name, i) => [name, res.usernames[i] ?? name]))
checkedKey.value = key
} catch {
if (seq !== checkSeq) return
existing.value = []
suggested.value = new Map()
checkedKey.value = ''
} finally {
checking.value = false
if (seq === checkSeq) checking.value = false
}
},
{ debounce: 500, immediate: true },
@@ -112,7 +123,7 @@ watchDebounced(
const tooMany = computed(() => rows.value.length > 500)
const issueCount = computed(() => (classError.value ? 1 : 0) + duplicates.value.length + existing.value.length + (tooMany.value ? 1 : 0))
const canSubmit = computed(() => rows.value.length > 0 && !issueCount.value && !checking.value && !!klass.value)
const canSubmit = computed(() => rows.value.length > 0 && !issueCount.value && !checking.value && !!klass.value && checkedKey.value === inputKey.value)
// ---------------------------------------------------------------- 建号
@@ -220,7 +231,7 @@ body{margin:12mm;font-family:"Microsoft YaHei","PingFang SC",sans-serif}
<template v-if="rows.length">
<div class="head">
<b>会建这 {{ rows.length }} 个账号</b>
<n-tag v-if="!issueCount && !checking && klass" type="success" :bordered="false" round> 没有重名 </n-tag>
<n-tag v-if="!issueCount && !checking && klass && checkedKey === inputKey" type="success" :bordered="false" round> 没有重名 </n-tag>
<n-tag v-if="issueCount" type="warning" :bordered="false" round> 有 {{ issueCount }} 处要先改 </n-tag>
</div>
+13 -2
View File
@@ -13,7 +13,7 @@ import UserNameCell from './components/UserNameCell.vue'
import PasswordCell from './components/PasswordCell.vue'
import { PROBLEM_PERMISSION, USER_TYPE } from 'utils/constants'
import { useRouteQuery } from '@vueuse/router'
import { NO_CLASS, STUDENT_ROLES, type ImportUsersRequest } from '@oj2/contract'
import { legacyClassOf, NO_CLASS, STUDENT_ROLES, type ImportUsersRequest } from '@oj2/contract'
import { downloadAccountsCsv } from './accountsCsv'
import { classLabel, groupClassOptions } from 'utils/classes'
import { useConfigStore } from 'shared/store/config'
@@ -315,10 +315,21 @@ async function handleEditUser() {
}
// http 拦截器只对 login-required / permission-denied 自动弹提示,
// 其余业务错误(比如班级号位数不对)不接住就什么都不显示
if (create.value && !userEditing.value.className && legacyClassOf(userEditing.value.username) === undefined) {
message.error('用户名里 ks 后面的班级号位数不对,班级号要是 3~4 位数字')
return
}
try {
if (create.value) {
const newUser: ImportUsersRequest['users'] = [
[userEditing.value.username, password.value, userEditing.value.email ?? '', userEditing.value.realName ?? '', userEditing.value.className],
[
userEditing.value.username,
password.value,
userEditing.value.email ?? '',
userEditing.value.realName ?? '',
// 没选班、但手写的是老格式 ks232张三:按前缀补上班级
userEditing.value.className ?? legacyClassOf(userEditing.value.username) ?? null,
],
]
await importUsers(newUser)
listUsers()