mybatis-plus+Vue代码实现增删改查Excel导入导出(第一篇)

先展示最后的效果图

在Navicat中导入sql(复制下来然后在查询中粘贴运行就可以了)

/*
 Navicat Premium Data Transfer
​
 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80020
 Source Host           : localhost:3306
 Source Schema         : the_sound
​
 Target Server Type    : MySQL
 Target Server Version : 80020
 File Encoding         : 65001
​
 Date: 25/04/2022 20:02:49
*/
​
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
​
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '密码',
  `nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '昵称',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '电话',
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地址',
  `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像',
  `role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '角色',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 45 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
​
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', 'admin', '程序员青戈', 'admin@qq.com', '13988997788', '安徽合肥', '2022-01-22 21:10:27', 'http://localhost:9090/file/8d966b0e6cf84fe191a72a58b8293b23.png', 'ROLE_ADMIN');
INSERT INTO `sys_user` VALUES (16, 'xiaoqiao', 'xiaoqiao', '小乔', 'xiaoqiao@qq.com', '18009800098', '河北', '2022-02-26 22:10:14', NULL, 'ROLE_TEACHER');
INSERT INTO `sys_user` VALUES (18, 'hanxin', 'hanxin', '韩信', 'hanxin@qq.com', '17809827734', '山东', '2022-03-29 16:59:44', '', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (19, 'yase', 'yase', '亚瑟', 'yase@qq.com', '19708374665', '山西', '2022-04-29 16:59:44', '', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (20, 'lixin', 'lixin', '李信', 'lixin@qq.com', '18273645535', '福建', '2022-05-29 17:12:04', '2', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (25, 'anqila', 'anqila', '安琪拉', 'anqila@qq.com', '18277364554', '山东太原', '2022-06-08 17:00:47', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (26, 'daji', 'daji', '妲己', 'daji@qq.com', '18763466653', '山东日照', '2022-07-08 17:20:01', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (28, 'yvji', 'yvji', '虞姬', 'yvji@qq.com', '19876544474', '武汉', '2022-11-09 10:41:07', 'http://localhost:9090/file/7de0e50f915547539db12023cf997276.jpg', 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (29, 'sunbin', 'sunbin', '孙膑', 'sunbin@qq.com', '19800987655', '山东', '2022-12-10 11:53:31', NULL, 'ROLE_STUDENT');
INSERT INTO `sys_user` VALUES (30, 'nezha', 'nezha', '哪吒', 'nezha@qq.com', '18099876778', '河北', '2022-04-19 20:53:57', NULL, NULL);
INSERT INTO `sys_user` VALUES (31, 'zhenji', 'zhenji', '甄姬', 'zhenji@qq.com', '19888765534', '山东', '2022-04-19 20:54:16', NULL, NULL);
INSERT INTO `sys_user` VALUES (32, 'tangseng', 'tangseng', '唐僧', 'tangseng@qq.com', '12999897767', '东土大唐', '2022-04-20 15:53:46', NULL, NULL);
INSERT INTO `sys_user` VALUES (33, 'zhubajie', 'zhubajie', '猪八戒', 'zhubajie@qq.com', '13387876655', '高老庄', '2022-04-20 15:56:02', NULL, NULL);
INSERT INTO `sys_user` VALUES (34, 'shaseng', 'shaseng', '沙僧', 'shaseng@qq.com', '18744332273', '流沙河', '2022-04-20 16:00:26', NULL, NULL);
INSERT INTO `sys_user` VALUES (35, 'bailongma', 'bailongma', '白龙马', 'bailongma@qq.com', '17787675844', '东海', '2022-04-20 16:01:55', NULL, NULL);
​
SET FOREIGN_KEY_CHECKS = 1;
​

 

 

前端vue


 

创建vue项目后运行

安装vue:npm install vue

安装axios: npm install axios

安装ElementUI:npm i element-ui -S


main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import './assets/gloable.css';
import request from "@/utils/request";
​
Vue.use(ElementUI)
Vue.config.productionTip = false
​
// 从utils中引入request对象,然后在页面上就可以使用了
Vue.prototype.request = request
​
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

 

在views中创建Manager.vue

<template>
  <div class="home">
    <el-container style="height: 100%;">
      <!--左侧边栏-->
      <el-aside :width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);height: 100%; box-shadow:2px 0 6px rgb(0, 21, 41, 0.35)">
        <Aside :isCollapse="isCollapse" :logoTextShow="logoTextShow"/>
      </el-aside>
      <!--右边-->
      <el-container>
        <!--头部-->
        <el-header style="border-bottom: 1px solid #ccc;">
          <Header :collapse="isCollapse" :collapseBtnClass="collapseBtnClass"/>
        </el-header>
        <!--主体-->
        <el-main>
          <!-- 表示当前页面的子路由会在 router-view 里面展示 -->
          <router-view/>
        </el-main>
​
      </el-container>
    </el-container>
  </div>
</template>
​
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import Header from "@/components/Header"
import Aside from "@/components/Aside";
// import request from "@/utils/request";
export default {
  name: 'Manage',
  components: {Aside,Header},
  data() {
    return {
      collapseBtnClass: 'el-icon-s-fold', // 收缩图标控制 默认展开
      isCollapse: false, // 点击收缩图标控制 默认是展开 false
      sideWidth: 200, // 导航栏宽度 动态 初始值是200
      logoTextShow: true, // 默认是显示的标题 当收缩是为false
    }
  },
  methods: {
    collapse() { // 点击收缩按钮时会触发按钮
      this.isCollapse = !this.isCollapse
      if (this.isCollapse) { // 收缩
        this.sideWidth = 65
        this.collapseBtnClass = 'el-icon-s-unfold' // 缩起图标控制
        this.logoTextShow = false // 收缩是不显示文字
      } else { // 展开
        this.sideWidth = 200
        this.collapseBtnClass = 'el-icon-s-fold' // 展开图标控制
        this.logoTextShow = true // 显示文字
      }
    }
  }
}
</script>
​
<style>
.home {
  height: 100%;
}
</style>
​

 

在views中创建User.vue

<template>
  <div>
    <!--          <div style="margin-bottom: 20px">
                <el-breadcrumb separator="/">
                  <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                </el-breadcrumb>
                <el-breadcrumb-item>用户管理</el-breadcrumb-item>
              </div>-->
​
    <div class="mg-10">
      <el-input v-model="username" style="width: 200px" suffix-icon="el-icon-search" placeholder="请输入名称"></el-input>
      <el-input v-model="email" style="width: 200px" suffix-icon="el-icon-message" placeholder="请输入邮箱" class="ml-5"></el-input>
      <el-input v-model="address" style="width: 200px" suffix-icon="el-icon-position" placeholder="请输入地址" class="ml-5"></el-input>
      <el-button class="ml-5" type="primary" @click="findPage()">搜索</el-button>
      <el-button class="ml-5" type="warning" @click="reset()">重置</el-button>
    </div>
​
    <div class="mg-10">
      <el-button type="primary" @click="handleAdd()">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-popconfirm
          confirm-button-text='确定'
          cancel-button-text='不删了'
          icon="el-icon-info"
          icon-color="red"
          title="您确定删除选中信息吗?"
          @confirm="delBatch()"
          class="ml-5">
        <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
      </el-popconfirm>
      <el-upload action="http://localhost:8081/user/import" :show-file-list="false" accept="xlsx" :on-success="handleExcelImportSuccess" style="display: inline-block">
        <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>
      </el-upload>
      <el-button type="primary" @click="exp()" class="ml-5">导出 <i class="el-icon-top"></i></el-button>
    </div>
​
    <el-table :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="60" align="center"></el-table-column>
      <el-table-column prop="username" label="用户名" width="120" align="center"></el-table-column>
      <el-table-column prop="nickname" label="昵称" width="120" align="center"></el-table-column>
      <el-table-column prop="email" label="邮箱" width="200" align="center"></el-table-column>
      <el-table-column prop="phone" label="电话" width="120" align="center"></el-table-column>
      <el-table-column prop="address" label="地址" width="250" align="center"></el-table-column>
      <el-table-column label="操作" width="205">
        <template slot-scope="scope">
          <el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
              confirm-button-text='确定'
              cancel-button-text='不删了'
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="handleDel(scope.row.id)"
              class="ml-5">
            <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
​
    <!-- 分页
        分页数据和表格数据产生了联动
        前端传的值:
          :current-page="pageNum" 页码
          :page-size="pageSize" 每页个数
        后端传的值:
          :total="total" 总条数
    -->
    <div style="padding: 10px 0">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[ 2, 5, 10, 15]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
    </div>
​
    <el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="40%">
      <el-form>
        <el-form-item label="用户名" :label-width="formLabelWidth">
          <el-input v-model="userForm.username" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="用户昵称" :label-width="formLabelWidth">
          <el-input v-model="userForm.nickname" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话" :label-width="formLabelWidth">
          <el-input v-model="userForm.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" :label-width="formLabelWidth">
          <el-input v-model="userForm.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="地址" :label-width="formLabelWidth">
          <el-input v-model="userForm.address" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save()">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
​
<script>
export default {
  name: "User",
  data() {
    return {
      tableData: [],
      total: 0, // 分页总条数
      pageNum: 1, // 页数
      pageSize: 5, // 条数
      username: "", // 查询绑定username
      email: "", // 查询绑定email
      address: "", // 查询绑定address
      nickname: "", // 绑定用户昵称
      userForm: {}, // 绑定一个对象,form表单中有用到
      dialogFormVisible: false, // 用户信息弹出框控制
      formLabelWidth: '120px', // 绑定input框宽
      headerBg: 'headerBg',
      multipleSelection: [] // 绑定多选按钮
    }
  },
  created() {
    this.findPage()
  },
  methods: {
    handleSizeChange(pageSize) {
      console.log(`每页 ${pageSize} 条`);
      // 传给我们上面绑定的pageSize
      this.pageSize = pageSize
      // 请求数据,请求数据之后它会自动更新 pageSize
      this.findPage()
    },
    handleCurrentChange(pageNum) {
      console.log(`当前页: ${pageNum}`);
      // 传给我们上面绑定的pageNum
      this.pageNum = pageNum
      // 请求数据,请求数据之后它会自动更新 tableData
      this.findPage()
    },
    findPage() { // 搜索按钮
      this.request.get("http://localhost:8081/user/page", {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          username: this.username,
          email: this.email,
          address: this.address
        }
      }).then(res => {
        console.log(res)
        this.tableData = res.records
        this.total = res.total
      })
      // 请求分页查询数据 fetch api 有以下几个参数,第一个参数是url
      // .then 返回一个res,之后对他进行一个json的处理,因为他返回的res是一个字符串,需要把字符串转成json
      // 通过res.json()函数 这个方法,把他转成json,再.then 这个res就是json类型的了
      // 拼接url路径使用es6的语法:`http://localhost:8081/user/findPage/${pageNum}`
      /*
      // 有了request后这些内容就被替换掉了,我们替换的一个是API的替换,还有一个就是复制方式的一个替换
      fetch("http://localhost:8081/user/findPage?pageNum="+this.pageNum+"&pageSize="+this.pageSize+"&username="+this.username+"&email="+this.email+"&address="+this.address).then(res => res.json()).then(res => {
        console.log(res)
        this.tableData = res.data
        this.total = res.total
        // 查询完成后清空输入框
        this.username = ""
        this.email = ""
        this.address = ""
      })*/
    },
    reset() { // 重置按钮
      // 清空输入框
      this.username = ""
      this.email = ""
      this.address = ""
      this.findPage()
    },
    handleAdd() { // 添加按钮
      // 打开弹窗
      this.dialogFormVisible = true
      // 将from表单内容置空
      this.userForm = {}
    },
    save() { // 添加/修改弹窗点击确定按钮
      this.request.post("http://localhost:8081/user", this.userForm).then(res => {
        if (res) {
          this.$message.success("操作成功")
          this.dialogFormVisible = false
          this.findPage()
        } else {
          this.$message.error("操作失败")
        }
      })
    },
    handleEdit(row){
      // this.userForm = JSON.parse(JSON.stringify(row)) // 如果出现编辑未确认就改变的问题,就使用它
      // 也可以使用 将row拷贝到空对象中
      // this.userForm = Object.assign({},row)
      this.userForm = {...row}
      // this.userForm = row
      this.dialogFormVisible = true
    },
    handleDel(id) { // 点击删除按钮执行
      this.request.delete("http://localhost:8081/user/" + id).then(res => {
        if (res) {
          this.$message.success("删除成功")
          this.findPage()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    // 批量删除
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val; // 将选中的对象数组赋进去
    },
    delBatch() {
      // 因为我们需要的是数组,所以我们通过map来把一个对象的数组变成了一个传id的一个数组
      let ids = this.multipleSelection.map(v => v.id) // [{},{},{}] => [1,2,3]
      // 因为delete不能传对象过去,所以改成post
      this.request.post("http://localhost:8081/user/del/batch", ids).then(res => {
        if (res) {
          this.$message.success("批量删除成功")
          this.findPage()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    // 导出
    exp() {
      window.open("http://localhost:8081/user/export")
    },
    // 导入
    handleExcelImportSuccess() {
      this.$message.success("导入成功")
      this.findPage()
    },
​
  }
}
</script>
​
<style scoped>
.el-menu-item,.el-menu-item-group, .el-submenu__title{
  background-color: rgb(48, 65, 86);
}
.headerBg {
  background: #eee!important;
}
</style>

在components文件夹中创建Aside.vue

<template>
  <el-menu :default-openeds="['1', '3']" style="height: 100%;background-color: rgb(48, 65, 86);overflow-x: hidden"
           default-active="2"
           background-color="#304156FF"
           text-color="#fff"
           active-text-color="#ffd04b"
           :collapse-transition="false"
           :collapse="isCollapse"
           class="el-menu-vertical-demo"
           router
           @select="handleSelect"
  >
    <div style="height: 60px;line-height: 60px;text-align: center">
      <img src="../assets/logo.png" alt="" style="width: 20px;position: relative;top: 5px;margin-right: 5px"/>
      <b style="color: #fff" v-show="logoTextShow">后台管理系统</b>
    </div>
    <el-menu-item>
      <template slot="title">
        <el-menu-item index="/"><i class="el-icon-s-home"></i> 主页</el-menu-item>
      </template>
    </el-menu-item>
    <el-submenu index="2">
      <template slot="title">
        <i class="el-icon-menu"></i>
        <span slot="title">系统管理</span>
      </template>
      <el-menu-item-group>
        <el-menu-item index="/user"><i class="el-icon-s-custom"></i> 用户管理</el-menu-item>
      </el-menu-item-group>
    </el-submenu>
  </el-menu>
</template>
​
<script>
export default {
  name: "Aside",
  props: {
    isCollapse: Boolean,
    logoTextShow: Boolean
  }
}
</script>
​
<style scoped>
​
</style>

在components文件夹中创建Header.vue

<template>
  <div style=" line-height: 60px;display: flex">
    <div style="flex: 1;font-size: 20px">
      <span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
      <el-breadcrumb separator="/" style="display: inline-block; margin-left: 10px" >
        <el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
        <el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <el-dropdown style="width: 70px;cursor: pointer">
      <span>王小虎<i class="el-icon-arrow-down" style="margin-left: 5px"></i></span>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item>个人信息</el-dropdown-item>
        <el-dropdown-item>退出</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>
​
<script>
export default {
  name: "Header",
  props: {
    collapseBtnClass: String
  },
  computed: {
    currentPathName(){
      // 在这里取出我们在route里面设置的当前路由信息currentPathName
      return this.$store.state.currentPathName // 需要监听的数据
    }
  },
  data() {
    return {
    }
  },
}
</script>
​
<style scoped>
​
</style>

在assets文件夹中创建gloable.css

html,body {
    height: 100%;
}
html,body,div {
    margin: 0px;
    padding: 0px;
}
/*
使用方法
  class="ml-5"
*/
.ml-5 {
    margin-left: 5px;
}
.mr-5 {
    margin-right: 5px;
}
.mg-10 {
    margin: 10px 0;
}
.pd-10 {
    padding: 10px 0;
}
​

在router文件夹中操作index.js文件

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from "@/store";
​
Vue.use(VueRouter)
​
const routes = [
  // {
  //   path: '/',
  //   name: 'home',
  //   component: HomeView
  // },
  {
    path: '/',
    name: 'Manage',
    component: () => import('../views/Manage.vue'),
    redirect: "/home",
    children: [
      { path: 'home', name: '首页', component: () => import('../views/Home.vue') },
      { path: 'user', name: '用户管理', component: () => import('../views/User.vue') }
    ]
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]
​
const router = new VueRouter({
  // mode: "history",
  // base: process.env.BASE_URL,
  routes
})
​
router.beforeEach((to, from, next) => { // 路由守卫
  localStorage.setItem("currentPathName", to.name) // 设置当前路由名称
  store.commit("setPath")  // 触发store的数据更新
  next()
})
​
export default router
​

在src中创建store文件夹然后在文件夹中创建index.js

import Vue from 'vue'
import Vuex from 'vuex'
​
Vue.use(Vuex)
​
const store = new Vuex.Store({
  state: {
    currentPathName: ''
  },
  mutations: {
    setPath(state) {
      state.currentPathName = localStorage.getItem("currentPathName")
    }
  }
})
​
export default store
​
​

在src中创建utils文件夹然后在文件夹中创建request.js

import axios from 'axios'
// 首先从axios导入axios对象
// 然后通过axios去create(添加)一个request对象
const request = axios.create({
    // 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加 '/api'了,
    // 否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
    baseURL: '/api',
    timeout: 5000
})
​
// 然后request 给它设置了一个 拦截器
// 对你发送的对象进行一个 拦截
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    // 然后可以在里面设置一些请求头
    // config.headers['token'] = user.token;  // 设置请求头
    return config
}, error => {
    return Promise.reject(error)
});
​
// response 拦截器
// 可以在接口响应后统一处理结果
// 这里就是设置了从后台请求过来的数据之后,对它的一个处理
request.interceptors.response.use(
    response => {
        // 首先获取到response里面的data,这个data是axios自己封装的,所以data是统一的,这是固定写法
        let res = response.data;
        // 如果是返回的文件,直接return
        if (response.config.responseType === 'blob') {
            return res
        }
        // 兼容服务端返回的字符串数据,如果是String的话,它就会给他一个parse,转换成json对象,然后把它return出来,因为String一般会返回一个json字符串
        if (typeof res === 'string') {
            // parse作用是将json字符串转换成json对象
            res = res ? JSON.parse(res) : res
        }
        return res;
    },
    error => { // 如果出现error的话就打印error日志
        console.log('err' + error) // for debug
        return Promise.reject(error)
    }
)
​
// 最后request出来
export default request
​
​
到这里我们的前端vue代码就完成了,其中路由有点问题,还没有解决,但是不影响功能的使用

我们写Excel用到的hutool工具网页

https://www.hutool.cn/docs/#/poi/Excel%E5%B7%A5%E5%85%B7-ExcelUtil


后端springboot项目中

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.boot</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Idea-boot</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
​
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
​
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
​
        <!--    mybatis-plus    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
​
        <!--    代码生成器    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>
​
        <!-- mybatis-plus代码生成器 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>
​
        <!-- hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
​
        <!--swagger-->
<!--        <dependency>-->
<!--            <groupId>io.springfox</groupId>-->
<!--            <artifactId>springfox-swagger2</artifactId>-->
<!--            <version>2.7.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>io.springfox</groupId>-->
<!--            <artifactId>springfox-swagger-ui</artifactId>-->
<!--            <version>2.7.0</version>-->
<!--        </dependency>-->
​
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
​
    <!-- 阿里云仓库配置 -->
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>aliyun-plugin</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>
​

在resources文件夹中创建application.yml

# 修改端口地址
server:
  port: 8081
​
spring:
  datasource:
    # 数据库的驱动名称
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 连接数据库的url serverTimezone=GMT%2b8 %2b是+ 设置时间为中巴区
    url: jdbc:mysql://localhost:3306/the_sound?serverTimezone=GMT%2b8
    # 配置数据库用户名
    username: root
    # 配置数据库密码
    password: root
​
mybatis:
  # mybatis 的 mapper.xlm配置 扫描所有mybatis的xml文件
  mapper-locations: classpath:mapper/*.xml
  # 最终 sql 拼接 打印log日志
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
​
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
​

在config文件夹中创建CorsConfig.java解决跨域问题

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
​
/**
 * @author ls
 * @version 1.0
 * @description: 解决跨域问题的工具类
 * @date 2022/4/19 10:46
 */
@Configuration
public class CorsConfig {
​
    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;
​
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许任何的源地址、源请求头、源请求方法访问接口
        corsConfiguration.addAllowedOrigin("http://localhost:8080"); // 1 设置访问源地址
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
}

配置mybatis-plus

这个是它的文件位置(SysUser开头的不用管)

在utils文件夹中的CodeGenerator.java文件配置mybatis-plus生成器代码

​
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
​
import java.util.Collections;
​
/**
 * @author ls
 * @version 1.0
 * @description: mybatis-plus代码生成器
 * @date 2022/4/21 16:42
 */
public class CodeGenerator {
​
    public static void main(String[] args) {
        generate();
    }
​
    private static void generate() {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/the_sound?serverTimezone=GMT%2b8", "root", "root")
        .globalConfig(builder -> {
            builder.author("theSound") // 设置作者
//                    .enableSwagger() // 开启 swagger 模式
                    .fileOverride() // 覆盖已生成文件
                    .outputDir("E:\\ideaProject\\Idea-boot\\src\\main\\java\\"); // 指定输出目录
        })
        .packageConfig(builder -> {
            builder.parent("com.boot.springboot") // 设置父包名
                    .moduleName("") // 设置父包模块名
                    .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E:\\ideaProject\\Idea-boot\\src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径
        })
        .strategyConfig(builder -> {
            builder.entityBuilder().enableLombok(); // 生成的entity使用lombok
            builder.mapperBuilder().enableMapperAnnotation().build(); // 添加mapper注解,相当于在每一个Mapper上面加一个@Mapper
            builder.controllerBuilder().enableHyphenStyle()  // 开启驼峰转连字符
                    .enableRestStyle();  // 开启生成@RestController 控制器
            builder.addInclude("sys_user") // 设置需要生成的表名
                    .addTablePrefix("t_", "sys_"); // 设置过滤表前缀
        })
//        .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
        .execute();
    }
}
​

在config文件夹中新建MybatisPlusConfig.java文件

​
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
/**
 * @author ls
 * @version 1.0
 * @description: TODO
 * @date 2022/4/19 19:45
 */
//@Configuration 这个注解是springboot提供配置的一个注解
//@MapperScan("com.qingge.springboot.mapper") 这个注释是注入mapper的一个注解
@Configuration
@MapperScan("com.boot.springboot.mapper")
public class MybatisPlusConfig {
    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // DbType是数据库类型,我们使用的是MySQL
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
​

根据步骤找到controller.java.vm这个文件,然后复制到resources中创建的templates文件夹中

package ${package.Controller};
​
##// 导入我们需要的包
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import javax.annotation.Resource;
import java.util.List;
​
##// 这是一种模板引擎的语法 导入我们的包
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
​
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
​
/**
 * <p>
 * $!{table.comment} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
​
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
## 在这里导入service包
#end
​
##    // 这里的table.serviceName就是我们的userService
    @Resource
    private ${table.serviceName} ${table.entityPath}Service;
​
##    // 在前端传入json数据时,@RequestBody可以把json映射成user对象
##    // 这里的SysUser是动态的 ${entity} sysUser也是动态的 ${table.entityPath}
    @PostMapping
    public boolean saveOrUpdateSysUser(@RequestBody ${entity} ${table.entityPath}) {
##      // 新增或者更新
        return ${table.entityPath}Service.saveOrUpdate(${table.entityPath});
    }
​
##  // 删除数据
##  // @PathVariable 是表示url路径的参数 {}里的id和参数列表里的id是一一对应的
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return ${table.entityPath}Service.removeById(id);
    }
​
##    // 批量删除数据
    @PostMapping("/del/batch")
    public boolean deleteBatch(@RequestBody List<Integer> ids) {
        return ${table.entityPath}Service.removeByIds(ids);
    }
​
##  // 查询所有数据
    @GetMapping
    public List<${entity}> findAll(){
        return ${table.entityPath}Service.list();
    }
​
##    // 根据id查询单查
    @GetMapping("/{id}")
    public ${entity} findOne(@PathVariable Integer id) {
        return ${table.entityPath}Service.getById(id);
    }
​
##    // 分页查询
    @GetMapping("/page")
    public Page<${entity}> findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize) {
        QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        return ${table.entityPath}Service.page(new Page<>(pageNum, pageSize), queryWrapper);
    }
}
​
#end

生成后按需求添加条件及逻辑就可以了!!!


使用导入导出时在生成的User.java中加入一个注解

package com.boot.springboot.entity;
​
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
​
/**
 * <p>
 * 
 * </p>
 *
 * @author theSound
 * @since 2022-04-22
 */
@Getter
@Setter
  @TableName("sys_user")
public class User implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
      /**
     * id
     */
      @TableId(value = "id", type = IdType.AUTO)
      private Integer id;
​
      /**
     * 用户名
     * 导入 hutool 下的 Alias 注解,它的作用是直接给表赋别名,导入导出时就不用再担心有不对应的问题了
     */
      @Alias("用户名")
      private String username;
​
      /**
     * 密码
     */
      @Alias("密码")
      private String password;
​
      /**
     * 昵称
     */
      @Alias("昵称")
      private String nickname;
​
      /**
     * 邮箱
     */
      @Alias("邮箱")
      private String email;
​
      /**
     * 电话
     */
      @Alias("电话")
      private String phone;
​
      /**
     * 地址
     */
      @Alias("地址")
      private String address;
​
      /**
     * 创建时间
     */
      @Alias("创建时间")
      private LocalDateTime createTime;
​
      /**
     * 头像
     */
      @Alias("头像")
      private String avatarUrl;
​
      /**
     * 角色
     */
      @Alias("角色")
      private String role;
​
​
}

在生成的UserController.java中

package com.boot.springboot.controller;
​
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
​
import com.boot.springboot.service.IUserService;
import com.boot.springboot.entity.User;
​
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
​
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author theSound
 * @since 2022-04-22
 */
@RestController
@RequestMapping("/user")
public class UserController {
    /** 
     * @description: 导入
     * @param: file 
     * @return: java.lang.Boolean 
     * @author ls
     * @date: 2022/4/25 
     */ 
    @PostMapping("/import")
    public Boolean imp(MultipartFile file) throws Exception { // MultipartFile 对象,文件上传后会转换成这样一个file对象
        InputStream inputStream = file.getInputStream(); // 然后我们从这个file文件对象中就能获取到inputStream
        ExcelReader reader = ExcelUtil.getReader(inputStream); // 在new这个ExcelReader中将inputStream存放进去
        // 方式1:(推荐) 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
        List<User> list = reader.readAll(User.class);
        // 如果不在 com.boot.springboot.entity.User 中加入@Alias("别名")注解 
        // 就使用方式2:忽略表头的中文,直接读取表的内容
//        List<List<Object>> list = reader.read(1);
//        List<User> users = CollUtil.newArrayList();
//        for (List<Object> row : list) {
//            User user = new User();
//            user.setUsername(row.get(0).toString());
//            user.setPassword(row.get(1).toString());
//            user.setNickname(row.get(2).toString());
//            user.setEmail(row.get(3).toString());
//            user.setPhone(row.get(4).toString());
//            user.setAddress(row.get(5).toString());
//            user.setAvatarUrl(row.get(6).toString());
//            users.add(user);
//        }
        userService.saveBatch(list);
        return true;
    }
​
    /** 
     * @description: 导出接口 
     * @param: response 
     * @return: void 
     * @author ls
     * @date: 2022/4/25 
     */ 
    @GetMapping("/export")
    public void export(HttpServletResponse response) throws Exception {
        // 首先从数据库查询出所有的数据
        List<User> list = userService.list();
        // 通过工具类创建writer 写出到磁盘路径
//        ExcelWriter writer = ExcelUtil.getWriter(filesUploadPath + "/用户信息.xlsx");
        // 第一步定义一个writer对象,然后往writer中写入数据
        // 在内存操作,写出到浏览器
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 第二步 自定义标题别名 如果不在 com.boot.springboot.entity.User 中加入@Alias("别名")注解 
        // 这一步就让我们实体类中的字段和他的意思对应起来
//        writer.addHeaderAlias("username", "用户名");
//        writer.addHeaderAlias("password", "密码");
//        writer.addHeaderAlias("nickname", "昵称");
//        writer.addHeaderAlias("email", "邮箱");
//        writer.addHeaderAlias("phone", "电话");
//        writer.addHeaderAlias("address", "地址");
//        writer.addHeaderAlias("createTime", "创建时间");
//        writer.addHeaderAlias("avatarUrl", "头像");
//        writer.addHeaderAlias("role", "角色");
​
        // 第三步 将我们上面查出的list对象,通过write()方法,写入writer面去
        // 一次性写出list内的对象到excel,使用默认样式,强制输出标题
        writer.write(list, true);
​
        // 设置浏览器响应的格式(格式基本固定)
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode("用户信息", "UTF-8"); // 将这个文件的名称,用encode这个方法进行编码
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); // 然后设置到response的Header中
​
        // 通过response对象,获取输出流
        ServletOutputStream out = response.getOutputStream();
        // 然后将这个writer对象里的东西刷新到这个输出流中,然后是通过输出流返回到浏览器
        writer.flush(out, true);
        // 最后将流关闭
        out.close();
        writer.close();
    }
​
    @Resource
    private IUserService userService;
​
    @PostMapping
    public boolean saveOrUpdateSysUser(@RequestBody User user) {
        return userService.saveOrUpdate(user);
    }
​
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return userService.removeById(id);
    }
​
    @PostMapping("/del/batch")
    public boolean deleteBatch(@RequestBody List<Integer> ids) {
        return userService.removeByIds(ids);
    }
​
    @GetMapping
    public List<User> findAll(){
        return userService.list();
    }
​
    @GetMapping("/{id}")
    public User findOne(@PathVariable Integer id) {
        return userService.getById(id);
    }
    
    /** 
     * @description: 分页+模糊查 
     * @param: pageNum,pageSize,username,email,address 
     * @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.boot.springboot.entity.User> 
     * @author ls
     * @date: 2022/4/26 
     */ 
    @GetMapping("/page")
    public Page<User> findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize,
                               @RequestParam(defaultValue = "") String username, @RequestParam(defaultValue = "") String email, @RequestParam(defaultValue = "") String address) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (!"".equals(username)){
            queryWrapper.like("username",username);
        }
        if (!"".equals(email)){
            queryWrapper.like("email",email);
        }
        if (!"".equals(address)){
            queryWrapper.like("address",address);
        }
        queryWrapper.orderByDesc("id");
        return userService.page(new Page<>(pageNum, pageSize), queryWrapper);
    }
}
​
​
posted @ 2022-04-25 18:53  回头是岸不可能回头  阅读(1004)  评论(0)    收藏  举报