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
- 导入样式
// 引入bootstrap
import '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-active
const 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-active
linkExactActiveClass: '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>
- 在组件初始化时,获取英雄列表数据
// 导入axios
import 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
// 导入moment
import moment from 'moment';
- 定义过滤器
filters: {
// formatTime 过滤器名字,对应的函数处理格式。
formatTime (value) {
// 使用过滤器的时候,管道符 | 前的js表达式执行结果,就是value
return 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构造函数的实例对象