尚硅谷在线教育五:尚硅谷在线教育讲师管理开发
文章目录
- 1. 前端登录功能的地址改变
- 1.1. 修改配置文件的请求地址
- 2.2登录调用的两个方法
- 2.2.1编写login和info接口
- 3 最终测试以及出现的问题
- 3.1跨域问题
- 3.2 跨域解决方式
- 4.框架的使用过程
- 4.1添加路由
- 5.讲师列表前端实现
- 5.1. 添加路由
- 5.2. 创建页面对应的路由
- 5.3在api文件夹创建teacher.js,定义访问接口路径
- 5.4 在讲师列表页面list.vue页面调用定义的接口方法,得到返回的数据
- 5.5请求接口获取数据在页面进行显示
- 5.6添加分页查询
- 5.7条件查询
- 5.8 清空功能
- 5.9讲师删除功能
- 5.10 添加讲师
- 5.10.1写添加讲师页面
- 5.10.2api定义接口地址
- 5.10.3在save.vue页面中实现调用
- 5.11讲师修改
- 5.11.1. 每条记录后面添加修改按钮
- 5.11.2. 点击修改按钮,进入表单页面,进行数据回显
- 5.11.3. 通过路由跳转进入数据回显页面,在路由index页面添加路由
- 5.11.4. 在表单页面中实现数据回显
- 5.11.5最终实现修改
- 6. 遇到的问题
- 7. 代码总汇
- 第六天内容
- 1. 对象存储OSS
- 1.1开通阿里云账号
- 1.2找到对象存储OSS
- 1.3 开通云对象存储OSS
- 1.4开通成功后进入管理控制台
- 1.4创建一个bucket
- 1.5进入文件管理,上传文件
- 2.使用java代码操作阿里云oss上传文件到阿里云oss操作
- 2.1 准备工作
- 2.2如何找到官方文档
- 2.3后端集成oss
- 2.3.1在service模块下创建一个service_oss子模块
- 2.3.2引入相关的依赖
- 2.3.3配置application.properties
- 2.3.4启动启动类报错
- 2.4 实现文件上传
- 2.4.1创建一个常用类用于读取配置文件
- 2.4.2创建controller
- 2.4.3创建service
- 3. Nginx
- 3.1 配置nginx实现请求转发功能
- 3.1.1找到nginx.conf文件
- 3.1.2修改nginx.conf文件
- 4.添加讲师上传头像前端整合
- 4.1 在添加讲师页面,创建上传组件,实现上传
- 4.2使用组件
- 4.3引入组件和声明组件
- 4.4修改上传地址
由于项目参考的是尚硅谷在线教育项目,前端代码我直接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跨域问题
通过一个地址去访问另一个地址, 这个过程中如果三个地方有任何一个地方不一样
- 访问协议 http https
- ip地址
- 端口号
3.2 跨域解决方式
- 在Controller上加注解@CrossOrigin
- 使用网关(后面笔记建)
跨域解决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.recordthis.total=respons.data.totalconsole.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-tablev-loading="listLoading" :data="list"element-loading-text="数据加载中"borderfithighlight-current-row><el-table-columnlabel="序号"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.recordthis.total=respons.data.totalconsole.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=pageteacher.getTeacherPageList(this.page,this.limit,this.teacherQuery).then(respons=>{//请求失败console.log("请求成功")console.log(respons)this.list=respons.data.recordthis.total=respons.data.totalconsole.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-pickerv-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-pickerv-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讲师删除功能
- 在每条记录后面添加删除按钮
- 绑定按钮对应的事件
- 在绑定事件的方法传递删除讲师的id值
- 在api文件夹teacher.js定义删除接口的地址
//删除讲师
deleteTeacherById(id)
{return request({url:`/eduservice/teacher/deleteTeacherById/${id}`,method:'delete'})
}
- 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. 在表单页面中实现数据回显
- 在teacher.js定义根据id查询的接口
//通过id得到讲师的信息
getTeacherById(id){return request({url:`/eduservice/teacher/getTeacher/${id}`,method:'get'
})
}
- 在save.vue中调用接口实现数据的回
//根据讲师id查询信息getInfo(id){teacherApi.getTeacherById(id).then(response => {this.teacher=response.data.eduTeacher})}
- 调用根据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最终实现修改
- 在api下面的teacher.js中定义修改接口
//修改讲师信息
updateTeacher(teacher){return request({url:`/eduservice/teacher/updateEduTeacher`,method:'post',data:teacher})}
- 想页面中调用修改的方法
//修改讲师信息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-loadingVue.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-viewname: '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-loadingVue.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-viewname: '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-pickerv-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-pickerv-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="数据加载中"borderfithighlight-current-row><el-table-columnlabel="序号"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=pageteacher.getTeacherPageList(this.page,this.limit,this.teacherQuery).then(respons=>{//请求失败console.log("请求成功")console.log(respons)this.list=respons.data.recordthis.total=respons.data.totalconsole.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和密钥)
- 点击头像,选择AccessKey管理
- 点击创建AccessKey
2.2如何找到官方文档
- 找到对象存储OSS新手学习路径
- 在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功能,没有配置数据库
解决方式
- 追加上数据库配置
- 在启动类添加属性,默认不去加载数据库配置
方式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;@Overridepublic 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 {@Autowiredprivate OssService ossService;//上传头像@PostMapping()@ApiOperation("上传讲师头像")public Res uploadOssFile(MultipartFile file){//上传文件 MultipartFileString 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 {@Overridepublic 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文件
将80端口改为81
配置nginx转发规则
修改前端请求地址为nginx地址
在dev.env.js中
重新启动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-cropperv-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=falsethis.imagecropperKey=this.imagecropperKey+1},cropSuccess(data)//上传成功的方法{this.imagecropperShow=false//上传接口返回图片地址console.log(data.url)this.teacher.avatar=data.urlthis.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>
尚硅谷在线教育五:尚硅谷在线教育讲师管理开发相关推荐
- 教育机构如何提升在线教育技术能力? | 云+社区技术沙龙
教育机构如何提升在线教育技术能力? 如何给用户稳定.清晰流畅的在线互动体验? AI和大数据的发展会给行业带来哪些变革? 腾讯云结合自身优势,从在线教育的业务营销.教学业务应用与管理方面助力行业合伙发展 ...
- 在线教育直播系统 一对一在线直播平台解决方案
在线教育直播系统 一对一在线直播平台解决方案 在线教育直播系统功能模块 在线教育直播系统讲师端功能 在线教育直播系统学生端功能 一对一在线教育系统功能介绍 一对一在线教育系统优势 在线教育直播系统功能 ...
- 校宝在线的延迟满足:做教育产业互联网平台
8月23日,教育产业服务平台校宝在线主办的"SEE 2019教育服务共建大会"在杭州云栖小镇召开,SEE 2019首届大会主题是"新教育",来自全国各地的数千名 ...
- 教育软件开发与在线教育之间的联系
如今互联网与科技的发展用日新月异来形容也不为过,同时结合与传统的在线教育也在不断走向成熟,其形式与内容已经逐渐走向了多样化.现今越来越多的学校和教育培训机构以及个人企业开始向教育软件开发靠拢.到目前为 ...
- 下面哪项属于计算机在教育教学中的应用,东师现代教育技术18秋在线作业2答案...
现代教育技术18秋在线作业2-0005 试卷总分:100 得分:0 一. 单选题 (共 10 道试题,共 25 分) 1.不仅具有路由的功能,而且能对两个网络段中使用不同传输协议的数据进行互相的 ...
- 在线教育、精品课程、直播课、课程交流、历史观看、订单、收藏、余额、推荐、关注、购买课程、充值、产品设计、线上教育、视频课程、教育app、在线课堂、网络教学、远程教育、教学辅导、学习平台、K12教育
在线教育.精品课程.直播课.课程交流.历史观看.订单.收藏.余额.推荐.关注.购买课程.充值.产品设计.线上教育.视频课程.教育app.在线课堂.网络教学.远程教育.教学辅导.学习平台.K12教育 A ...
- 基于html5的在线教育,基于HTML5的在线学习系统的设计与实现
基于HTML5的在线学习系统的设计与实现 发布时间:2019-11-18所属分类:科技论文浏览:1次 摘 要: 摘 要: 在线课程学习网站的发展迅速,吸引了广大用户.基于 HTML5 的在线学习系统经 ...
- 北师大计算机在线作业9,北师大网络教育《计算机动画》在线作业-20210614020942.pdf-原创力文档...
北师大网络教育<计算机动画>在线作业 以下哪个工具不可以用来绘制或者调整路径 ( ) . [线条工具] [铅笔工具] [选择工具] [手形工具] 如果想要改变绘制图形的线条颜色 , 应该使 ...
- 在线教育20年:在线教育的未来发展趋势
作者:刘燕.即构科技在线教育产品首席架构师,在线教育行业资深应用专家,服务在线教育行业近20年. 文章为刘燕基于多年的行业观察,结合在即构科技服务多家头部教育客户的经验,分享对在线教育行业未来演变的思 ...
- itutorgroup:在线教育与传统线下教育的七大区别你知多少?
说起教育培训行业,现在已经分为两大阵营了,一是传统的线下培训,另一个则是最近几年大火的在线教育,为什么会这样呢?两者有什么区别呢?为什么现在越来越多的人开始选择在线教育,传统的线下培训输在哪里呢?下面 ...
最新文章
- 数据蒋堂 | 怎样看待存储过程的移植困难
- [未解决]jQuery中autocomplete的source格式问题
- JVM内存管理------JAVA语言的内存管理概述
- 初探儿童编程,如何学习编程及幼儿逻辑训练的必要性
- 谈判高手子贡---引导“用户”的专家
- 无代码绘制基因表达箱线图
- java 点击改变_java 单击按钮改变背景颜色
- Java 初始化 代码块_Java中初始化块详解及实例代码
- hdu2847(2009多校第四场) 01串添加最少01使被k整除(暴力)
- 显示隐藏dataGrid控件的标题栏,改变标题栏高度.
- java getname threads_Java8并发教程:Threads和Executors
- 测试苹果电脑性能软件xbench在哪,Mac OS操作系统性能对比测试
- 微信公众号H5合成图片长按下载
- excel冻结行和列_说一说有些人在EXCEL中还不会用的冻结窗格
- IT行业的发展前景分析
- python 绘图及可视化
- 小程序怎么弄?小程序开发多少钱?
- android 仿微信账单生成器手机版式,至尊版清理大师app
- 2015年最新苹果开发者账号注册流程详解
- (47)【漏洞发现】漏扫工具合集、WAF绕过分类