功能演示
后端:Spring boot + MyBatis-Plus
前端:Vue + Element UI + ECharts + Axios
创建 Vue 工程
安装 Vue 及脚手架:
参考:https://blog.csdn.net/dream_summer/article/details/108867317
在 cmd 输入 vue ui
命令,打开 Vue 项目管理器:
创建新项目:
选择【创建】→【编辑路径】→点击【在此创建新项目】→【详情】→【预设】→【功能】→【配置】→点击【创建项目】
安装插件:
选择【插件】→【添加插件】→【查找插件】→【安装插件】→【完成安装】
用 IDEA 打开项目,在 Terminal 终端输入 npm run serve
命令,尝试运行:
成功创建 Vue 工程。
使用 Element UI 和 ECharts
编写 Bar.vue 组件,用于绘制柱状图:
<template>
<div id="myChart" :style="{width: '800px', height: '600px'}"></div>
</template>
<script>
export default {
name: "Bar",
mounted() {
// 基于准备好的dom,初始化echarts实例
let myChart = this.$echarts.init(document.getElementById('myChart'))
// 绘制图表,setOption的内容可以按需替换
myChart.setOption({
title: {
text: '水果销量统计-柱状图',
left: 'center',
top: 20,
textStyle: {
color: '#555555',
},
},
tooltip: {},
xAxis: {
data: [
"苹果",
"香蕉",
"橘子",
"火龙果",
"葡萄",
"西瓜",
],
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [
{
value: 333,
itemStyle: {color: "#f35959"},
},
{
value: 133,
itemStyle: {color: "#e7e694"},
},
{
value: 99,
itemStyle: {color: "#e87f47"},
},
{
value: 222,
itemStyle: {color: "#5fa54e"},
},
{
value: 399,
itemStyle: {color: "#7d5f9a"},
},
{
value: 123,
itemStyle: {color: "#3b9265"},
},
],
}],
});
}
}
</script>
在 router.js 中配置路由:
{
path: '/bar', // 路由地址
component: () => import('./views/Bar') // 对应跳转的组件
},
在 App.vue 中添加 <router-view/>
,当浏览器访问时,可以替换成指定的组件:
在 ECharts 网站中,可以选择各类图表的模板:
另外,在 Element 网站中,可以选择各类组件的模板:
搭建后端服务
在 IDEA 中创建 Spring Boot 项目:
直接添加依赖:
在 pom.xml 中导入 MyBatis-Plus 依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
编写 MyBatis-Plus 的代码生成器,所有配置都可以使用 Java Config 完成:
package com.example;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class GenerateTest {
public static void main(String[] args) {
// 创建generator对象,操纵这个对象生成代码
AutoGenerator autoGenerator = new AutoGenerator();
// 数据库信息配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL); // 数据库类型
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); // 驱动名称
dataSourceConfig.setUsername("root"); // 用户名
dataSourceConfig.setPassword("123456"); // 登录密码
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/fruit"); // url
autoGenerator.setDataSource(dataSourceConfig);
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); // user.dir获取当前工程的src目录路径
globalConfig.setAuthor("admin"); // 注释作者
globalConfig.setOpen(false); // 生成代码后是否打开资源管理器
globalConfig.setFileOverride(false); // 重新生成代码后是否覆盖原文件
globalConfig.setServiceName("%sService"); // 去掉Service接口的首字母I
autoGenerator.setGlobalConfig(globalConfig);
// 包信息配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setModuleName(null); // 模块名
packageConfig.setParent("com.example"); // 包名
packageConfig.setController("controller"); // 前端控制器,控制请求和响应,负责前后端交互
packageConfig.setEntity("entity"); // 存放实体类,一张数据库表对应一个model类
packageConfig.setMapper("mapper"); // 数据存储对象,作用是访问数据库,向数据库发送sql语句,完成数据的增删改查任务
packageConfig.setService("service"); // 数据服务层,负责调用mapper的接口进行业务逻辑应用的处理
packageConfig.setServiceImpl("service.impl"); // 数据服务的实现接口,把mapper和service进行整合
autoGenerator.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("fruit_info"); // 数据库表名,可以指定多个表进行处理
strategyConfig.setNaming(NamingStrategy.underline_to_camel); // 数据库表名映射到实体类的命名策略,underline_to_camel表示下划线转驼峰
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库表字段名映射到成员变量的命名策略
strategyConfig.setEntityLombokModel(true); // 是否使用Lombok注解
autoGenerator.setStrategy(strategyConfig);
// 运行
autoGenerator.execute();
}
}
运行其中的 main 方法,就可以生成 Entity、Mapper、Service、Controller 模块代码:
在 application.yml 中添加配置信息:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/fruit
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:com/example/mapper/xml/*.xml
server:
port: 8181
在 FruitInfoController 中编写增删改查数据接口:
package com.example.controller;
import com.example.entity.FruitInfo;
import com.example.service.FruitInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author admin
* @since 2021-08-11
*/
@RestController
@RequestMapping("/fruitInfo")
public class FruitInfoController {
@Autowired
private FruitInfoService fruitInfoService;
// 查询数据接口
@GetMapping("/list")
public List<FruitInfo> list() {
return this.fruitInfoService.list();
}
// 删除数据接口
@DeleteMapping("/delete/{id}")
public boolean delete(@PathVariable("id") Integer id) {
return this.fruitInfoService.removeById(id);
}
// 编辑数据接口
@GetMapping("/find/{id}")
public FruitInfo find(@PathVariable("id") Integer id) {
return this.fruitInfoService.getById(id);
}
// 更新数据接口
@PutMapping("/update")
public boolean update(@RequestBody FruitInfo fruitInfo) {
return this.fruitInfoService.updateById(fruitInfo);
}
// 添加数据接口
@PostMapping("/add")
public boolean add(@RequestBody FruitInfo fruitInfo) {
return this.fruitInfoService.save(fruitInfo);
}
}
运行程序,测试查询数据接口:
前后端数据对接
使用 Axios 连接查询数据接口:
// 连接查询数据接口
axios
.get('http://localhost:8181/fruitInfo/list')
.then(function(response) {
that.tableData = response.data; // 返回所有数据
});
在后台添加 configuration 文件,解决跨域问题:
package com.example.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrosConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域的请求路径
.allowedOriginPatterns("*")
.allowedMethods("*") // 允许提交请求的方法
.allowedHeaders("*") // 允许访问的头信息
.maxAge(3600) // 预请求的结果有效期
.allowCredentials(true);
}
}
编写 Table.vue 组件,用于查询数据和删除数据:
<template>
<el-table :data="tableData" border style="width: 100%"
:cell-style="rowClass" :header-cell-style="headClass">
<el-table-column prop="id" label="水果ID" width="150"></el-table-column>
<el-table-column prop="name" label="水果名称" width="150"></el-table-column>
<el-table-column prop="sale" label="水果销量" width="150"></el-table-column>
<el-table-column prop="icon" label="图片" width="150">
<template slot-scope="scope">
<img :src="scope.row.icon" style="width: 100px; height: 100px" />
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button @click="findFruit(scope.row)" type="text" size="small">编辑</el-button>
<el-button @click="deleteFruit(scope.row)" type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
name: "Table",
methods: {
// 表头内容样式设置
headClass() {
return 'text-align: center;';
},
// 表格内容样式设置
rowClass() {
return 'text-align: center;';
},
// 编辑数据操作
findFruit(row) {
this.$router.push('/edit?id='+row.id); // 跳转到/edit页面
},
// 删除数据操作
deleteFruit(row) {
let that = this;
this.$confirm('是否确定删除'+row.name+'?', '删除数据', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => { // 点击确定
// 连接删除数据接口
axios
.delete('http://localhost:8181/fruitInfo/delete/'+row.id)
.then(function(response) {
if(response.data) { // 接口返回bool类型,true表示删除成功
that.$alert('删除成功!', '删除数据', {
confirmButtonText: '确定',
callback: action => {
location.reload(); // 刷新当前页面
}
});
}
})
}).catch(() => { // 点击取消
});
},
},
// 显示表格数据
created() {
let that = this;
// 连接查询数据接口
axios
.get('http://localhost:8181/fruitInfo/list')
.then(function(response) {
that.tableData = response.data; // 返回所有数据
});
},
// 默认表格数据
data() {
return {
tableData: null,
};
},
}
</script>
编写 Edit.vue 组件,用于编辑数据和更新数据:
<template>
<el-form ref="fruitRules" :model="fruitData" :rules="rules"
label-width="80px" style="width: 600px" class="demo-ruleForm">
<el-form-item label="水果ID" prop="id">
<el-input v-model="fruitData.id" readonly></el-input>
</el-form-item>
<el-form-item label="水果名称" prop="name">
<el-input v-model="fruitData.name"></el-input>
</el-form-item>
<el-form-item label="水果销量" prop="sale">
<el-input v-model.number="fruitData.sale"></el-input>
</el-form-item>
<el-form-item label="图片" prop="icon">
<el-input v-model="fruitData.icon"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('fruitRules')">立即修改</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: "Edit",
methods: {
onSubmit(formName){
this.$refs[formName].validate((valid) => {
if(valid) {
let that = this;
// 连接更新数据接口
axios
.put('http://localhost:8181/fruitInfo/update', this.fruitData)
.then(function (response) {
if(response.data){
that.$alert(that.fruitData.name+'修改成功!', '修改数据', {
confirmButtonText: '确定',
callback: action => {
that.$router.push('/table'); // 跳转到/table页面
}
});
}
});
}
});
}
},
// 显示表格数据
created() {
let id = this.$route.query.id;
let that = this;
// 连接编辑数据接口
axios
.get('http://localhost:8181/fruitInfo/find/'+id)
.then(function(response) {
that.fruitData = response.data; // 返回对应id的数据
});
},
data() {
return {
fruitData: {
name: '',
sale: '',
icon: '',
},
rules:{
name:[
{ required: true, message: '请输入水果名称', trigger: 'blur' },
],
sale:[
{ required: true, message: '请输入水果销量', trigger: 'blur' },
{ type: 'number', message: '水果销量必须为数字值'},
],
icon:[
{ required: true, message: '请输入图片链接', trigger: 'blur' },
],
},
}
},
}
</script>
编写 Add.vue 组件,用于添加数据:
<template>
<el-form ref="fruitRules" :model="fruitData" :rules="rules"
label-width="80px" style="width: 600px" class="demo-ruleForm">
<el-form-item label="水果ID" prop="id">
<el-input v-model="fruitData.id"></el-input>
</el-form-item>
<el-form-item label="水果名称" prop="name">
<el-input v-model="fruitData.name"></el-input>
</el-form-item>
<el-form-item label="水果销量" prop="sale">
<el-input v-model.number="fruitData.sale"></el-input>
</el-form-item>
<el-form-item label="图片" prop="icon">
<el-input v-model="fruitData.icon"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('fruitRules')">立即创建</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: "Add",
methods: {
onSubmit(formName){
this.$refs[formName].validate((valid) => {
if(valid) {
let that = this;
// 连接添加数据接口
axios
.post('http://localhost:8181/fruitInfo/add', this.fruitData)
.then(function (response) {
if(response.data){
that.$alert(that.fruitData.name+'添加成功!', '添加数据', {
confirmButtonText: '确定',
callback: action => {
that.$router.push('/table'); // 跳转到/table页面
}
});
}
});
}
});
}
},
data() {
return {
fruitData: {
id: '',
name: '',
sale: '',
icon: '',
},
rules:{
id:[
{ required: true, message: '请输入水果ID', trigger: 'blur' },
],
name:[
{ required: true, message: '请输入水果名称', trigger: 'blur' },
],
sale:[
{ required: true, message: '请输入水果销量', trigger: 'blur' },
{ type: 'number', message: '水果销量必须为数字值'},
],
icon:[
{ required: true, message: '请输入图片链接', trigger: 'blur' },
],
},
}
},
}
</script>