SpringBoot + Vue Element UI 实现前后端分离

GitHub地址:https://github.com/saysky/springboot-vue-example/

前几天一个朋友找我帮她做毕设,功能比较简单,后台只需要设计3张表,但是要求前端必须使用 Vue,且使用 Element UI。正好最近准备学习 Vue,就把这个活接下来了,给自己压力去 learn and do.

功能很简单,服务器信息管理系统,后台可以添加服务器,然后可以对其申请、释放、删除。

先看一下效果图

一、部分效果图

1.服务器管理页面,服务器信息分页显示,添加服务器,申请服务器,释放服务器,删除服务器,查询服务器

SpringBoot + Vue Element UI 实现前后端分离

 

2.新增服务器弹出对话框

SpringBoot + Vue Element UI 实现前后端分离

 

3.申请服务器,使用datepicker选择时间

SpringBoot + Vue Element UI 实现前后端分离

 

 

二、代码结构

1、后端,典型 SpringBoot+MyBatis 框架,后端控制器使用 @RestController,全部返还JSON格式数据

SpringBoot + Vue Element UI 实现前后端分离

 

2、前端

SpringBoot + Vue Element UI 实现前后端分离

 

三、主要代码介绍

通过服务器信息管理,其实本质上就是一张表的 CRUD,来介绍后端代码和前端代码

 

1.后端代码,这里只贴 Controller 里的代码

  1. package com.example.sens.controller;
  2. import com.baomidou.mybatisplus.plugins.Page;
  3. import com.example.sens.entity.Server;
  4. import com.example.sens.entity.User;
  5. import com.example.sens.enums.ServerStatus;
  6. import com.example.sens.service.ServerService;
  7. import com.example.sens.utils.Response;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.web.bind.annotation.*;
  10. import javax.servlet.http.HttpSession;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import java.util.Objects;
  14. /**
  15.  * @author 言曌
  16.  * @date 2019-05-08 13:10
  17.  */
  18. @RestController
  19. @RequestMapping("/api/server")
  20. public class ServerController {
  21.     @Autowired
  22.     private ServerService serverService;
  23.     /**
  24.      * 分页展示服务器
  25.      * @param pageNo
  26.      * @param pageSize
  27.      * @param queryType
  28.      * @param queryKeyword
  29.      * @return
  30.      */
  31.     @GetMapping("/paging")
  32.     public Response<Page<Server>> paging(@RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo,
  33.                                          @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
  34.                                          @RequestParam(value = "queryType", required = false) String queryType,
  35.                                          @RequestParam(value = "queryKeyword", required = false) String queryKeyword) {
  36.         Page pageable = new Page(pageNo, pageSize);
  37.         Page<Server> page = null;
  38.         try {
  39.             page = serverService.findAll(queryType, queryKeyword, pageable);
  40.         } catch (Exception e) {
  41.             return Response.no();
  42.         }
  43.         return Response.yes(page);
  44.     }
  45.     /**
  46.      * 申请服务器
  47.      * @param server
  48.      * @param session
  49.      * @return
  50.      */
  51.     @PostMapping("/apply")
  52.     public Response applyServer(@RequestBody Server server, HttpSession session) {
  53.         User user = (User) session.getAttribute("user");
  54.         try {
  55.             Long now = System.currentTimeMillis();
  56.             //1.判断项目名称是否存在
  57.             Server checkProjectName = serverService.findByProjectName(server.getProjectName());
  58.             if(checkProjectName != null && !Objects.equals(checkProjectName.getId(), server.getId())) {
  59.                 return Response.no("项目名已存在!");
  60.             }
  61.             //2.添加
  62.             if ((Objects.equals(server.getServerStatus(), ServerStatus.FREE.getDesc())) || (now > (server.getReleaseTime() != null ? server.getReleaseTime() : 0))) {
  63.                 server.setApplyUserName(user.getUserName());
  64.                 server.setApplyTime(now);
  65.                 server.setServerStatus(ServerStatus.USED.getDesc());
  66.                 serverService.saveByServer(server);
  67.             } else {
  68.                 return Response.no("服务器在使用中,不能申请!");
  69.             }
  70.         } catch (Exception e) {
  71.             e.printStackTrace();
  72.             return Response.no("申请失败!");
  73.         }
  74.         return Response.yes();
  75.     }
  76.     /**
  77.      * 释放服务器
  78.      * @param id
  79.      * @return
  80.      */
  81.     @PostMapping("/release")
  82.     public Response releaseServer(@RequestParam("id") Long id) {
  83.         try {
  84.             Server server = serverService.findByServerId(id);
  85.             if (server != null) {
  86.                 Long now = System.currentTimeMillis();
  87.                 if (!(Objects.equals(server.getServerStatus(), ServerStatus.FREE.getDesc()))) {
  88.                     server.setReleaseTime(now);
  89.                     server.setServerStatus(ServerStatus.FREE.getDesc());
  90.                     serverService.saveByServer(server);
  91.                 } else {
  92.                     return Response.no("服务器已经释放!");
  93.                 }
  94.             }
  95.         } catch (Exception e) {
  96.             e.printStackTrace();
  97.             return Response.no("申请失败!");
  98.         }
  99.         return Response.yes();
  100.     }
  101.     /**
  102.      * 创建服务器
  103.      * @param server
  104.      * @return
  105.      */
  106.     @PostMapping("/create")
  107.     public Response createServer(@RequestBody Server server) {
  108.         try {
  109.             serverService.saveByServer(server);
  110.         } catch (Exception e) {
  111.             e.printStackTrace();
  112.             return Response.no("保存失败!");
  113.         }
  114.         return Response.yes();
  115.     }
  116.     /**
  117.      * 删除服务器
  118.      * @param id
  119.      * @return
  120.      */
  121.     @PostMapping("/delete")
  122.     public Response<Boolean> delete(@RequestParam("id") Long id) {
  123.         try {
  124.             serverService.removeServer(id);
  125.         } catch (Exception e) {
  126.             e.printStackTrace();
  127.             return Response.no();
  128.         }
  129.         return Response.yes();
  130.     }
  131.     /**
  132.      * 批量删除服务器
  133.      * @param ids
  134.      * @return
  135.      */
  136.     @PostMapping(value = "/batchRemove")
  137.     @ResponseBody
  138.     public Response<Boolean> batchRemove(@RequestParam("ids") String ids) {
  139.         if("".equals(ids)) {
  140.             return Response.no("请至少选择一项!");
  141.         }
  142.         String[] arr = ids.split(",");
  143.         List<Long> list = new ArrayList<>();
  144.         for (int i = 0; i < arr.length; i++) {
  145.             list.add(Long.valueOf(arr[i]));
  146.         }
  147.         try {
  148.             serverService.batchRemove(list);
  149.         } catch (Exception e) {
  150.             e.printStackTrace();
  151.             return Response.no();
  152.         }
  153.         return Response.yes();
  154.     }
  155. }

 

2.前端代码,主要贴vue文件代码

  1. <template>
  2.     <div class="main-login">
  3.         <lyz-layout
  4.                 :pagination="pagination"
  5.                 :label="label"
  6.                 @handleSizeChange="handleSizeChange"
  7.                 @handleCurrentChange="handleCurrentChange">
  8.             <div slot="banner" class="top-right">
  9.                 <el-button type="primary" size="small" @click="createServer" slot="reference">新增</el-button> &nbsp;
  10.                 <el-select v-model="queryType" placeholder="请选择查询类型" class="right-select" @change="queryTypeChange"
  11.                            clearable @clear="queryList">
  12.                     <el-option label="服务器名称" value="server_name">服务器名称</el-option>
  13.                     <el-option label="项目名称" value="project_name">项目名称</el-option>
  14.                 </el-select>
  15.                 <el-input placeholder="请输入搜索内容" v-model="queryKeyword" class="input-with-select" clearable>
  16.                     <el-button slot="append" icon="el-icon-search" @click="queryList"></el-button>
  17.                 </el-input>
  18.                 <el-button type="danger" size="small" @click="batchRemove" slot="reference">批量删除</el-button> &nbsp;
  19.             </div>
  20.             <div slot="main" class="main-body">
  21.                 <el-table
  22.                         :data="tableData"
  23.                         stripe
  24.                         v-loading="loginLoading"
  25.                         tooltip-effect="light"
  26.                         height="100%"
  27.                         style="width: 100%"
  28.                         @selection-change="handleSelectionChange">
  29.                     <el-table-column type="selection" width="55"></el-table-column>
  30.                     <el-table-column
  31.                             v-for="(data,index) in tableHeader"
  32.                             :show-overflow-tooltip="true"
  33.                             :key="index"
  34.                             :prop="data.prop"
  35.                             :label="data.label"
  36.                             :min-width="data['min-width']"
  37.                             :align="data.align">
  38.                     </el-table-column>
  39.                     <el-table-column
  40.                             fixed="right"
  41.                             label="操作"
  42.                             min-width="120">
  43.                         <template slot-scope="scope">
  44.                             <el-button type="text" size="mini"
  45.                                        :disabled="scope.row.serverStatus === '使用中'"
  46.                                        @click="applyServer(scope.row)">申请
  47.                             </el-button>
  48.                             <el-button type="text" size="mini"
  49.                                        :disabled="scope.row.serverStatus === '空闲'"
  50.                                        @click="releaseServer(scope.row.id)">释放
  51.                             </el-button>
  52.                             <el-button type="text" size="mini" class="danger-text"
  53.                                        @click="deleteServer(scope.row.id)">删除
  54.                             </el-button>
  55.                         </template>
  56.                     </el-table-column>
  57.                 </el-table>
  58.             </div>
  59.         </lyz-layout>
  60.         <el-dialog :title="operate==='apply'?'申请服务器':'添加服务器信息'" :visible.sync="messageVisible" width="33%" center
  61.                    class="user-dialog">
  62.             <el-form :model="messageForm" :label-width="messageLabelWidth" ref="messageForm" :rules="messageRule"
  63.                      :validate-on-rule-change=false>
  64.                 <el-form-item label="名称" v-if="operate==='create'" prop="serverName">
  65.                     <el-input v-model="messageForm.serverName" placeholder="请输入服务器名称"></el-input>
  66.                 </el-form-item>
  67.                 <el-form-item label="状态" v-if="operate==='create'" pop="serverStatus">
  68.                     <el-radio-group v-model="messageForm.serverStatus">
  69.                         <el-radio label="free" checked>空闲</el-radio>
  70.                         <el-radio lebel="used">使用中</el-radio>
  71.                     </el-radio-group>
  72.                 </el-form-item>
  73.                 <el-form-item label="项目名称" v-if="operate==='apply'" prop="projectName">
  74.                     <el-input v-model="messageForm.projectName" placeholder="请输入项目名称"></el-input>
  75.                 </el-form-item>
  76.                 <el-form-item label="申请时间" v-if="operate==='apply'" prop="applyTime">
  77.                     <el-date-picker
  78.                             v-model="messageForm.applyTime"
  79.                             type="datetime"
  80.                             value-format="timestamp"
  81.                             placeholder="选择申请时间"
  82.                             align="right"
  83.                             :picker-options="pickerOptions">
  84.                     </el-date-picker>
  85.                 </el-form-item>
  86.                 <el-form-item label="释放时间" v-if="operate==='apply'" prop="releaseTime">
  87.                     <el-date-picker
  88.                             v-model="messageForm.releaseTime"
  89.                             type="datetime"
  90.                             value-format="timestamp"
  91.                             placeholder="选择释放时间"
  92.                             align="right"
  93.                             :picker-options="pickerOptions">
  94.                     </el-date-picker>
  95.                 </el-form-item>
  96.             </el-form>
  97.             <div slot="footer" class="dialog-footer">
  98.                 <el-button @click="messageVisible = false">取 消</el-button>
  99.                 <el-button type="primary" @click=saveServer>确 定</el-button>
  100.             </div>
  101.         </el-dialog>
  102.     </div>
  103. </template>
  104. <script>
  105.     import lyzLayout from '@/components/lyzLayout';
  106.     import manage from '../manage.component';
  107.     import {formatDateTime, responseText, debounce} from '../../../config/utils.js';
  108.     export default {
  109.         name: "server",
  110.         data() {
  111.             return {
  112.                 queryType: '',
  113.                 queryKeyword: '',
  114.                 pagination: {
  115.                     pageIndex: 1,
  116.                     pageSize: 10,
  117.                     total: 0,
  118.                 },
  119.                 label: '服务器信息管理',
  120.                 messageForm: {},
  121.                 messageVisible: false,
  122.                 messageLabelWidth: '90px',
  123.                 messageRule: {
  124.                     serverName: [
  125.                         {required: true, message: '请输入服务器名称', trigger: 'blur'}
  126.                     ],
  127.                     serverStatus: [
  128.                         {required: true, message: '请选择服务器状态', trigger: 'blur'}
  129.                     ],
  130.                     projectName: [
  131.                         {required: true, message: '请输入项目名称', trigger: 'blur'}
  132.                     ],
  133.                     applyTime: [
  134.                         {required: true, message: '请输入申请时间', trigger: 'blur'}
  135.                     ],
  136.                     releaseTime: [
  137.                         {required: true, message: '请输入释放时间', trigger: 'blur'}
  138.                     ]
  139.                 },
  140.                 operate: '',
  141.                 multipleSelection: [],//多选的数据
  142.                 pickerOptions: {
  143.                     shortcuts: [{
  144.                         text: '今天',
  145.                         onClick(picker) {
  146.                             picker.$emit('pick', new Date());
  147.                         }
  148.                     }, {
  149.                         text: '一天后',
  150.                         onClick(picker) {
  151.                             const date = new Date();
  152.                             date.setTime(date.getTime() + 3600 * 1000 * 24);
  153.                             picker.$emit('pick', date);
  154.                         }
  155.                     }, {
  156.                         text: '一周后',
  157.                         onClick(picker) {
  158.                             const date = new Date();
  159.                             date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
  160.                             picker.$emit('pick', date);
  161.                         }
  162.                     }, {
  163.                         text: '一月后',
  164.                         onClick(picker) {
  165.                             const date = new Date();
  166.                             date.setTime(date.getTime() + 3600 * 1000 * 24 * 30);
  167.                             picker.$emit('pick', date);
  168.                         }
  169.                     }],
  170.                     disabledDate: function (now) {
  171.                         if (now < new Date())
  172.                             return true;
  173.                         return false
  174.                     }
  175.                 },
  176.                 tableData: [],
  177.                 loginLoading: false,
  178.                 tableHeader: [
  179.                     {
  180.                         prop: 'id',
  181.                         label: 'ID',
  182.                         'min-width': 40,
  183.                         align: 'center',
  184.                     },
  185.                     {
  186.                         prop: 'applyUserName',
  187.                         label: '使用人',
  188.                         'min-width': 80,
  189.                         align: 'center',
  190.                     },
  191.                     {
  192.                         prop: 'serverName',
  193.                         label: '服务器名称',
  194.                         'min-width': 120,
  195.                         align: 'center',
  196.                     },
  197.                     {
  198.                         prop: 'serverStatus',
  199.                         label: '服务器状态',
  200.                         'min-width': 100,
  201.                         align: 'center',
  202.                     },
  203.                     {
  204.                         prop: 'applyTime',
  205.                         label: '申请时间',
  206.                         'min-width': 120,
  207.                         align: 'center',
  208.                     },
  209.                     {
  210.                         prop: 'releaseTime',
  211.                         label: '释放时间',
  212.                         'min-width': 120,
  213.                         align: 'center',
  214.                     },
  215.                     {
  216.                         prop: 'projectName',
  217.                         label: '项目名称',
  218.                         'min-width': 120,
  219.                         align: 'center',
  220.                     },
  221.                 ]
  222.             };
  223.         },
  224.         components: {
  225.             lyzLayout,
  226.         },
  227.         mixins: [manage],
  228.         created() {
  229.             this.queryList();
  230.         },
  231.         mounted() {
  232.             this.$watch('queryKeyword', debounce(() => {
  233.                 this.pagination.pageIndex = 1;
  234.                 this.queryList();
  235.             }, 1000));
  236.         },
  237.         methods: {
  238.             queryTypeChange(val) {
  239.                 if (val !== '') {
  240.                     this.queryType = val;
  241.                     this.queryList();
  242.                 }
  243.             },
  244.             queryList() {
  245.                 this.loginLoading = true;
  246.                 let params = {
  247.                     queryType: this.queryType,
  248.                     queryKeyword: this.queryKeyword,
  249.                     pageNo: this.pagination.pageIndex,
  250.                     pageSize: this.pagination.pageSize
  251.                 };
  252.                 this.$http.get('/server/paging', {params: params}).then(({body}) => {
  253.                     if (body.success === true) {
  254.                         responseText(body.data.records).forEach((item) => {
  255.                             itemitem.serverStatus = item.serverStatus == 'free' ? '空闲' : '使用中';
  256.                             if (item.releaseTime != null) {
  257.                                 item.releaseTime = new Date(item.releaseTime).toLocaleString().replace(/:\d{1,2}$/, ' ');
  258.                             }
  259.                             if (item.applyTime != null) {
  260.                                 item.applyTime = new Date(item.applyTime).toLocaleString().replace(/:\d{1,2}$/, ' ');
  261.                             }
  262.                         })
  263.                         this.tableData = responseText(body.data.records);
  264.                         this.pagination.total = body.data.records ? body.data.total : 0;
  265.                     } else {
  266.                         this.$message.error(body.message);
  267.                     }
  268.                 }).finally(() => {
  269.                     this.loginLoading = false;
  270.                 })
  271.             },
  272.             handleSelectionChange(val) {
  273.                 this.multipleSelection = val;
  274.             },
  275.             batchRemove() {
  276.                 let ids = '';
  277.                 this.multipleSelection.forEach(selectedItem => {
  278.                     // 删除请求
  279.                     ids += selectedItem.id + ',';
  280.                 });
  281.                 let params = {
  282.                     ids: ids
  283.                 };
  284.                 this.$http.post("/server/batchRemove", {}, {params: params}).then(({body}) => {
  285.                     if (body.success === true) {
  286.                         this.queryList();
  287.                         this.$message.success(body.message);
  288.                     } else {
  289.                         this.$message.error(body.message);
  290.                     }
  291.                 }).catch(() => {
  292.                     this.$message.error('删除失败');
  293.                 })
  294.             },
  295.             deleteServer(id) {
  296.                 let params = {
  297.                     id: id,
  298.                 };
  299.                 this.delete('/server/delete', params);
  300.             },
  301.             createServer(row) {
  302.                 console.log(row);
  303.                 this.messageVisible = true;
  304.                 this.operate = 'create';
  305.                 let _form = Object.assign({}, row);
  306.                 this.messageForm = _form;
  307.             },
  308.             applyServer(row) {
  309.                 console.log(row);
  310.                 this.messageVisible = true;
  311.                 this.operate = 'apply';
  312.                 let _form = Object.assign({}, row);
  313.                 this.messageForm = _form;
  314.             },
  315.             saveServer() {
  316.                 console.log('save');
  317.                 let params = {
  318.                     applyUserName: this.messageForm.applyUserName,
  319.                     serverName: this.messageForm.serverName,
  320.                     projectName: this.messageForm.projectName,
  321.                     applyTime: this.messageForm.applyTime,
  322.                     releaseTime: this.messageForm.releaseTime,
  323.                 };
  324.                 this.$refs['messageForm'].validate((valid) => {
  325.                     if (valid) {
  326.                         if (params.applyTime > params.releaseTime) {
  327.                             this.$message.error('释放时间必须大于申请时间!');
  328.                         } else {
  329.                             if (this.messageForm.id) {
  330.                                 params.serverStatus = this.messageForm.serverStatus == '空闲' ? 'free' : 'used';
  331.                                 params.id = this.messageForm.id;
  332.                                 this.save('/server/apply', params, 'messageVisible');
  333.                             } else {
  334.                                 params.serverStatus = this.messageForm.serverStatus;
  335.                                 this.save('/server/create', params, 'messageVisible');
  336.                             }
  337.                         }
  338.                     }
  339.                 })
  340.             },
  341.             releaseServer(id) {
  342.                 this.$http.post('/server/release?id=' + id, {}, {}).then(({body}) => {
  343.                     if (body.success === true) {
  344.                         this.$message.success(body.message);
  345.                         this.queryList();
  346.                     } else {
  347.                         this.$message.error(body.message);
  348.                     }
  349.                 }).catch(() => {
  350.                     this.$message.error('释放失败');
  351.                 })
  352.             }
  353.         }
  354.     }
  355. </script>
  356. <style scoped>
  357.     .main-login {
  358.         height: 100%;
  359.     }
  360.     .danger-text {
  361.         color: #F56C6C;
  362.     }
  363. </style>

 

其他代码就不贴了,见GitHub

该项目实现了前后端分离,后面的开发只需要在原有的基础上复制粘贴就行了

 

GitHub地址:https://github.com/saysky/springboot-vue-example/

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
言曌

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:4   其中:访客  4   博主  0

    • avatar m_xy

      请求发一下github链接谢谢

      • avatar Iohags

        能发送下git地址吗? 谢谢!

        • avatar 郑州

          请大佬发送下git地址。

          • avatar 楚狂人

            步骤很详细,谢谢