尚硅谷在线教育五:尚硅谷在线教育讲师管理开发

文章目录


由于项目参考的是尚硅谷在线教育项目,前端代码我直接copy的所以没有写,后端代码大部分都是配置

1. 前端登录功能的地址改变

1.1. 修改配置文件的请求地址

在config文件夹里面有dev.env.js,修改BASE_API

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

module.exports = merge(prodEnv, {
 NODE_ENV: '"development"',
 // BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',
 BASE_API: '"http://localhost:8001"',
})

2.2登录调用的两个方法

login登录操作方法和登录之后获取用户信息的方法,所有创建两个接口的方法实现登录
在这里插入图片描述
根据前端的请求可以得出
(1)login 返回token值
(2)info 返回roles name avatar

2.2.1编写login和info接口

package com.blb.eduservice.controller;

import com.blb.common_utils.result.Res;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@RestController
@Api(tags = "登录模块")
@RequestMapping("/eduservice/user")
public class EduLoginController {

    //登录
    @PostMapping("/login")
    @ApiOperation("登录")
    public Res login()
    {
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("token","admin");
        return Res.ok().data(hashMap);
    }

    @PostMapping("/info")
    @ApiOperation("获取用户信息")
    public Res  info()
    {
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("roles","admin");
        hashMap.put("name","admin");
        hashMap.put("avatar","https://pics0.baidu.com/feed/d8f9d72a6059252d08b2669f1919f9335ab5b90b.jpeg?token=94ad5f47db03f206e334510ba906055a");
        return Res.ok().data(hashMap);
    }
}

3 最终测试以及出现的问题

在这里插入图片描述

3.1跨域问题

通过一个地址去访问另一个地址, 这个过程中如果三个地方有任何一个地方不一样

  1. 访问协议 http https
  2. ip地址
  3. 端口号

3.2 跨域解决方式

  1. 在Controller上加注解@CrossOrigin
  2. 使用网关(后面笔记建)
    跨域解决1(加注解@CrossOrigin)
package com.blb.eduservice.controller;

import com.blb.common_utils.result.Res;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

@RestController
@Api(tags = "登录模块")
@RequestMapping("/eduservice/user")
@CrossOrigin
public class EduLoginController {

    //登录
    @PostMapping("/login")
    @ApiOperation("登录")
    public Res login()
    {
        System.out.println("111");
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("token","admin");
        return Res.ok().data(hashMap);
    }

    @GetMapping("/info")
    @ApiOperation("获取用户信息")
    public Res  info()
    {
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("roles","admin");
        hashMap.put("name","admin");
        hashMap.put("avatar","https://pics0.baidu.com/feed/d8f9d72a6059252d08b2669f1919f9335ab5b90b.jpeg?token=94ad5f47db03f206e334510ba906055a");
        return Res.ok().data(hashMap);
    }
}

4.框架的使用过程

4.1添加路由

在这里插入图片描述
在router.js中添加路由
2. 点击路由显示的路由对应页面的内容
在这里插入图片描述
3. 在views创建Vue页面
在这里插入图片描述
4. 在api文件夹创建js文件,定义接口地址和参数
在这里插入图片描述
5. 在创建的vue页面引入js文件,调用方法实现功能
data、created、methods

5.讲师列表前端实现

5.1. 添加路由

在router目录下的index.js中添加路由信息
在这里插入图片描述


```{
    path: '/teacher',
    component: Layout,
    redirect: '/teacher/table',
    name: '讲师管理',
    meta: { title: '讲师管理', icon: 'example' },
    children: [
      {
        path: 'table',
        name: '讲师列表',
        component: () => import('@/views/table/index'),
        meta: { title: '讲师列表', icon: 'table' }
      },
      {
        path: 'save',
        name: '添加讲师',
        component: () => import('@/views/tree/index'),
        meta: { title: '添加讲师', icon: 'tree' }
      }
    ]
  },

5.2. 创建页面对应的路由

在这里插入图片描述
有路由信息可知,讲师管理的地址的views/deu/teacher/list文件而添加讲师在views/deu/teacher/list(.vue可以省略不写),创建响应的文件
在这里插入图片描述

5.3在api文件夹创建teacher.js,定义访问接口路径

在这里插入图片描述

import request from '@/utils/request'

export default{
    //讲师列表,分页查询(条件分页查询)
getTeacherPageList(current,limit,teacherQuery)
{
  return request({
      url:`/eduservice/teacher/pageTeacherCondition/${current}/${limit}`,
      method:post,
      data:teacherQuery
  })
}
}

5.4 在讲师列表页面list.vue页面调用定义的接口方法,得到返回的数据

<script>
// 引入调用teacher.js的文件
import teacher from '@/api/edu/teacher'
import request from '@/utils/request'
export default {
    //写核心代码的位置
    
    data() {//定义变量以及初始值
          return{
          list:null,
          page:1,
          limit:10,
          total:0,
          teacherQuery:{}
          }

    },
    created() {//页面渲染之前执行,一般调用methods定义的方法
          this.getTeacherList()
    },
    methods:{//创建具体的方法。调用teacher.js定义的方法
            //讲师列表
            getTeacherList(){
                teacher.getTeacherPageList(this.page,this.limit,this.teacherQuery)
                .then(respons=>{//请求失败
                console.log("请求成功")
                console.log(respons)
                this.teacherQuery=respons.data.record
                this.total=respons.data.total
                console.log(this.teacherQuery)
                console.log(this.total)
                })
                .catch(err=>{//请求成功
                console.log("请求失败")
                    console.log(err)
                })
            }

     }
 }
</script>

5.5请求接口获取数据在页面进行显示

list.vue

<template>
    <div class="app-container">
        讲师列表
         <!-- 表格 -->
    <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>
    </div>
</template>
<script>
// 引入调用teacher.js的文件
import teacher from '@/api/edu/teacher'
import request from '@/utils/request'
export default {
    //写核心代码的位置
    
    data() {//定义变量以及初始值
          return{
          list:null,
          page:1,
          limit:10,
          total:0,
          teacherQuery:{}
          }

    },
    created() {//页面渲染之前执行,一般调用methods定义的方法
          this.getTeacherList()
    },
    methods:{//创建具体的方法。调用teacher.js定义的方法
            //讲师列表
            getTeacherList(){
                teacher.getTeacherPageList(this.page,this.limit,this.teacherQuery)
                .then(respons=>{//请求失败
                console.log("请求成功")
                console.log(respons)
                this.list=respons.data.record
                this.total=respons.data.total
                console.log(this.list)
                console.log(this.total)
                })
                .catch(err=>{//请求成功
                console.log("请求失败")
                    console.log(err)
                })
            }

     }
 }
</script>

5.6添加分页查询

list.vue
在上面的基础上添加分页操作
在这里插入图片描述

  <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="getTeacherList"
    />

修改getTeacherList函数

  getTeacherList(page = 1){
  //page默认是1,如果查询其他页,会自动边参数,
                this.page=page
                teacher.getTeacherPageList(this.page,this.limit,this.teacherQuery)
                .then(respons=>{//请求失败
                console.log("请求成功")
                console.log(respons)
                this.list=respons.data.record
                this.total=respons.data.total
                console.log(this.list)
                console.log(this.total)
                })
                .catch(err=>{//请求成功
                console.log("请求失败")
                    console.log(err)
                })
            }

我将data中的limit改成了5,也就是没有显示5条数据,结果如下
在这里插入图片描述

5.7条件查询

使用element-ui组件来实现条件查询,在列表上添加条件输入表单,使用v-model数据绑定
data中的teacherQuery 会通过v-module会将属性自动传过来
在list,vue中

 <!--查询表单-->
    <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="getTeacherList()">查询</el-button>
      <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>

5.8 清空功能

实现:1. 清空表单输入的条件数据 2. 查询所有数据
根据清空请求的函数,创建函数,即:resetData()函数
list.vue中

 resetData(){//清空方法  1.查询所有数据 2.清空表单数据
            //清空表单数据
            this.teacherQuery={}
            //查询所有数据
            this.getTeacherList()
            }

5.9讲师删除功能

  1. 在每条记录后面添加删除按钮
  2. 绑定按钮对应的事件
  3. 在绑定事件的方法传递删除讲师的id值
    在这里插入图片描述
  4. 在api文件夹teacher.js定义删除接口的地址
//删除讲师
deleteTeacherById(id)
{
  return request({
    url:`/eduservice/teacher/deleteTeacherById/${id}`,
    method:'delete'
  })
}
  1. list.vue页面调用方法实现删除
  removeDataById(id)
            {
                this.$confirm('此操作将永久删除该g该讲师的记录, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                    })
                    .then(() => { //点击确定执行then方法
                        //调用删除方法
                        teacher.deleteTeacherById(id)
                        .then(response=>{  //删除成功
                        this.$message({
                        type: 'success',
                        message: '删除成功!'
                    });
                        })
                        //回到列表页面
                         this.getTeacherList()
                    })

            }

5.10 添加讲师

需求:点击添加按钮,进入表单页面,输入讲师信息,在表单页面点击保存,提交接口,添加数据到数据库,调转到讲师列表

5.10.1写添加讲师页面

save.vue

 <div class="app-container">
      <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>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="saveTeacher">保存</el-button>
      </el-form-item>
    </el-form>
    </div>

5.10.2api定义接口地址

teacher.js中

//添加讲师
addTeacher(teacher){
  return request({
    url:`/eduservice/teacher/addTeacher`,
    method:'post',
    data:teacher
})    
}

5.10.3在save.vue页面中实现调用

重点:路由跳转

<script>
import teacherApi from '@/api/edu/teacher'
export default {
    data(){
        return {
            teacher: {},
            saveBtnDisabled:false
        }
    },
    created(){

    },
    methods: {
              saveOrUpdate()
              {

              },
              saveTeacher()
              {
                    teacherApi.addTeacher(this.teacher)
                    .then(response=>{
                        //提示信息
                         this.$message({
                        type: 'success',
                        message: '添加成功!'
                    });
                    //回到列表页面 路由跳转
                    this.$router.push({path: '/teacher/table'})
                    })
              }
    }
    
}
</script>

5.11讲师修改

5.11.1. 每条记录后面添加修改按钮

前端已写了,可以不用写了

5.11.2. 点击修改按钮,进入表单页面,进行数据回显

根据id查询数据显示

5.11.3. 通过路由跳转进入数据回显页面,在路由index页面添加路由

在/teacher下面

  {
        path: 'edit/:id',  // :/id相当于占位符  
        name: 'EduTeacherEdit',
        component: ()=>import('@/views/edu/teacher/save'),
        meta: { title: '编辑讲师', noCache: true},
        hidden: true  //路由不显示即隐藏起来
      }

在这里插入图片描述

路由链接修改
在list.vue中修改路由地址
在这里插入图片描述

5.11.4. 在表单页面中实现数据回显

  1. 在teacher.js定义根据id查询的接口
//通过id得到讲师的信息
getTeacherById(id){
  return request({
    url:`/eduservice/teacher/getTeacher/${id}`,
    method:'get'
}) 
}
  1. 在save.vue中调用接口实现数据的回
//根据讲师id查询信息
                getInfo(id)
                {
                    teacherApi.getTeacherById(id)
                    .then(response => {
                        this.teacher=response.data.eduTeacher
                    })
                }
  1. 调用根据id查询的方法
    因为添加和修改都使用了save页面,需要区分添加还是修改,只有修改是查询数据回显
    方法:判断路径的方法里面是否有讲师的id值,如果有id值修改,没有id值直接添加
   created(){//渲染页面之前执行
        if(this.$route.params && this.$route.params.id)
        {
          const id=this.$route.params.id;
          console.log(id)
          this.getInfo(id)
        }
    },

在这里插入图片描述

5.11.5最终实现修改

  1. 在api下面的teacher.js中定义修改接口
//修改讲师信息
updateTeacher(teacher){
  return request({
    url:`/eduservice/teacher/updateEduTeacher`,
    method:'post',
    data:teacher
  })

}
  1. 想页面中调用修改的方法
     //修改讲师信息
                updateTeacher(){
                  teacherApi.updateTeacher(this.teacher)
                  .then(response => {//提示信息
                     this.$message({
                        type: 'success',
                        message: '修改成功!'
                    });
                    //页面跳转
                    this.$router.push({path: '/teacher/table'})
                  })
                }
saveOrUpdate()
              {
                //判断是否有id 有id修改  没有id添加
                    if(this.teacher.id)
                    {
                      this.updateTeacher()
                    }
                    else
                    {
                      this.saveTeacher()
                    }
              }

6. 遇到的问题

第一次点击修改进行了数据回显,但第二次点击添加讲师,进入表单页面,但是表单页面显示修改时回显的数据,正确效果应该是将表单数据清空
解决方式
在这里插入图片描述
问题没有解决的分析
多次路由跳转到同一个页面,在页面中created方法只会执行一次,后面在执行中不会进行跳转
最终方案:使用vue监听器

   watch:{ //监听
      $route(to,from)   //路由变化方式,路由发生变化方法会执行
      {
            this.init()
      }
    },
  methods: {
 init(){
                  if(this.$route.params && this.$route.params.id)
                  {
                    const id=this.$route.params.id;
                    // this.getInfo(id)
                  }
                  else
                  {
                        this.teacher={}
                  }
              },
              }

7. 代码总汇

index.js

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

// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading

Vue.use(Router)

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

/**
* hidden: true                   if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true               if set true, will always show the root menu, whatever its child routes length
*                                if not set alwaysShow, only more than one route under the children
*                                it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect           if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name'             the name is used by <keep-alive> (must set!!!)
* meta : {
    title: 'title'               the name show in submenu and breadcrumb (recommend set)
    icon: 'svg-name'             the icon show in the sidebar,
  }
**/
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',
    hidden: true,
    children: [{
      path: 'dashboard',
      component: () => import('@/views/dashboard/index')
    }]
  },
  {
    path: '/teacher',
    component: Layout,
    redirect: '/teacher/table',
    name: '讲师管理',
    meta: { title: '讲师管理', icon: 'example' },
    children: [
      {
        path: 'table',
        name: '讲师列表',
        component: () => import('@/views/edu/teacher/list'),
        meta: { title: '讲师列表', icon: 'table' }
      },
      {
        path: 'save',
        name: '添加讲师',
        component: () => import('@/views/edu/teacher/save'),
        meta: { title: '添加讲师', icon: 'tree' }
      },
      {
        path: 'edit/:id',  // :/id相当于占位符  
        name: 'EduTeacherEdit',
        component: ()=>import('@/views/edu/teacher/save'),
        meta: { title: '编辑讲师', noCache: true},
        hidden: true  //路由不显示即隐藏起来
      }
    ]
  },
  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: 'Example', icon: 'example' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table', icon: 'table' }
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree', icon: 'tree' }
      }
    ]
  },



  {
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: 'Form', icon: 'form' }
      }
    ]
  },

  {
    path: '/nested',
    component: Layout,
    redirect: '/nested/menu1',
    name: 'Nested',
    meta: {
      title: 'Nested',
      icon: 'nested'
    },
    children: [
      {
        path: 'menu1',
        component: () => import('@/views/nested/menu1/index'), // Parent router-view
        name: 'Menu1',
        meta: { title: 'Menu1' },
        children: [
          {
            path: 'menu1-1',
            component: () => import('@/views/nested/menu1/menu1-1'),
            name: 'Menu1-1',
            meta: { title: 'Menu1-1' }
          },
          {
            path: 'menu1-2',
            component: () => import('@/views/nested/menu1/menu1-2'),
            name: 'Menu1-2',
            meta: { title: 'Menu1-2' },
            children: [
              {
                path: 'menu1-2-1',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
                name: 'Menu1-2-1',
                meta: { title: 'Menu1-2-1' }
              },
              {
                path: 'menu1-2-2',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
                name: 'Menu1-2-2',
                meta: { title: 'Menu1-2-2' }
              }
            ]
          },
          {
            path: 'menu1-3',
            component: () => import('@/views/nested/menu1/menu1-3'),
            name: 'Menu1-3',
            meta: { title: 'Menu1-3' }
          }
        ]
      },
      {
        path: 'menu2',
        component: () => import('@/views/nested/menu2/index'),
        meta: { title: 'menu2' }
      }
    ]
  },

  {
    path: 'external-link',
    component: Layout,
    children: [
      {
        path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
        meta: { title: 'External Link', icon: 'link' }
      }
    ]
  },

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

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

teacher.js

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

// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading

Vue.use(Router)

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

/**
* hidden: true                   if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true               if set true, will always show the root menu, whatever its child routes length
*                                if not set alwaysShow, only more than one route under the children
*                                it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect           if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name'             the name is used by <keep-alive> (must set!!!)
* meta : {
    title: 'title'               the name show in submenu and breadcrumb (recommend set)
    icon: 'svg-name'             the icon show in the sidebar,
  }
**/
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',
    hidden: true,
    children: [{
      path: 'dashboard',
      component: () => import('@/views/dashboard/index')
    }]
  },
  {
    path: '/teacher',
    component: Layout,
    redirect: '/teacher/table',
    name: '讲师管理',
    meta: { title: '讲师管理', icon: 'example' },
    children: [
      {
        path: 'table',
        name: '讲师列表',
        component: () => import('@/views/edu/teacher/list'),
        meta: { title: '讲师列表', icon: 'table' }
      },
      {
        path: 'save',
        name: '添加讲师',
        component: () => import('@/views/edu/teacher/save'),
        meta: { title: '添加讲师', icon: 'tree' }
      },
      {
        path: 'edit/:id',  // :/id相当于占位符  
        name: 'EduTeacherEdit',
        component: ()=>import('@/views/edu/teacher/save'),
        meta: { title: '编辑讲师', noCache: true},
        hidden: true  //路由不显示即隐藏起来
      }
    ]
  },
  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: 'Example', icon: 'example' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table', icon: 'table' }
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree', icon: 'tree' }
      }
    ]
  },



  {
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: 'Form', icon: 'form' }
      }
    ]
  },

  {
    path: '/nested',
    component: Layout,
    redirect: '/nested/menu1',
    name: 'Nested',
    meta: {
      title: 'Nested',
      icon: 'nested'
    },
    children: [
      {
        path: 'menu1',
        component: () => import('@/views/nested/menu1/index'), // Parent router-view
        name: 'Menu1',
        meta: { title: 'Menu1' },
        children: [
          {
            path: 'menu1-1',
            component: () => import('@/views/nested/menu1/menu1-1'),
            name: 'Menu1-1',
            meta: { title: 'Menu1-1' }
          },
          {
            path: 'menu1-2',
            component: () => import('@/views/nested/menu1/menu1-2'),
            name: 'Menu1-2',
            meta: { title: 'Menu1-2' },
            children: [
              {
                path: 'menu1-2-1',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
                name: 'Menu1-2-1',
                meta: { title: 'Menu1-2-1' }
              },
              {
                path: 'menu1-2-2',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
                name: 'Menu1-2-2',
                meta: { title: 'Menu1-2-2' }
              }
            ]
          },
          {
            path: 'menu1-3',
            component: () => import('@/views/nested/menu1/menu1-3'),
            name: 'Menu1-3',
            meta: { title: 'Menu1-3' }
          }
        ]
      },
      {
        path: 'menu2',
        component: () => import('@/views/nested/menu2/index'),
        meta: { title: 'menu2' }
      }
    ]
  },

  {
    path: 'external-link',
    component: Layout,
    children: [
      {
        path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
        meta: { title: 'External Link', icon: 'link' }
      }
    ]
  },

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

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

list.vue

<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="getTeacherList()">查询</el-button>
      <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>

         <!-- 表格 -->
    <el-table
     :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="'/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="getTeacherList"
    />
    
    </div>
</template>
<script>
// 引入调用teacher.js的文件
import teacher from '@/api/edu/teacher'
import request from '@/utils/request'
export default {
    //写核心代码的位置
    
    data() {//定义变量以及初始值
          return{
          list:null,
          page:1,
          limit:5,
          total:0,
          teacherQuery:{} //通过v-module会将属性自动传过来
        
          }

    },
    created() {//页面渲染之前执行,一般调用methods定义的方法
          this.getTeacherList()
    },
    methods:{//创建具体的方法。调用teacher.js定义的方法
            //讲师列表
            getTeacherList(page = 1){
                this.page=page
                teacher.getTeacherPageList(this.page,this.limit,this.teacherQuery)
                .then(respons=>{//请求失败
                console.log("请求成功")
                console.log(respons)
                this.list=respons.data.record
                this.total=respons.data.total
                console.log(this.list)
                console.log(this.total)
                })
                .catch(err=>{//请求成功
                console.log("请求失败")
                    console.log(err)
                })
            },
            resetData(){//清空方法  1.查询所有数据 2.清空表单数据
            //清空表单数据
            this.teacherQuery={}
            //查询所有数据
            this.getTeacherList()
            },
            removeDataById(id)
            {
                this.$confirm('此操作将永久删除该g该讲师的记录, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                    })
                    .then(() => { //点击确定执行then方法
                        //调用删除方法
                        teacher.deleteTeacherById(id)
                        .then(response=>{  //删除成功
                        this.$message({
                        type: 'success',
                        message: '删除成功!'
                    });
                     this.getTeacherList()
                        })
                        //回到列表页面
                         this.getTeacherList()
                    })

            }



     }
 }
</script>

save.vue

<template>
    <div class="app-container">
      <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>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存</el-button>
      </el-form-item>
    </el-form>
    </div>
</template>
<script>
import teacherApi from '@/api/edu/teacher'
export default {
    data(){
        return {
            teacher: {},
            saveBtnDisabled:false
        }
    },
    created(){//渲染页面之前执行
      this.init()
    },
    watch:{ //监听
      $route(to,from)   //路由变化方式,路由发生变化方法会执行
      {
            this.init()
      }
    },
    methods: {
              init(){
                  if(this.$route.params && this.$route.params.id)
                  {
                    const id=this.$route.params.id;
                    // this.getInfo(id)
                  }
                  else
                  {
                        this.teacher={}
                  }
              },
              saveOrUpdate()
              {
                //判断是否有id 有id修改  没有id添加
                    if(this.teacher.id)
                    {
                      this.updateTeacher()
                    }
                    else
                    {
                      this.saveTeacher()
                    }
              },
              saveTeacher()
              {
                    teacherApi.addTeacher(this.teacher)
                    .then(response=>{
                        //提示信息
                         this.$message({
                        type: 'success',
                        message: '添加成功!'
                    });
                    //回到列表页面 路由跳转
                    this.$router.push({path: '/teacher/table'})
                    })
              },
              //根据讲师id查询信息
                getInfo(id)
                {
                  console.log(id)
                    teacherApi.getTeacherById(id)
                    .then(response => {

                        this.teacher=response.data.eduTeacher
                    })
                },
                //修改讲师信息
                updateTeacher(){
                  teacherApi.updateTeacher(this.teacher)
                  .then(response => {//提示信息
                     this.$message({
                        type: 'success',
                        message: '修改成功!'
                    });
                    //页面跳转
                    this.$router.push({path: '/teacher/table'})
                  })
                }
    }
    
}
</script>

第六天内容

1. 对象存储OSS

为了解决海量数据存储与弹性扩容,项目中我们采用云存储的解决方案- 阿里云OSS。

1.1开通阿里云账号

1.2找到对象存储OSS

在这里插入图片描述

1.3 开通云对象存储OSS

在这里插入图片描述

1.4开通成功后进入管理控制台

在这里插入图片描述

1.4创建一个bucket

在这里插入图片描述
在这里插入图片描述

1.5进入文件管理,上传文件

在这里插入图片描述

2.使用java代码操作阿里云oss上传文件到阿里云oss操作

2.1 准备工作

创建操作阿里云oss许可证(阿里颁发的id和密钥)

  1. 点击头像,选择AccessKey管理
    在这里插入图片描述
  2. 点击创建AccessKey
    在这里插入图片描述

2.2如何找到官方文档

  1. 找到对象存储OSS新手学习路径
    在这里插入图片描述
  2. 在oss学习路径中找到API和SDK选择JavaSDK
    在这里插入图片描述
    打开后查看操作文档
    在这里插入图片描述

2.3后端集成oss

2.3.1在service模块下创建一个service_oss子模块

在这里插入图片描述

2.3.2引入相关的依赖

ervice-oss上级模块service已经引入service的公共依赖,所以service-oss模块只需引入阿里云oss相关依赖即可,
service父模块已经引入了service-base模块,所以Swagger相关默认已经引入

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 阿里云oss依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </dependency>
        <!-- 日期工具栏依赖 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
    </dependencies>

2.3.3配置application.properties

#服务端口
server.port=8002
#服务名
spring.application.name=service-oss
#环境设置:dev、test、prod
spring.profiles.active=dev

#阿里云 OSS
#不同的服务器,地址不同

aliyun.oss.file.endpoint=your endpoint
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制台创建,也可以使用java代码创建
aliyun.oss.file.bucketname=guli-file

2.3.4启动启动类报错

在这里插入图片描述
原因:启动的时候找数据库的配置,但这个模块不需要操作数据库,知识做到上传到oss功能,没有配置数据库
解决方式

  1. 追加上数据库配置
  2. 在启动类添加属性,默认不去加载数据库配置
    方式2解决问题(启动类上面不加载数据库)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ServiceOssApplication {

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

}

2.4 实现文件上传

2.4.1创建一个常用类用于读取配置文件

//当项目启动后,spring接口 spring加载之后 执行接口一个方法
@Component
public class PropertiesUtils implements InitializingBean {
    //读取配置文件中的内容

    @Value("${aliyun.oss.file.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.file.keyid}")
    private String keyId;

    @Value("${aliyun.oss.file.keysecret}")
    private String keySecret;

    @Value("${aliyun.oss.file.bucketname}")
    private String bucketName;

    //定义公开静态常量
    public static String END_POINT;
    public static String KEY_ID;
    public static String KEY_SECRET;
    public static String BUCKET_NAME;
    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT=endpoint;
        KEY_ID=keyId;
        KEY_SECRET=keySecret;
        BUCKET_NAME=bucketName;
    }
}

2.4.2创建controller

@RestController
@RequestMapping("/eduoss/fileoss")
@Api(tags = "上传头像")
@CrossOrigin
public class OssController {

    @Autowired
    private OssService ossService;

    //上传头像
    @PostMapping()
    @ApiOperation("上传讲师头像")
    public Res uploadOssFile(MultipartFile file)
    {
        //上传文件 MultipartFile
        String url= ossService.uploadFileAvatar(file);
        //返回上传路径
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("url",url);
        return Res.ok().data(hashMap);
    }
}

2.4.3创建service

@Service
public class OssServiceImpl implements OssService {
    @Override
    public String uploadFileAvatar(MultipartFile file) {
        // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
        String endpoint = PropertiesUtils.END_POINT;
        String accessKeyId = PropertiesUtils.KEY_ID;
        String accessKeySecret = PropertiesUtils.KEY_SECRET;
        String bucketName = PropertiesUtils.BUCKET_NAME;

        InputStream inputStream = null;
        try {

                // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

                   // 获取上传文件的输入流
            inputStream = file.getInputStream();
            //获取前文件名称
            String filename = file.getOriginalFilename();

            //在文件名称里面生成一个唯一的值
            String uuid = UUID.randomUUID().toString().replace("-", ".");
            //生成不同的文件名,避免导致最后一次上传打之前上传的文件覆盖
            filename=uuid+filename;
            //把文件进行日期分类
            //获取当前日期
            String datePath = new DateTime().toString("yyyy/MM/dd");
            filename=datePath+'/'+filename;
            //调用oss方法实现上传
            /**
             * 参数一:Bucket名称
             * 参数二:上传到oss的文件路径和文件名
             * 参数三:上传文件输入流
             */
            ossClient.putObject(bucketName,filename,inputStream);
            // 关闭OSSClient。
            ossClient.shutdown();

            //上传文件后返回文件路径
            //需要将上传到阿里云的oss路径手动拼接处理
            String url="https://"+bucketName+"."+endpoint+"/"+filename;
            return url;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

3. Nginx

Nginx是一个反向代理服务器具有请求转发、负载均衡、动静分离的作用
特点:使用cmd命令启动Nginx,如果关闭cmd窗口,Nginx不会停止,需要使用nginx.exe -s stop

3.1 配置nginx实现请求转发功能

3.1.1找到nginx.conf文件

在这里插入图片描述

3.1.2修改nginx.conf文件

  1. 将80端口改为81
    在这里插入图片描述

  2. 配置nginx转发规则
    在这里插入图片描述

  3. 修改前端请求地址为nginx地址
    在dev.env.js中
    在这里插入图片描述

  4. 重新启动nginx(先停止在启动)
    停止:nginx.exe -s stop
    启动:nginx.exe

4.添加讲师上传头像前端整合

4.1 在添加讲师页面,创建上传组件,实现上传

使用element-ui组件实现
在源码里面找到组件,复制到前端项目src components里面
在这里插入图片描述
在这里插入图片描述

4.2使用组件

data定义变量好初始化值

   data(){
        return {
            teacher: {},
            imagecropperShow:false,//上传弹框组件是否显示
            imagecropperKey:0,//上传组件key值
            BASE_API:process.env.BASE_API, //dev.env.jd里面的地址
            saveBtnDisabled:false //报存按钮是否禁用
        }
    },

4.3引入组件和声明组件

import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'
export default {
  components: { ImageCropper, PanThumb },

4.4修改上传地址

在这里插入图片描述在上面的基础上save.vue进行了修改。最终代码

<template>
    <div class="app-container">
      <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:上传成功后的回调 -->
    <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'
import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'
export default {
  components: { ImageCropper, PanThumb },
    data(){
        return {
            teacher: {},
            imagecropperShow:false,//上传弹框组件是否显示
            imagecropperKey:0,//上传组件key值
            BASE_API:process.env.BASE_API, //dev.env.jd里面的地址
            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
                   //上传接口返回图片地址
                   console.log(data.url)
                   this.teacher.avatar=data.url
                   this.imagecropperKey=this.imagecropperKey+1
              },
              init(){
                  if(this.$route.params && this.$route.params.id)
                  {
                    const id=this.$route.params.id;
                    this.getInfo(id)
                  }
                  else
                  {
                        this.teacher={}
                  }
              },
              saveOrUpdate()
              {
                //判断是否有id 有id修改  没有id添加
                    if(this.teacher.id)
                    {
                      this.updateTeacher()
                    }
                    else
                    {
                      this.saveTeacher()
                    }
              },
              saveTeacher()
              {
                    teacherApi.addTeacher(this.teacher)
                    .then(response=>{
                        //提示信息
                         this.$message({
                        type: 'success',
                        message: '添加成功!'
                    });
                    //回到列表页面 路由跳转
                    this.$router.push({path: '/teacher/table'})
                    })
              },
              //根据讲师id查询信息
                getInfo(id)
                {
                  console.log(id)
                    teacherApi.getTeacherById(id)
                    .then(response => {

                        this.teacher=response.data.eduTeacher
                    })
                },
                //修改讲师信息
                updateTeacher(){
                  teacherApi.updateTeacher(this.teacher)
                  .then(response => {//提示信息
                     this.$message({
                        type: 'success',
                        message: '修改成功!'
                    });
                    //页面跳转
                    this.$router.push({path: '/teacher/table'})
                  })
                }
    }
    
}
</script>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值