瞧不起CRUD?Vue 神帶你領略王者級CRUD程序媛的風采

最近在學習Spring Boot + Vue的整合,通過尚硅谷的一個穀粒學院項目進行練習。

一、課程介紹

系統後端接口部分,使用目前流行的SpringBoot+SpringCloud進行微服務架構,使用Feign、Gateway、Hystrix,以及阿里巴巴的Nacos等組件搭建了項目的基礎環境。項目中還使用MyBatisPlus進行持久層的操作,使用了OAuth2+JWT實現了分佈式的訪問,項目中整合了SpringSecurity進行了權限控制。除此之外,項目中使用了阿里巴巴的EasyExcel實現對Excel的讀寫操作,使用了Redis進行首頁數據的緩存,使用Git進行代碼的版本控制,還整合了Swagger生成接口文檔 。

系統前端部分,使用主流的前端框架Vue,使用Es6的開發規範,採用模塊化的開發模式,搭建頁面環境使用了Nuxt框架和vue-admin-template模板,使用Element-ui進行頁面佈局。前端環境中使用Npm進行依賴管理,使用Babel進行代碼轉換,使用Webpack進行靜態資源的打包,採用axios進行Ajax請求調用,使用了ECharts進行數據的圖表展示。

二、前置知識梳理

1、Swagger

Swagger 是一個規範和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。

作用:

(1)接口的文檔在線自動生成。

(2)功能測試。

方法測試

2、輕量級後臺管理系統基礎模板,vue-admin-template

3、MySQL數據庫

建表語句:

CREATE TABLE `edu_teacher` (
  `id` char(19) NOT NULL COMMENT '講師ID',
  `name` varchar(20) NOT NULL COMMENT '講師姓名',
  `intro` varchar(500) NOT NULL DEFAULT '' COMMENT '講師簡介',
  `career` varchar(500) DEFAULT NULL COMMENT '講師資歷,一句話說明講師',
  `level` int(10) unsigned NOT NULL COMMENT '頭銜 1高級講師 2首席講師',
  `avatar` varchar(255) DEFAULT NULL COMMENT '講師頭像',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
  `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '邏輯刪除 1(true)已刪除, 0(false)未刪除',
  `gmt_create` datetime NOT NULL COMMENT '創建時間',
  `gmt_modified` datetime NOT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='講師';

4、nginx

(1)簡介

nginx (engine x) 是一個高性能的HTTP和反向代理web服務器。

(2)Nginx的主要作用

  • 請求轉發
  • 負載均衡
  • 動態分離

(3)啓動

cmd路徑,執行nginx.exe命令即可。

(4)配置

目前暫時只有兩個子端口,9001爲nginx端口號,8001爲講師模塊,8002爲阿里雲oss上傳頭像模塊。

三、Spring Boot + Vue 實現CRUD

1、EduTeacherController

package com.atguigu.eduservice.controller;

import com.atguigu.commonutils.R;
import com.atguigu.eduservice.entity.EduTeacher;
import com.atguigu.eduservice.entity.vo.TeacherQuery;
import com.atguigu.eduservice.service.EduTeacherService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@Api(description="講師管理")
@RestController
@RequestMapping("/eduservice/teacher")
@CrossOrigin
public class EduTeacherController {
    @Autowired
    private EduTeacherService teacherService;

    @ApiOperation(value = "所有講師列表")
    @GetMapping("findAll")
    public R findAllTeacher(){
        List<EduTeacher> list = teacherService.list(null);
        return R.Ok().data("items",list);
    }

    @ApiOperation(value = "根據ID查詢講師")
    @GetMapping("findById{id}")
    public R findById(@ApiParam(name="id",value="講師ID",required=true) @PathVariable String id){
        EduTeacher teacher = teacherService.getById(id);
        return R.Ok().data("items",teacher.getJson());
    }

    @ApiOperation(value = "根據ID刪除講師")
    //邏輯刪除講師的方法
    @DeleteMapping("{id}")
    public R removeTeacher(@ApiParam(name="id",value="講師ID",required=true) @PathVariable String id){
        Boolean flag = teacherService.removeById(id);
        if(flag){
            return R.Ok();
        }else{
            return R.error();
        }
    }

    //分頁
    @GetMapping("pageTeacher/{current}/{limit}")
    public R pageListTeacher(@PathVariable long current,@PathVariable long limit){
        //創建page對象
        Page<EduTeacher> pageTeacher = new Page<>(current,limit);
        //調用方法的時候,底層封裝,把分頁所有數據封裝到pageTeacher中
        teacherService.page(pageTeacher,null);
        long total = pageTeacher.getTotal();
        List<EduTeacher> records = pageTeacher.getRecords();
        //Map map = new HashMap();
        //map.put("total",total);
        //map.put("rows",records);
        return R.Ok().data("total",total).data("rows",records);
    }

    @PostMapping("pageTeacherCondition/{current}/{limit}")
    public R pageTeacherCondition(@PathVariable long current,@PathVariable long limit,
                                  @RequestBody(required = false) TeacherQuery teacherQuery){
        //創建一個page對象
        Page<EduTeacher> pageTeacher = new Page<>(current,limit);
        QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
        //多條件組合查詢
        //動態SQL
        //判斷條件值是否爲空,不爲空就拼接
        String name = teacherQuery.getName();
        Integer level = teacherQuery.getLevel();
        String begin = teacherQuery.getBegin();
        String end = teacherQuery.getEnd();
        if(!StringUtils.isEmpty(name)){
            wrapper.like("name",name);
        }
        if(!StringUtils.isEmpty(level)){
            wrapper.eq("level",level);
        }
        if(!StringUtils.isEmpty(begin)){
            wrapper.ge("gmt_create",begin);
        }
        if(!StringUtils.isEmpty(end)){
            wrapper.le("gmt_modified",end);
        }
        wrapper.orderByDesc("gmt_create");
        //wrapper.
        teacherService.page(pageTeacher,wrapper);
        long total = pageTeacher.getTotal();
        List<EduTeacher> records = pageTeacher.getRecords();
        return R.Ok().data("total",total).data("rows",records);
    }

    //添加講師接口的方法
    @PostMapping("addTeacher")
    public R addTeacher(@RequestBody EduTeacher eduTeacher){
        boolean save = teacherService.save(eduTeacher);
        if(save){
            return R.Ok();
        }else {
            return R.error();
        }
    }

    @GetMapping("getTeacher/{id}")
    public R getTeacher(@PathVariable String id){
        EduTeacher byId = teacherService.getById(id);
        return R.Ok().data("teacher",byId);
    }

    @PostMapping("updateTeacher")
    public R updateTeacher(@RequestBody EduTeacher eduTeacher){
        boolean flag = teacherService.updateById(eduTeacher);
        if(flag){
            return R.Ok();
        }else {
            return R.error();
        }
    }
}

2、前端講師列表

<template>
    <div>
        <el-form label-width="120px">
            <el-form-item label="講師名稱">
                <el-input v-model="teacher.name"/>
            </el-form-item>
            <el-form-item label="講師排序">
                <el-input-number v-model="teacher.sort" controls-position="right" min="0"/>
            </el-form-item>
            <el-form-item label="講師頭銜">
                <el-select v-model="teacher.level" clearable placeholder="請選擇">
                <!--
                    數據類型一定要和取出的json中的一致,否則沒法回填
                    因此,這裏value使用動態綁定的值,保證其數據類型是number
                -->
                <el-option :value="1" label="高級講師"/>
                <el-option :value="2" label="首席講師"/>
                </el-select>
            </el-form-item>
            <el-form-item label="講師資歷">
                <el-input v-model="teacher.career"/>
            </el-form-item>
            <el-form-item label="講師簡介">
                <el-input v-model="teacher.intro" :rows="10" type="textarea"/>
            </el-form-item>

            <!-- 講師頭像:TODO -->
            <!-- 講師頭像 -->
            <el-form-item label="講師頭像">

                <!-- 頭銜縮略圖 -->
                <pan-thumb :image="teacher.avatar"/>
                <!-- 文件上傳按鈕 -->
                <el-button type="primary" icon="el-icon-upload" @click="imagecropperShow=true">更換頭像
                </el-button>

                <!--
                  v-show:是否顯示上傳組件
                  :key:類似於id,如果一個頁面多個圖片上傳控件,可以做區分
                  :url:後臺上傳的url地址
                  @close:關閉上傳組件
                  @crop-upload-success:上傳成功後的回調 
                  <input type="file" name="file"/>
                -->
                <image-cropper
                              v-show="imagecropperShow"
                              :width="300"
                              :height="300"
                              :key="imagecropperKey"
                              :url="BASE_API+'/eduoss/fileoss'"
                              field="file"
                              @close="close"
                              @crop-upload-success="cropSuccess"/>
            </el-form-item>

            <el-form-item>
                <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>
<script>
import teacherApi from '@/api/edu/teacher.js'
import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'
export default {
  data() {
    return {
      teacher: {
        name: '',
        sort: 0,
        level: 1,
        career: '',
        intro: '',
        avatar: ''
      },
      //上傳彈框組件是否顯示
      imagecropperShow:false,
      imagecropperKey:0,//上傳組件key值
      BASE_API:process.env.BASE_API, //獲取dev.env.js裏面地址
      saveBtnDisabled:false  // 保存按鈕是否禁用,
    }
  },
  created(){
    this.init()
  },
  watch: {  //監聽
    $route(to, from) { //路由變化方式,路由發生變化,方法就會執行
      this.init()
    }
  },
  methods: {
    close() { //關閉上傳彈框的方法
        this.imagecropperShow=false
        //上傳組件初始化
        this.imagecropperKey = this.imagecropperKey+1
    },
    //上傳成功方法
    cropSuccess(data) {
      this.imagecropperShow=false
      //上傳之後接口返回圖片地址
      this.teacher.avatar = data.url
      this.imagecropperKey = this.imagecropperKey+1
    },
    init() {
      //判斷路徑有id值,做修改
      if(this.$route.params && this.$route.params.id) {
          //從路徑獲取id值
          const id = this.$route.params.id
          //調用根據id查詢的方法
          this.getInfo(id)
      } else { //路徑沒有id值,做添加
        //清空表單
        this.teacher = {}
      }
    },
    //根據講師id查詢的方法
    getInfo(id) {
      teacherApi.getTeacherInfo(id)
        .then(response => {
          this.teacher = response.data.teacher
        })
    },
    saveOrUpdate() {
      //根據teacher中是否有ID
      if(!this.teacher.id){
        this.saveTeacher();
      }else{
          this.updateTeacher();
      }
    },
    updateTeacher() {
      teacherApi.updateTeacherInfo(this.teacher)
      .then(response => {
            //提示信息
            this.$message({
                type: 'success',
                message: '修改成功!'
            });
            //回到列表頁面,路由跳轉
            this.$router.push({ path: '/teacher/table' })
      })
    },
    // 保存
    saveTeacher() {
        teacherApi.addTeacher(this.teacher)
        .then(response => {
            //提示信息
            this.$message({
                type: 'success',
                message: '添加成功!'
            });
            //回到列表頁面,路由跳轉
            this.$router.push({ path: '/teacher/table' })
        })
    }
  }
}
</script>

3、添加講師頁面

<template>
    <div class="app-container">
         <!--查詢表單-->
        <el-form :inline="true" class="demo-form-inline">
            <el-form-item>
                <el-input v-model="teacherQuery.name" placeholder="講師名"/>
            </el-form-item>

            <el-form-item>
                <el-select v-model="teacherQuery.level" clearable placeholder="講師頭銜">
                    <el-option :value="1" label="高級講師"/>
                    <el-option :value="2" label="首席講師"/>
                </el-select>
            </el-form-item>

            <el-form-item label="添加時間">
                <el-date-picker
                    v-model="teacherQuery.begin"
                    type="datetime"
                    placeholder="選擇開始時間"
                    value-format="yyyy-MM-dd HH:mm:ss"
                    default-time="00:00:00"
                />
            </el-form-item>
            <el-form-item>
                <el-date-picker
                    v-model="teacherQuery.end"
                    type="datetime"
                    placeholder="選擇截止時間"
                    value-format="yyyy-MM-dd HH:mm:ss"
                    default-time="00:00:00"
                />
            </el-form-item>
            
            <el-button type="primary" icon="el-icon-search" @click="getList()">查詢</el-button>
            <el-button type="default" @click="resetData()">清空</el-button>
        </el-form>
        <!-- 表格 -->
        <el-table
            :data="list"
            border
            fit
            highlight-current-row>

            <el-table-column
                label="序號"
                width="70"
                align="center">
                <template slot-scope="scope">
                    {{ (page - 1) * limit + scope.$index + 1 }}
                </template>
            </el-table-column>

            <el-table-column prop="name" label="名稱" width="80" />
            
            <el-table-column label="頭銜" width="80">
                <template slot-scope="scope">
                    {{ scope.row.level===1?'高級講師':'首席講師' }}
                </template>
            </el-table-column>

            <el-table-column prop="intro" label="資歷" />

            <el-table-column prop="gmtCreate" label="添加時間" width="160"/>

            <el-table-column prop="sort" label="排序" width="60" />

            <el-table-column label="操作" width="200" align="center">
                <template slot-scope="scope">
                <router-link :to="'/teacher/edit/'+scope.row.id">
                    <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
                </router-link>
                <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">刪除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分頁 -->
        <el-pagination
            :current-page="page"
            :page-size="limit"
            :total="total"
            style="padding: 30px 0; text-align: center;"
            layout="total, prev, pager, next, jumper"
            @current-change="getList"
        /> 
    </div>
</template>
<script>
//引入teacher.js
import teacher from '@/api/edu/teacher.js'
export default {
    data() { //定義變量
        return {
            list:null,
            page:1,
            limit:10,
            total:0,
            teacherQuery:{}    
        }
    },
    created() {//調用方法
        this.getList()
    },
    methods: {//定義方法
        //講師列表的方法
        getList(page =1){
            this.page = page
            teacher.getTeacherListPage(this.page,this.limit,this.teacherQuery)
                .then(response => {//請求成功
                    //response接收返回的數據
                    this.list = response.data.rows
                    this.total = response.data.total
                    console.log(this.list)
                    console.log(this.total)
                })
                .catch(error => {//請求失敗
                    console.log(error)
                })
        },
        resetData() {
            this.teacherQuery = {}
            this.getList()
        },
        removeDataById(id) {
            // debugger
            // console.log(memberId)
            this.$confirm('此操作將永久刪除講師記錄, 是否繼續?', '提示', {
                confirmButtonText: '確定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                teacher.deleteTeacherId(id)
                    .then(response =>{
                    this.$message({
                        type: 'success',
                        message: '刪除成功!'
                    });
                    this.getList()
                })
            })
        }
    }
}
</script>

4、teacher.js

import request from '@/utils/request'

export default{
    //1 講師列表(條件查詢分頁)
    getTeacherListPage(current,limit,teacherQuery) {
        return request({
          url: `/eduservice/teacher/pageTeacherCondition/${current}/${limit}`,
          method: 'post',
          //後端用RequestBody,JSON傳遞
          //data表示把對象轉換成json進行傳遞到接口裏面
          data: teacherQuery
        })
    },
    deleteTeacherId(id){
        return request({
            url: `/eduservice/teacher/${id}`,
            method: 'delete'
          })
    },
    addTeacher(teacher){
        return request({
            url: `/eduservice/teacher/addTeacher`,
            method: 'post',
            data: teacher
        })
    },
    getTeacherInfo(id){
        return request({
            url: `/eduservice/teacher/getTeacher/${id}`,
            method: 'get'
        })
    },
    updateTeacherInfo(teacher){
        return request({
            url: '/eduservice/teacher/updateTeacher',
            method: 'post',
            data: teacher
        })
    }
}

以上爲SpringBoot + Vue實現CRUD的主要代碼!

5、頁面效果展示

 

上一篇:上雲就上阿里雲,提升CRUD程序媛B格必備技能!

下一篇:超詳細的springBoot學習筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章