SpringBoot+Vue之表格的CRUD与导入导出

SpringBoot+Vue–表格操作

工具环境:jdk1.8,myql5.7,webstorm2018, idea2018,SQLyog,postman

该项目推荐刚入门springboot开发的朋友们练手使用

业务简介

创建一个页面,展示玩家信息,支持玩家的增删查改,并支持页面表格的导入导出。

目的

梳理业务逻辑和开发流程,理解并独立开发。步骤尽量简要

效果预览

SpringBoot+Vue之表格的CRUD与导入导出_第1张图片

开发流程

创建数据库脚本

导入userinfo脚本信息

/*Table structure for table `userInfo` */

DROP TABLE IF EXISTS `userInfo`;

CREATE TABLE `userInfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL COMMENT '用户名',
  `gameLevel` enum('黑铁','黄铜','白银','黄金','钻石') DEFAULT NULL,
  `createDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `enabled` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8;

/*Data for the table `userInfo` */

INSERT  INTO `userInfo`(`id`,`username`,`gameLevel`,`createDate`,`enabled`) VALUES 
(1,'张三','钻石','2018-01-11 21:19:14',1),
(2,'李四',NULL,'2018-01-11 21:19:20',1),
(3,'王五','白银','2018-01-11 21:35:39',1),
(4,'赵六','黄铜','2018-01-11 22:42:12',0),
(5,'小红','黑铁','2018-01-14 16:18:50',1),
(6,'小明','黄金','2018-01-14 16:19:00',1),
(7,'小花','黄铜','2018-01-14 16:19:14',1),
(8,'韩梅梅','白银','2018-01-14 16:19:24',1);

效果如下:
SpringBoot+Vue之表格的CRUD与导入导出_第2张图片

配置后端SpringBoot项目

1、导入相关依赖

(pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>2.0.1version>
    dependency>

    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druid-spring-boot-starterartifactId>
        <version>1.1.10version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.27version>
        <scope>runtimescope>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>com.google.code.google-collectionsgroupId>
        <artifactId>google-collectartifactId>
        <version>snapshot-20080530version>
    dependency>
    <dependency>
        <groupId>org.apache.poigroupId>
        <artifactId>poiartifactId>
        <version>4.0.1version>
    dependency>
dependencies>

注意

  • idea默认安装最新的版本依赖,我这里是mysql5.7,所以特别指定一下版本

  • 可以先定义一个HelloController测试类,确保web项目可以正常启动。
    SpringBoot+Vue之表格的CRUD与导入导出_第3张图片

3、配置数据库连接和网络端口

(application.properties)

server.port=8081
#打印日志
logging.level.com.chety.sheetdemo.mapper=debug 

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=1234

4、创建实体类

  • 定义实体类User.java和持久层UserMapper接口和xml映射文件,可以用逆向工程生成。

  • 因为表中有日期的字段,需要JSON转换时,会出错,所以要注解标明内容格式

(User.java)

public class User {
    private Integer id;

    private String nickname;

    private String gamelevel;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")
    private Date createdate;

    private Boolean enabled;
    
    //getter and setter...
    
}    

5、启动类中添加数据库组件并配置mybatis映射文件的扫描

(SheetdemoApplication.java)

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = "com.chety.sheetdemo.mapper")
public class SheetdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SheetdemoApplication.class, args);
    }

}

6、配置数据源路径(pom.xml)

<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.xmlinclude>
            includes>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
        resource>
    resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>

为节省篇幅,只讲查询的前后端详细步骤。目的是理清思路,了解业务逻辑,后面的可以举一反三,也可以参考后面的完整代码。

编写后端接口代码

1、定义用户查询方法

(UserController.java)

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    UserService userService;

    @GetMapping("/")
    public List<User> getAllUser() {
        return userService.getAllUser();
    }
}    

2、查询服务类

(UserService.java)

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    public List<User> getAllUser() {
        return userMapper.getAllUser();
    }
}    

3、定义接口方法

(UserMapper.java)

List<User> getAllUser();

4、sql语句

(UserMapper.xml)

<select id="getAllUser" resultMap="BaseResultMap">
  select * from userinfo;
select>

5、使用postman进行后端测试,get请求http://127.0.0.1:8081/user/。返回一组玩家信息的JSON数组
SpringBoot+Vue之表格的CRUD与导入导出_第4张图片

前端Vue项目搭建

页面模板

1、打开webstorm的terminal窗口,安装ElementUI和Axios 的依赖

npm i element-ui -S
npm install axios

2、引入刚下载下的资源

(main.js)

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

import {postRequest} from "./utils/api";
import {postKeyValueRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {uploadFileRequest} from "./utils/api";

Vue.use(ElementUI)

Vue.config.productionTip = false

Vue.prototype.postRequest = postRequest;
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;
Vue.prototype.uploadFileRequest = uploadFileRequest;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: ''
})

3、配置后端访问的路径8081

(config\index.js)

// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
  '/': {
    target: 'http://localhost:8081',
    changeOrigin: true,
    pathRewrite: {
      '^/': ''
    }
  }
},

4、导入一个http请求接口的工具文件

(src\utils\api.js)

'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        pathRewrite: {
          '^/': ''
        }
      }
    },

    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    
    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-module-eval-source-map',

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true,

    cssSourceMap: true
  },

  build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

    /**
     * Source Maps
     */

    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

5、新建一个展示玩家信息的组件,在里面先创建一个表格,表格样式来自ElementUi官网组件ElementUi

(src/components/User.vue)

搭建表格结构





6、配置玩家信息的访问路由

(src/router/index.js)

// ...
import User from '@/components/User'

routes: [{
    path:'/user',
    name:'User',
    component:User,
    hidden:true
  }
   // ...    
]

7、运行dev,用浏览器访问端口http://localhost:8080/#/user
SpringBoot+Vue之表格的CRUD与导入导出_第5张图片

表格完善

1、特殊字段处理

”段位“单元格中加入插槽,有段位显示段位名,没有段位的地方是空,显示”暂无段位“


  

“是否可用”单元格中加入插槽,可用显示“是”,否则显示”否“


  

“操作”单元格,加入两个按钮,编辑和删除


  

2、创建查询所有玩家的方法


3、再次查看页面,已有数据,ok
SpringBoot+Vue之表格的CRUD与导入导出_第6张图片

后续

后面的增删改都很类似。主要是为了给大家理清前后端开发api的思路和步骤

下面是完整的DRUD及导入导出文件的代码

1、前端页面(src/components/User.vue)

由于Vue双向绑定的特性,在修改数据数据时,重新拷贝定义了一组行数据。






2、后端接口

2.1 统一返回信息类

public class RespEntity {
    // 状态码
    private Integer status;
    // 返回信息
    private String msg;
    // 返回对象,可选
    private Object obj;

    public static RespEntity success(String msg, Object obj) {
        return new RespEntity(200, msg, obj);
    }

    public static RespEntity success(String msg) {
        return new RespEntity(200, msg, null);
    }

    public static RespEntity fail(String msg, Object obj) {
        return new RespEntity(500, msg, obj);
    }

    public static RespEntity fail(String msg) {
        return new RespEntity(500, msg, null);
    }

    private RespEntity(Integer status, String msg, Object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }
    private RespEntity() {   }
    
    // getter and setter...
}    

2.2 文件操作的工具类

public class PoiUtils {

    /**
     * 根据集合返回一个http响应体
     * @param allUsers 职称等级列表
     * @return http响应实体
     */
    public static ResponseEntity<byte[]> exportUserExcel(List<User> allUsers) throws IOException {
        // 创建一个excel文件
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 设置文档属性
        workbook.createInformationProperties();
        // 获取文档的文档摘要信息
        DocumentSummaryInformation sumInfo = workbook.getDocumentSummaryInformation();
        // 设置摘要信息
        sumInfo.setCompany("towards");
        sumInfo.setManager("chet");
        sumInfo.setCategory("user information");
        // 创建一个新的单元格样式并将其添加到工作簿的样式表中。
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        // 获取与给定格式字符串匹配的格式索引,自动将“文本”转换为excel的格式字符串来表示文本
        short format = HSSFDataFormat.getBuiltinFormat("m/d/yy");
        // 设置表格中的日期格式,设置数据格式(必须是有效格式)
        cellStyle.setDataFormat(format);

        // 为这个HSSFWorkbook创建一个HSSFSheet,将它添加到工作表中
        HSSFSheet sheet = workbook.createSheet("玩家信息表");

        // 设置表格列名
        // 在工作表中创建一个新行
        HSSFRow row = sheet.createRow(0);
        // 在行中创建新的单元格,参数为列号
        HSSFCell cell0 = row.createCell(0);
        HSSFCell cell1 = row.createCell(1);
        HSSFCell cell2 = row.createCell(2);
        HSSFCell cell3 = row.createCell(3);
        HSSFCell cell4 = row.createCell(4);

        // 为单元格设置一个字符串值
        cell0.setCellValue("编号");
        cell1.setCellValue("昵称");
        cell2.setCellValue("段位");
        cell3.setCellValue("创建时间");
        cell4.setCellValue("是否可用");

        // 循环设置表格中的数据
        for (int i = 0; i < allUsers.size(); i++) {
            // 返回列表中指定位置的元素
            User level = allUsers.get(i);
            HSSFRow r = sheet.createRow(i + 1);
            HSSFCell c0 = r.createCell(0);
            HSSFCell c1 = r.createCell(1);
            HSSFCell c2 = r.createCell(2);
            HSSFCell c3 = r.createCell(3);
            HSSFCell c4 = r.createCell(4);

            c0.setCellValue(level.getId());
            c1.setCellValue(level.getNickname());
            c2.setCellValue(level.getGamelevel()==null?"暂无段位":level.getGamelevel());
            // 设置日期格式
            c3.setCellStyle(cellStyle);
            c3.setCellValue(level.getCreatedate());
            c4.setCellValue(level.getEnabled() ? "是" : "否");
        }
        // 创建一个http请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置,参数:1.控制方式-内嵌,2.文件名,在浏览器需转换格式
        headers.setContentDispositionFormData("attachment",
                new String("玩家信息表.xls".getBytes("UTF-8"), "iso-8859-1"));
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        // 创建一个字节数组输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        workbook.write(baos);
        // 使用给定的主体、头和状态代码创建一个新的http实体
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(baos.toByteArray(), headers, HttpStatus.CREATED);
        return responseEntity;
    }

    public static List<User> parseFileList(MultipartFile file) throws IOException {
        List<User> Users = new ArrayList<>();
        HSSFWorkbook workbook = new HSSFWorkbook(file.getInputStream());
        HSSFSheet sheet = workbook.getSheetAt(0);
        int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
        for (int i = 1; i < physicalNumberOfRows; i++) {
            HSSFRow row = sheet.getRow(i);
            HSSFCell c0 = row.getCell(0);
            HSSFCell c1 = row.getCell(1);
            HSSFCell c2 = row.getCell(2);
            HSSFCell c3 = row.getCell(3);
            HSSFCell c4 = row.getCell(4);

            double numericCellValue = c0.getNumericCellValue();
            User level = new User();
            level.setId((int)numericCellValue);
            level.setNickname(c1.getStringCellValue());
            level.setGamelevel(c2.getStringCellValue().equals("暂无段位")?null:c2.getStringCellValue());
            level.setCreatedate(c3.getDateCellValue());
            level.setEnabled(c4.getStringCellValue().equals("是"));
            Users.add(level);
        }
        return Users;
    }
}

2.3 控制层(UserController.java)

/**
 * 玩家信息表格操作,crud与文件操作
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    /**
     * 查询所有玩家信息
     * @return User列表
     */
    @GetMapping("/")
    public List<User> getAllUser() {
        return userService.getAllUser();
    }

    /**
     * 添加用户
     * @param user
     * @return RespEntity对象
     */
    @PostMapping("/")
    public RespEntity addUser(@RequestBody User user) {
        if (userService.addUser(user) == 1) {
            return RespEntity.success("添加成功");
        }
        return RespEntity.fail("添加失败");
    }

    /**
     * 根据玩家编号删除
     * @param id
     * @return RespEntity对象
     */
    @DeleteMapping("/{id}")
    public RespEntity deleteUserById(@PathVariable("id") Integer id) {
        if ((userService.deleteUserById(id)) == 1) {
            return RespEntity.success("删除成功");
        }
        return RespEntity.fail("删除失败");
    }

    /**
     * 更新玩家信息,只有昵称和段位可改
     * @param user
     * @return RespEntity对象
     */
    @PutMapping("/")
    public RespEntity updateUser(@RequestBody User user) {
        if ((userService.updateUser(user)) == 1) {
            return RespEntity.success("更新成功");
        }
        return RespEntity.fail("更新失败");
    }

    /**
     * 导出excel文件
     * @return
     * @throws IOException
     */
    @GetMapping("/export")
    public ResponseEntity<byte[]> exportExcel() throws IOException {
        return PoiUtils.exportUserExcel(userService.getAllUser());
    }

    /**
     * 导入excel文件
     * @param file
     * @param req
     * @return
     * @throws IOException
     */
    @PostMapping("/import")
    public RespEntity importFile(MultipartFile file, HttpServletRequest req) throws IOException {
        List<User> users = PoiUtils.parseFileList(file);
        System.out.println(users);
        int n = userService.listFileToDb(users);
        System.out.println(n);
        return RespEntity.success("导入成功");
    }
}

2.4 服务类(UserService.java),略。这里没有添加判断逻辑,直接注入dao层调用即可。

注:在文件导入时,即添加玩家时,不建议使用Java代码循环insert插入,而是直接使用sql语句批量导入即可。
SpringBoot+Vue之表格的CRUD与导入导出_第7张图片

<insert id="listFileToDb">
  INSERT INTO userinfo(
  nickname,gameLevel,createDate,enabled)
  VALUES
  <foreach collection="users" item="user" separator=",">
    (#{user.nickname},#{user.gamelevel},#{user.createdate},#{user.enabled})
  foreach>
insert>

就这样,跑起来试试吧

你可能感兴趣的:(SpringBoot,springboot,vue,文件导入,文件导出)