feat(web): 申请页改用中文姓名,前端转拼音提交

- ApplyView 的'姓名'字段(2~10 个汉字)替代自定义用户名
- 前端经 pinyin-pro(toneType:none + surname:head 处理姓氏多音字)转拼音作为用户名
- 提交前兜底校验拼音用户名长度(后端规则 [a-z][a-z0-9]{1,31})
- alert 文案改为实时预览 '通过后自动创建账号 ext_<username>'
- 新增依赖 pinyin-pro@3.29.3
This commit is contained in:
2026-08-30 17:28:05 +08:00
parent 7340a3dd48
commit ab3a874556
3 changed files with 44 additions and 15 deletions
+7
View File
@@ -12,6 +12,7 @@
"axios": "^1.7.9",
"element-plus": "^2.9.3",
"pinia": "^2.3.1",
"pinyin-pro": "^3.29.3",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
@@ -1830,6 +1831,12 @@
}
}
},
"node_modules/pinyin-pro": {
"version": "3.29.3",
"resolved": "https://registry.npmjs.org/pinyin-pro/-/pinyin-pro-3.29.3.tgz",
"integrity": "sha512-+UU9bx6vfDw8amOJGHm0TE0rdQl8VPylsDWviQ5OOQ3e+on1xRP4OqDbiDuMT5OISgvfl/Y6ez1BBRaIP80GLQ==",
"license": "MIT"
},
"node_modules/postcss": {
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+1
View File
@@ -14,6 +14,7 @@
"axios": "^1.7.9",
"element-plus": "^2.9.3",
"pinia": "^2.3.1",
"pinyin-pro": "^3.29.3",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
+36 -15
View File
@@ -1,26 +1,24 @@
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { pinyin } from 'pinyin-pro'
import * as api from '@/api'
const router = useRouter()
const submitting = ref(false)
const form = reactive({
username: '',
realName: '', // 中文姓名,提交时转换为拼音作为用户名
email: '',
supervisor: '',
purpose: '',
})
// 前端校验:中文姓名、邮箱、挂靠老师、用途均在前端完成
const rules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{
pattern: /^[a-z][a-z0-9_]{1,30}$/,
message: '小写字母开头,仅小写字母/数字/下划线,2~31 位',
trigger: 'blur',
},
realName: [
{ required: true, message: '请输入中文姓名', trigger: 'blur' },
{ pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入 2~10 个汉字', trigger: 'blur' },
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
@@ -32,21 +30,44 @@ const rules = {
const formRef = ref()
// 姓名 → 拼音(小写、无空格;首字按姓氏读音处理多音字)
function nameToPinyin(name: string): string {
const parts = pinyin(name, { toneType: 'none', type: 'array', surname: 'head' })
return parts.join('')
}
// 实时预览将创建的账号名;姓名非法或未输入时返回空
const accountPreview = computed(() => {
const n = form.realName.trim()
if (!/^[\u4e00-\u9fa5]{2,10}$/.test(n)) return ''
return `ext_${nameToPinyin(n)}`
})
// 提交前的前端校验(姓名→拼音合法性兜底,防止姓名过长)
function validUsername(uname: string): boolean {
return /^[a-z][a-z0-9]{1,31}$/.test(uname)
}
async function onSubmit() {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
const uname = nameToPinyin(form.realName.trim())
if (!validUsername(uname)) {
ElMessage.warning('姓名拼音过长,无法生成可用账号,请联系管理员')
return
}
submitting.value = true
try {
await api.submitApproval({
username: form.username.trim(),
username: uname,
email: form.email.trim(),
supervisor: form.supervisor.trim(),
purpose: form.purpose.trim(),
})
ElMessage.success('申请已提交,等待管理员审批(结果将邮件通知)')
Object.assign(form, { username: '', email: '', supervisor: '', purpose: '' })
Object.assign(form, { realName: '', email: '', supervisor: '', purpose: '' })
} catch {
// 拦截器已提示(如用户名已被占用
// 拦截器已提示(如拼音与已有申请重名
} finally {
submitting.value = false
}
@@ -65,14 +86,14 @@ async function onSubmit() {
</div>
<el-alert type="info" :closable="false" show-icon class="apply-tip">
通过后自动创建账号用户名将带 <code>ext_</code> 前缀
通过后自动创建账号 {{ accountPreview || 'ext_<username>' }}
</el-alert>
<el-divider />
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px" @submit.prevent="onSubmit">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" placeholder="如 zhangsan(自动成为 ext_zhangsan" maxlength="31" />
<el-form-item label="名" prop="realName">
<el-input v-model="form.realName" placeholder="输入中文姓名,如 张三" maxlength="10" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="接收通知与 OTP 验证码" />