从这篇文章开始我们就进入到了孢子记账的前端开发,在本专栏中我默认大家的电脑上都已经配置好了开发环境。下面我们一起开始编写孢子记账的Web版吧。
一、功能
登录大模块功能包括注册、登录和找回密码功能,在本篇文章中我只会展示注册界面的实现,以及通用功能的实现。下面我们先来看一下这三个功能的原型设计图。
界面的左侧根据不同功能切换不同的图片,右侧是功能。
二、代码编写
2.1 注册功能数据模型
在Interface
文件夹下新建登录大模块的数据模型文件login.ts
,这个文件内容主要是登录、注册和找回密码的表单数据的模型。代码如下:
export interface Register {userName: string;password: string;rePassword: string;phoneNumber: string;email: string;
}
2.2 注册功能
根据原型图我们可以得知实现注册功能需要五个输入框:用户名、密码、确认密码、手机号和邮箱,以及一个注册按钮和一个登录跳转链接。在src/components
文件夹下新建login
文件夹,并在里面创建Register.vue
文件。接下来让我们对代码中的每个部分进行详细讲解,涵盖其目的和功能。
-
引入依赖
import { ref, defineEmits, reactive, inject } from "vue"; import { ElMessage, type FormInstance, type FormRules } from "element-plus"; import type { Register } from "../../Interface/login.ts"; import type { Response } from "../../Interface/response.ts";
这些导入的模块和类型为组件提供了基本的响应式能力、事件机制、表单验证以及类型安全,确保在开发过程中可以获得更好的类型提示和错误检查。通过 TypeScript 和这些工具的结合,可以提高代码的可靠性和可维护性。其中
FormInstance
定义了 Element Plus 中表单实例的类型,用于 TypeScript 类型检查,确保表单方法和属性的类型安全。FormRules
定义表单验证规则的类型,用于为表单字段设定验证逻辑,比如必填、格式验证等。Register
和Response
分别是注册表单模型和通用返回数据模型。 -
定义组件内部逻辑
- 事件和依赖注入
const emit = defineEmits(['switch']); const axios: any = inject('axios');
emit
定义了一个名为switch
的事件,允许从当前组件向父组件传递消息或切换状态。inject
从外部注入 axios 实例,避免直接在组件中引入 axios,这样遵循依赖注入原则,提高代码的灵活性和可测试性。 - 创建响应式数据
const ruleRegisterRef = ref<FormInstance>(); const registerData = reactive<Register>({userName: '',password: '',rePassword: '',phoneNumber: '',email: '' });
ruleRegisterRef
使用ref
创建的表单实例引用,用于操作表单。registerData
是一个reactive
对象,表示注册表单的数据模型。每个字段如userName
、password
等,都与表单中的输入绑定。 - 定义表单验证规则
const rules = reactive<FormRules<Register>>({userName: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],password: [{ required: true, message: '密码不能为空', trigger: 'blur' }],rePassword: [{ required: true, message: '确认密码不能为空', trigger: 'blur' },{validator: (rule, value, callback) => {if (value !== registerData.password) {callback(new Error('两次输入密码不一致'));} else {callback();}}, trigger: 'blur'}],phoneNumber: [{ required: true, message: '手机号不能为空', trigger: 'blur' },{ pattern: /^1[3456789]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }],email: [{ required: true, message: '邮箱不能为空', trigger: 'blur' },{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }] });
rules
为表单字段定义验证规则,规则很明白就不讲解了。rePassword
不仅要验证是否为空,还需要验证是否与password
匹配,因此我们自定义了验证器函数来验证与passowrd
是否匹配。
- 事件和依赖注入
-
注册方法
const register = (formEl: FormInstance | undefined) => {if (!formEl) return;formEl.validate().then(() => {axios.post(import.meta.env.VITE_API_BASE_URL + '/api/SysUser/Register', registerData).then((res: any) => {const response: Response<boolean> = res.data;if (response.statusCode === 200) {emit('switch', "login");} else {ElMessage.error(response.errorMessage);}}).catch((err: any) => {console.log(err);});}).catch(() => {return;}); };
register
负责提交注册表单。首先调用formEl.validate()
进行表单验证,若通过则发送 HTTP POST 请求。请求成功后,如果服务器返回成功状态,则触发switch
事件,切换到登录页面。如果失败,使用ElMessage.error
显示错误消息。 -
切换到登录页面方法
const toLogin = () => {emit('switch', "login"); };
toLogin
简单地通过emit
触发switch
事件,切换到登录页面。 -
模板部分
<template><div style="padding-top:10%"><div style="text-align: center"><img src="../../assets/logo.png" style="width:200px;height: 49px; max-height: 100%;" alt="Login Image"></div><el-form ref="ruleRegisterRef" :model="registerData" :rules="rules" label-position="top" status-icon><el-form-item label="用户名" prop="userName"><el-input style="height:40px;line-height: 40px" v-model="registerData.userName" placeholder="请输入..."></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input style="height:40px;line-height: 40px" v-model="registerData.password" type="password" placeholder="请输入..."></el-input></el-form-item><el-form-item label="确认密码" prop="rePassword"><el-input style="height:40px;line-height: 40px" v-model="registerData.rePassword" type="password" placeholder="请输入..."></el-input></el-form-item><el-form-item label="手机号" prop="phoneNumber"><el-input style="height:40px;line-height: 40px" v-model="registerData.phoneNumber" type="text" placeholder="请输入..."></el-input></el-form-item><el-form-item label="邮箱" prop="email"><el-input style="height:40px;line-height: 40px" v-model="registerData.email" type="text" placeholder="请输入..."></el-input></el-form-item><el-form-item><el-button type="primary" style="width: 100%;height:40px;line-height: 40px" @click="register(ruleRegisterRef)">注册</el-button><label>已有账号?</label><el-link :underline="false" type="primary" v-on:click="toLogin">登录</el-link></el-form-item></el-form></div> </template>
这里使用
el-form
组件渲染表单,绑定registerData
和验证规则rules
。每个输入项用el-form-item
包裹,定义了标签和输入框。“注册”按钮点击时调用register
方法提交表单。“登录”链接,点击时调用toLogin
方法切换页面。
2.3 通用界
通用界面主要是登录、注册、找回密码共用的UI界面,也就是原型图的左侧部分。在src/pages
目录下新建Login.vue
文件。
这段代码是一个 Vue 3 组件,使用了 script setup
和 TypeScript 语法,实现了在登录、注册、找回密码三个子组件之间的切换。以下是对代码的详细讲解:
-
导入部分
import Login from '../components/login/Login.vue' import Register from '../components/login/Register.vue' import RetrievePassword from "../components/login/RetrievePassword.vue"; import { computed, ref } from 'vue'
Login
、Register
、RetrievePassword
分别导入登录、注册、找回密码的组件。computed
和ref
是从 Vue 导入用于创建响应式引用和计算属性。 -
状态管理和逻辑处理
const componentAVisible = ref(0)
componentAVisible
使用ref
创建一个响应式引用,初始值为0
。这个值决定当前显示的组件:0
代表Login
,1
代表Register
,2
代表RetrievePassword
。const toggleComponent = (param: String) => {if (param === "login")componentAVisible.value = 0else if (param === "register")componentAVisible.value = 1elsecomponentAVisible.value = 2 }
toggleComponent
是一个方法,用于根据传入的字符串参数切换componentAVisible
的值,从而改变当前显示的组件。const currentComponent = computed(() => componentAVisible.value == 0 ? Login : componentAVisible.value == 1 ? Register : RetrievePassword)
计算属性
currentComponent
根据componentAVisible
的值动态返回对应的组件对象。这个属性决定了<component>
标签中实际渲染的组件。 -
模板部分
<template><div class="container"><div class="image-section"><img src="../assets/login.svg" v-if="currentComponent==Login" class="image" alt="Login Image"><img src="../assets/register.svg" v-else-if="currentComponent==Register" class="image" alt="Register Image"><img src="../assets/findPassword.svg" v-else class="image" alt="FindPassword Image"></div><div class="section"><div class="border"><component :is="currentComponent" @switch="toggleComponent"/></div></div></div><footer class="footer"><p>© 2025 MiaoShu Studio. All rights reserved.</p></footer> </template>
模板部分比较简单,通过
v-if
和v-else-if
根据currentComponent
的值显示对应的图片。使用<component>
标签动态渲染currentComponent
。@switch
事件绑定了toggleComponent
方法,用于子组件触发组件切换。
三、总结
这篇文章是Web版开发的第一篇文章,因此讲解的比较详细,但是在后续的文章中将只会讲解核心代码和重要代码。完整代码大家可以在github上下载,但是我建议大家先自己实现代码,然后再对比github上的代码。