本篇文章,咱们就来好好聊聊 AccountBook.vue
这个文件,它可是咱们孢子记账Web版里管理账本的核心。通过它,咱们能实现账本的增删改查,让你的财务管理井井有条。
一、功能概览
先来看看 AccountBook.vue
都能干些啥。简单来说,它就是账本的“大管家”,负责展示你的所有账本,并且提供新建、编辑、删除账本的功能。界面上,你会看到一个个账本卡片,每个卡片上都显示着账本的名称、描述和余额,旁边还有编辑和删除的按钮,操作起来非常直观。
二、实现细节
接下来,咱们深入代码,看看这些功能是怎么实现的。AccountBook.vue
是一个Vue单文件组件,采用了 <script setup>
语法糖,让代码更简洁、更易读。它主要依赖 vue
核心库、element-plus
组件库以及咱们自己定义的接口文件。
2.1 引入与依赖
在 <script setup>
块的开头,咱们可以看到一些必要的引入:
import {ref, onMounted, inject} from 'vue'
import type {AccountBook, CreateAccountBookRequest, UpdateAccountBookRequest} from '../Interface/accountBook.ts'
import type {Response} from '../Interface/response.ts'
import {ElMessage, ElMessageBox} from 'element-plus'
import {Plus, Edit, Delete, Setting} from '@element-plus/icons-vue'
const axios: any = inject('axios')
这里 ref
和 onMounted
是Vue 3的API,用于创建响应式数据和处理组件挂载生命周期。inject('axios')
则是从全局注入 axios
实例,方便咱们进行HTTP请求。AccountBook
、CreateAccountBookRequest
、UpdateAccountBookRequest
和 Response
都是咱们自定义的TypeScript接口,它们定义了数据结构,让代码更健壮。element-plus
提供了丰富的UI组件,比如 ElMessage
用于消息提示,ElMessageBox
用于弹窗确认,还有各种图标,让界面更美观。
2.2 状态管理
为了管理账本数据和UI状态,咱们定义了一些响应式变量:
// 账本列表
const accountBooks = ref<AccountBook[]>([])
const loading = ref(false)
// 创建账本相关
const createDialogVisible = ref(false)
const createForm = ref<CreateAccountBookRequest>({
name: '',
remarks: ''
})
// 编辑账本相关
const editDialogVisible = ref(false)
const editForm = ref<UpdateAccountBookRequest>({
accountBookId: '',
name: '',
remarks: ''
})
accountBooks
用来存储从后端获取到的账本列表,loading
则表示数据是否正在加载中。createDialogVisible
和 editDialogVisible
控制新建和编辑账本的弹窗显示与隐藏。createForm
和 editForm
分别绑定了新建和编辑账本表单的数据,它们都是 ref
响应式对象,确保表单数据的实时更新。
2.3 表单验证
为了保证数据的合法性,咱们还定义了表单验证规则 formRules
:
// 表单验证规则
const formRules = {
name: [
{required: true, message: '请输入账本名称', trigger: 'blur'},
{max: 20, message: '账本名称不能超过20个字符', trigger: 'blur'}
],
remarks: [
{max: 100, message: '账本描述不能超过100个字符', trigger: 'blur'}
]
}
// 表单引用
const createFormRef = ref()
const editFormRef = ref()
formRules
定义了 name
和 remarks
字段的验证规则,比如 name
是必填项,且最大长度为20个字符。createFormRef
和 editFormRef
则是对表单组件的引用,方便咱们在提交表单时调用 validate()
方法进行手动验证。
2.4 获取账本列表
getAccountBooks
函数负责从后端获取账本列表数据:
// 获取账本列表
const getAccountBooks = async () => {
loading.value = true
try {
const response = await axios.post(import.meta.env.VITE_API_BASE_URL + '/api/AccountBook/Query',
{
data: {
pageIndex: 1,
pageSize: 100
}
},
{
headers: {
'Authorization': localStorage.getItem('token'),
'Content-Type': 'application/json'
}
})
const result: Response<AccountBook[]> = response.data
console.log('获取账本列表响应:', result)
if (result.statusCode === 200) {
accountBooks.value = result.data.data || []
} else {
ElMessage.error(result.errorMessage || '获取账本列表失败')
}
} catch (error) {
console.error('获取账本列表失败:', error)
ElMessage.error('获取账本列表失败')
} finally {
loading.value = false
}
}
这个函数是一个异步函数,首先将 loading
设置为 true
,表示正在加载。然后通过 axios.post
向后端 /api/AccountBook/Query
接口发送请求,请求体中包含了分页信息(这里是获取前100条数据),请求头中携带了 Authorization
token 进行身份认证。请求成功后,会根据 statusCode
判断是否成功,如果成功则将返回的账本数据赋值给 accountBooks
,否则显示错误消息。无论成功失败,最后都会将 loading
设置为 false
。
2.5 创建账本
createAccountBook
函数用于新建账本:
// 创建账本
const createAccountBook = async () => {
try {
// 表单验证
await createFormRef.value.validate()
const response = await axios.post(import.meta.env.VITE_API_BASE_URL + '/api/AccountBook/Add',
createForm.value, {
headers: {
'Authorization': localStorage.getItem('token')
}
}
)
const result: Response<boolean> = response.data
if (result.statusCode === 200) {
ElMessage.success('创建账本成功')
createDialogVisible.value = false
// 重置表单
createForm.value = {
name: '',
remarks: ''
}
// 重新获取列表
await getAccountBooks()
} else {
ElMessage.error(result.errorMessage || '创建账本失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('创建账本失败:', error)
ElMessage.error('创建账本失败')
}
}
}
在创建账本之前,会先调用 createFormRef.value.validate()
进行表单验证。验证通过后,通过 axios.post
向 /api/AccountBook/Add
接口发送请求,请求体就是 createForm
的数据。成功后,会显示“创建账本成功”的消息,关闭新建弹窗,重置表单数据,并重新获取账本列表以更新界面。
2.6 删除账本
deleteAccountBook
函数用于删除账本:
// 删除账本
const deleteAccountBook = async (id: string, name: string) => {
try {
await ElMessageBox.confirm(`确定要删除账本"${name}"吗?`, '确认删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const response = await axios.delete(import.meta.env.VITE_API_BASE_URL + `/api/AccountBook/Delete/${id}`, {
headers: {
'Authorization': localStorage.getItem('token')
}
})
const result: Response<boolean> = response.data
if (result.statusCode === 200) {
ElMessage.success('删除账本成功')
await getAccountBooks()
} else {
ElMessage.error(result.errorMessage || '删除账本失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('删除账本失败:', error)
ElMessage.error('删除账本失败')
}
}
}
删除操作会先弹出一个确认框,询问用户是否确定删除。如果用户点击“确定”,则通过 axios.delete
向 /api/AccountBook/Delete/{id}
接口发送删除请求。成功后,显示“删除账本成功”的消息,并重新获取账本列表。
2.7 编辑账本
openEditDialog
和 updateAccountBook
函数用于编辑账本:
// 关闭创建对话框
const closeCreateDialog = () => {
createDialogVisible.value = false
// 重置表单验证状态
createFormRef.value?.resetFields()
}
// 打开编辑对话框
const openEditDialog = (book: AccountBook) => {
editForm.value = {
accountBookId: book.accountBookId,
name: book.name,
remarks: book.remarks || ''
}
editDialogVisible.value = true
}
// 更新账本
const updateAccountBook = async () => {
try {
// 表单验证
await editFormRef.value.validate()
const response = await axios.put(import.meta.env.VITE_API_BASE_URL + '/api/AccountBook/Update',
editForm.value, {
headers: {
'Authorization': localStorage.getItem('token')
}
}
)
const result: Response<boolean> = response.data
if (result.statusCode === 200) {
ElMessage.success('更新账本成功')
editDialogVisible.value = false
// 重新获取列表
await getAccountBooks()
} else {
ElMessage.error(result.errorMessage || '更新账本失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('更新账本失败:', error)
ElMessage.error('更新账本失败')
}
}
}
// 关闭编辑对话框
const closeEditDialog = () => {
editDialogVisible.value = false
// 重置表单验证状态
editFormRef.value?.resetFields()
}
openEditDialog
函数在点击编辑按钮时调用,它会将当前账本的数据填充到 editForm
中,并打开编辑弹窗。updateAccountBook
函数则负责提交编辑后的数据,同样会先进行表单验证,然后通过 axios.put
向 /api/AccountBook/Update
接口发送请求。成功后,显示“更新账本成功”的消息,关闭编辑弹窗,并重新获取账本列表。
closeCreateDialog
和 closeEditDialog
分别用于关闭新建和编辑弹窗,并重置表单的验证状态。
2.8 生命周期钩子
最后,在组件挂载时,咱们会调用 getAccountBooks
函数来获取账本列表,确保页面加载时就能看到数据:
onMounted(() => {
getAccountBooks()
})
三、页面结构 (<template>
)
页面的HTML结构主要由以下几个部分组成:
<template>
<!--面包屑导航-->
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item ><span style="font-weight: bold">账本</span></el-breadcrumb-item>
</el-breadcrumb>
<div class="account-book-page">
<!-- 页面标题和操作按钮 -->
<div class="page-header">
<p></p>
<el-button type="primary" @click="createDialogVisible = true" :icon="Plus">
新建账本
</el-button>
</div>
<!-- 账本列表 -->
<div class="account-book-list" v-loading="loading">
<el-row :gutter="20">
<el-col
v-for="(book) in accountBooks"
:key="book.accountBookId"
:xs="24"
:sm="12"
:md="8"
:lg="6"
:xl="4"
class="book-item-col"
>
<div class="book-item">
<div class="book-header">
<div class="book-icon">
<el-icon size="24">
<Setting/>
</el-icon>
</div>
</div>
<div class="book-info">
<h3 class="book-name">{{ book.name }}</h3>
<p class="book-description" v-if="book.remarks">{{ book.remarks }}</p>
<div class="book-meta">
<span class="book-balance">{{ book.balance }}</span>
</div>
</div>
<div class="book-actions">
<el-button
type="primary"
size="small"
circle
:icon="Edit"
title="编辑"
@click="openEditDialog(book)"
/>
<el-button
type="danger"
size="small"
circle
:icon="Delete"
title="删除"
@click="deleteAccountBook(book.accountBookId, book.name)"
/>
</div>
</div>
</el-col>
</el-row>
<!-- 空状态 -->
<div v-if="!loading && accountBooks.length === 0" class="empty-state">
<el-empty description="暂无账本,点击上方按钮创建第一个账本">
<el-button type="primary" @click="createDialogVisible = true">创建账本</el-button>
</el-empty>
</div>
</div>
<!-- 创建账本对话框 -->
<el-dialog
v-model="createDialogVisible"
title="新建账本"
width="500px"
center
>
<el-form :model="createForm" label-width="80px" ref="createFormRef" :rules="formRules">
<el-form-item label="账本名称" prop="name" required>
<el-input v-model="createForm.name" placeholder="请输入账本名称"/>
</el-form-item>
<el-form-item label="账本描述" prop="remarks">
<el-input
v-model="createForm.remarks"
type="textarea"
placeholder="请输入账本描述(可选)"
:rows="3"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeCreateDialog">取消</el-button>
<el-button type="primary" @click="createAccountBook">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 编辑账本对话框 -->
<el-dialog
v-model="editDialogVisible"
title="编辑账本"
width="500px"
center
>
<el-form :model="editForm" label-width="80px" ref="editFormRef" :rules="formRules">
<el-form-item label="账本名称" prop="name" required>
<el-input v-model="editForm.name" placeholder="请输入账本名称"/>
</el-form-item>
<el-form-item label="账本描述" prop="remarks">
<el-input
v-model="editForm.remarks"
type="textarea"
placeholder="请输入账本描述(可选)"
:rows="3"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeEditDialog">取消</el-button>
<el-button type="primary" @click="updateAccountBook">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
- 面包屑导航: 位于页面顶部,显示当前页面的路径,方便用户了解自己在应用中的位置。
- 页面标题和操作按钮: 包含一个“新建账本”按钮,点击后会打开新建账本的弹窗。
- 账本列表: 使用
el-row
和el-col
布局,以卡片的形式展示每个账本。每个卡片包含账本名称、描述、余额以及编辑和删除按钮。当没有账本时,会显示一个空状态提示。 - 创建账本对话框: 使用
el-dialog
实现,包含一个表单,用于输入账本名称和描述。表单数据双向绑定到createForm
,并应用了formRules
进行验证。 - 编辑账本对话框: 结构与创建账本对话框类似,用于编辑现有账本的信息,表单数据绑定到
editForm
。
四、样式 (<style scoped>
)
最后,咱们来看看 <style scoped>
部分,这里定义了组件的样式,scoped
属性确保样式只作用于当前组件,避免样式冲突。
<style scoped>
.account-book-page {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
/* ... 其他样式 ... */
@media (max-width: 768px) {
.page-header {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.page-header h2 {
text-align: center;
}
}
</style>
样式部分主要定义了页面的布局、账本卡片的样式、按钮的样式等。值得一提的是,它还包含了响应式设计,通过 @media
查询,在小屏幕设备上调整了页面头部布局,确保在不同设备上都有良好的用户体验。
五、小结
AccountBook.vue
通过Vue 3的组合式API、Element Plus组件库以及前后端交互,实现了一个功能完善、用户友好的账本管理界面。从数据定义到API调用,再到UI渲染和交互,整个流程清晰流畅,充分体现了现代前端开发的最佳实践。希望通过这篇讲解,你能对 AccountBook.vue
的代码有更深入的理解!