Vue基础-Day07
综合项目实战
项目介绍
项目整体功能:图书管理;人员管理;楼层管理
- 图书管理
- 图书列表
- 添加图书
- 删除图书
- 修改图书

总结:主要做图书的增删改查,会使用路由,并且整合登录组件
初始化项目
目标:能够基于VueCli创建项目
- 通过vue create命令创建项目
vue create mydemo
- 进入项目跟目录
cd mydemo
- 运行项目
npm run serve
页面基本布局
目标:能够基于Bootstrap实现案例的基本布局结构
- 分析页面的组件结构
- NavBar.vue 顶部导航组件
- Aside.vue 左侧菜单组件
- BookList.vue 英雄列表组件
- BookAdd.vue 添加英雄组件
- BookEdit.vue 编辑英雄组件
- PersonList.vue 装备列表组件
- FloorList.vue 技能列表组件
- 安装bootstrap包
npm i bootstrap@3.3.7
- 导入样式
// 引入bootstrapimport 'bootstrap/dist/css/bootstrap.min.css'
总结:
- 我们准备的布局基于Bootstrap的,并且有版本号要求
- 导入样式的方式采用import,而不是在HTML页面中link引入
路由配置
目标:实现点击侧边栏的连接。需要切换右侧的内容。这个需要路由来实现。
- 安装vue-router
npm i vue-router
- 导入路由
import VueRouter from 'vue-router'
- 注册路由(配置路由插件)
Vue.use(VueRouter)
- 准备路由组件
- Login.vue
- Home.vue
- 配置路由规则
// 导入路由组件import Login from './views/Login.vue'import Home from './views/Home.vue'// 配置路由映射const routes = [{ path: '/', redirect: '/login' },{ path: '/login', component: Login },{ path: '/home', component: Home }]
- 实例化路由对象
// 创建路由对象const router = new VueRouter({routes})
- 挂载路由对象到Vue实例上
new Vue({render: h => h(App),router}).$mount('#app')
- 配置路由填充位App.vue
<template><div><!-- 路由填充位:Login.vue/Home.vue --><router-view></router-view></div></template>
总结:
- 在脚手架环境下配置路由
- 配置一级路由组件Login.vue和Home.vue
实现基本的登录跳转
目标:熟悉编程式导航应用场景
export default {data () {return {uname: '',pwd: ''}},methods: {handleLogin () {// 调用接口实现登录if (this.uname === 'admin' && this.pwd === 'admin') {// 登录成功,跳转到主页面this.$router.push('/home')} else {alert('用户名或者密码错误')}}}}
总结:登录时,调用接口后,判断成功与否,如果成功跳转到主页(编程式导航)
导航组件
实现顶部导航组件
- 拆分组件实现布局
NavBar.vue
<template><nav class="navbar navbar-inverse"><a class="navbar-brand" href="#">CURD</a></nav></template>
- 导入组件并使用
Home.vue
<!-- 组件的模板HTML --><template><div id="app" class="container"><!-- 顶部导航栏组件 --><NavBar/></div></template><!-- 组件的配置选项JS --><script>// 导入另外一个组件import NavBar from './components/NavBar.vue'export default {components: {NavBar}}</script>
总结:局部组件的使用流程:导入组件;配置组件;使用组件。
侧边栏组件
拆分侧边栏组件布局
- 组件模板布局
<template><div class="col-md-2"><div class="row"><div class="list-group"><a href="#" class="list-group-item active">图书列表</a><a href="#" class="list-group-item">人员列表</a><a href="#" class="list-group-item">楼层列表</a></div></div></div></template>
- 导入组件用法
<template><div class="container"><!-- 在脚手架的环境下,使用如下的两种命名方式都可以 --><!-- <NavBar/> --><!-- 顶部导航栏组件 --><nav-bar/><!-- 左侧菜单组件:单个单词的组件名称不可以使用纯小写的方式,但是首字符大写是可以的 --><aside-menu/></div></template><script>// 导入子组件import NavBar from './components/NavBar.vue'import AsideMenu from './components/Aside.vue'export default {// 配置局部子组件components: {NavBar,AsideMenu}}</script>
总结:局部组件的用法,导入组件;配置组件;使用组件
配置二级路由
目标:熟悉嵌套路由的应用场景
- 在主页路由组件中再次配置路由填充位,左侧导航菜单使用router-link标签实现跳转
<template><div class="col-md-2"><div class="row"><div class="list-group"><a href="#" class="list-group-item active">英雄列表</a><a href="#" class="list-group-item"></a><a href="#" class="list-group-item">技能列表</a><router-link>图书列表</router-link><router-link>人员列表</router-link><router-link>楼层列表</router-link></div></div></div></template><div class="col-md-10"><!-- 右侧内容区:二级路由填充位 --><router-view></router-view></div>
总结:熟悉嵌套路由的应用场景
控制路由跳转菜单激活
目标:实现路由跳转菜单高亮控制
激活(高亮):点击哪一个链接,哪一个链接应该添加一个类名active,没有点中的链接标签去掉类名
实现原理:基于vue-router的相关配置,自动给链接标签添加指定的类名
// linkExactActiveClass属性的作用:控制点中链接标签后添加的类名名称,默认的名称是router-link-exact-activeconst router = new VueRouter({ routes , linkExactActiveClass: 'active'})
- 如果不加linkExactActiveClass: ‘active’属性配置,需要如何控制点中高亮?需要自己给router-link-exact-active类名添加样式
<style>.router-link-exact-active {color: red !important;}</style>
总结:
- 定制类名,基于Bootstrap提供类名active控制高亮
- 自己给router-link-exact-active类名添加高亮样式
拆分路由模块
目标:
main.js的职责足够单一,让代码更好维护。全局资源导入,根实例初始化。
- 封装路由模块:
src/router/index.js
// 拆分路由映射模块import Vue from 'vue'import VueRouter from 'vue-router'Vue.use(VueRouter)// 导入路由组件// @表示src目录import Login from '@/views/Login.vue'import Home from '@/views/Home.vue'import BookList from '@/views/BookList.vue'import PersonList from '@/views/PersonList.vue'import FloorList from '@/views/FloorList.vue'// 配置路由映射const routes = [{ path: '/', redirect: '/login' },{ path: '/login', component: Login },{path: '/home',component: Home,redirect: '/home/books',children: [{ path: 'books', component: BookList },{ path: 'persons', component: PersonList },{ path: 'floors', component: FloorList }]}]// 创建路由对象const router = new VueRouter({// 自定义vue-router默认点中链接后添加的类名// 类名的默认值是router-link-exact-activelinkExactActiveClass: 'active',routes})export default router
- 导入路由模块
// 导入路由模块import router from '@/router/index.js'new Vue({render: h => h(App),// 挂载路由实例router}).$mount('#app')
总结:简化入口文件的代码,路由模块可以单独维护。
准备接口
目标:基于json-server模拟接口。
- 创建db.json文件
{ "books": [ { "id": 1, "bname": "西游记", "author": "吴承恩", "ctime": "2020-03-21 10:38:20" }, { "id": 2, "bname": "红楼梦", "author": "曹雪芹", "ctime": "2020-03-21 10:38:20" }, { "id": 3, "bname": "三国演义", "author": "罗贯中", "ctime": "2020-03-21 10:38:20" } ]}
- 启动接口
# 注意:需要在db.json文件所在的路径执行命令json-server db.json# 测试接口地址:http://localhost:3000/books
- 安装axios包
npm i axios
总结:
- 模拟后端接口
- 安装axios包,用于项目中调用接口
图书列表组件
目标:实现图书列表组件
- 组件基本布局
<template><div class="list-container"><a href="heroes-form.html" class="btn btn-primary">添加英雄</a><hr /><table class="table table-hover"><thead><tr><th>ID</th><th>英雄名称</th><th>英雄性别</th><th>创建时间</th><th>操作</th></tr></thead><tbody><tr><td>101</td><td>亚索</td><td>男</td><td>2019-02-10 10:50:12</td><td><button class="btn btn-success">编辑</button> <button class="btn btn-danger">删除</button></td></tr></tbody></table></div></template><script>export default {}</script><style></style>
- 在组件初始化时,获取英雄列表数据
// 导入axiosimport axios from 'axios'export default {data () {return {list: []}},created () {this.loadBookList()},methods: {// 获取英雄列表数据async loadBookList () {// 通过axios发查询列表请求const ret = await axios.get('http://localhost:3000/books')this.list = ret.data}}}
- 根据list数据,进行模板渲染。
<tbody> <tr :key='item.id' v-for='item in bookList'> <td>{{item.id}}</td> <td>{{item.bname}}</td> <td>{{item.author}}</td> <td>{{item.ctime}}</td> <td> <button class="btn btn-success">编辑</button> <button class="btn btn-danger">删除</button> </td> </tr></tbody>
总结:调用接口获取图书列表数据;动态渲染页面 注意:axios需要导入使用
添加图书组件
目标:实现添加图书功能
<form><legend>添加图书</legend><div class="form-group"><label>图书名称</label><input v-model='bname' type="text" class="form-control"></div><div class="form-group"><label>图书作者</label><input v-model='author' type="text" class="form-control"></div><button @click='handleSubmit' type="button" class="btn btn-primary">提交</button></form>
- 控制添加表单页面的路由跳转
<router-link to='/home/add' class="btn btn-primary">添加图书</router-link>
const routes = [{ path: '/', redirect: '/login' },{ path: '/login', component: Login },{path: '/home',component: Home,redirect: '/home/books',children: [{ path: 'books', component: BookList },{ path: 'persons', component: PersonList },{ path: 'floors', component: FloorList },+ { path: 'add', component: BookAdd }]}]
- 提交表单
methods: { async handleSubmit () { // 调用接口添加图书 const ret = await axios.post('http://localhost:3000/books', { bname: this.bname, author: this.author, ctime: new Date() }) if (ret.status === 201) { // 添加图书成功,跳转到图书列表页面 this.$router.push('/home/books') } else { alert('添加图书失败') } } }
总结:点击跳转页面;准备布局绑定表单数据;绑定提交事件;调用接口添加;成功后跳转到列表页面。
编辑图书功能
目标:实现编辑英雄功能
- 根据图书id查询图书信息并显示到表单里
- 控制页面跳转
// 控制跳转到编辑图书页面handleJump (id) { // /home/edit/1 this.$router.push('/home/edit/' + id)},
- 基于动态路由传递参数
{ path: 'edit/:id', component: BookEdit, props: true },
- 根据id查询图书的信息
import axios from 'axios'export default {props: ['id'],data () {return {book: {}}},methods: {// 根据id查询图书的信息async loadBookInfo () {const ret = await axios.get('http://localhost:3000/books/' + this.id)this.book = ret.data}},created () {this.loadBookInfo()}}
- 把数据填充到表单里面
<form><legend>编辑图书</legend><div class="form-group"><label>图书名称</label><input v-model='book.bname' type="text" class="form-control"></div><div class="form-group"><label>图书作者</label><input v-model='book.author' type="text" class="form-control"></div><button @click='handleSubmit' type="button" class="btn btn-primary">提交</button></form>
总结:页面跳转;获取图书列表数据;填充表单。
- 编辑完成数据后提交表单
- 绑定表单提交事件
<button @click='handleSubmit' type="button" class="btn btn-primary">提交</button>
- 实现提交功能
// 提交表单事件函数async handleSubmit () {const ret = await axios.patch('http://localhost:3000/books/' + this.book.id, this.book)if (ret.status === 200) {// 更新成功,跳转到列表页面this.$router.push('/home/books')} else {alert('编辑失败')}}
总结:绑定表单提交事件,实现提交功能
删除图书功能
目标:实现删除英雄功能
- 绑定删除按钮点击事件
- 弹出确认框
- 点击确认,发送删除请求
- 删除成功,更新当前列表
<button @click="handleDelete(item.id)" class="btn btn-danger">删除</button>
// 处理删除操作async handleDelete (id) { if (confirm('确实要删除吗?')) { // 如果点击了确定,那么执行这个流程 const ret = await axios.delete('http://localhost:3000/books/' + id) if (ret.status === 200) { // 删除成功,刷新列表 this.loadBookList() } else { alert('删除失败') } }},
总结:绑定事件;调用接口;刷新列表
时间过滤器
目标:基于moment包实现时间格式化过滤器
- 安装 moment
npm i moment
- 导入moment
BookList.vue
// 导入momentimport moment from 'moment';
- 定义过滤器
filters: {// formatTime 过滤器名字,对应的函数处理格式。formatTime (value) {// 使用过滤器的时候,管道符 | 前的js表达式执行结果,就是valuereturn moment(value).format('YYYY-MM-DD HH:mm:ss')}},
- 使用过滤器
<td>{{item.cTime|formatTime}}</td>
总结:
- 过滤器用法
- 第三方包moment的用法
axios全局配置
目标:全局配置axios
// 进行axios的全局挂载import axios from 'axios'// 将来通过vue的实例访问$http,其实就是axios。Vue.prototype.$http = axios
总结:
- 面向对象:实例对象可以访问构造函数原型上的属性和方法
- axios可以统一配置基准路径
- 所有的组件都是Vue构造函数的实例对象
