天天看點

前後端聯調執行個體-講師管理

之前學習了一下前後端聯調的一般步驟和Nginx的簡單配置,現在以講師管理功能為例來實戰一下。

項目環境

後端:SpringBoot + MyBatisPlus +MySQL+Nginx

前端:vue-cli + axios

後端

1、編寫講師管理Controller,包含根據id删除講師、分頁查詢講師清單、新增講師等請求接口。

@Api(description = "講師管理")
@RestController
@RequestMapping("/admin/edu/teacher")
@CrossOrigin   //跨域支援
public class TeacherAdminController {
  
  @Autowired
  private TeacherService teacherService;

  @ApiOperation(value = "根據id删除講師")
  @DeleteMapping("{id}")
  public R removeById(@ApiParam(name = "id",value="講師id",required = true)
                                    @PathVariable String id){
    teacherService.removeById(id);
    return R.ok();
  }
  
  @ApiOperation(value = "分頁查詢講師清單")
  @GetMapping("{page}/{limit}")
  public R pageList(@ApiParam(name = "page",value = "目前頁碼",required = true)
                               @PathVariable Long page,
                               @ApiParam(name = "limit",value = "每頁記錄數",required = true)
                               @PathVariable Long limit,
                               @ApiParam(name = "teacherQuery",value = "查詢對象",required = true)
                               TeacherQuery teacherQuery){
    if(page <=0 || limit <=0){  
      //抛出統一異常,好處:所有開發人員協同時可保證錯誤結構一緻
      throw new CodingException(ResultCodeEnum.PARAM_ERROR);
    }
    Page<Teacher> pageParam = new Page<>(page,limit);
    //調用自己擴充的查詢方法
    teacherService.pageQuery(pageParam,teacherQuery);
    //結果封裝
    List<Teacher> records = pageParam.getRecords();
    long total = pageParam.getTotal();
    return R.ok().data("total",total).data("rows",records);
  }
  
  @ApiOperation(value = "新增講師")
  @PostMapping
  public R save(@ApiParam(name = "teacher",value = "講師對象",required = true)
                        @RequestBody Teacher teacher){
    teacherService.save(teacher);
    return R.ok();
  }
}
           

2、編寫講師服務接口,并繼承IService接口。

public interface TeacherService extends IService<Teacher> {
    void pageQuery(Page<Teacher> pageParam,TeacherQuery teacherQuery);
}
           

3、編寫講師服務接口實作類,并繼承ServiceImpl類。

@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper,Teacher> implements TeacherService {
    
    @Override
    public void pageQuery(Page<Teacher> pageParam,TeacherQuery teacherQuery){
        //MP 讓我們可以通過面向對象的方式編寫SQL
        QueryWrapper<Teacher> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("sort");
        if(teacherQuery == null){
            baseMapper.selectPage(pageParam,queryWrapper);
            return;
        }
        String name = teacherQuery.getName();
        String level = teacherQuery.getLevel();
        String begin = teacherQuery.getBegin();
        String end = teacherQuery.getEnd();
        if(!StringUtils.isEmpty(name)){
            queryWrapper.like("name",name);//%name%
        }
        if(!StringUtils.isEmpty(level)){
            queryWrapper.eq("level",level);// =level
        }
        if(!StringUtils.isEmpty(begin)){
            queryWrapper.ge("gmt_create",begin); // >begin
        }
        if(!StringUtils.isEmpty(end)){
            queryWrapper.le("gmt_create",end); // <end
        }
        baseMapper.selectPage(pageParam,queryWrapper);
    }
}
           

4、編寫講師Mapper接口,并繼承BaseMapper接口,無需定義方法。

public interface TeacherMapper extends BaseMapper<Teacher> {}
           

5、本例中背景有多個端口,使用Nginx來分發前端請求,前端請求直接通路8210端口即可,Nginx配置如下。

server{
  listen 8210;   #監聽端口(預設監聽接口為80)
  server_name localhost; #服務名
  location ~ /edu/{  #請求路徑含 /edu/的請求轉發到8110端口
    proxy_pass http://localhost:8110;
  }
  location ~ /admin/sysuser{ #請求路徑含 /admin/sysuser的請求轉發到8210端口
    proxy_pass http://localhost:8210;
  }
}
           

前端

1、前端使用axios架構來通路後端接口擷取資料,axios請求調用函數封裝在

src/utils/request.js

中,請求後端接口的根路徑

BASE_API

定義在

config/dev.env.js

中,根據不同運作環境,env.js的字首有所不同,如

prod.env.js

(生産環境),這裡則使用的是開發環境。

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv,{
    NODE_ENV:'"development"',
    BASE_API:'"http://localhost:8210"'
})
           

2、在

src/api

目錄下添加

teacher.js

,封裝後端請求的api操作,一個Controller對應一個api操作。

import request from '@utils/request'

const api_name = "/admin/edu/teacher"
export default {
   	//分頁查詢講師清單
    getPageList(page,limit,searchObj){
        return request({
            url:`${api_name}/${page}/${limit}`,
            method:'get',
            params:searchObj
        })
    },
    //删除指定id講師
    removeById(id) {
        return request({
            url:`${api_name}/${id}`,
            method:'delete',
        })
    },
    //新增講師
    save(teacher) {
        return request({
            url:`${api_name}`,
            method:'post',
            data:teacher
        })
    }
}
           

3、在

src/views/edu/teacher

目錄下添加新增講師頁

form.vue

,編寫頁面腳本。

<template>
	<div>
        <! -- 省略html代碼 -->
    </div>
</template>

<script>
import teacher from '@/api/edu/teacher'
//綁定表單資料
const defaultForm = {
	name:'',
    sort:1,
    level:2,
    career:'',
    intro:'',
    avatar:''    
}
export default {
    data(){
        return {
      		teacher:defaultForm,
            saveBtnDisabled:false  //儲存按鈕是否禁用
        }
    },
    
    methods:{
        //watch監視 當路由發生變化時,執行此方法
        watch:{
            $router(to,from){
                console.log("watch $router")
                this.init();
            }
        },
        //鈎子函數 此方法在頁面渲染前調用,如果頁面是同一個,資料不會重新整理
        created(){
            console.log("created")
            this.init()
        }
        init(){
            if(this.$router.params && this.$router.params.id){//如果請求中含有id參數,則需要從背景查詢資料填充表單
                const id = this.$router.params.id
                this.fetchDataById(id)
            }else{
                this.teacher = {...defaultForm} //對象拷貝
            }
        },
        save(){
            this.saveBtnDisabled = true;
            this.saveData();
        },
        saveData(){ 
            teacher.save(this.teacher).then(response =>{
                return this.$message({
                    type:'success',
                    message:'儲存成功!'
                }).then(response =>{
                    this.$router.push({path:'/edu/teacher'}) //路由跳轉
                })
            })
        },
        //背景資料查詢
        fetchDataById(id){
            teacher.getById(id).then(response =>{
                console.log(response)
                this.teacher = response.data.item
            })
        }
    }
}
</script>
           

4、在

src/views/edu/teacher

目錄下添加講師清單頁

list.vue

,編寫頁面腳本。

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

      <el-form-item>
        <el-select v-model="searchObj.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="searchObj.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="searchObj.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="fetchData()">查詢</el-button>
      <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>
    <!-- 表格 -->
    <el-table
      v-loading="listLoading"
      :data="list"
      element-loading-text="資料加載中"
      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="'/edu/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="fetchData"
    />
  </div>
</template>
<script>
	import teacher from '@/api/edu/teacher'
    export default {
        data(){
            listLoading:true,// 顯示loading資訊
            list:null,// 資料清單
            total:0,// 總記錄數
            page:1,// 頁碼
            limit:10,// 每頁記錄數
            searchObj:{} // 查詢條件
        },
        created(){//當頁面加載完成還未渲染時調用
            this.fetchData();
        },
        methods:{
            //查詢講師清單
            fetchData(){
                console.log('加載清單')
                teacher.getPageList(this.page,this.limit,this.searchObj).then(response=>{
                    console.log(response)
                    if(response.success === true){
                        this.list = response.data.rows
                        this.total = response.data.total
                    }
                    this.listLoading = false //不顯示loading資訊
                })
            },
            //清空資料
            resetData(){
                this.searchObj={}
                this.fetchData();
            },
            //删除講師
            removeDataById(id){
        		this.$confirm('此操作将永久删除該檔案,是否繼續','提示',{
                    confirmButtonText:'确定',
                    cancelButtonText:'取消',
                    type:'warning'
                }).then(()=>{
                    return teacher.removeById(id);
                }).then(()=>{
                    this.fetchData();
                    this.$message({
                        type:'success',
                        message:'删除成功'
                    })
                }).catch(()=>{
                    this.$message({
                        type:'info',
                        message:'已取消删除'
                    })
                })       
    		}
        }
    }
</script>
           

5、在

src/router/index.js

中配置講師路由。

import Vue from 'vue'
import Router from 'vue-router'

import Layout from '../views/layout/Layout'

export const constantRouterMap = [{
        path: '/login',
        component: () =>
            import ('@/views/login/index'),
        hidden: true
    },
    {
        path: '/404',
        component: () =>
            import ('@/views/404'),
        hidden: true
    },
    {
        path: '/',
        component: Layout,
        redirect: '/dashboard',
        name: 'Dashboard',
        children: [{
            path: 'dashboard',
            component: () =>
                import ('@/views/dashboard/index'),
            meta: { title: '首頁', icon: 'dashboard' }

        }]
    },
    // 講師路由管理!
    {
        path: '/edu/teacher',
        component: Layout,
        redirect: '/edu/teacher/list',
        name: 'Teacher',
        meta: {
            title: '講師管理'
        },
        children: [{
            path: 'list',
            component: () =>
                import ('@/views/edu/teacher/list'),
            meta: {
                title: '講師清單'
            }
        }, 
        {
            path: 'create',
            component: () =>
                import ('@/views/edu/teacher/form'),
            meta: {
                title: '添加講師'
         },
		 {
			path:'edit/:id',
			name:'EduTeacherEdit',
			component:()=>
				import('@/views/edu/teacher/form'),
			meta:{
				title:'編輯講師',
				noCache:true
			},
			hidden:true
		}}]
    },

    { path: '*', redirect: '/404', hidden: true }
]

export default new Router({
    // mode: 'history', //後端支援可開
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRouterMap
})
           

6、在終端中輸入指令

npm run dev

啟動項目進行測試即可。

總結

1、背景接口如果部署多台伺服器或多個端口時,可以使用Nginx來對前端的請求進行分發。

2、前端業務代碼編寫順序:

env.js->api->views->router

3、當頁面路由變化,而資料需要重新整理時,可以使用watch來進行監視路由變化,一旦路由發生變化則執行指定的更新操作

4、背景接口最好進行統一異常處理,這樣做的好處是可以讓所有開發人員的錯誤結構保持一緻。