diff --git a/lesingle-creation-frontend/src/views/public/Login.vue b/lesingle-creation-frontend/src/views/public/Login.vue index 18f8367..6cf0b31 100644 --- a/lesingle-creation-frontend/src/views/public/Login.vue +++ b/lesingle-creation-frontend/src/views/public/Login.vue @@ -7,84 +7,41 @@ {{ isRegister ? "加入乐绘世界,开启创作之旅" : "欢迎回来,继续你的创作" }}

- + - + - +
- - + + {{ smsCountdown > 0 ? `${smsCountdown}s 后重发` : "获取验证码" }}
- - + + - + - + - + {{ isRegister ? "注册并登录" : "登录" }} @@ -124,6 +81,7 @@ import { ref, reactive, computed, onUnmounted } from "vue" import { useRouter, useRoute } from "vue-router" import { message } from "ant-design-vue" import { publicAuthApi } from "@/api/public" +import type { FormInstance } from "ant-design-vue" import type { Rule } from "ant-design-vue/es/form" const router = useRouter() @@ -148,7 +106,9 @@ const smsCountdown = ref(0) const smsSending = ref(false) let smsTimer: ReturnType | null = null -const isPhoneValid = computed(() => /^1[3-9]\d{9}$/.test(form.phone)) +const formRef = ref() + +const isPhoneValid = computed(() => /^1[3-9]\d{9}$/.test(form.phone.trim())) // 控制字段显示的 computed const showNicknameField = computed(() => isRegister.value) @@ -157,15 +117,32 @@ const showSmsCodeField = computed(() => isRegister.value || (!isRegister.value & const showUsernameField = computed(() => isRegister.value || (!isRegister.value && loginMethod.value === 'password')) const showPasswordField = computed(() => isRegister.value || (!isRegister.value && loginMethod.value === 'password')) -// 基础校验规则 +// 账号名:仅英文字母与数字 +const USERNAME_PATTERN = /^[a-zA-Z0-9]+$/ +// 密码:英文字母、数字、ASCII 可打印特殊字符(不含空格) +const PASSWORD_PATTERN = /^[\x21-\x7E]+$/ + +// 基础校验规则(与后端 PublicRegisterDto / PublicLoginDto 一致) const baseRules: Record = { username: [ - { required: true, message: "请输入用户名", trigger: "blur" }, - { min: 4, message: "用户名至少4个字符", trigger: "blur" }, + { required: true, message: "请输入账号名", trigger: "blur" }, + { min: 4, message: "账号名为4-12个字符", trigger: "blur" }, + { max: 12, message: "账号名为4-12个字符", trigger: "blur" }, + { + pattern: USERNAME_PATTERN, + message: "账号名只能包含英文字母与数字", + trigger: "blur", + }, ], password: [ { required: true, message: "请输入密码", trigger: "blur" }, - { min: 6, message: "密码至少6个字符", trigger: "blur" }, + { min: 6, message: "密码为6-16个字符", trigger: "blur" }, + { max: 16, message: "密码为6-16个字符", trigger: "blur" }, + { + pattern: PASSWORD_PATTERN, + message: "密码只能包含英文字母、数字与特殊字符", + trigger: "blur", + }, ], confirmPassword: [ { required: true, message: "请确认密码", trigger: "blur" }, @@ -181,7 +158,8 @@ const baseRules: Record = { ], nickname: [ { required: true, message: "请输入昵称", trigger: "blur" }, - { min: 2, message: "昵称至少2个字符", trigger: "blur" }, + { min: 2, message: "昵称为2-20个字符", trigger: "blur" }, + { max: 20, message: "昵称为2-20个字符", trigger: "blur" }, ], phone: [ { required: true, message: "请输入手机号", trigger: "blur" }, @@ -218,6 +196,7 @@ const currentRules = computed(() => { }) const handleSendSms = async () => { + form.phone = form.phone.trim() if (!isPhoneValid.value || smsCountdown.value > 0) return smsSending.value = true try { @@ -259,6 +238,25 @@ const switchToLogin = () => { loginMethod.value = 'password' } +/** 校验前去除各字段前后空格并写回表单,再触发校验 */ +function trimFormFields() { + const t = (v: string) => (typeof v === "string" ? v.trim() : v) + form.nickname = t(form.nickname) + form.phone = t(form.phone) + form.smsCode = t(form.smsCode) + form.username = t(form.username) + form.password = t(form.password) + form.confirmPassword = t(form.confirmPassword) +} + +function submitForm() { + trimFormFields() + formRef.value + ?.validate() + .then(() => handleSubmit()) + .catch(() => { }) +} + const handleSubmit = async () => { loading.value = true try {