图书管理练习(Vue 基础练习 )

这是在学习 Vue 的模板语法和常用特性后自己敲的一个小练习。目的是避免眼高手低,做到掌握 Vue 的基本使用。其中具体涉及的模板语法和常用特性详细介绍在 xxx 记录(相关笔记还未总结~~)。

页面结构的搭建

静态页面如下图所示:

在这里插入图片描述

功能简要描述:在表单中输入编号和图书名称在编号唯一(且还未存在)情况下可以添加书籍,添加成功后在表格中展示所有的图书信息。点击删除时可以删除相应的书籍。点击修改时将相应的图书信息展示到表单中,用户可以对书籍名称进行修改(编号无法修改),点击提交即可完成修改。

书写页面结构是第一个步骤,根据上图的静态页面书写的页面结构示例如下:

<body>
    <div class="container">
        <div class="header">
            <h2>图书管理</h2>
            <div class="form">
                <label for="bookId"> 编号:</label>
                <input type="text" id="bookId">
                <label for="bookName"> &nbsp;&nbsp;&nbsp;名称:</label>
                <input type="text" id="bookName">
                <button>提交</button>
            </div>
        </div>
        <table>
            <thead>
                <tr>
                    <th>编号</th>
                    <th>名称</th>
                    <th>时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <!-- 由于只是结构所以就只写一行了 -->
                <tr>
                    <td>1</td>
                    <td>三国演义</td>
                    <td>2021-04-28</td>
                    <td><a href="#">修改</a> | <a href="#">删除</a></td>
                </tr>
            </tbody>
        </table>

    </div>
</body>

简单的样式书写

样式的话能看就行,纯粹只是为了练习 Vue 基础,没有必要那么严格,所以下面的示例代码可以略过(不同的人可以不一样的实现)。

<style>
	.container {
    width: 530px;
    height: 700px;
    margin: auto;
    /* background-color: yellow; */
    text-align: center;
	}
	
	.form {
	    padding: 5px 0 10px;
	    border-bottom: 1px solid black;
	    background-color: rgb(243, 220, 171);
	}
	
	table {
	    width: 100%;
	    border-collapse: collapse;
	    line-height: 35px;
	}
	
	thead {
	    background-color: rgb(243, 220, 171);
	}
	
	tbody {
	    border-left: 1px dashed rgb(243, 220, 171);
	    border-top: 1px dashed rgb(243, 220, 171);
	}
	
	td {
	    height: 35px;
	    border-right: 1px dashed rgb(243, 220, 171);
	    border-bottom: 1px dashed rgb(243, 220, 171);
	}
	
	a {
	    text-decoration: none;
	    color: #333;
	}
	
	a:hover {
	    color: red;
	}
	
	
	/* button {
	height: 22px;
	line-height: 22px;
	} */
	
	button:hover {
	    cursor: pointer;
	}
	
	input {
	    outline-style: none;
	}
</style>

功能的实现的步骤

交互功能包含图书添加功能,图书编辑功能、图书删除功能和图书的总数统计功能。其中图书的添加和编辑功能均在表单内操作图书信息。其中编辑功能需要在表单中展示图书信息,并且图书 id 信息无法修改(其实就是只能修改图书名称)。

添加图书功能

1、实现图书例表的展示功能,利用 Vue 生命周期中执行的钩子函数 mounted() 模拟从后端获取数据并展示至页面。

2、在展示数据时对于时间数据需要使用过滤器 dataFormat 将原始数据转换为特定的格式(在此需要注意过滤器的使用语法)。

3、提交按钮添加点击事件,但是由于后面编辑功能同时用到该按钮所以需要设置一个 flag 标记执行的是编辑功能(true)还是添加功能(false)。

4、为验证表单信息是否完整和图书名称是否存在可以设置两个监听 id 和 name 的侦听器(判断图书 id 是否已存在放在事件处理函数内的添加逻辑内处理,因为图书编辑功能 id 无法编辑,即 id 没有必要在侦听器内判断是否存在。)。

5、当填写的数据信息完整时,提交按钮不再禁用。可以进行提交,提交后验证图书名称或者 id 是否已存在。如果不存在则进行添加操作,反之进行用户提示和终止操作。

6、最后添加一个自定义指令用于表单自动获取焦点。至此整个添加功能已经全部实现。

图书编辑功能

1、为删除按钮添加点击事件,因为是 a 元素所以需要使用事件修饰符阻止默认的跳转行为,绑定 modify 事件处理函数。

2、将用于区分提交按钮的添加和修改功能的 flag 标记值改为 true(修改功能,id 表单不可编辑),并将相应的图书 id 和 name 数据填充至表单。

3、在提交按钮的点击事件处理程序中实现图书编辑逻辑,但是这里需要深刻理解数据响应式,特别是数组和对象引用类型数据的响应式特点

4、最后还需要注意的是,书籍名称可以不修改直接提交。但是修改后的名称不能和其他的书籍名称重复(重名)。最后恢复表单状态,即完成整个编辑功能。

书籍删除功能

1、删除功能相对比较简单,为删除按钮绑定点击事件并向事件处理函数传递该书籍 id 。

2、可以通过书籍 id 在 books(保存书籍数据的集合)使用 findIndex() 首先查找出该对象在数组中的索引,然后使用 splice() 方法删除数组中对应索引的元素。

3、还可以使用 filter() 方法筛选出新数组,然后将新数组赋值给 books 。

书籍总数的统计

在此使用的是 Vue 中计算属性的方式实现。设置一个计算属性 total 用于统计书籍总数。这里主要是巩固计算属性的相关知识,需要理解其和方法的区别。

<style>
    [v-cloak] {
        display: none;
    }
</style>

<body>
    <div class="container" id="app">
        <div class="header">
            <h2>图书管理</h2>
            <div class="form">
                <label for="bookId"> 编号:</label>
                <input type="text" id="bookId" v-model.trim="id" v-focus :disabled="flag">
                <label for="bookName"> &nbsp;&nbsp;&nbsp;名称:</label>
                <input type="text" id="bookName" v-model.trim="name">
                <button @click="submit" :disabled='!isFull'>提交</button>
            </div>
            <div class="total">
                <span>图书总数: </span>
                <span>{{total}}</span>
                <button @click="booksSort(-1)">降序</button>
                <button @click="booksSort(1)">升序</button>                
            </div>
        </div>
        <table>
            <thead>
                <tr>
                    <th>编号</th>
                    <th>名称</th>
                    <th>时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <!-- 由于只是结构所以就只写一行了 -->
                <tr :key="item.id" v-for="item in books" v-cloak>
                    <td>{{item.id}}</td>
                    <td>{{item.name}}</td>
                    <td>{{ item.date | dateFormat('yyyy-MM-dd hh:mm') }}</td>
                    <td>
                        <a href="#" @click.prevent="modify(item)">修改</a>
                        <span>|</span>
                        <a href="#" @click.prevent="del(item.id)">删除</a>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

<script src="js/vue.js"></script>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            id: '',
            name: '',
            flag: false,
            isExist: false,
            isFull: false,
            books: []
        },
        methods: {
            submit: function() {
                if (this.flag) {
                    var index = this.books.findIndex((item) => {
                        return this.id == item.id;
                    });
                    // console.log(index);
                    // 防止修改名称为其他书籍名称
                    if (this.isExist && this.books[index].name != this.name) {
                        return alert('书籍重名,无法修改');

                    }
                    // 要深刻理解使用数组索引的方式更新数组数据不是响应式的问题
                    this.books[index].name = this.name;
                    this.id = '';
                    this.name = '';
                    this.flag = false;

                } else {
                    // 图书添加功能
                    if (this.isExist) {
                        return alert('图书已存在不能重复添加');

                    }
                    // 这里需要使用箭头函数否则里面的this指向的就不再是vm
                    var flag = this.books.some((item) => {
                        // console.log(this);
                        return item.id == this.id;

                    })
                    if (flag) {
                        return alert('该图书 id 已被占用请重新编辑');
                    }
                    var book = {};
                    book.id = this.id;
                    book.name = this.name;
                    book.date = +new Date();
                    this.books.push(book);
                    this.id = '';
                    this.name = ''
                }
            },
            modify: function(item) {
                this.flag = true;
                this.id = item.id;
                this.name = item.name
            },
            del: function(id) {
                // 删除图书
                // 根据id从数组中查找元素的索引
                // var index = this.books.findIndex(function(item){
                //   return item.id == id;
                // });
                // 根据索引删除数组元素
                // this.books.splice(index, 1);
                // -------------------------
                // 方法二:通过filter方法进行删除

                this.books = this.books.filter(function(item) {
                    return item.id != id;
                });
            },
            booksSort: function(flag) {
                this.books = this.books.sort((a, b) => {
                    if (flag == -1) return b.id - a.id;
                    if (flag == 1) return a.id - b.id;
                })

            }
        },
        // 监听name和id的侦听器
        watch: {
            name: function(val) {
                // 数据完整时将isFull设置为true,默认为false
                this.isFull = this.id && this.name;
                // 如果书籍名称已经存在则将isExist设置为true
                this.isExist = this.books.some(function(item) {
                    return item.name == val;
                });
            },
            id: function(val) {
                this.isFull = this.id && this.name;
            }
        },
        mounted: function() {
            this.books = [{
                id: '1',
                name: '三国演义',
                date: 1619776363317
            }]
        },
        directives: {
            focus: {
                // 指令定义
                inserted: function(el) {
                    el.focus();
                }
            }
        },
        filters: {
            // 用于格式化时间数据的过滤器
            dateFormat: function(date, format) {
                if (typeof date === "string") {
                    var mts = date.match(/(\/Date\((\d+)\)\/)/);
                    if (mts && mts.length >= 3) {
                        date = parseInt(mts[2]);
                    }
                }
                date = new Date(date);
                if (!date || date.toUTCString() == "Invalid Date") {
                    return "";
                }
                var map = {
                    "M": date.getMonth() + 1, //月份 
                    "d": date.getDate(), //日 
                    "h": date.getHours(), //小时 
                    "m": date.getMinutes(), //分 
                    "s": date.getSeconds(), //秒 
                    "q": Math.floor((date.getMonth() + 3) / 3), //季度 
                    "S": date.getMilliseconds() //毫秒 
                };
                format = format.toString().replace(/([yMdhmsqS])+/g, function(all, t) {
                    var v = map[t];
                    if (v !== undefined) {
                        if (all.length > 1) {
                            v = '0' + v;
                            v = v.substr(v.length - 2);
                        }
                        return v;
                    } else if (t === 'y') {
                        return (date.getFullYear() + '').substr(4 - all.length);
                    }
                    return all;
                });
                return format;
            }
        },
        computed: {
            total: function() {
                return this.books.length ? this.books.length : '0';
            }
        }
    })
</script>

在实现上述功能后,发现了一个不足的地方。书籍的展示顺序完全与添加时的顺序有关,为什么不能按照书籍 id 的大小来显示呢?常用的开发场景是有一个按钮可以控制排序方式,可以给按钮添加事件的形式添加排序功能,但是在添加新的书籍后新增的书籍并未按照该规则进行排序展示。因此我将新增的书籍使用 unshift 方法将其展示在最前面,这样可能比较符合用户需求。

各部分功能实现效果展示。

按照上述的思路将各部分功能实现,当然对于排序功能的实现代码我就不去贴代码了。现在展示各部分功能实际的效果。

1、添加功能效果演示:书籍信息不全时提交按钮禁用,书籍 id 或名称已存在还试图添加书籍时终止操作并提示,所有信息符合时添加成功,将添加的书籍信息在第一项展示。注意此时的图书总数和升降序按钮也可以正常使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VvZrvFgc-1619789792587)(https://z3.ax1x.com/2021/04/30/gE6528.gif)]

2、编辑功能:点击修改按钮自动在表单展示书籍信息,id 表单项禁用,当输入的书籍名称为已有名称且不是其原有名称时终止修改操作并提示。但是注意,如果是原名称则可以提交修改操作(这里没有演示,所以特别说明了一下),因为客户可能误点了修改而不想做出修改。

在这里插入图片描述
3、删除功能:点击删除删除对应的书籍,不妥之处就是直接删除没有给用户确定操作的交互效果,会出现误删。因此可以添加一个 confirm() 进行询问。

在这里插入图片描述

总结

所有的需求都已经实现,某些需求是为了能够覆盖所学知识而增加的或者刻意使用所学知识去实现的。整个练习是本人在看完教程后,为避免眼高手低而自己去从零开始实现的,并对其进行了小小的改进。感觉收获还是有的。下一站 Vue 组件化开发,冲~~~~~

  • 16
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
### 回答1: Vue3.0是一款前端框架,相比于2.x版本,3.0版本提供了更好的性能和更好的类型支持。那么,如何从零入门Vue3.0呢? 首先,需要了解一些基础概念,例如Vue实例、组件、指令和计算属性等。可以通过阅读Vue官方文档或者相关书籍进行学习。另外,熟练掌握HTML、CSS以及JavaScript基础知识也很重要。 接着,可以通过搭建一个简单的开发环境来完成Vue3.0的入门。可以搜索相关的教程或者跟着官方文档进行安装步骤,然后创建一个Vue实例,引入一些组件和指令,最后将它们渲染到页面上。 当了解了Vue3.0的基本用法后,可以通过阅读文档和实际操作来进一步提高自己的技能水平。一些高级的特性,例如Composition API、Teleport和Suspense等,可以帮助我们更加高效地开发应用程序。 最后,通过练习和写一些小项目来巩固学习成果,例如创建一个简单的To-Do-List应用或者一个博客系统等,这样可以更好地掌握Vue3.0的相关知识和技巧。 综上所述,Vue3.0的零基础入门需要掌握一些基础知识、搭建开发环境、了解基本用法、学习高级特性和练习实践等步骤,通过不断地探索和实践,可以更加深入地了解并掌握Vue3.0的技术。 ### 回答2: Vue 3.0 是一个开源的 JavaScript 框架,用于构建现代化的 Web 应用和单页面应用。如果您是一个零基础的入门者,现在学习 Vue 3.0 并不会太难,以下是一些入门技巧: 第一步,需要了解 Vue 3.0 的基础概念和语法。Vue 入门者需要了解 Vue CLI、Vue 组件、Vue 模板、指令、插件等基础概念。同时,需要使用 web 开发技术,如 HTML、CSS 等,并能够熟练使用 JavaScript。 第二步,您可以通过阅读官方文档或参加在线教学平台来进行学习。对于初学者,Vue 3.0 官方文档提供了基础特性、组件、Api、路由、状态管理、插件等信息。此外,优质的教学网站也是一个好的选择。 第三步,您可以开始进行实践项目。这些项目可以是基于 Vue 3.0 的 ToDo 应用程序、新闻客户端、简单的博客等。这些应用可以将概念与实践相结合,以加深您对 Vue 3.0 的理解。 第四步,加入开发社区并与开发者互动交流。开源社区比较活跃,您可以参与到其中,通过问答、发表评论、提出问题等方式得到帮助和学习经验。 最后,Vue 3.0 由于其易于学习和使用而受到了开发者的喜爱。只要您学习基础知识和实践经验,并成为 Vue 3.0 开发社区中的一员,那么在未来的开发实践中,您将会收获很多成果。 ### 回答3: Vue.js是一款简单易用、高效灵活的JavaScript框架。它被广泛应用于前端开发,不仅可以用于构建单页应用和复杂的Web应用程序,还可以用于构建管理面板、后台系统等。 Vue.js 3.0是Vue.js框架的最新版本,它带来了一些令人兴奋的新特性,包括更少的内存消耗、更快的运行速度和更好的类型推断。下面是一些零基础入门Vue.js 3.0的步骤: 1.准备环境 在开始使用Vue.js 3.0之前,您需要在计算机上安装Node.js和npm。然后,您可以使用npm安装Vue.js 3.0的CLI。 2.创建Vue.js应用程序 使用Vue.js CLI创建一个新的Vue.js应用程序。使用以下命令: vue create my-project 这条命令将创建一个新的Vue.js项目。 3.学习Vue.js基础知识 Vue.js是一种基于组件的框架,它允许您将复杂的应用程序拆分成多个小部件。了解Vue.js的基础知识非常重要,包括模板、组件、数据绑定和事件处理程序等。 4.编写组件 编写组件是Vue.js应用程序开发的核心。您可以使用Vue.js的组件系统来创建可重用的组件,并将它们组合成一个完成的应用程序。 5.使用Vue.js的路由器 Vue.js的路由器系统可以帮助您管理应用程序中的不同视图和组件之间的导航。学习使用Vue.js的路由器非常重要。 总之,Vue.js是一款易于学习和使用的前端开发框架。通过学习Vue.js的基础知识、编写组件、使用路由器和其他相关技术,您可以轻松地创建可重用的Vue.js应用程序。
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值