From bf589dce92f79a4cb993a069383884f79cb36278 Mon Sep 17 00:00:00 2001 From: Zyronon Date: Tue, 11 Nov 2025 11:47:59 +0000 Subject: [PATCH] save --- src/apis/auth.ts | 23 +- src/assets/css/style.scss | 7 +- src/components/BaseButton.vue | 1 + src/components/base/BaseInput.vue | 133 ++-- src/components/base/form/Form.vue | 24 +- src/components/base/form/FormItem.vue | 81 ++- src/components/base/form/types.ts | 65 ++ src/config/auth.ts | 4 +- src/pages/layout.vue | 8 +- src/pages/user/Notice.vue | 15 + src/pages/user/login.vue | 864 +++++++++++--------------- src/router.ts | 4 +- src/stores/auth.ts | 1 + src/utils/validation.ts | 164 +---- 14 files changed, 662 insertions(+), 732 deletions(-) create mode 100644 src/components/base/form/types.ts create mode 100644 src/pages/user/Notice.vue diff --git a/src/apis/auth.ts b/src/apis/auth.ts index f557cd50..4a4891e3 100644 --- a/src/apis/auth.ts +++ b/src/apis/auth.ts @@ -62,7 +62,23 @@ export interface WechatLoginParams { // API 函数定义 export function login(params: LoginParams) { - return http('auth/login', params, null, 'post') + // 暂时直接返回成功响应,等待后端接入 + return Promise.resolve({ + success: true, + code: 200, + msg: '登录成功', + data: { + token: 'mock_token_' + Date.now(), + user: { + id: '1', + email: params.email, + phone: params.phone, + nickname: '测试用户', + avatar: '' + } + } + }) + // return http('auth/login', params, null, 'post') } export function register(params: RegisterParams) { @@ -70,6 +86,11 @@ export function register(params: RegisterParams) { } export function sendCode(params: SendCodeParams) { + return Promise.resolve({ + success: true, + code: 200, + msg: '登录成功', + }) return http('auth/sendCode', params, null, 'post') } diff --git a/src/assets/css/style.scss b/src/assets/css/style.scss index 6f357ea5..1659ba57 100644 --- a/src/assets/css/style.scss +++ b/src/assets/css/style.scss @@ -72,7 +72,7 @@ --color-progress-bar: #d1d5df !important; --color-label-bg: whitesmoke; - --color-link: rgb(64, 158, 255) + --color-link: #2563EB; } .footer { @@ -219,6 +219,11 @@ a { text-decoration: none; } +.link{ + color: var(--color-link); + @apply hover:opacity-80; +} + .cp{ @apply cursor-pointer; } diff --git a/src/components/BaseButton.vue b/src/components/BaseButton.vue index 2f92769a..9918a4f3 100644 --- a/src/components/BaseButton.vue +++ b/src/components/BaseButton.vue @@ -86,6 +86,7 @@ defineEmits(['click']) padding: 0 1.3rem; height: 2.4rem; font-size: 0.9rem; + border-radius: .5rem; } & > span { diff --git a/src/components/base/BaseInput.vue b/src/components/base/BaseInput.vue index 4a0533c7..eb03222e 100644 --- a/src/components/base/BaseInput.vue +++ b/src/components/base/BaseInput.vue @@ -1,13 +1,18 @@ diff --git a/src/components/base/form/Form.vue b/src/components/base/form/Form.vue index 94095f90..7f6ac7ea 100644 --- a/src/components/base/form/Form.vue +++ b/src/components/base/form/Form.vue @@ -5,7 +5,8 @@ diff --git a/src/components/base/form/FormItem.vue b/src/components/base/form/FormItem.vue index 0247c43f..8f6ba091 100644 --- a/src/components/base/form/FormItem.vue +++ b/src/components/base/form/FormItem.vue @@ -11,7 +11,7 @@ let error = $ref('') // 拿到 form 的 model 和注册函数 const formModel = inject('formModel') -const registerField = inject('registerField') +const registerField = inject('registerField') const formRules = inject('formRules', {}) const myRules = $computed(() => { @@ -31,43 +31,94 @@ const validate = (rules) => { error = rule.message return false } + if (rule.min && val && val.toString().length < rule.min) { + error = rule.message + return false + } + if (rule.max && val && val.toString().length > rule.max) { + error = rule.message + return false + } + if (rule.validator) { + try { + rule.validator(rule, val) + return true + } catch (e) { + error = e.message + return false + } + } } return true } // 自动触发 blur 校验 -const handleBlur = () => { +function handleBlur() { const blurRules = myRules.filter((r) => r.trigger === 'blur') if (blurRules.length) validate(blurRules) } +function handChange() { + error = '' +} + // 注册到 Form onMounted(() => { registerField && registerField({prop: props.prop, modelValue: value, validate}) }) + let slot = useSlots() + +function patchVNode(vnode, patchFn) { + if (!vnode) return vnode + + // 如果当前节点就是我们要找的 BaseInput + if (vnode.type && vnode.type.name) { + return patchFn(vnode) + } + + // 如果有子节点,则递归修改 + if (Array.isArray(vnode.children)) { + vnode.children = vnode.children.map(child => patchVNode(child, patchFn)) + } + + return vnode +} + + defineRender(() => { - let DefaultNode = slot.default()[0] - return
+ let DefaultNode: any = slot.default()[0] + + // 对 DefaultNode 深度查找 BaseInput 并加上 onBlur / error + DefaultNode = patchVNode(DefaultNode, vnode => { + return { + ...vnode, + props: { + ...vnode.props, + error: !!error, + onBlur: handleBlur, + onChange: handChange + }, + } + }) + + return
{props.label && - } + }
- -
{error}
+ +
{error}  
}) diff --git a/src/components/base/form/types.ts b/src/components/base/form/types.ts new file mode 100644 index 00000000..f3834630 --- /dev/null +++ b/src/components/base/form/types.ts @@ -0,0 +1,65 @@ +// Form 组件的 TypeScript 类型定义 + +// 表单字段接口 +export interface FormField { + prop: string + modelValue: any + validate: (rules: FormRule[]) => boolean +} + +// 表单规则接口 +export interface FormRule { + required?: boolean + message?: string + pattern?: RegExp + validator?: (rule: FormRule, value: any, callback: (error?: Error) => void) => void + min?: number + max?: number + len?: number + type?: string +} + +// 表单规则对象类型 +export type FormRules = Record + +// 表单模型对象类型 +export type FormModel = Record + +// Form 组件的 Props 接口 +export interface FormProps { + model?: FormModel + rules?: FormRules +} + +// Form 组件的实例接口 +export interface FormInstance { + /** + * 校验整个表单 + * @param callback 校验完成后的回调函数,接收校验结果 + */ + validate: (callback: (valid: boolean) => void) => void + + /** + * 校验指定字段 + * @param fieldName 要校验的字段名称 + * @param callback 可选的回调函数,接收校验结果 + * @returns 校验是否通过 + */ + validateField: (fieldName: string, callback?: (valid: boolean) => void) => boolean +} + +// 注入的上下文类型 +export interface FormContext { + registerField: (field: FormField) => void + formModel: FormModel + formValidate: (callback: (valid: boolean) => void) => void + formRules: FormRules +} + +// 验证状态枚举 +export enum ValidateStatus { + Success = 'success', + Error = 'error', + Validating = 'validating', + Pending = 'pending' +} \ No newline at end of file diff --git a/src/config/auth.ts b/src/config/auth.ts index 69b957c2..4590b319 100644 --- a/src/config/auth.ts +++ b/src/config/auth.ts @@ -30,7 +30,7 @@ export const PHONE_CONFIG = { sendInterval: 60, // 手机号正则表达式(中国大陆) - phoneRegex: /^1[3-9]\d{9}$/ + phoneRegex: /^1[2-9]\d{9}$/ } // 邮箱配置 @@ -45,7 +45,7 @@ export const EMAIL_CONFIG = { // 密码配置 export const PASSWORD_CONFIG = { // 密码最小长度 - minLength: 6, + minLength: 9, // 密码最大长度 maxLength: 20 diff --git a/src/pages/layout.vue b/src/pages/layout.vue index 52bcf7cc..ba5cdf60 100644 --- a/src/pages/layout.vue +++ b/src/pages/layout.vue @@ -44,10 +44,10 @@ function goHome() { 设置
- - - - +
+ + 用户 +
+ + + + diff --git a/src/pages/user/login.vue b/src/pages/user/login.vue index 8c03e966..c7ba312b 100644 --- a/src/pages/user/login.vue +++ b/src/pages/user/login.vue @@ -1,14 +1,18 @@ -