Quasar企业用户注册表单(含头像上传功能)实现文档
企业用户注册表单(含头像上传功能)实现文档
一、功能概述
本文档提供一个企业级用户注册表单的完整实现,核心功能包括:
- 基础信息注册(手机号、邮箱、密码等)
- 头像上传与预览(支持图片选择、预览、移除及文件验证)
- 表单验证(含密码强度检测)
- 注册状态反馈与跳转
二、核心功能说明
|
功能模块 |
描述 |
|
头像上传组件 |
点击头像区域触发文件选择,支持 JPG/PNG/GIF 格式,最大 2MB。 |
|
预览与编辑 |
实时预览选中图片,悬浮提示可编辑,支持移除已选头像。 |
|
文件验证 |
自动检测文件类型和大小,提供友好错误提示(如“文件过大,请选择小于 2MB 的图片”)。 |
|
表单数据处理 |
使用FormData提交多部分表单数据(含文件),兼容后端文件接收逻辑。 |
|
内存优化 |
组件卸载或移除头像时释放预览 URL,避免内存泄漏。 |
三、完整代码实现
<template>
<q-card class="enterprise-register-card">
<!-- 注册头部 -->
<q-card-section class="bg-primary text-white text-center">
<div class="text-h5 text-weight-bold">企业用户注册</div>
</q-card-section>
<q-card-section>
<q-form @submit.prevent="onSubmit" class="q-gutter-md">
<!-- 头像上传 -->
<div class="row items-center q-mb-md">
<div class="col-12">
<div class="text-subtitle2 q-mb-sm">头像</div>
</div>
<div class="col-auto">
<!-- 头像预览 -->
<q-avatar size="80px" class="cursor-pointer" @click="triggerAvatarUpload">
<q-img
:src="avatarPreviewUrl || 'default-avatar.png'"
:ratio="( ) => ({ width: 0, height: '100%' })"
class="avatar-preview"
> <!-- 修复 ratio 语法错误 -->
<template v-slot:loading>
<q-spinner-oval color="white" />
</template>
</q-img>
<q-badge
v-if="avatarPreviewUrl"
floating
color="primary"
icon="edit"
class="avatar-edit-badge"
/>
</q-avatar>
</div>
<div class="col q-pl-md">
<div class="text-caption text-grey">
点击头像可上传自定义图片,支持 JPG、PNG 和 GIF 格式,最大 2MB
</div>
<q-btn
v-if="avatarPreviewUrl"
label="移除头像"
flat dense color="negative" size="sm" @click="removeAvatar"
class="q-mt-xs"
/>
</div>
</div>
<!-- 隐藏的文件输入 -->
<q-file
ref="avatarFileInput"
v-model="avatarFile"
accept=".jpg,.jpeg,.png,.gif"
style="display: none"
@update:model-value="handleAvatarSelected"
/>
<!-- 手机号输入 -->
<q-input
v-model="registerData.mobile"
label="手机号 *"
outlined
lazy-rules
:rules="mobileRules"
mask="### #### ####"
unmasked-value
hint="请输入11位中国大陆手机号"
autocomplete="tel"
>
<template v-slot:prepend>
<q-icon name="phone" />
</template>
</q-input>
<!-- 邮箱输入 -->
<q-input
v-model="registerData.email"
label="邮箱"
outlined
type="email" :rules="emailRules" hint="可选,用于找回密码" autocomplete="email"
>
<template v-slot:prepend><q-icon name="email" /></template>
</q-input>
<!-- ...其他表单字段省略(昵称/密码/确认密码等)... -->
<!-- 提交按钮 -->
<div class="q-mt-lg">
<q-btn
label="注册"
type="submit"
color="primary"
:loading="isSubmitting"
class="full-width"
/>
</div>
<!-- 成功注册提示对话框 -->
<q-dialog v-model="showSuccessDialog" persistent>...</q-dialog>
</q-form>
</q-card-section>
<!-- 成功注册提示对话框 -->
<q-dialog v-model="showSuccessDialog" persistent>
<q-card>...</q-card> <!-- 内容省略 -->
</q-dialog>
</q-card>
</template>
<script setup lang="ts">
import { ref, computed, onUnmounted } from 'vue'
import { useQuasar } from 'quasar' import { apiClient } from 'src/services/axios'
import type { AxiosError } from 'axios'
import { useRouter } from 'vue-router' import { logSecurityEvent } from 'src/services/audit'
// 使用 Quasar 插件
const $q = useQuasar()
const router = useRouter() const avatarFile = ref<File | null>(null)
const avatarPreviewUrl = ref<string>('')
const avatarFileInput = ref<any>(null)
// ...其他状态与规则定义省略...
// 触发头像上传
const triggerAvatarUpload = () => {
avatarFileInput.value?.pickFiles()
}
// 处理头像选择
const handleAvatarSelected = (file: File | null) => {
if (!file) return
// 文件类型验证
const validTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!validTypes.includes(file.type)) {
$q.notify({ type: 'negative', message: '不支持的文件类型,请选择 JPG、PNG 或 GIF 格式的图片', position: 'top' })
avatarFile.value = null
return
}
// 文件大小验证(2MB)
if (file.size > 2 * 1024 * 1024) {
$q.notify({ type: 'negative', message: '文件过大,请选择小于 2MB 的图片', position: 'top' })
avatarFile.value = null
return
}
// 创建预览 URL
if (avatarPreviewUrl.value) URL.revokeObjectURL(avatarPreviewUrl.value)
avatarPreviewUrl.value = URL.createObjectURL(file)
avatarFile.value = file
}
// 移除头像
const removeAvatar = () => {
if (avatarPreviewUrl.value) {
URL.revokeObjectURL(avatarPreviewUrl.value)
avatarPreviewUrl.value = ''
}
avatarFile.value = null
}
// 提交注册表单
const onSubmit = async () => {
// ...表单验证逻辑省略...
try {
const formData = new FormData()
formData.append('mobile', registerData.value.mobile)
// ...其他字段省略...
if (avatarFile.value) formData.append('avatar', avatarFile.value) // 添加头像文件
const response = await apiClient.post<RegisterResponse>('/users/register/', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
// ...成功处理逻辑省略...
} catch (error) {
// ...错误处理逻辑省略...
} finally {
isSubmitting.value = false
}
}
// 组件卸载时清理预览 URL
onUnmounted(() => {
if (avatarPreviewUrl.value) URL.revokeObjectURL(avatarPreviewUrl.value)
})
</script>
<style lang="scss" scoped>
.enterprise-register-card {
max-width: 500px;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
.avatar-preview {
border: 2px solid #e0e0e0;
border-radius: 50%;
transition: border-color 0.3s ease;
&:hover {
border-color: var(--q-primary);
}
}
.avatar-edit-badge {
bottom: 0;
right: 0;
transform: translate(25%, 25%);
}
// ...其他样式省略...
}
</style>
四、关键功能解析
①1.头像上传流程
- 触发选择:点击<q-avatar>调用triggerAvatarUpload(),通过隐藏的<q-file>组件打开文件选择器。
- 文件验证:handleAvatarSelected()检查文件类型(JPG/PNG/GIF)和大小(≤2MB),不符合时提示错误并清除文件。
- 预览生成:通过URL.createObjectURL(file)创建临时预览 URL,赋值给<q-img>的src属性。
- 预览 URL 释放:
②2.内存优化
o 移除头像时(removeAvatar())调用URL.revokeObjectURL()释放临时资源。
o 组件卸载时(onUnmounted)同样释放 URL,避免内存泄漏。
③3.表单提交
- 使用FormData封装文本字段与头像文件,通过apiClient.post提交multipart/form-data格式数据,兼容后端文件接收逻辑。
- 交互反馈:头像悬停时边框变色(border-color: var(--q-primary)),提示可点击。
- 状态提示:编辑徽章(avatar-edit-badge)悬浮于头像右下角,直观提示可编辑。
- 错误处理:文件验证失败时,使用 Quasar 的$q.notify提供顶部错误提示,不阻断用户操作。
五、UI/UX 设计亮点
六、使用说明
1. 依赖环境:Vue 3 + TypeScript + Quasar UI 框架。
2. 后端要求:支持multipart/form-data格式的文件上传接口(如/users/register/)。
3. 扩展建议:可添加图片裁剪功能(如集成vue-cropper),进一步优化头像上传体验。
此实现兼顾功能完整性与用户体验,代码结构清晰,可直接集成到企业级注册系统中。
浙公网安备 33010602011771号