Vue笔记07 总结
coconutnut

这个教程6p学完了~总结一下这个图书增删改查的demo

https://www.bilibili.com/video/av85793766


工程创建
数据库部分略

后端

项目结构

  • Book 实体类
  • BookRepository 继承JpaRepository,提供数据库增删改查接口
  • BookHandler 相应请求
  • BookRepositoryTest 测试BookRepository的方法

Book

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.coconutnut.bookstore_server.entity;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity // 根据类名和表名对应绑定
@Data // 自动生成get/set
public class Book {
@Id // 主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // id自增
private Integer id;
private String name;
private String author;
}

BookRepository

1
2
3
4
5
6
7
8
package com.coconutnut.bookstore_server.repository;

import com.coconutnut.bookstore_server.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;

// <>中第一个是实体类,第二个是主键类型
public interface BookRepository extends JpaRepository<Book,Integer> {
}

JpaRepository中有常用的增删改查方法,可以直接调用

& 通常流程:先在Test中测试方法,没问题在Controller中写对外接口,浏览器看一下返回值对不对,最后写前端

Spring工程自带一个测试类BookstoreServerApplicationTests
自己创建可在接口名上右键->Go To->Test->Create New Test
生成的测试类加@SpringBootTest,要测试的类@Autowired自动注入,写的方法加@Test即可

BookHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.coconutnut.bookstore_server.controller;

import com.coconutnut.bookstore_server.entity.Book;
import com.coconutnut.bookstore_server.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/book")
public class BookHandler {

@Autowired
private BookRepository bookRepository;

// 查所有书
@GetMapping("/findAll")
public List<Book> findAll(){
return bookRepository.findAll();
}

// 查一页
@GetMapping("/findAll/{page}/{size}")
// @PathVariable获取参数
public Page<Book> findAll(@PathVariable("page") Integer page,@PathVariable("size") Integer size){
PageRequest pageRequest = PageRequest.of(page,size);
return bookRepository.findAll(pageRequest);
}

// 新增
@PostMapping("/save")
// @RequestBody把json格式转成java对象
public String save(@RequestBody Book book){
Book result = bookRepository.save(book);
if(result != null){
return "success";
}else{
return "error";
}
}

// 查一个
@GetMapping("/findById/{id}")
public Book findById(@PathVariable("id") Integer id){
return bookRepository.findById(id).get();
}

// 修改
@PutMapping("/update")
public String update(@RequestBody Book book){
Book result = bookRepository.save(book);
if(result != null){
return "success";
}else{
return "error";
}
}

// 删除
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") Integer id){
bookRepository.deleteById(id);
}

}

前端

项目结构

  • Index.js 配路由
  • App.vue 常驻的页面
  • 其它vue 嵌入的页面

index.js

配置页面的从属关系与路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from "../views/Index";
import BookManage from '../views/BookManage'
import AddBook from '../views/AddBook'
import BookUpdate from '../views/BookUpdate'

Vue.use(VueRouter)

const routes = [
{
path: '/',
name: '图书管理',
show: true,
component:Index,
redirect:'book-manage',
children:[
{
path: '/book-manage',
name: '查询图书',
component: BookManage
},
{
path: '/add-book',
name: '添加图书',
component: AddBook
},
]
},
{
path: '/book-update',
name: '修改',
show: false,
component: BookUpdate
},
]

const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})

export default router

页面嵌套与跳转

App.vue中template结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">
<el-container>

<el-aside>
<el-menu>
<!-- ... -->
</el-menu>
</el-aside>

<el-main>
<router-view></router-view>
</el-main>

</el-container>
</div>
</template>
1
2
3
4
5
6
7
8
9
+------------------------------------+ 
| App |
| +------+-------------------------+ |
| | menu | main | |
| | | +---------------------+ | |
| | | | router-view | | |
| | | +---------------------+ | |
| +------+-------------------------+ |
+------------------------------------+

左侧aside区域中放menu,右侧main中由router动态加载

Index.vue中template结构

1
2
3
<template>
<router-view></router-view>
</template>

Index.vue中其它都是默认的

根据index.js中的配置,’/‘就加载Index.vue到App.vue的router-view区域,再’/book-manage’加载Index.vue的router-view区域。而Index重定向到book-manage,于是’http:localhost:4000/‘默认就显示了book-manage

而’/book-update’加载Update.vue到App.vue的router-view区域

故显示所有子页面的时候都可以看到菜单

根据routes配置动态加载菜单栏 -> App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template>
<div id="app">
<el-container style="height: 500px; border: 1px solid #eee">

<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<!-- 这里添加router标签,el-menu-item的index就会被作为router路径-->
<el-menu router :default-openeds="['0','1']">
<!-- show属性为true则遍历-->
<el-submenu v-for="(item,index) in $router.options.routes" :index="index+''" v-if="item.show">
<template slot="title"><i class="el-icon-setting"></i>{{item.name}}</template>
<!-- 条件判断当前路径是否等于index,更改选中状态,主要是在初始化时增加默认的选中状态-->
<el-menu-item v-for="item2 in item.children" :index="item2.path" :class="$route.path==item2.path ? 'is-active' : ''">{{item2.name}}</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>

<el-main>
<!-- 上面又index确定的router被显示在这里,也就是main区域中-->
<router-view></router-view>
</el-main>

</el-container>
</div>
</template>

<script>
export default {
name: 'app',
}
</script>

<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

表单显示所有书籍、翻页、删除 -> BookManage.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<template>
<div>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
fixed
prop="id"
label="编号"
width="150">
</el-table-column>
<el-table-column
prop="name"
label="书名"
width="150">
</el-table-column>
<el-table-column
prop="author"
label="作者"
width="150">
</el-table-column>
<el-table-column>
<!-- fixed="right"-->
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="edit(scope.row)" type="text" size="small">修改</el-button>
<el-button @click="deleteBook(scope.row)" type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
layout="prev, pager, next"
:page-size="pageSize"
:total="total"
@current-change="clickPage">
</el-pagination>
</div>
</template>

<script>
export default {
methods: {
// 删除
deleteBook(row) {
const _this = this
axios.delete('http://localhost:8181/book/deleteById/'+row.id).then(function(resp){
// 这里没有管返回信息,也可以判断状态,200就是成功
_this.$alert('《'+row.name+'》删除成功!', '消息', {
confirmButtonText: '确定',
callback: action => {
// 刷新
window.location.reload()
}
})
})
},
// 跳转到编辑
edit(row) {
// this.$router.push('/book-update')
this.$router.push({
path: '/book-update',
query: {
id: row.id
}
})
},
// 翻页
clickPage(index){
const _this = this
axios.get('http://localhost:8181/book/findAll/'+(index-1)+'/6').then(function(resp){
_this.tableData = resp.data.content
_this.pageSize = resp.data.size
_this.total = resp.data.totalElements
})
},
},

data() {
return {
pageSize:'6',
total:'',
tableData:''
}
},

// 加载书籍信息到表单
created(){
const _this = this
axios.get('http://localhost:8181/book/findAll/0/6').then(function(resp){
_this.tableData = resp.data.content
_this.pageSize = resp.data.size
_this.total = resp.data.totalElements
})
}
}
</script>

<style scoped>

</style>

新增 -> AddBook.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<template>
<!-- model设置数据绑定,rules设置校验规则,ref是名字,在提交时使用-->
<el-form style="width: 60%" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="书名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model="ruleForm.author"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>

<script>
export default {
data() {
return {
ruleForm: {
name: '',
author: '',
},
rules: {
name: [
{ required: true, message: '请输入书名', trigger: 'blur' },
],
author: [
{ required: true, message: '请输入作者', trigger: 'blur' },
],
}
};
},
methods: {
// 提交表单
submitForm(formName) {
const _this = this
this.$refs[formName].validate((valid) => {
if (valid) {
axios.post('http://localhost:8181/book/save',this.ruleForm).then(function(resp){
if(resp.data == 'success'){
_this.$message({
message: '《'+_this.ruleForm.name+'》'+'添加成功',
type: 'success'
})
_this.$router.push('/book-manage')
}
})
}
});
},
// 重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>

修改 -> BookUpdate.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<template>
<!-- model设置数据绑定,rules设置校验规则,ref是名字,在提交时使用-->
<el-form style="width: 60%" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="编号">
<el-input v-model="ruleForm.id" readonly></el-input>
</el-form-item>
<el-form-item label="书名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model="ruleForm.author"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">修改</el-button>
<el-button @click="submitForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>

<script>
export default {
data() {
return {
ruleForm: {
id: '',
name: '',
author: '',
},
rules: {
name: [
{ required: true, message: '请输入书名', trigger: 'blur' },
],
author: [
{ required: true, message: '请输入作者', trigger: 'blur' },
],
}
};
},
methods: {
// 提交表单
submitForm(formName) {
const _this = this
this.$refs[formName].validate((valid) => {
if (valid) {
axios.put('http://localhost:8181/book/update',this.ruleForm).then(function(resp){
if(resp.data == 'success'){
_this.$message({
message: '《'+_this.ruleForm.name+'》'+'修改成功',
type: 'success'
})
_this.$router.push('/book-manage')
}
})
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
// 填入初始信息
created(){
// 是route不是router!
// alert(this.$route.query.id)
const _this = this
axios.get('http://localhost:8181/book/findById/'+this.$route.query.id).then(function(resp){
_this.ruleForm = resp.data
})
}
}
</script>

一点想法

Spring Boot + Vue 真是好方便

后端基本不用写什么,要用的方法都有

前端要什么模版直接在element ui里找来用

跟着敲这个demo的时候碰到的坑基本都是和路由有关的,router转来转去有点晕

但是 总之

我真的从来没有在哪个教程听见这么多“就行了”、“自动”、“不用管了”…极度舒适!有架子不用徒手爬墙真好