8. 【Vue实战--孢子记账--Web 版开发】-- 账户账本管理

发布于:2025-06-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

本篇文章,咱们就来好好聊聊 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')

这里 refonMounted 是Vue 3的API,用于创建响应式数据和处理组件挂载生命周期。inject('axios') 则是从全局注入 axios 实例,方便咱们进行HTTP请求。AccountBookCreateAccountBookRequestUpdateAccountBookRequestResponse 都是咱们自定义的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 则表示数据是否正在加载中。createDialogVisibleeditDialogVisible 控制新建和编辑账本的弹窗显示与隐藏。createFormeditForm 分别绑定了新建和编辑账本表单的数据,它们都是 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 定义了 nameremarks 字段的验证规则,比如 name 是必填项,且最大长度为20个字符。createFormRefeditFormRef 则是对表单组件的引用,方便咱们在提交表单时调用 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 编辑账本

openEditDialogupdateAccountBook 函数用于编辑账本:

// 关闭创建对话框
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 接口发送请求。成功后,显示“更新账本成功”的消息,关闭编辑弹窗,并重新获取账本列表。

closeCreateDialogcloseEditDialog 分别用于关闭新建和编辑弹窗,并重置表单的验证状态。

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-rowel-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 的代码有更深入的理解!


网站公告

今日签到

点亮在社区的每一天
去签到