学习笔记:Spring Boot + Vue 前后端分离极简上手

功能演示

后端: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>

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值