【手把手教】SpringBoot+Vue开发简单的前后端分离系统
git:https://github.com/2020GetGoodOffer/test安装node和vue首先安装node.js,下载地址:node.js下好后.exe直接安装就行了,然后在命令行通过node -v查看是否安装成功然后初始化,通过node init完成,一路按回车就行然后设置一个镜像点,不然可能因为网速问题一直无法成功之后下载vuenpm install -g...
git:https://github.com/2020GetGoodOffer/test
安装node和vue
首先安装node.js,下载地址:node.js
下好后.exe直接安装就行了,然后在命令行通过node -v
查看是否安装成功
然后初始化,通过node init
完成,一路按回车就行
然后设置一个镜像点,不然可能因为网速问题一直无法成功
之后下载vuenpm install -g @vue/cli
等待一段时间后,vue -V
查看是否安装成功
创建vue项目
通过vue ui命令
然后访问localhost:8000这个地址
创建新工程
下一步,手动
下一步打开router和vuex,关闭代码校验
下一步,选中使用历史记录,然后完成,创建项目,不保存预设,稍微等待几分钟
进行测试,点击任务,serve,启动,然后输出,观察url地址
访问localhost:8080 ,没有问题
然后命令行CTRL+c退出,关闭服务
IDEA导入VUE工程
直接import刚才的工程就行,一路next和finish
然后需要安装一个vue的插件,如果网不好就用手机热点,多试几次。。
大概试了1个多小时,安装成功后,有一个app.vue
在命令行通过npm run serve
启动
通过ctrl+c终止
router-link标签表示映射的视图,app.vue是主视图,可以跳到home和about
映射关系配置在router目录下的index.js
创建SpringBoot项目
选择组件
这里我下载不下来2.2.6,使用以前下载的2.2.1.RELEASE
创建数据库表
数据库叫stock,表叫stock,有四个字段表示id,股票名,当前价格和涨跌幅比率
配置文件
在resources下新建一个application.yml,配置你自己的数据库信息
创建Vue视图
在vue工程创建一个Vue组件,叫Stock
template就是html,script就是js,style就是css
编写假数据进行一个简单的测试
在index.js中配置路由
命令行启动
访问localhost:8080/stock,成功了
增加两组测试数据
接收结果
Vue是动态加载的,此时再看刚才的页面
更好的写法是通过循环来返回结果
创建实体类
回到spring boot工程,创建股票对应实体类
@Entity
@Data
public class Stock {
@Id
private Integer id;
private String name;
private Double price;
private Double rate;
}
创建操作实体类的接口
只需要基础JpaRepository即可,第一个参数是实体类类型,第二个参数是主键类型
public interface StockRepository extends JpaRepository<Stock,Integer> {
}
在接口处右键点击goto->test,创建一个test测试类
@SpringBootTest
class StockRepositoryTest {
@Autowired
private StockRepository stockRepository;
@Test
void findAll(){
System.out.println(stockRepository.findAll());
}
}
插入两行测试数据
运行
测试查询
新建一个controller包,创建一个Controller
@RestController//声明基于rest的控制器
@RequestMapping("/stock")//根url配置
public class StockController {
@Autowired//自动注入
private StockRepository stockRepository;
@GetMapping("/findAll")//get请求所有数据
public List<Stock> findAll(){
return stockRepository.findAll();
}
}
启动springboot
访问localhost:8181/stock/findAll
,OJBK
替换假数据
之前我们Vue里的数据是假的,是我们自己加入测试的,不是数据库中的,所以我们现在要让前端的数据请求后端的。
安装axios
回到Vue项目,在控制台CTRL+c停止服务,输入vue add axios,添加该服务
等待一段时间,安装成功
编写请求
我们希望每次刷新页面时都会加载新数据,写到初始化函数中,还是要写到export default代码块中:
then里面的回调函数表示输出当前获取的结果,由于我们的Vue项目在8080,而后端的SpringBoot在8181,因此存在一个跨域问题
解决跨域问题
回到SpringBoot项目
创建一个config包,一个配置类,需要实现WebMvcConfigurer接口,并重写addCorsMappings方法,允许任何跨域请求
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST","HEAD","PUT","DELETE","OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
然后重新启动SpringBoot服务器,此时我们刷新刚才的vue视图,打开F12开发者模式,在其中可以发现请求数据成功了,在console中可以看到
然后将测试的输出语句改为替换语句,将请求结果的值赋值给stocks股票数组,created里axios代码块外的this是vue对象,而axios里的this是回调函数,所以我们通过中间变量_this存储代码块外面的this,也就是vue对象,然后才能获取stocks。
此时,已经显示的是数据库中的数据,35.42,而不是我们写的假数据,34.12
Element UI
按之前的步骤,重新创建一个Vue工程,之后,搜索element ui
选中并安装
安装成功
待Vue项目创建成功后,打开IDEA,导入Vue项目
如果打开后有这个el-button就代表成功了
修改App.Vue的内容为以下内容:
<template>
<div id="app">
<el-container style="height: 500px; border: 1px solid #eee">
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<el-menu :default-openeds="['1', '3']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-message"></i>导航一</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-menu"></i>导航二</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title"><i class="el-icon-setting"></i>导航三</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="3-1">选项1</el-menu-item>
<el-menu-item index="3-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="3-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="3-4">
<template slot="title">选项4</template>
<el-menu-item index="3-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>查看</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</el-header>
<el-main>
<el-table :data="tableData">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
</el-table>
</el-main>
</el-container>
</el-container>
</div>
</template>
<style>
.el-header {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
<script>
export default {
data() {
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(20).fill(item)
}
}
};
</script>
再刷新首页
其中的一些标签含义如下:
- el-container 构建整个页面框架
- el-aside 构建左侧菜单
- el-menu 左侧菜单内容,常用属性:
default-openeds:默认展开的菜单,通过菜单的index值来关联
default-active:默认选中的菜单,通过菜单的index值来关联 - el-submenu:可展开的菜单,常用属性:
index:菜单下标,文本类型,不能是数值类型 - template:对应el-submenu的菜单名
- i:设置菜单图标,通过class属性
- el-menu-item:菜单的子结点,不可再展开,常用属性
index:菜单下标,文本类型,不能是数值类型
通过router动态构建左侧菜单
在view下创建四个Vue组件,分别为PageOne/Two/Three/Four
只需要修改每个h1标签中的数字即可
在index.jsp中,先导入四个组件
修改映射
将App.vue的main标签中的内容替换为
<router-view></router-view>
此时的效果
接着修改index.jsp中的配置
const routes = [
{
path:"/",
name:"导航1",
component:App,
children:[
{
path:"/pageOne",
name:"页面1",
component:PageOne
},
{
path:"/pageTwo",
name:"页面2",
component:PageTwo
}
]
},
{
path:"/navigation",
name:"导航2",
component:App,
children:[
{
path:"/pageThree",
name:"页面3",
component:PageThree
},
{
path:"/pageFour",
name:"页面4",
component:PageFour
}
]
}
]
此时的效果
修改el-aside标签中的内容
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<el-menu>
<el-submenu v-for="(item,index) in $router.options.routes" :index="index+''">
<template slot="title"><i class="el-icon-message"></i>{{item.name}}</template>
<el-menu-item v-for="(item2,index2) in item.children" :index="index+'-'+index2">{{item2.name}}</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
此时的效果
最后,只留下aside和main部分
此时的效果
设置动态路由导航栏
App.vue里只保留一行router-view标签,把内容移到新创建的Index.vue
在index.js配置映射,将app替换为刚才创建的index,因为Vue里App.vue是基础视图,其他视图相当于都在它的基础之上显示
此时的效果
接下来实现通过点击左边的页面1234切换页面的功能:
- 给el-menu添加router属性
- 在页面添加router-view标签
- 设置el-menu-item的index值,即为跳转页面
此时的效果,选哪个就跳哪个
通过redirect属性设置访问loaclhost8080的默认页面
此时访问8080会默认跳到pageOne
但是没有选中效果,通过class属性指定,利用三面运算符,当route.path(浏览器url的地址)和item2.path(设置的路由地址)一样时,具有选中效果
此时具有选中效果
前后端分离开发数据对接
复制table代码
将pageOne.vue复制为以下表单代码
<template>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
fixed
prop="date"
label="日期"
width="150">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="120">
</el-table-column>
<el-table-column
prop="province"
label="省份"
width="120">
</el-table-column>
<el-table-column
prop="city"
label="市区"
width="120">
</el-table-column>
<el-table-column
prop="address"
label="地址"
width="300">
</el-table-column>
<el-table-column
prop="zip"
label="邮编"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
methods: {
handleClick(row) {
console.log(row);
}
},
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-04',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1517 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1519 弄',
zip: 200333
}, {
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1516 弄',
zip: 200333
}]
}
}
}
</script>
复制分页代码
创建一个div标签,将刚才的table放进去,再加入分页代码
<el-pagination
background
layout="prev, pager, next"
:total="1000">
</el-pagination>
此时效果
创建假数据
table中的数据来自data,先修改data数据
修改table数据,prop就是data中的属性,label是表单中的列名
此时效果
修改分页
每页3条记录,一共30条10页,点击事件为page方法
page-size前面要和total一样加上冒号,当时漏掉了
page(currentPage){
switch (currentPage) {
case 1:
this.tableData=[{
id: 1,
name: '科技股',
price: 45.43,
rate: +1.57
}, {
id: 2,
name: '教育股',
price: 35.22,
rate: +0.19
}, {
id: 3,
name: '石油股',
price: 45.43,
rate: -4.61
}, {
id: 4,
name: '餐饮股',
price: 17.43,
rate: -9.77
}];
break;
case 2:
this.tableData=[{
id: 4,
name: '科技股1',
price: 45.43,
rate: +1.57
}, {
id: 5,
name: '教育股2',
price: 35.22,
rate: +0.19
}, {
id: 6,
name: '石油股3',
price: 45.43,
rate: -4.61
}, {
id: 7,
name: '餐饮股4',
price: 17.43,
rate: -9.77
}];
break;
}
}
此时点击第二页会显示新的数据
实现分页查询
由于前端是分页显示,但是可以看到后端查询的数据是一次全部查出来,需要进行分页
先增加一点数据,然后查询,显示了全部
修改StockController中的方法,传入page和size,表示第几页,显示几条记录
如果第一页就是0,page=要显示的页-1
@GetMapping("/findAll/{page}/{size}")//get请求所有数据
public Page<Stock> findAll(@PathVariable("page")Integer page, @PathVariable("size")Integer size){
PageRequest pageRequest=PageRequest.of(page,size);
return stockRepository.findAll(pageRequest);
}
重启springboot,测试,传入0和3
查询第二页就传1和3
跟之前一样,先安装axios
替换为真数据,跟之前一样,还是通过create参数和_this的赋值
此时默认显示前3条
修改pageOne为以下代码
<template>
<div>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
fixed
prop="id"
label="股票id"
width="150">
</el-table-column>
<el-table-column
prop="name"
label="股票名"
width="120">
</el-table-column>
<el-table-column
prop="price"
label="股票当前价格"
width="120">
</el-table-column>
<el-table-column
prop="rate"
label="股票涨跌幅"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button 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="page">
</el-pagination>
</div>
</template>
<script>
export default {
methods: {
handleClick(row) {
console.log(row);
},
page(currentPage){
const _this=this;
axios.get('http://localhost:8181/stock/findAll/'+(currentPage-1)+'/3').then(function (resp) {
_this.tableData=resp.data.content;
_this.pageSize=resp.data.size;
_this.total=resp.data.totalElements;
})
}
},
data() {
return {
pageSize:'',
total:'',
tableData:''
}
},
created() {
const _this=this;
axios.get('http://localhost:8181/stock/findAll/0/3').then(function (resp) {
console.log(resp);
_this.tableData=resp.data.content;
_this.pageSize=resp.data.size;
_this.total=resp.data.totalElements;
})
}
}
</script>
此时成功实现分页
添加数据
在前端添加数据
修改index.js,把之前的pageOne改为stockQuery,pageTwo改为stockUpdate
现在的效果
接下来开始完成添加数据的功能,将stockUpdate.vue修改为以下内容
model对象绑定数据,rules对象校验表单
<template>
<el-form :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="price">
<el-input v-model="ruleForm.price"></el-input>
</el-form-item>
<el-form-item label="股票涨跌幅" prop="rate">
<el-input v-model="ruleForm.rate"></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: '',
price: '',
rate: '',
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
],
price: [
{ required: true, message: '请输入价格', trigger: 'change' }
],
rate: [
{required: true, message: '请输入涨跌幅', trigger: 'change' }
]
}
};
},
methods: {
submitForm(formName) {
//_this表示当前vue对象
const _this=this;
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
return false;
}
});
},
//重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
当前效果
接下来需要写后台保存数据的代码
在后台添加数据
给id先加上自增注解
在controller中增加接口
@PostMapping("/save")//post请求提交数据
public void save(@RequestBody Stock stock){
stockRepository.save(stock);
}
重启SpringBoot,回到Vue项目,添加提交逻辑
测试,输入数据
点击添加,再查询数据库
前端也可以即时查询
优化提示效果
使用message方法
此时添加会弹出提示框
添加成功后回跳查询页
submitForm(formName) {
const _this=this;
this.$refs[formName].validate((valid) => {
if (valid) {
axios.post('http://localhost:8181/stock/save',this.ruleForm).then(function (resp) {
_this.$alert(_this.ruleForm.name, '添加成功', '消息',{
confirmButtonText: '确定',
callback: action => {
}
});
//回跳
_this.$router.push('/StockQuery');
})
} else {
return false;
}
});
}
会提示添加成功,并回跳查询页面
修改和删除数据
这里老师的代码有点问题,还需要做如下调整:
App.vue和Index.vue
把Index.vue和App.vue的template里的内容互换
配置index.js
将之前的StockUpdate改名StockAdd,新创建一个StockUpdate,在index.js配置
修改查询页
当点击修改时,将股票的行号传过去
修改StockUpdate
<template>
<el-form :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="price">
<el-input v-model="ruleForm.price"></el-input>
</el-form-item>
<el-form-item label="股票涨跌幅" prop="rate">
<el-input v-model="ruleForm.rate"></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: '',
price: '',
rate: '',
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
],
price: [
{ required: true, message: '请输入价格', trigger: 'change' }
],
rate: [
{ required: true, message: '请输入涨跌幅', trigger: 'change' }
]
}
};
},
methods: {
submitForm(formName) {
const _this=this;
this.$refs[formName].validate((valid) => {
if (valid) {
axios.post('http://localhost:8181/stock/save',this.ruleForm).then(function (resp) {
_this.$alert(_this.ruleForm.name, '添加成功', '消息',{
confirmButtonText: '确定',
callback: action => {
}
});
_this.$router.push('/StockQuery');
})
} else {
return false;
}
});
},
//重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
},
},created() {
alert(this.$route.query.id);
}
}
</script>
先进行简单测试,例如点击第一行
弹出1
然后跳了过来
提供根据id查询接口
在Springboot的controller增加方法
@GetMapping("/findById/{id}")//get请求单个数据
public Stock findById(@PathVariable("id")Integer id){
return stockRepository.findById(id).get();
}
简单测试:
没有问题,回到StockUpdate.vue的created方法
此时点击修改按钮,会自动查询显示现在的最新值,为了显示id列,增加一段标签
增加id属性,设置为只读
此时会显示id
修改功能
先把按钮改为修改
成功
提供修改接口,之后重启SpringBoot服务器
@PutMapping("/update")//put请求修改数据
public void update(@RequestBody Stock stock){
stockRepository.save(stock);
}
只需要修改这三处
测试,给名字加上test
成功
增加删除点击事件del
增加删除接口del
@DeleteMapping("/deleteById/{id}")
public void delete(@PathVariable("id")Integer id){
stockRepository.deleteById(id);
}
然后重启服务器
调用接口
回到Vue项目
测试:
成功
如果希望删除后可以刷新,使用window.location.reload
END
更多推荐
所有评论(0)