讲师前端搭建 - 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 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' ), 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 ({ url :'/eduservice/edu-teacher/moreCondtionPageList/' +current+'/' +size, method :'post' , 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 const service = axios.create ({ baseURL : 'http://localhost:8080' , timeout : 5000 , })
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 @PostMapping("/login") public R login () { return R.ok().data("token" ,"admin-token" ); } @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. 跨源问题 控制器添加注解
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 }) }
直接从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.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 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 @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(); } } public boolean deleteTeacherById (String id) { int i = baseMapper.deleteById(id); return i>0 ; }