(三)在线教育网站搭建-讲师模块前端搭建

讲师前端搭建 - Vue框架,element-ui效果实现

1. 模块下载

https://github.com/PanJiaChen/vue-admin-template

1
git clone https://github.com/PanJiaChen/vue-admin-template.git

导入到Visual Studio Code中

项目配置

1
2
3
cd vue-admin-template
npm install
npm run dev

访问http://localhost:9528/

2. 项目结构

1
2
3
4
5
6
7
├── build // 构建脚本
├── mock // easy-mock模拟数据
├── node_modules // 项目依赖模块
├── public // 静态资源
├── src //项目源代码
├── tests
└── package.json // 项目信息和依赖配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
src 
├── api // 各种接口
├── assets // 图片等资源
├── components // 各种公共组件,非公共组件在各自view下维护
├── icons //svg icon
├── router // 路由表
├── store // 存储
├── styles // 各种样式
├── utils // 公共工具,非公共工具,在各自view下维护
├── views // 各种layout
├── App.vue //***项目顶层组件***
├── main.js //***项目入口文件***
├── permission.js //认证入口
└── setting.js //网页公共信息配置

3. 修改配置

package.json

1
2
3
"name": "",
"description": "",
"author": "",

登录页

1
2
3
4
5
6
7
<!-- 5-7行 -->
<div class="title-container">
<h3 class="title">在线教育后台管理系统</h3>
</div>

<!-- 44行 -->
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>

中文

1
2
//7行
import locale from 'element-ui/lib/locale/lang/zh-CN'

4. 添加路由

4.1. 修改路由-src/router/index.js

src/main.js 中注册了路由

路由在src/router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export const constantRoutes = [
{
path: '/teacher',
component: Layout,
redirect: '/teacher/list',
name: '讲师管理',
meta: { title: '讲师管理', icon: 'example' },
children: [
{
path: 'list',
name: '讲师列表',
component: () => import('@/views/edu/teacher/index'), //默认为index.vue
meta: { title: '讲师列表', icon: 'table' }
},
{
path: 'insert',
name: '添加讲师',
component: () => import('@/views/tree/index'),
meta: { title: '添加讲师', icon: 'tree' }
}
]
}]

4.2. 新建src/views/edu/teacher/index.vue

src/views/ 新建edu/teacher/index.vue,用于对应router/index.js中讲师列表的@/views/edu/teacher/index

4.3. 请求定义-api/teacher.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import request from '@/utils/request'

export default{
//条件分页查询
getTeacherPageList(current,size,searchObj){
return request({
//controller请求方式
url:'/eduservice/edu-teacher/moreCondtionPageList/'+current+'/'+size,
method:'post',
//传递的是json用data:searchObj , 参数的话使用params:searchObj
data:searchObj
})
}
}

4.4. 数据显示-src/views/edu/teacher/index.vue

从api/teacher 导入方法

显示表格数据样式参考 https://element.eleme.cn/#/zh-CN/component/table

el-table的:data对应的是整个记录数组list,每个el-table-column的prop对应数组中key值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
<div class="app-container">
<el-table :data=list>
<el-table-column prop="id" label="id"/>
<el-table-column prop="name" label="讲师姓名"/>
</el-table>
</div>
</template>

<script>
import teacher from '@/api/teacher'

export default{
//定义初值
data(){
return {
list:null, //每页数据集合
current: 1, //当前页
size: 10, //页内记录数
searchObj: {}, //条件
total :0 //总记录数
}
},
//页面渲染前,调用方法
created(){
this.getTeacherList()

},
//方法定义
methods:{
getTeacherList(){
teacher.getTeacherPageList(this.current,this.size,this.searchObj)
//当状态码为20000时,才能执行then操作
.then(reponse=>{
//console.log(reponse)
this.list = reponse.data.items
this.total = reponse.data.items.length
console.log(this.list)
console.log(this.total)

})
//请求失败调用
.catch()
}
}
}
</script>

4.5. 修改根url-util/request.js

在util/request.js中定义的创建axios实例,修改baseURL

1
2
3
4
5
6
// create an axios instance
const service = axios.create({
baseURL: 'http://localhost:8080',//process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000, // request timeout
})

4.6. 首页不能登录

更换了baseURL后,登录页的登录数据就不交给easy-mock了,需要模拟登录,返回响应数据。

查看原登录响应的数据

login返回

1
{"code":20000,"data":{"token":"admin-token"}}

info?token=admin-token返回

1
{"code":20000,"data":{"roles":["admin"],"introduction":"I am a super administrator","avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif","name":"Super Admin"}}

4.6.1. 模拟返回json数据-controller

1
2
3
4
5
6
7
8
9
10
11
12
13
//{"code":20000,"data":{"token":"admin-token"}}
@PostMapping("/login")
public R login(){
return R.ok().data("token","admin-token");
}
//{"code":20000,"data":{"roles":["admin"],"introduction":"I am a super administrator","avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif","name":"Super Admin"}}
@GetMapping("/info")
public R Info(String token){
return R.ok().data("roles","[admin]").data("introduction","I am a super administrator")
.data("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif")
.data("name","Super Admin");
}

4.6.2. 跨源问题

控制器添加注解

1
@CrossOrigin

4.6.3. 修改登录页请求路径-api/user.js

修改请求的url,查看请求方式,修改GetMapping,PostMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import request from '@/utils/request'

export function login(data) {
return request({
url: '/eduservice/edu-teacher/login',
method: 'post',
data
})
}

export function getInfo(token) {
return request({
url: '/eduservice/edu-teacher/info',
method: 'get',
params: { token }
})
}

export function logout() {
return request({
url: '/eduservice/edu-teacher/logout',
method: 'post'
})
}

4.7. 最终显示数据

5. 讲师列表list-views/edu/teacher/index.vue

5.1. 显示列表数据

https://element.eleme.cn/#/zh-CN/component/table

整体样式

1
2
3
4
5
<el-table :data="list">
<el-table-column prop="id" label="名称" width=""/>
<el-table-column prop="name" label="名称" width=""/>
<el-table-column prop="intro" label="资历" width=""/>
</el-table>

v-loading 用于在数据请求时,显示加载中的样式

listLoading是自定义的变量 data中初始化,methods中请求时加载,响应成功时关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<script>
import teacher from '@/api/teacher'

export default{
//定义初值
data(){
return {
list:null, //每页数据集合
current: 1, //当前页
size: 10, //页内记录数
searchObj: {}, //条件
total :0, //总记录数
listLoading: true //加载标志
}
},
//页面渲染前,调用方法
created(){
this.getTeacherList()
},
//方法定义
methods:{
getTeacherList(current=1){
this.current=current
this.listLoading=true
teacher.getTeacherPageList(this.current,this.size,this.searchObj)
//当状态码为20000时,才能执行then操作
.then(reponse=>{
//console.log(reponse)
this.list = reponse.data.items
this.total = reponse.data.items.length
//console.log(this.list)
//console.log(this.total)
this.listLoading=false

})
//请求失败调用
.catch()
}
}
}
</script>

template 中slot-scope=”scope”得到的scpoe是当前列表对象

可用于得到id和做一些三目运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<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">
{{ (current - 1) * size + 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/update/'+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>

5.2. 添加分页

el-pagination标签

传入参数

:current-page=定义的当前页变量

:page-size=定义的页记录数

@current-change=定义的多条件分页查询方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div class="app-container">
<!-- 表格 --->
<el-table>...</el-table>
<!-- 分页 -->
<el-pagination
:current-page="current"
:page-size="size"
:total="total"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="getTeacherList"/>
</div>
</template>

5.2.1. 仅有第一页,并不能跳转

后台传入总记录数,当初取的是list的数组长度,不对!

.data(“total”,pageTeacher.getTotal());

1
2
3
4
5
6
7
8
9
10
11
12
//多条件查询
@ApiOperation("多条件查询讲师信息")
@PostMapping("/moreCondtionPageList/{current}/{size}")
public R MoreCondtionPageList(
@ApiParam(value = "当前页") @PathVariable Long current,
@ApiParam(value = "页内记录数") @PathVariable Long size,
@ApiParam(value = "查询信息") @RequestBody(required = false) QueryTeacher queryTeacher){
Page<EduTeacher> pageTeacher = new Page<>(current,size);
eduTeacherService.pageListCondition(pageTeacher,queryTeacher);
List<EduTeacher> teachers = pageTeacher.getRecords();
return R.ok().data("items",teachers).data("total",pageTeacher.getTotal());
}

前台获取数据 views/edu/teacher/index.vue的methos方法

this.total = reponse.data.total

1
2
3
4
5
6
7
8
9
10
getTeacherList(current=1){
this.current=current
this.listLoading=true
teacher.getTeacherPageList(this.current,this.size,this.searchObj)
.then(reponse=>{
console.log(reponse)
this.list = reponse.data.items
this.total = reponse.data.total
})
}

5.3. 输入查询

form表单,格式

1
2
3
4
5
6
7
<el-form>
<el-form-item></el-form-item>
<el-form-item></el-form-item>
<el-form-item></el-form-item>
<el-button></el-button>
<el-button></el-button>
</el-form>

el-x相当于html中的x标签

el-input =input 其中v-model=定义的查询对象成员变量

el-button中的@click=”getTeacherList()”就是调用定义的查询方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<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="getTeacherList()">查询</el-button>
<el-button type="default" @click="resetData()">清空</el-button>
</el-form>
<!-- 表格 --->
<el-table>..</el-table>
<!-- 分页 -->
<el-pagination/>
</div>
</template>

5.4. 清空功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
import teacher from '@/api/teacher'

export default{
data(){
return {
}
},
created(){
//调用方法
this.resetData()
},
methods:{
getTeacherList(){},
resetData(){
//清空数据
this.searchObj={},
this.getTeacherList()
}
}
}
</script>

5.5. 最终讲师列表

5.6. 后台数据处理

1
2
==>  Preparing: SELECT id,name,intro,career,level,avatar,sort,is_deleted,gmt_create,gmt_modified FROM edu_teacher WHERE is_deleted=0 AND (level = ? AND gmt_create >= ?) LIMIT ?,? 
==> Parameters: 2(String), 2019-09-05 00:00:00(String), 0(Long), 10(Long)

6. 删除讲师-ById

6.1. api/teacher.js

export default中添加removeDataById方法

1
2
3
4
5
6
7
8
export default{
removeDataById(id){
return request({
url:'/eduservice/edu-teacher/deleteTeacher/'+id,
method:'delete'
})
}
}

6.2. 调用removeDataById

删除按钮链接该方法 @click=”removeDataById(scope.row.id)

1
2
3
4
5
6
methods:{
removeDataById(id){
teacher.removeDataById(id)
.then().catch()
}
}

6.3. 添加提示框

https://element.eleme.cn/#/zh-CN/component/message-box 确认消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script>
removeDataById(id){
this.$confirm('此操作将永久删除该讲师信息, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
//return 后then方法才会执行
return teacher.removeDataById(id)
})
.then(()=> {
//重新查看所有讲师
this.getTeacherList()
this.$message({
type: 'success',
message: '删除成功!'
})
}).catch(response => {
//判断错误操作类别
//console.log(response)
if(response === 'cancel'){
this.$message({
type: 'info',
message: '已取消删除'
})
} else{
this.$message({
type: 'error',
message: '删除失败'
})
}
})
}
</script>

6.4. 效果演示

7. 添加讲师-insert

7.1. router/index.js

添加在讲师管理模块

1
2
3
4
5
6
{
path: 'insert',
name: '添加讲师',
component: () => import('@/views/edu/teacher/form'),
meta: { title: '添加讲师', icon: 'form' }
}

7.2. api/teacher.js

添加讲师

1
2
3
4
5
6
7
insertTeacher(teacherObj){
return request({
url:'/eduservice/edu-teacher/insertTeacher',
method:'post',
data:teacherObj
})
}

7.3. views/edu/teacher/form.vue

直接从form/index.vue复制,修改显示

v-model 双方绑定,:model的对象定义在script的data方法中

路由跳转实现 this.$router.push({path:’/teacher’ })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<template>
<div class="app-container">
<el-form ref="form" :model="teacherObj" label-width="120px">
<el-form-item label="讲师头像">
<el-input v-model="teacherObj.avator" />
</el-form-item>
<el-form-item label="讲师姓名">
<el-input v-model="teacherObj.name" />
</el-form-item>
<el-form-item label="讲师排序">
<el-input-number v-model="teacherObj.sort" controls-position="right" min="0"/>
</el-form-item>
<el-form-item label="讲师头衔">
<el-select v-model="teacherObj.level" clearable placeholder="请选择">
<el-option label="高级讲师" :value="1" />
<el-option label="首席讲师" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="讲师资历">
<el-input v-model="teacherObj.career" placeholder="请输入讲师资历"/>
</el-form-item>
<el-form-item label="讲师简介">
<el-input v-model="teacherObj.intro" type="textarea" :rows="10"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveOrUpadte()">创建</el-button>
<el-button @click="onCancel">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>

<script>
import teacher from '@/api/teacher'
export default {
data() {
return {
teacherObj: {
name: '',
sort: 0,
level: '',
career: '',
intro: '',
avator: ''
}
}
},
created(){

},
methods: {
//修改和添加使用同一个表单
saveOrUpadte(){
this.insertTeacher()
},
insertTeacher(){
teacher.insertTeacher(this.teacherObj)
.then(()=>{
return this.$message({
message:'添加成功',
type:'success'
})
}).then(()=>{
//返回到讲师列表,路由方式
this.$router.push({
path:'/teacher'
})
}).catch(()=>{
this.$message({
message:'添加失败',
type:'error'
})

})
},
onCancel() {
this.teacherObj = {}
this.$message({
message: '数据已清空',
type: 'warning'
})
}
}
}
</script>

<style scoped>
.line{
text-align: center;
}
</style>

8. 更新讲师-update

8.1. /update/:id – router/index.js

在views/edu/teacher/index.vue中定义了修改的路由链接

1
<router-link :to="'/teacher/update/'+scope.row.id">

在讲师管理模块下添加更新讲师模块 - path根据该路由定义

/:id 表示传递的参数,hidden表示更新讲师模块并不像添加,列表一样显示在页面上,但注册了这个路由

1
2
3
4
5
6
7
{
path: 'update/:id',
name: '更新讲师',
component: ()=> import('@/views/edu/teacher/form'),
meta: {title:'更新讲师', icon: 'form'},
hidden: true
}

8.2. api/teacher.js

定义根据id查询记录方法和更新方法

传递的是json格式是要注意定义data:对象,也必须使用post,使后端控制器可以调用RequestBody注解

1
2
3
4
5
6
7
8
9
10
11
12
13
getTeacherById(id){
return request({
url:'/eduservice/edu-teacher/selectTeacher/'+id,
method: 'get'
})
},
updateTeacherById(id,teacherObj){
return request({
url: '/eduservice/edu-teacher/updateTeacher/'+id,
method:'post',
data: teacherObj
})
}

8.3. views/edu/teacher/form.vue

8.3.1. 判断是否存在id参数

在原有添加功能的代码上,判断是否传入参数id来判断是添加还是更新操作

  • this.$route.params && this.$route.params.id判断路由中是否有参数,参数中是否存在参数id
  • 在created方法中执行是因为该方法是在页面渲染之前执行的
1
2
3
4
5
6
7
8
9
<script>
created(){
if(this.$route.params&&this.$route.params.id){
const id = this.$route.params.id
this.getTeacherById(id)
this.isShow = false
}
}
</script>

8.3.2. 定义查询方法-getTeacherById

通过id查询对应记录,将查询到的数据赋值给teacherObj,数据就能显示在页面中

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
methods:{
//根据id查询
getTeacherById(id){
teacher.getTeacherById(id)
.then(response=>{
//console.log(response)
this.teacherObj=response.data.items
//console.log(teacherObj)
})
}
}
</script>

8.3.3. 定义修改方法-updateTeaher

修改数据,修改后跳转到列表页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
//更新讲师
updateTeaher(){
teacher.updateTeacherById(this.teacherObj.id,this.teacherObj)
.then(()=>{
return this.$message({
message: '修改成功',
type:'success'
})
}).then(()=>{
this.$router.push({path:'/teacher'})
}).catch(()=>{
this.$message({
message:'修改失败',
type:'error'
})
})
}
</script>

8.3.4. 操作判断

操作判断,创建按钮的点击事件判断

更新操作的话,teacherObj中存在id值,以此判断操作

1
2
3
4
5
6
7
8
9
10
11
<script>
//修改和添加使用同一个表单
saveOrUpadte(){
//console.log(this.teacherObj.id)
if(!this.teacherObj.id){
this.insertTeacher()
}else{
this.updateTeaher()
}
}
</script>

8.3.5. 根据不同操作修改按钮样式

当更新操作时:

创建按钮文字为更新,定义op参数,当更新操作时,赋值

隐藏重置按钮,定义isShow参数,当更新操作时,设值为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<el-button type="primary" @click="saveOrUpadte()" v-text="op"></el-button>
<el-button @click="onCancel" v-show="isShow">重置</el-button>
<script>
data(){
return{
op: '创建',
isShow:true
}
},
created(){
if(this.$route.params&&this.$route.params.id){
this.isShow = false
this.op = '更新'
}
}
</script>

8.4. 修改项目

8.5. 优化请求路径-api/teacher.js

1
2
3
4
//提取公共url
const base = '/eduservice/edu-teacher'
//方法中使用,注意是反斜冒号
url:`${base}/deleteTeacher/`+id

8.6. 删除方法

目前定义的删除是调用service的方法,修改调用baseMapper的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//EduController
@ApiOperation(value = "根据讲师ID删除讲师")
@DeleteMapping("/deleteTeacher/{id}")
public R deletebyId(@ApiParam(value = "讲师ID") @PathVariable String id){
boolean flag = eduTeacherService.deleteTeacherById(id);
if (flag){
return R.ok();
}else{
return R.error();
}
}

//EduTeacherServiceImpl
public boolean deleteTeacherById(String id) {
int i = baseMapper.deleteById(id);
return i>0;
}
本文结束  感谢您的阅读