基于Vue.js的图书管理系统前端界面设计的javascript逻辑部分

发布于:2025-06-24 ⋅ 阅读:(17) ⋅ 点赞:(0)

        前文,我们实现了一个使用Vue 3构建的图书管理系统的html部分,接下来我们看一下JavaScript逻辑部分html部分以及整个系统的完整代码可以查看我的文章:基于Vue.js的图书管理系统前端界面设计-CSDN博客 

一、Vue 3简介

Vue 3是渐进式JavaScript框架Vue.js的第三个主要版本,它延续了Vue易于上手、灵活性高和高效的特点,并在性能、可维护性和开发体验上进行了显著提升。

(一)性能优化

(1)Proxy响应式系统Vue 3使用ES6的Proxy对象实现响应式系统,相较于Vue 2的Object.defineProperty,它能更好地支持嵌套对象的响应式追踪,减少了初始化时的递归遍历开销,同时能够检测到对象属性的新增和删除,使响应式数据的处理更加高效和全面。

(2)编译优化:Vue 3的编译器引入了静态提升、事件缓存等优化策略。静态提升可以将模板中的静态节点提取出来,避免在每次渲染时重复创建,减少了虚拟DOM的比对和创建开销;事件缓存则可以避免在每次渲染时重新创建事件处理函数,提高了事件绑定的性能。

(二)Composition API

(1)逻辑复用与组织:Composition API提供了一种基于函数的方式来组织组件逻辑,使得代码更加模块化和可复用。通过setup函数,开发者可以将相关的逻辑封装在一个函数中,并在组件中复用这些逻辑,解决了Vue 2中选项式API在处理复杂逻辑时可能导致的代码碎片化问题。

(2)更好的类型推导:由于Composition API基于函数,它与TypeScript的集成更加自然,能够提供更好的类型推导和静态类型检查,有助于开发者在开发过程中更早地发现错误,提高代码的健壮性。

(三)更好的Tree - Shaking支持

Vue 3的代码结构进行了重构,使得它能够更好地支持Tree - Shaking。Tree - Shaking是一种优化技术,它可以在打包时移除未使用的代码,从而减小打包文件的体积,提高应用的加载速度。

(四)新特性与改进

(1)Teleport组件:Teleport组件允许开发者将组件的内容渲染到DOM中的其他位置,这在处理模态框、下拉菜单等需要脱离文档流的组件时非常有用。

(2)Suspense组件:Suspense组件可以在异步组件加载完成之前显示一个加载状态,简化了异步组件的处理逻辑,提高了用户体验。

(3)自定义渲染器API:Vue 3提供了自定义渲染器API,开发者可以使用该API创建自定义的渲染器,将Vue组件渲染到不同的目标平台,如Canvas、WebGL等。

在这个图书管理系统的index.html文件中,虽然没有直接应用Vue 3完整的JavaScript代码实现,但HTML中使用的v-if、v-for、v-model、@click等指令以及响应式数据绑定(如 :class、{{ }} 插值),它使用了Vue 3的响应式系统和模板语法来构建交互式的图书管理系统界面,如导航栏的状态切换图书列表的展示分页借阅记录的显示等功能。

二、JavaScript逻辑部分的树形结构

以下是该index.html文件中script部分的树形结构图,并对每个属性、方法进行简要说明:

script

├── Vue 应用逻辑setup函数

│   ├── 导航相关

│   │   ├── isScrolled - 布尔值,用于判断页面是否滚动,当页面滚动时可能会改变导航栏的样式

│   │   ├── currentView - 字符串,用于表示当前显示的视图,如 'dashboard'、'books'、'borrows'、'users' 等

│   │   ├── isMobileMenuOpen - 布尔值,用于控制移动端菜单的展开和收起状态

│   ├── 数据模型

│   │   ├── books - 数组,存储系统中所有图书的信息,包含每本图书的详细数据

│   │   ├── borrowedBooksCount - 数字,记录当前已借出图书的数量

│   │   ├── users - 数组,存储系统中所有用户的信息,包含每个用户的详细数据

│   │   ├── overdueBooksCount - 数字,记录当前逾期未还图书的数量

│   │   ├── recentBorrows - 数组,存储最近的图书借阅记录,包含借阅的详细信息

│   ├── 搜索和筛选

│   │   ├── bookSearchQuery - 字符串,用于图书管理视图中的搜索输入框,用户输入的搜索关键词

│   │   ├── bookCategoryFilter - 字符串,用于图书管理视图中按图书分类进行筛选,可选值如 '计算机'、'文学' 等

│   │   ├── bookStatusFilter - 字符串,用于图书管理视图中按图书状态进行筛选,可选值如 'available'、'borrowed'

│   │   ├── borrowSearchQuery - 字符串,用于借阅管理视图中的搜索输入框,用户输入的搜索关键词

│   │   ├── borrowStatusFilter - 字符串,用于借阅管理视图中按借阅状态进行筛选,可选值如 'borrowed'、'returned'

│   ├── 分页

│   │   ├── currentPage - 数字,用于图书列表的分页,当前显示的页码

│   │   ├── booksPerPage - 数字,用于图书列表的分页,每页显示的图书数量

│   ├── 模态框

│   │   ├── isBookModalOpen - 布尔值,用于控制图书编辑或添加模态框的显示和隐藏

│   │   ├── currentBook - 对象,当前正在编辑或添加的图书信息

│   ├── 表单数据

│   │   ├── newBookTitle - 字符串,添加或编辑图书时的图书标题

│   │   ├── newBookAuthor - 字符串,添加或编辑图书时的图书作者

│   │   ├── newBookCategory - 字符串,添加或编辑图书时的图书分类

│   │   ├── newBookCover - 字符串,添加或编辑图书时的图书封面链接

│   │   ├── newBookIsBorrowed - 布尔值,添加或编辑图书时的图书借阅状态

│   ├── 计算属性

│   │   ├── filteredBooks - 根据搜索关键词、分类和状态筛选后的图书列表

│   │   ├── totalPages - 图书列表的总页数

│   │   ├── displayedBooks - 当前页要显示的图书列表

│   │   ├── filteredBorrows - 根据搜索关键词和借阅状态筛选后的借阅记录列表

│   ├── 方法

│   │   ├── toggleMobileMenu - 函数,用于切换移动端菜单的展开和收起状态

│   │   ├── openBookModal - 函数,用于打开图书编辑或添加的模态框,接受一个图书对象作为参数,若为 null 则表示添加新图书

│   │   ├── closeBookModal - 函数,用于关闭图书编辑或添加的模态框

│   │   ├── saveBook - 函数,用于保存新添加或编辑的图书信息

│   │   ├── deleteBook - 函数,用于删除指定 ID 的图书

│   │   ├── prevPage - 函数,用于图书列表分页时切换到上一页

│   │   ├── nextPage - 函数,用于图书列表分页时切换到下一页

│   │   ├── isOverdue - 函数,接受一个日期作为参数,判断该日期是否逾期,返回布尔值

│   ├── 初始化图表

│   │   ├── initBorrowChart - 函数,用于初始化借阅趋势图表

│   │   ├── initCategoryChart - 函数,用于初始化图书分类统计图表

│   ├── 生命周期钩子

│   │   ├── onMounted - 生命周期钩子,在组件挂载后执行,用于初始化数据和图表

│   ├── return

│       ├── isScrolled

│       ├── currentView

│       ├── isMobileMenuOpen

│       ├── books

│       ├── borrowedBooksCount

│       ├── users

│       ├── overdueBooksCount

│       ├── recentBorrows

│       ├── bookSearchQuery

│       ├── bookCategoryFilter

│       ├── bookStatusFilter

│       ├── borrowSearchQuery

│       ├── borrowStatusFilter

│       ├── currentPage

│       ├── booksPerPage

│       ├── isBookModalOpen

│       ├── currentBook

│       ├── newBookTitle

│       ├── newBookAuthor

│       ├── newBookCategory

│       ├── newBookCover

│       ├── newBookIsBorrowed

│       ├── filteredBooks

│       ├── totalPages

│       ├── displayedBooks

│       ├── filteredBorrows

│       ├── toggleMobileMenu

│       ├── openBookModal

│       ├── closeBookModal

│       ├── saveBook

│       ├── deleteBook

│       ├── prevPage

│       ├── nextPage

│       ├── isOverdue

        这个树形结构详细展示了一个完整的Vue 3 setup()函数可能包含的各个部分(不是全是属性、方法)。Composition API提供了一种基于函数的方式来组织组件逻辑,使得代码更加模块化和可复用。通过setup函数,开发者可以将相关的逻辑封装在一个函数中,并在组件中复用这些逻辑。当前的setup()函数中包含了:导航相关、数据模型、搜索和筛选、分页、模态框、表单数据、计算属性(很多个)、方法(很多个)、初始化图表、生命周期钩子以及return。

        在实际的HTML文件中,Vue的响应式数据和方法通常是通过Vue.createApp()创建应用实例并在实例中定义的,但在我们这个系统的HTML文件里是直接在模板中使用v-bind和v-on指令来绑定数据和事件。

三、JavaScript逻辑部分代码解析

下面我们将从几个关键方面详细解析这段代码:

初始化Vue应用

javascript代码框架如下:

const { createApp, ref, computed, onMounted } = Vue;
createApp({
    setup() {

        // ...

    }}).mount('#app');

代码说明:

(1)从 Vue 对象中解构出 createApp、ref、computed 和 onMounted 这些函数。

(2)使用 createApp 创建一个新的Vue应用实例,并通过mount('#app') 将其挂载到 HTML中 id 为 app 的元素上

响应式数据

1.什么是响应式数据

在Vue 3中,响应式数据是指当数据发生变化时,与之绑定的DOM元素会自动更新以反映这些变化的数据。这是Vue框架的核心特性之一,它使得开发者可以专注于数据的逻辑处理,而无需手动操作DOM来更新页面显示。

原理:Vue 3使用Proxy对象来实现响应式数据。当一个对象被转换为响应式对象时,Vue会创建一个代理对象,该代理对象会拦截对象的属性访问和修改操作。当属性被访问时,Vue 会追踪哪些代码依赖于这个属性;当属性被修改时,Vue会通知所有依赖于该属性的代码进行更新。

在我们的script代码中,使用了ref和reactive来创建响应式数据

const { createApp, ref, computed, onMounted } = Vue;
createApp({
    setup() {
        // 使用 ref 创建响应式数据
        const currentView = ref('borrows');
        const isScrolled = ref(false);
        const isMobileMenuOpen = ref(false);

        // 使用 ref 创建数组形式的响应式数据
        const books = ref([
            {
                id: 1,
                title: "Python数据分析实战",
                // ... 其他属性
            },
            // ... 其他图书对象
        ]);

        const users = ref([
            {
                id: 1,
                name: "张三",
                // ... 其他属性
            },
            // ... 其他用户对象
        ]);

        const borrows = ref([
            {
                id: 1,
                bookId: 2,
                // ... 其他属性
            },
            // ... 其他借阅记录对象
        ]);

        // 搜索和筛选相关的响应式数据
        const bookSearchQuery = ref('');
        const bookCategoryFilter = ref('');
        const bookStatusFilter = ref('');
        ......

        // 分页相关的响应式数据
        const currentPage = ref(1);
        const booksPerPage = ref(9);
        ......

        return {
            currentView,
            isScrolled,
            ......
            borrowsPerPage
        };
    }
}).mount('#app');

代码解释:

(1)ref用于创建一个响应式的引用对象通常用于基本数据类型(如字符串、数字、布尔值)。当你需要修改ref对象的值时,需要通过 .value 属性来访问和修改。例如,要修改currentView的值,可以使用currentView.value = 'books'

(2)reactive:我们的代码中没有使用,但reactive用于创建一个响应式的对象,通常用于对象和数组。与ref不同,使用reactive创建的对象可以直接访问和修改属性,无需使用 .value。例如:

const state = reactive({
    count: 0,
    message: 'Hello, Vue!'
});


// 直接修改属性
state.count++;
state.message = 'Updated message';

响应式的作用:当这些响应式数据发生变化时,Vue会自动更新与之绑定的DOM元素。例如,如果你在模板中使用 {{ currentView }} 来显示当前视图,当 currentView.value 发生变化时,页面上显示的内容也会自动更新。这样,开发者只需要关注数据的变化,而无需手动更新DOM,大大提高了开发效率。

2.导航相关

使用ref创建常规类型的响应式数据。

const currentView = ref('borrows');
const isScrolled = ref(false);
const isMobileMenuOpen = ref(false);

代码说明:

(1)currentView:用于存储当前显示的视图,初始值为 'dashboard',表示默认显示仪表盘视图。有'dashboard'、'books'、'borrows'、'users' 等值。

(2)isScrolled:记录页面是否滚动,当页面滚动时用于控制导航栏样式。

(3)isMobileMenuOpen:控制移动端菜单的显示与隐藏。

3.数据模型

使用ref创建数组形式的响应式数据,包括了三个数据模型:

const books = ref([...]);
const users = ref([...]);
const borrows = ref([...]);

代码说明:

(1)books:存储图书信息的数组。

(2)users:存储用户信息的数组。

(3)borrows:存储借阅记录的数组。

4.搜索和筛选

使用ref创建搜索和筛选相关的响应式数据:

const bookSearchQuery = ref('');
const bookCategoryFilter = ref('');
const bookStatusFilter = ref('');
const borrowSearchQuery = ref('');
const borrowStatusFilter = ref('');
const userSearchQuery = ref('');
const userRoleFilter = ref('');
const userStatusFilter = ref('');

这些 ref 对象用于存储搜索关键词和筛选条件,分别对应图书、借阅记录和用户的搜索与筛选。

5.分页

使用ref创建分页相关的响应式数据:

const currentPage = ref(1);
const booksPerPage = ref(9);
const currentBorrowPage = ref(1);
const borrowsPerPage = ref(10);
const currentUserPage = ref(1);
const usersPerPage = ref(6);

用于实现图书、借阅记录和用户列表的分页功能,currentPage 表示当前页码,booksPerPage 表示每页显示的记录数。

6.模态框

使用ref创建模态框相关的响应式数据:

const isBookModalOpen = ref(false);
const isBorrowModalOpen = ref(false);
const isUserModalOpen = ref(false);
const isBorrowDetailsModalOpen = ref(false);
const isConfirmDialogOpen = ref(false);
const editingBook = ref(null);
const editingUser = ref(null);
const selectedBorrow = ref(null);
const confirmDialogTitle = ref('');
const confirmDialogMessage = ref('');
let confirmCallback = null;

代码说明:

(1)前面5个isBookModalOpen、isBorrowModalOpen、isUserModalOpen、isBorrowDetailsModalOpen、isConfirmDialogOpen控制各个模态框和确认对话框的显示与隐藏。

(2)editingBook 和 editingUser 分别存储正在编辑的图书和用户信息。

(3)selectedBorrow 存储当前选中的借阅记录。

(4)confirmDialogTitle和confirmDialogMessage存储确认对话框的标题和消息,confirmCallback 存储确认操作的回调函数。

7.表单数据

使用ref创建表单相关的响应式数据:

const form = ref({
    bookId: null,
    bookCover: '',
    bookTitle: '',
    bookAuthor: '',
    bookPublisher: '',
    bookYear: '',
    bookCategory: '计算机',
    bookISBN: '',
    bookDescription: '',

    borrowBookId: '',
    borrowUserId: '',
    borrowDate: '',
    dueDate: '',

    userId: null,
    userAvatar: '',
    userName: '',
    userStudentId: '',
    userRole: 'student',
    userContact: '',
    userEmail: '',
    userNotes: ''});

存储图书、借阅和用户表单的输入数据。

计算属性

1.什么是计算属性

计算属性(Computed Properties)是Vue.js中一个非常有用的特性,它允许你基于已有的响应式数据来定义新的响应式数据。这些新的数据会根据依赖的响应式数据自动更新,并且会进行缓存,只有当依赖的数据发生变化时才会重新计算。

(1)为什么需要计算属性

在模板中可以使用表达式来进行简单的计算,但如果表达式过于复杂,会让模板变得难以维护和阅读。计算属性可以将复杂的逻辑封装起来,让模板更加简洁,同时也提高了代码的可维护性。

(2)计算属性的特点

缓存机制计算属性会根据其依赖的数据进行缓存,只有当依赖的数据发生变化时,计算属性才会重新计算。这意味着如果依赖的数据没有变化,多次访问计算属性时会直接返回之前缓存的结果,而不会重复计算,从而提高了性能。

响应式更新当计算属性依赖的响应式数据发生变化时,计算属性会自动更新,并且与之绑定的DOM元素也会相应地更新

(3)代码中的计算属性示例

在我们的代码中,有多个计算属性的定义,下面是几个典型的例子:

// 过滤图书列表,根据搜索查询、分类和状态进行筛选

const filteredBooks = computed(() => {

    return books.value.filter(book => {

        const titleMatch = book.title.toLowerCase().includes(bookSearchQuery.value.toLowerCase());

        const authorMatch = book.author.toLowerCase().includes(bookSearchQuery.value.toLowerCase());

        const categoryMatch = bookCategoryFilter.value ? book.category === bookCategoryFilter.value : true;

        const statusMatch = bookStatusFilter.value === 'available' ? !book.isBorrowed :

                            bookStatusFilter.value === 'borrowed' ? book.isBorrowed : true;

        

        return (titleMatch || authorMatch) && categoryMatch && statusMatch;

    });

});



// 计算总页数

const totalPages = computed(() => {

    return Math.ceil(filteredBooks.value.length / booksPerPage.value);

});



// 分页显示图书

const paginatedBooks = computed(() => {

    const start = (currentPage.value - 1) * booksPerPage.value;

    const end = start + booksPerPage.value;

    return filteredBooks.value.slice(start, end);

});

代码解释:

  1. filteredBooks:根据bookSearchQuery、bookCategoryFilter和bookStatusFilter的值对books数组进行过滤,返回符合条件的图书列表。当这些依赖的数据发生变化时,filteredBooks会自动重新计算。
  2. totalPages:根据filteredBooks的长度和booksPerPage的值计算总页数。由于totalPages依赖于filteredBooks,当filteredBooks发生变化时,totalPages也会重新计算。
  3. paginatedBooks:根据currentPage和booksPerPage的值对filteredBooks进行分页,返回当前页的图书列表。当currentPage、booksPerPage或filteredBooks发生变化时,paginatedBooks会重新计算。

(4)计算属性的使用

在模板中,可以像使用普通数据一样使用计算属性:

<div>

    <!-- 显示过滤后的图书数量 -->

    <p>过滤后的图书数量: {{ filteredBooks.length }}</p>

    <!-- 显示总页数 -->

    <p>总页数: {{ totalPages }}</p>

    <!-- 显示当前页的图书 -->

    <ul>

        <li v-for="book in paginatedBooks" :key="book.id">{{ book.title }}</li>

    </ul>

</div>

通过使用计算属性,我们可以将复杂的逻辑封装在JavaScript代码中,让模板更加简洁,同时也提高了代码的可维护性和性能。

2.图书管理相关的计算属性

上面的例子已经讲清楚了,接下来再简化讲一下。

const filteredBooks = computed(() => {

// ...

});

const totalPages = computed(() => {

    return Math.ceil(filteredBooks.value.length / booksPerPage.value);});

const paginatedBooks = computed(() => {

    const start = (currentPage.value - 1) * booksPerPage.value;

    const end = start + booksPerPage.value;

    return filteredBooks.value.slice(start, end);});

代码解释:

(1)filteredBooks:根据搜索关键词和筛选条件过滤图书列表。

(2)totalPages:计算图书列表的总页数。

(3)paginatedBooks:根据当前页码和每页显示的记录数,截取当前页要显示的图书列表。

在搜索框中输入关键词和筛选条件,不需要点击,将直接呈现结果:

3.借阅管理相关的计算属性

const filteredBorrows = computed(() => {

  return borrows.value

.map(borrow => {

  const book = books.value.find(b => b.id === borrow.bookId);

  const user = users.value.find(u => u.id === borrow.userId);

  

  // 添加调试信息

  if (!book) {

console.warn('找不到对应的图书:', borrow);

  }

  if (!user) {

console.warn('找不到对应的用户:', borrow);

  }

  

  return { ...borrow, book, user };

})

.filter(borrow => {

  // 过滤掉没有关联图书或用户的记录

  if (!borrow.book || !borrow.user) {

console.warn('过滤无效借阅记录:', borrow);

return false;

  }

  

  // 应用搜索和筛选条件

  const bookMatch = borrow.book.title.toLowerCase().includes(borrowSearchQuery.value.toLowerCase());

  const userMatch = borrow.user.name.toLowerCase().includes(borrowSearchQuery.value.toLowerCase());

  

  let statusMatch = true;

  if (borrowStatusFilter.value === 'borrowed') {

statusMatch = !borrow.isReturned && !isOverdue(borrow.dueDate);

  } else if (borrowStatusFilter.value === 'returned') {

statusMatch = borrow.isReturned;

  } else if (borrowStatusFilter.value === 'overdue') {

statusMatch = !borrow.isReturned && isOverdue(borrow.dueDate);

  }

  

  return (bookMatch || userMatch) && statusMatch;

});

});



const totalBorrowPages = computed(() => {

  return Math.ceil(filteredBorrows.value.length / borrowsPerPage.value);

});



const paginatedBorrows = computed(() => {

  const start = (currentBorrowPage.value - 1) * borrowsPerPage.value;

  const end = start + borrowsPerPage.value;

  return filteredBorrows.value.slice(start, end);

});

代码解释:

(1)filteredBorrows:根据搜索关键词和筛选条件过滤借阅记录列表。

(2)totalBorrowPages:计算借阅记录列表的总页数。

(3)paginatedBorrows:根据当前页码和每页显示的记录数,截取当前页要显示的借阅记录列表。

在搜索框中输入关键词和筛选条件,不需要点击,将直接呈现结果:

4.用户管理相关的计算属性

上面的例子已经讲清楚了,接下来再简化讲一下。

const filteredUsers = computed(() => {

  return users.value.filter(user => {

const nameMatch = user.name.toLowerCase().includes(userSearchQuery.value.toLowerCase());

const idMatch = user.studentId.toLowerCase().includes(userSearchQuery.value.toLowerCase());

const roleMatch = userRoleFilter.value ? user.role === userRoleFilter.value : true;

const statusMatch = userStatusFilter.value === 'active' ? !user.isBlocked :

   userStatusFilter.value === 'blocked' ? user.isBlocked : true;



return (nameMatch || idMatch) && roleMatch && statusMatch;

  });

});



const totalUserPages = computed(() => {

  return Math.ceil(filteredUsers.value.length / usersPerPage.value);

});



const paginatedUsers = computed(() => {

  const start = (currentUserPage.value - 1) * usersPerPage.value;

  const end = start + usersPerPage.value;

  return filteredUsers.value.slice(start, end);

});

代码解释:

(1)filteredUsers:根据搜索关键词和筛选条件过滤用户列表。

(2)totalUserPages:计算用户列表的总页数。

(3)paginatedUsers :根据当前页码和每页显示的记录数,截取当前页要显示的用户列表。

5.其他计算属性

除了上述有关图书管理、借阅管理、用户管理的计算属性外,还定义了四个计算属性,这些计算属性基于响应式数据books、borrows和users动态计算得出新的数据,并且会根据依赖数据的变化自动更新。以下是对每个计算属性的详细解析:

(1)borrowedBooksCount

const borrowedBooksCount = computed(() => {
  return books.value.filter(book => book.isBorrowed).length;
});

功能:计算当前被借出的图书数量

依赖:books响应式数据。

实现细节:

  1. 使用books.value获取books数组。
  2. 通过filter方法筛选出isBorrowed属性为true的图书。
  3. 使用length属性获取筛选后数组的长度,即被借出的图书数量。

(2)overdueBooksCount

const overdueBooksCount = computed(() => {
  return borrows.value.filter(borrow => !borrow.isReturned && isOverdue(borrow.dueDate)).length;
});

功能:计算当前逾期未归还的借阅记录数量

依赖:borrows响应式数据和isOverdue方法。

实现细节:

  1. 使用borrows.value获取borrows数组。
  2. 通过filter方法筛选出isReturned属性为false(未归还)且isOverdue方法返回true(逾期)的借阅记录。
  3. 使用length属性获取筛选后数组的长度,即逾期未归还的借阅记录数量。

(3)recentBorrows

const recentBorrows = computed(() => {
  return [...borrows.value]
    .sort((a, b) => new Date(b.borrowDate) - new Date(a.borrowDate))
    .slice(0, 5)
    .map(borrow => {
      return {
        ...borrow,
        book: books.value.find(b => b.id === borrow.bookId),
        user: users.value.find(u => u.id === borrow.userId)
      };
    });
});

功能:获取最近的5条借阅记录,并为每条记录关联对应的图书和用户信息

依赖:borrows、books和users响应式数据。

实现细节:

  1. 使用[...borrows.value]创建borrows数组的副本,避免修改原始数组。
  2. 使用sort方法按borrowDate降序排序,确保最近的借阅记录排在前面。
  3. 使用slice(0, 5)截取前5条记录。
  4. 使用map方法遍历截取后的数组,为每条记录添加book和user属性,分别表示借阅的图书和借阅的用户。

(4)availableBooks

const availableBooks = computed(() => {
  return books.value.filter(book => !book.isBorrowed);
});

功能:获取当前可借阅的图书列表

依赖:books 响应式数据。

实现细节:

  1. 使用books.value获取books数组。
  2. 通过filter方法筛选出isBorrowed属性为false的图书,即未被借出的图书。

这些计算属性的好处在于,当它们所依赖的响应式数据发生变化时,计算属性会自动重新计算,并且与之绑定的DOM元素也会相应地更新,从而保证数据的实时性和一致性。

方法

1.Vue 3里定义方法的目的和作用

在Vue 3里定义方法具备多方面的目的和作用,以下将结合你提供的代码进行详细阐述:

(1)处理用户交互

当用户与界面进行互动时,如点击按钮、提交表单等操作,需要特定的方法来对这些交互做出响应,从而实现相应的功能。

示例代码:

const toggleMobileMenu = () => {
  isMobileMenuOpen.value = !isMobileMenuOpen.value;
};

const changeView = (view) => {
  currentView.value = view;
  isMobileMenuOpen.value = false;
};
  1. toggleMobileMenu方法用于切换移动端菜单的显示与隐藏状态。当用户点击菜单切换按钮时,调用此方法来更新 isMobileMenuOpen 的值,进而控制菜单的显示或隐藏。
  2. changeView方法用于切换当前视图,同时关闭移动端菜单。当用户点击导航链接时,调用该方法更新 currentView 的值,实现视图的切换。

(2)封装业务逻辑

把复杂的业务逻辑封装到方法中,能够提升代码的可读性和可维护性,使代码结构更加清晰。

示例代码:

const saveBook = () => {

  if (!form.value.bookTitle || !form.value.bookAuthor) {

    alert('请填写图书标题和作者');

    return;

  }

  if (editingBook.value) {

    // 更新现有图书

    const index = books.value.findIndex(b => b.id === form.value.bookId);

    if (index !== -1) {

      books.value[index] = {

        ...books.value[index],

        cover: form.value.bookCover,

        title: form.value.bookTitle,

        author: form.value.bookAuthor,

        publisher: form.value.bookPublisher,

        year: form.value.bookYear,

        category: form.value.bookCategory,

        isbn: form.value.bookISBN,

        description: form.value.bookDescription

      };

    }

  } else {

    // 添加新图书

    const newBook = {

      id: books.value.length > 0 ? Math.max(...books.value.map(b => b.id)) + 1 : 1,

      cover: form.value.bookCover,

      title: form.value.bookTitle,

      author: form.value.bookAuthor,

      publisher: form.value.bookPublisher,

      year: form.value.bookYear,

      category: form.value.bookCategory,

      isbn: form.value.bookISBN,

      description: form.value.bookDescription,

      isBorrowed: false

    };

    books.value.push(newBook);

  }

  isBookModalOpen.value = false;

  showToast(editingBook.value ? '图书更新成功' : '图书添加成功');

};

saveBook方法封装了保存图书信息的业务逻辑,涵盖了表单验证、更新现有图书信息以及添加新图书信息等操作。将这些逻辑封装在一个方法中,能够避免在模板或其他地方重复编写相同的代码,提高代码的复用性。

(3)数据处理与计算

方法可用于对数据进行处理和计算,例如日期计算、数据筛选等。

示例代码:

const isOverdue = (dueDate, returnDate = new Date().toISOString().split('T')[0]) => {

  return dueDate && returnDate > dueDate;

};



const getOverdueDays = (dueDate, returnDate = new Date().toISOString().split('T')[0]) => {

  if (!isOverdue(dueDate, returnDate)) return 0;



  const due = new Date(dueDate);

  const ret = new Date(returnDate);

  const diffTime = Math.abs(ret - due);

  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

  return diffDays;

};
  1. isOverdue方法用于判断图书是否逾期,通过比较应归还日期和实际归还日期来确定。
  2. getOverdueDays方法用于计算图书逾期的天数,在isOverdue方法的基础上进行日期计算。

(4)控制组件状态

方法可以用来控制组件的显示与隐藏、启用与禁用等状态。

示例代码:

const openBookModal = (book) => {

  editingBook.value = book;



  if (book) {

    form.value = {

      bookId: book.id,

      bookCover: book.cover,

      bookTitle: book.title,

      bookAuthor: book.author,

      bookPublisher: book.publisher,

      bookYear: book.year,

      bookCategory: book.category,

      bookISBN: book.isbn,

      bookDescription: book.description

    };

  } else {

    form.value = {

      bookId: null,

      bookCover: 'https://picsum.photos/seed/default/200/300',

      bookTitle: '',

      bookAuthor: '',

      bookPublisher: '',

      bookYear: '',

      bookCategory: '计算机',

      bookISBN: '',

      bookDescription: ''

    };

  }



  isBookModalOpen.value = true;

};



const closeBookModal = () => {

  isBookModalOpen.value = false;

};
  1. openBookModal方法用于打开图书编辑模态框,并根据传入的图书信息初始化表单数据。
  2. closeBookModal方法用于关闭图书编辑模态框,通过修改isBookModalOpen的值来控制模态框的显示与隐藏。

综上所述,在Vue 3中定义方法能够有效地处理用户交互、封装业务逻辑、进行数据处理和计算以及控制组件状态,从而提高代码的可维护性和可扩展性。

接下来详细解析本系统中设计的一些方法:

2.导航和滚动处理

const toggleMobileMenu = () => {
    isMobileMenuOpen.value = !isMobileMenuOpen.value;};

const changeView = (view) => {
    currentView.value = view;
    isMobileMenuOpen.value = false;};

const handleScroll = () => {
    if (window.scrollY > 10) {
        isScrolled.value = true;
    } else {
        isScrolled.value = false;
    }};
  1. toggleMobileMenu:切换移动端菜单的显示与隐藏。
  2. changeView:切换当前显示的视图,并关闭移动端菜单。
  3. handleScroll:监听页面滚动事件,根据滚动位置更新 isScrolled 的值。

3.日期处理

const isOverdue = (dueDate, returnDate = new Date().toISOString().split('T')[0]) => {
    return dueDate && returnDate > dueDate;};

const getOverdueDays = (dueDate, returnDate = new Date().toISOString().split('T')[0]) => {
    if (!isOverdue(dueDate, returnDate)) return 0;

    const due = new Date(dueDate);
    const ret = new Date(returnDate);
    const diffTime = Math.abs(ret - due);
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
    return diffDays;};
  1. isOverdue:判断借阅记录是否逾期。
  2. getOverdueDays:计算逾期的天数。

4.模态框操作

const openBookModal = (book) => {
    // ...};

const closeBookModal = () => {
    isBookModalOpen.value = false;};

const saveBook = () => {
    // ...};

const deleteBook = (id) => {
    // ...};
  1. openBookModal:打开添加或编辑图书的模态框,并根据传入的图书信息初始化表单数据。
  2. closeBookModal:关闭图书模态框。
  3. saveBook:保存图书信息,根据 editingBook 的值判断是添加还是更新图书。
  4. deleteBook:删除图书,先弹出确认对话框,确认后检查图书是否正在被借阅,若未被借阅则删除。

5.其他方法

const openBorrowModal = () => {
    // ...};

const closeBorrowModal = () => {
    isBorrowModalOpen.value = false;};

const saveBorrow = () => {
    // ...};

const returnBook = (borrow) => {
    // ...};

const openUserModal = (user) => {
    // ...};

const closeUserModal = () => {
    isUserModalOpen.value = false;};

const saveUser = () => {
    // ...};

const toggleUserBlock = (user) => {
    // ...};

const viewBorrowDetails = (borrow) => {
    // ...};

const closeBorrowDetailsModal = () => {
    isBorrowDetailsModalOpen.value = false;};

const closeConfirmDialog = () => {
    isConfirmDialogOpen.value = false;
    confirmCallback = null;};

const confirmAction = () => {
    if (typeof confirmCallback === 'function') {
        confirmCallback();
    }
    closeConfirmDialog();};

const getBorrowingCount = (userId) => {
    // ...};

const getReturnedCount = (userId) => {
    // ...};

const getOverdueCount = (userId) => {
    // ...};

const getBorrowHistory = (bookId) => {
    // ...};

const getStatusText = (borrow) => {
    // ...};

const getStatusColor = (borrow) => {
    // ...};

const prevPage = () => {
    // ...};

const nextPage = () => {
    // ...};

const prevBorrowPage = () => {
    // ...};

const nextBorrowPage = () => {
    // ...};

const prevUserPage = () => {
    // ...};

const nextUserPage = () => {
    // ...};

const showToast = (message) => {
    // ...};

这些方法分别用于处理借阅记录、用户信息的添加、编辑、删除、归还等操作,以及分页、显示提示信息等功能。

生命周期钩子

1.什么是什么周期钩子

在Vue 3里,生命周期钩子(Lifecycle Hooks)是一些特殊的函数,它们允许开发者在组件生命周期的特定阶段执行自定义代码。组件的生命周期涵盖了从创建、挂载到更新,再到销毁的整个过程,而生命周期钩子就像是在这个过程中设置的监控点,让开发者可以在关键时间点插入自己的逻辑。有关周期钩子的内容可以看我的CSDN文章:Vue 3里的生命周期钩子(Lifecycle Hooks)-CSDN博客 

通过使用生命周期钩子,开发者可以更好地控制组件在不同阶段的行为,确保组件的正常运行和资源的合理利用。

onMounted(() => {
    window.addEventListener('scroll', handleScroll);
    initCharts();});

在组件挂载完成后,添加滚动事件监听器,并初始化图表。

返回值

即setup()函数的返回值。

return {

    // 数据
    currentView,
    isScrolled,
    isMobileMenuOpen,
    // ...

    // 计算属性
    filteredBooks,
    totalPages,
    paginatedBooks,
    // ...

    // 方法
    toggleMobileMenu,
    changeView,
    handleScroll,
    // ...};

将响应式数据、计算属性和方法返回,以便在模板中使用。

综上所述,这段代码实现了一个完整的图书管理系统的逻辑,包括数据的存储、筛选、分页、模态框操作、日期处理等功能。


网站公告

今日签到

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