Spring Cloud + Vue前后端分离-第13章 网站开发

 源代码在GitHub - 629y/course: Spring Cloud + Vue前后端分离-在线课程

Spring Cloud + Vue前后端分离-第13章 网站开发

13-1 网站模块的搭建

新建web模板

1.网站开发,增加web模块,使用命令:vue create web

vue版本4.2.3 

大家拿到一个vue项目后,要先执行npm install,才能运行项目。这一步会去下载node module,类似于maven项目要先下载jar包。

1.创建vue create web

2.cd web

npm run serve 

多环境配置

1.网站开发,web模块增加多环境配置,启动命令中,增加--port 8081来修改端口

env.dev

env.prod

package.json

增加路由配置

1.网站开发,web模块增加路由配置,web路由版本是4.2.5,而admin路由版本是3.6.5

使用命令:npm install --save vue-router

注意:要先跳到web模块下,再安装router模块

以下这两个文件都是自动改的 

package.json,package-lock.json

这里只是增加了router依赖,后续开发页面时,会增加router的配置

13-2 集成bootstrap官方模板

我们的控台admin,用的是免费的ace admin模版,ace也是基于bootstrap做的封装,有很多组织和个人以卖各种前端模板为生。

本章开发的前端网站,是基于bootstrap官方提供的原生模板

bootstrap4.4.1文档介绍

1.网站开发,增加bootstrap-4.4.1的js,css

jquery.slim是jquery的简化版,也可以引入原版jquery。popper.js是一个js提示插件。

网站首页选用album模板,相册主题 

集成album模板

1.网站首页开发,集成album模板

做前端的同学,会经常看到bundle这个词,就是像几个依赖的js或css,打包成一个bundle.js

 App.vue

将album模板中的body标签里的内容,复制到template标签中

删除HelloWorld.vue

album.css

 

.jumbotron {padding-top: 3rem;padding-bottom: 3rem;margin-bottom: 0;background-color: #fff;
}
@media (min-width: 768px) {.jumbotron {padding-top: 6rem;padding-bottom: 6rem;}
}.jumbotron p:last-child {margin-bottom: 0;
}.jumbotron h1 {font-weight: 300;
}.jumbotron .container {max-width: 40rem;
}footer {padding-top: 3rem;padding-bottom: 3rem;
}footer p {margin-bottom: .25rem;
}

index.html 

<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><link rel="stylesheet" href="<%= BASE_URL %>bootstrap-4.4.1/css/bootstrap.min.css"><link rel="stylesheet" href="<%= BASE_URL %>static/css/album.css"><script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="<%= BASE_URL %>bootstrap-4.4.1/js/bootstrap.bundle.min.js"></script><title>甲蛙在线视频课程系统</title></head><body><div id="app"></div></body>
</html>

 互联网早期,js脚本容易被黑客利用,所以很多浏览器都有禁用js的选项,现在都不会禁用了。

main.js

因为之前的vue版本太高,所以这里我做了一些调整,将vue的版本调到了4.2.3 

vue会将我们写的vue文件做编译压缩,导致我们写的html的换行没有了,所以按钮之间的空隙没有了

顶部组件和底部组件提取 

1.网站首页开发,将顶部提取成the-header组件

 将app.vue中的header复制过来

the-header.vue

app.vue

小提示:在父组件中打入子组件的标签名,会自动引入子组件。

测试

然后再改过来就可以了

1.网站首页开发,将底部提取成the-footer组件

the-footer.vue

app.vue

13-3 首页开发

路由配置与文案修改

1.网站首页开发,增加路由配置router.js,增加网站首页index.vue

index.vue

router.js

main.js

没有使用route-view的话,就没有路由效果

app.vue

测试

再恢复回去

回车

导航条改为bootstrap 导航条组件

1.网站首页开发,导航条改为bootstrap 导航条组件

文档:https://v4.bootcss.com/docs/components/navbar/

the-header.vue

<template><header><nav class="navbar navbar-expand-lg navbar-light bg-light"><a class="navbar-brand" href="#">Navbar</a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="#">Link</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-expanded="false">Dropdown</a><div class="dropdown-menu"><a class="dropdown-item" href="#">Action</a><a class="dropdown-item" href="#">Another action</a><div class="dropdown-divider"></div><a class="dropdown-item" href="#">Something else here</a></div></li><li class="nav-item"><a class="nav-link disabled">Disabled</a></li></ul><form class="form-inline my-2 my-lg-0"><input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"><button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button></form></div></nav></header>
</template>
<script>
export default {name: 'theHeader',
}
</script>

导航条改为bootstrap 导航条组件

1.网站首页开发,导航条美化:菜单名称修改;增加container布局;样式改为dark;

2.集成fontawesome图标

index.html

fontawesome是一个图标库,可以到官网注册后,获取自己的cdn链接

the-header.vue

<template><header><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container"><a class="navbar-brand" href="#"><i class="ace-icon fa fa-video-camera"></i>&nbsp;甲蛙课程</a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="#">主页 <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="#">全部课程</a></li><li class="nav-item dropdown active"><a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-expanded="false">更多</a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item" href="#">关于我们</a><a class="dropdown-item" href="#">渠道合作</a><div class="dropdown-divider"></div><a class="dropdown-item" href="#">更多信息</a></div></li></ul><span class="text-white">欢迎:</span><button class="btn btn-outline-light my-2 my-sm-0" type="submit">登录/注册</button></div></div></nav></header>
</template>
<script>
export default {name: 'theHeader',
}
</script>

测试

底部文案修改

1.网站首页开发,底部文案修改

the-footer.vue

大屏文案修改

1.网站首页开发,大屏文案修改

index.vue

 bootstrap4提供了很多便捷的css样式,p-3就是padding:1rem。大家可以使用p-1,p-2来不断调整自己的样式。类似的还有m-x表示margin。

最新上线与好课推荐

1.网站首页开发,增加【新上好课】

后端接口测试地址:http://127.0.0.1:9002/business/web/course/list-new

以后web模块的请求全会放到web包下,将admin请求和web请求分开,方便做权限控制

CourseService.java

CourseController.java

spring提供两种类名生成方式,默认使用的是AnnotationBeanNameGenerator,它生成的方式就是取当前类名(不含包名)。

测试

首页显示新上好课真实数据bootstrap4图片自适应:使用img-fluid

1.网站首页开发,首页显示新上好课真实数据bootstrap4图片自适应:使用img-fluid

安装axios:npm install axios --save

2.初始user表,test/test

前后端交互,使用axios,也可以用jquery

安装axios:npm install axios --save

cd web

npm install axios --save

会自动对package.json,package-lock.json两个文件进行更新

main.js

index.vue

示例代码不要急于删除,在查看新代码显示出来的样式没问题后,再把示例代码删除

首页显示新上好课真实数据bootstrap4图片自适应:使用img-fluid

1.网站首页开发,

课程card美化,

增加【最新上线】和【好课推荐】,【好课推荐】和【最新上线】使用的一样数据,

响应式的页面,使用rem代替px

style.css

如果设置html标签的font-size=16px,则1rem=16px,0.5rem=8px。后续所有字体,边距都可以用rem作为单位

index.html

index.vue

<template><main role="main"><section class="jumbotron text-center"><div class="container"><h1>在线视频课程平台</h1><p class="lead text-muted m-3">知识付费时代刚刚起步,在这个领域有很多的发展机会。整个课程以实战为基础,手把手从零开始,一步一步搭建一个完整的企业级开发架构。不讲废话,只讲干货。</p><p><a href="#" class="btn btn-primary my-2 p-3 font-weight-bold">点击进入所有课程</a></p></div></section><div class="album py-5 bg-light"><div class="container"><div class="title1">最新上线</div><div class="row"><div v-for="o in news" class="col-md-4"><div class="card mb-4 shadow-sm course"><img class="img-fluid" v-bind:src="o.image"><div class="card-body"><h4 class="">{{o.name}}</h4><p class="card-text">{{o.summary}}</p><div class="d-flex justify-content-between align-items-center"><div class="btn-group"><button type="button" class="btn btn-sm btn-outline-secondary">View</button><button type="button" class="btn btn-sm btn-outline-secondary">Edit</button></div><div class="text-muted"><span class="badge badge-info"><i class="fa fa-yen" aria-hidden="true"></i>&nbsp;{{o.price}}</span>&nbsp;<span class="badge badge-info"><i class="fa fa-user" aria-hidden="true"></i>&nbsp;123</span>&nbsp;</div></div></div></div></div></div><hr><div class="title2">好课推荐</div><div class="row"><div v-for="o in news" class="col-md-4"><div class="card mb-4 shadow-sm course"><img class="img-fluid" v-bind:src="o.image"><div class="card-body"><h4 class="">{{o.name}}</h4><p class="card-text">{{o.summary}}</p><div class="d-flex justify-content-between align-items-center"><div class="btn-group"><button type="button" class="btn btn-sm btn-outline-secondary">View</button><button type="button" class="btn btn-sm btn-outline-secondary">Edit</button></div><div class="text-muted"><span class="badge badge-info"><i class="fa fa-yen" aria-hidden="true"></i>&nbsp;{{o.price}}</span>&nbsp;<span class="badge badge-info"><i class="fa fa-user" aria-hidden="true"></i>&nbsp;123</span>&nbsp;</div></div></div></div></div></div></div></div></main>
</template>
<script>
export default {name: 'index',data:function (){return{news:[],}},mounted() {let _this = this;_this.listNew();},methods:{/*** 查询新上好课*/listNew(){let _this = this;_this.$ajax.get(process.env.VUE_APP_SERVER + '/business/web/course/list-new').then((response)=>{console.log("查询新上好课结果:",response);let resp = response.data;if (resp.success){_this.news = resp.content;}}).catch((response)=>{console.log("error:",response);})},}
}
</script>
<style>
.title1{margin-bottom: 2rem;color: #fafafa;letter-spacing: 0;text-shadow: 0px 1px 0px #999, 0px 2px 0px #888, 0px 3px 0px #777, 0px 4px 0px #666, 0px 5px 0px #555, 0px 6px 0px #444, 0px 7px 0px #333, 0px 8px 7px #001135;font-size: 2rem;
}
.title2{margin-bottom: 2rem;color: transparent;-webkit-text-stroke: 1px black;letter-spacing: 0.04em;font-size: 2rem;
}
.course h4{font-size: 1.25rem;margin:15px 0;
}
.course .text-muted .badge{font-size: 1rem;
}
</style>

课程信息组件提取

增加课程card组件the-course

1.网站首页开发,增加课程card组件the-course

the-course.vue

<template><div class="card mb-4 shadow-sm course"><img class="img-fluid" v-bind:src="course.image"><div class="card-body"><h4 class="">{{course.name}}</h4><p class="card-text">{{course.summary}}</p><div class="d-flex justify-content-between align-items-center"><div class="btn-group"><button type="button" class="btn btn-sm btn-outline-secondary">课程详情</button></div><div class="text-muted"><span class="badge badge-info"><i class="fa fa-yen" aria-hidden="true"></i>&nbsp;{{course.price}}</span>&nbsp;<span class="badge badge-info"><i class="fa fa-user" aria-hidden="true"></i>&nbsp;123</span>&nbsp;</div></div></div></div>
</template>
<script>
export default {name: 'the-course',props:{course:{}},data:function (){return{}},
}
</script>
<style>.course h4{font-size: 1.25rem;margin:15px 0;}.course .text-muted .badge{font-size: 1rem;}
</style>

index.vue

课程card组件增加登记显示

1.网站首页开发,课程card组件增加登记显示

2.增加过滤器filter.js

EnumGenerator.java

Enum生成器里没有自动创建js目录,所以我们要先手动创建js目录,再执行Enum生成器

index.html

filter.js

将admin中的filter文件拷贝过来

main.js

the-course.vue


13-4 课程列表页面开发

课程列表数据显示

1.网站首页开发,增加课程列表页【全部课程】,并增加路由

list.vue

router.js

index.vue

the-header.vue

router-link + to,相当于a + href

测试

课程列表页调用后端接口显示真实数据

1.网站首页开发,课程列表页调用后端接口显示真实数据

CourseController.java

list.vue

后端接口只能查询已发布的课程

1.课程列表页面开发,后端接口只能查询已发布的课程

如果列表查询接口的请求参数,只有分页相关的参数,可以使用PageDto做为传参的类。

如果请求参数包含其它参数,则需要新建一个类CoursePageDto,继承PageDto,来传递更多的参数

CoursePageDto.java

CourseController.java

这里只是修改了接收参数的类,控台端课程列表查询传递的分页参数并没有变化,功能不影响。

CourseController.java(admin)

CourseService.java 

status查询条件不是必需的,控台接口进来的就不需要这个条件,网站接口进来的有这个条件,所以增加一个status条件判断。

这段写法比较常见,参数有值就做为条件查询,没值就不加条件,相当于写动态sql

前端分页,增加分页组件,比admin简单,使用rem

1.课程列表页面开发,前端分页

2.增加分页组件,比admin简单,使用rem

pagination.vue

list.vue


分类筛选功能开发

1.课程列表页面开发,加载所有分类

2.增加axios接口访问日志拦截器

main.js

tool.js

把admin中的tool.js复制过来

index.html

list.vue

CategoryController.java

两级分类访问幕课网显示

1.课程列表页面开发,两级分类访问幕课网显示

list.vue

<template><main role="main"><div class="header-nav"><div class="clearfix"><div class="container"><div class="row"><div class="col-12"><a v-on:click="onClickLevel1('00000000')" id="category-00000000" href="javascript:;" class="cur">全部</a><a v-for="o in level1" v-on:click="onClickLevel1(o.id)" v-bind:id="'category-' + o.id" href="javascript:;">{{o.name}}</a></div></div></div></div></div><div class="skill clearfix"><div class="container"><div class="row"><div class="col-12"><a v-on:click="onClickLevel2('11111111')" id="category-11111111" href="javascript:;" class="on">不限</a><a v-for="o in level2" v-on:click="onClickLevel2(o.id)" v-bind:id="'category-' + o.id" href="javascript:;">{{o.name}}</a><div style="clear:both"></div></div></div></div></div><div class="album py-5 bg-light"><div class="container"><div class="row"><div class="col-md-12"><pagination ref="pagination" v-bind:list="listCourse"></pagination></div></div><br><div class="row"><div v-for="o in courses" class="col-md-4"><the-course v-bind:course="o"></the-course></div><h3 v-show="courses.length === 0">课程还未上架</h3><!--这是一个比较常见的写法:如果列表有值,就显示列表,没有值就显示某段文字--></div></div></div></main>
</template>
<script>
import TheCourse from "@/components/the-course.vue";
import Pagination from "@/components/pagination.vue";export default {name: 'list',components: {Pagination, TheCourse},data:function (){return{courses:[],level1:[],level2:[],}},mounted() {let _this = this;_this.$refs.pagination.size = 1;_this.listCourse(1);_this.allCategory();},methods:{/*** 查询课程列表*/listCourse(page){let _this = this;_this.$ajax.post(process.env.VUE_APP_SERVER + '/business/web/course/list',{page:page,size:_this.$refs.pagination.size,}).then((response)=>{let resp = response.data;if (resp.success){_this.courses = resp.content.list;_this.$refs.pagination.render(page,resp.content.total);}}).catch((response)=>{console.log("error:",response);})},/*** 所有分类查询*/allCategory() {let _this = this;_this.$ajax.post(process.env.VUE_APP_SERVER +"/business/web/category/all").then((response) => {let resp = response.data;_this.categorys = resp.content;//将所有记录格式化成树形结构_this.level1 = [];for (let i = 0; i < _this.categorys.length; i++) {let c = _this.categorys[i];//如果分类的父id是00000000,表示它是一级分类,否则是二级分类if (c.parent === '00000000'){_this.level1.push(c);for (let j = 0; j < _this.categorys.length; j++) {let child = _this.categorys[j];if (child.parent === c.id){if(Tool.isEmpty(c.children)){c.children = [];}c.children.push(child);}}}else{_this.level2.push(c);}}})},/*** 点击一级分类时* @param level1Id*/onClickLevel1(level1Id) {let _this = this;},/*** 点击一级分类时* @param level2Id*/onClickLevel2(level2Id) {let _this = this;},}
}
</script>
<style>
/* 头部 一级分类 */
.header-nav {height: auto;background: #fff;box-shadow: 0 8px 16px 0 rgba(28,31,33,.1);padding: 16px 0;box-sizing: border-box;position: relative;z-index: 1;/*background-color: #d6e9c6;*/
}
.header-nav>div {width: 100%;padding-left: 12px;box-sizing: border-box;margin-left: auto;margin-right: auto;/*background-color: #B4D5AC;*/
}
.header-nav a {float: left;font-size: 16px;color: #07111b;line-height: 50px;height: 45px;position: relative;margin-right: 46px;font-weight: 700;
}
.header-nav a:hover {color: #c80;
}
.header-nav a.cur {color: #c80;
}
.header-nav a.cur:before {display: block;
}
.header-nav a:before {display: none;content: ' ';position: absolute;bottom: 0;background: #c80;width: 16px;height: 3px;left: 50%;margin-left: -8px;
}
/* 二级分类 */
.skill {width: 100%;padding: 24px 0 0;position: relative;margin: 0 auto;
}
.skill a.on {color: #c80;background: rgba(204,136,0,.1);
}
.skill a {float: left;margin-right: 20px;padding: 0 12px;font-size: 14px;color: #4d555d;line-height: 32px;border-radius: 6px;margin-bottom: 12px;
}
.skill a:hover {background: #d9dde1;
}
</style>

测试

点击一级分类时显示激活状态,并显示对应的二级分类,如果点击的是【全部】,则显示所有的二级分类

1.课程列表页面开发,点击一级分类时显示激活状态,并显示对应的二级分类,如果点击的是【全部】,则显示所有的二级分类

list.vue

测试

点击二级分类时显示激活状态

1.课程列表页面开发,点击二级分类时显示激活状态

list.vue

测试

根据点击的分类进行课程筛选,动态sql不只可以进行动态列查询,也可以进行动态表关联

1.课程列表页面开发,根据点击的分类进行课程筛选,动态sql不只可以进行动态列查询,也可以进行动态表关联

完成功能:当点分类时,筛选出这个分类下所有的视频

list.vue

一级分类的“全部”激活时,level1Id清空;二级分类的“不限”激活时,level2Id清空,这样categoryId这个参数就是空,即查询全部课程。

【课程表】+ 【课程分类关联表】两张表关联。多表关联的查询,需要写自定义sql.

MyCourseMapper.java

CoursePageDto.java

MyCourseMapper.xml

我们正常写的动态sql,一般是动态的where条件,但其实可以写成动态关联表。其实就是动态的拼接sql字符串。

CourseService.java

不加分类做为查询条件时,生成的sql就是course表的单表查询,性能快一些。

测试


13-5 课程详情页面开发

新增详情页面并配置路由

1.课程详情页面开发,增加课程详情页面和路由

2.课程报名数显示真实数据

detail.vue

router.js

the-course.vue

测试

增加后端课程详情接口

1.课程详情页面开发,增加课程后端接口,只能查询已发布的课程,包含内容、讲师、大章、小节信息,测试地址:http://127.0.0.1:9002/business/web/course/find/00000001

CourseDto.java

一次性返回页面上需要有的所有数据,可以减少前后端交易次数,提升用户体验。

TeacherService.java

ChapterService.java

SectionService.java

CourseService.java

CourseController.java

测试

前端界面显示课程信息

1.课程详情页面开发,增加课程信息显示,可以使用@media(max-width:700px)来精细控制不同分辨率的显示样式

detail.vue

如果地址是xxx?id=a。那在vue代码里要获取id的值的话,可以使用this.$router.query.id

测试

增加课程内容显示

1.课程详情页面开发,增加课程内容显示

detail.vue

使用v-html,可以直接绑定显示html字符串 

测试

增加讲师信息显示

1.课程详情页面开发,增加讲师信息显示

detail.vue

13-6 章节显示与视频播放 

大章小节分组显示

1.课程详情页面开发,增加大章小节显示,

使用d-none d-sm-inline组合:超小屏隐藏,其它显示,

v-for的第二变量是索引号

detail.vue

第一个变量是遍历的每一个实体,第二变量是数组索引,从0开始

d-none d-sm-inline 组合,让小图标在小屏时不显示。屏幕小时,节约空间,只显示核心信息。

增加大章小节收缩和展开,注意:在v-for里写v-show,只修改属性不起作用,需要$set

1.课程详情页面开发,增加大章小节收缩和展开,注意:在v-for里写v-show,只修改属性不起作用,需要$set

 detail.vue

从后端加载出来的大章数据,是没有folded属性的,相当于chapter.folded=false.

folded表示收缩,初始是false,表示不收缩,即初始的时候,所有章节都是展开的。

注意:v-for里面,如果用到了v-show,且v-show里面的控制属性值是随着界面操作而变化的,此时只修改属性值是不生效的,vue监听不到数组里的某个元素的值的变化。需要用到$set.

测试

可自行扩展,增加全部收缩,全部展开的功能,提示:全部收缩即所有大章的folded=true;全部展开即所有大章的folded=false.

视频播放

1.课程详情页面开发,增加视频播放功能

video-image.png

 index.html

代码从admin复制过来

modal-player.vue

player.vue

再次提示:控台和网站的接口分开,控台使用admin前缀,网站使用web前缀,方便接口权限控制。

VodController.java

detail.vue

只需要引入modal-player组件,不需要引入play组件

测试

 对每个大章里的小节进行排序显示

1.课程详情页面开发,对每个大章里的小节进行排序显示

tool.js

detail.vue

只有免费的视频才可直接播放,收费视频需要登录后才能播放

1.课程详情页面开发,只有免费的视频才可直接播放,收费视频需要登录后才能播放

代码从admin复制过来

toast.js

tool.js

index.html

detail.vue

测试

点击收费的,就不能直接可以播放,而是弹出请先登录

13-7 增加会员注册功能

控台增加会员管理功能 

1.增加会员管理功能,新增member表,在之前代码生成器的步骤后面,加上以下步骤:

1) 在resource.json中增加新资源

2) 在【资源管理】页面,配置最新的resource.json

3) 在【角色管理】页面,为相应的角色配置新的资源

4) 重新登录控台

自从为控台加入了权限管理功能后,新做的页面,需要增加这四步,配置相应的权限,才能使用新表的管理功能。

all.sql

会员头像的上传和显示,类似于讲师头像的上传和显示,所以我们就不再重复做了。

 generatorConfig.xml

ServerGenerator.java

VueGenerator.java

admin.vue

router.js

 到目前为止,跟我们前面介绍的制作单表管理功能的流程一样,但由于加入权限功能,所以需要配置权限后才能看到页面。

1) 在resource.json中增加新资源

2) 在【资源管理】页面,配置最新的resource.json

3) 在【角色管理】页面,为相应的角色配置新的资源

4) 重新登录控台 

 但是一般不会在这里有新增

控台的会员管理,不能新增,修改,删除。可以查看会员信息,重置密码,报表统计等操作

1.增加会员管理功能,控台的会员管理,不能新增,修改,删除。可以查看会员信息,重置密码,报表统计等操作

MemberController.java

member.vue

只剩下这一个查询功能

测试

网站增加会员注册功能

1.会员登录注册功能开发,增加登录注册模态框,增加注册功能

登录表单、注册表单、忘记密码表单放在同一个模态框中

MemberController.java

MemberService.java

md5.js

index.html

login.vue

把登录注册框做成单独的组件,方便维护

和播放器组件类似,引入组件后,初始是看不到组件内的元素的

登录注册框可以做的大气一点,所以把输入框的高度调高,字体调大

the-header.vue

测试

13-8 增加登录与退出登录功能

登录、注册、忘记密码之间的切换

1.会员登录注册功能开发,增加登录框和忘记密码框,并实现登录框、注册框、忘记密码框切换,默认显示登录框

login.vue

<template><div id="login-modal" class="modal fade" tabindex="-1" role="dialog"><div class="modal-dialog modal-login" role="document"><div  class="modal-content"><div class="modal-body"><div class="login-div" v-show="MODAL_STATUS === STATUS_LOGIN"><h3>登&nbsp;&nbsp;录</h3><div class="form-group"><input v-model="member.mobile" class="form-control" placeholder="手机号"></div><div class="form-group"><input class="form-control" type="password" placeholder="密码" v-model="member.passwordOriginal"></div><div class="form-group"><div class="input-group"><input id="image-code-input" class="form-control" type="text" placeholder="验证码"v-model="member.imageCode"><div class="input-group-addon" id="image-code-addon"><img id="image-code" alt="验证码" v-on:click="loadImageCode()"/></div></div></div><div class="form-group"><button class="btn btn-primary btn-block submit-button">登  录</button></div><div class="form-group"><div class="checkbox"><label><input type="checkbox" class="rememberMe" v-model="rememberMe">记住密码</label><div class="pull-right"><a href="javascript:;" v-on:click="toForgeDiv">忘记密码</a>&nbsp;&nbsp;<a href="javascript:;" v-on:click="toRegisterDiv">我要注册</a></div></div></div><div class="form-group to-register-div"></div></div><div class="register-div" v-show="MODAL_STATUS === STATUS_REGISTER"><h3>注  册</h3><div class="form-group"><input id="register-mobile" v-model="memberRegister.mobile"class="form-control" placeholder="手机号"></div><div class="form-group"><div class="input-group"><input id="register-mobile-code" class="form-control"placeholder="手机验证码" v-model="memberRegister.code"><div class="input-group-append"><button class="btn btn-outline-secondary" id="register-send-code-btn"v-on:click="sendSmsForRegister()">发送验证码</button></div></div></div><div class="form-group"><input id="register-name" v-model="memberRegister.name"class="form-control" placeholder="昵称"></div><div class="form-group"><input id="register-password" v-model="memberRegister.passwordOriginal"class="form-control" placeholder="密码" type="password"></div><div class="form-group"><input id="register-confirm-password" v-model="memberRegister.confirm"class="form-control" placeholder="确认密码"name="memberRegisterConfirm" type="password"></div><div class="form-group"><button class="btn btn-primary btn-block submit-button" v-on:click="register()">注  册</button></div><div class="form-group to-login-div"><a href="javascript:;" v-on:click="toLoginDiv">我要登录</a></div></div><div class="forget-div" v-show="MODAL_STATUS === STATUS_FORGET"><h3>忘记密码</h3><div class="form-group"><input id="forget-mobile" v-model="memberForget.mobile" class="form-control" placeholder="手机号"></div><div class="form-group"><div class="input-group"><input id="forget-mobile-code" class="form-control"placeholder="手机验证码" v-model="memberForget.code"><div class="input-group-append"><button class="btn btn-outline-secondary" id="forget-send-code-btn">发送验证码</button></div></div></div><div class="form-group"><input id="forget-password" v-model="memberForget.passwordOriginal"class="form-control" placeholder="密码" type="password"></div><div class="form-group"><input id="forget-confirm-password" v-model="memberForget.confirm"class="form-control" placeholder="确认密码" type="password"></div><div class="form-group"><button class="btn btn-primary btn-block submit-button">重  置</button></div><div class="form-group to-login-div"><a href="javascript:;" v-on:click="toLoginDiv">我要登录</a></div></div></div></div></div></div>
</template>
<script>export default {name:'the-login',data:function (){return{//模态框内容切换:登录、注册、忘记密码STATUS_LOGIN:"STATUS_LOGIN",STATUS_REGISTER:"STATUS_REGISTER",STATUS_FORGET:"STATUS_FORGET",MODAL_STATUS:"",member:{},memberForget:{},memberRegister:{},rememberMe:true,//记住密码imageCodeToken:""}},mounted() {let _this = this;_this.toLoginDiv();},methods:{/*** 打开登录注册窗口*/openLoginModal(){let _this = this;$("#login-modal").modal("show");},//------------------登录框、注册框、忘记密码框切换---------------------------------toLoginDiv(){let _this = this;_this.MODAL_STATUS = _this.STATUS_LOGIN},toRegisterDiv(){let _this = this;_this.MODAL_STATUS = _this.STATUS_REGISTER},toForgeDiv(){let _this = this;_this.MODAL_STATUS = _this.STATUS_FORGET},register(){let _this = this;_this.memberRegister.password = hex_md5(_this.memberRegister.passwordOriginal + KEY);//调服务端注册接口_this.$ajax.post(process.env.VUE_APP_SERVER + '/business/web/member/register',_this.memberRegister).then((response)=>{let resp = response.data;if (resp.success){Toast.success("注册成功");}else {Toast.warning(resp.message);}})},}}
</script>
<style>/*登录框*/.login-div .input-group-addon{padding: 0;border: 0;}#login-modal h3{text-align: center;margin-bottom: 20px;}#login-modal .modal-login{max-width: 400px;}#login-modal input:not(.rememberMe){height: 45px;font-size: 16px;}#login-modal .submit-button{height: 50px;font-size: 20px;}#login-modal .to-login-div{text-align: center;}
</style>

通过修改MODAL_STATUS变量值,来达到三块div的切换显示

css3 not : 排除选择器

测试

增加登录验证码功能

1.会员登录注册功能开发,登录框显示验证码

KaptchaController.java

login.vue

测试

增加登录功能,包含图片验证码

1.会员登录注册功能开发,增加登录功能,包含图片验证码

将控台用户登录功能相关代码拷贝到会员登录,并将user替换成member

可以从user相关的功能拷过来改一改

local-storage.js(admin)

session-storage.js

local-storage.js

index.html

前端网站叫会员;运营控台叫用户。

tool.js

login.vue

如果有些代码是需要后面再加的,为了防止忘记,可以加TODO,IDEA会帮我们记录所有带TODO的地方。可以在底部TODO窗口查看

ControllerExceptionHandler.java

MemberController.java(web)

控台保存登录用户信息用的是LoginUserDto,网站保存会员信息用的是LoginMemberDto

LoginMemberDto.java

MemberDto.java

BusinessExceptionCode.java

MemberService.java

这里登录时,需要验证手机号是否存在,和控台登录类似,控台登录时,需要验证登录名是否存在

UserService.java

UserController.java

ControllerExceptionHandler.java

测试

完善【记住我】功能,会员登录名是mobile,不是loginName

1.会员登录注册功能开发,完善【记住我】功能

会员登录名是mobile,不是loginName

login.vue

测试

之前“记住我”的逻辑里,登录名写的是loginName,而会员登录名是mobile,所以没记住。

登录成功后,头部导航显示会员昵称

1.会员登录注册功能开发,登录成功后,头部导航显示会员昵称

login.vue

要在子组件(login组件)中调用父组件(the-header组件)的xx方法(setLoginMember方法),可以使用this.$parent.xxx

the-header.vue

测试

增加退出登录功能

1.会员登录注册功能开发,增加退出登录功能

MemberController.java

the-header.vue

测试

13-9 增加发送短信验证码功能

增加短信验证码管理功能

1.会员登录注册功能开发,增加短信管理,生成代码后,需要配置资源权限

all.sql

初始生成的短信状态是未使用,验证过之后,状态改为已使用,防止同一个验证码被重复使用

SmsStatusEnum.java

SmsUseEnum.java

generatorController.xml

ServerGenerator.java 

VueGenerator.java  

EnumGenerator.java

admin.vue

router.js

resource.json

测试

短信管理只能查看

1.会员登录注册功能开发,短信管理只能查看

主要是删除多余内容

SmsController.java

sms.vue

只剩下list

测试

增加发送验证码功能

1.会员登录注册功能开发,增加发送注册短信功能,同手机号同操作1分钟内不能重复发送短信

一分钟内发了一次注册验证码和一次忘记密码验证码,是可以的。但是发两次注册验证码或两次忘记密码验证码,是不可以的。

EnumGenerator.java

BusinessExceptionCode.java

SmsService.java

短信验证码可以接阿里云短信服务,在阿里云控台配置短信模板,然后在代码里配置模板id,再调用阿里云jar包里的发送短信方法就可以了。收费是0.045元/条

SmsController.java(web)

这个接口是通用的发送短信验证码的接口

login.vue

测试

增加发送短信后倒计时功能,可修改countdown参数进行调试

1.会员登录注册功能开发,增加发送短信后倒计时功能,可修改countdown参数进行调试

前端的校验都是不可靠的,容易被绕过

login.vue

点击发送短信后,按钮变成不可点击,且显示倒计时

setTimeout,只执行一次的定时器。

setInterval,重复执行的定时器。

测试

为了测试

虽然倒计时是6s,但是6s过后也不能直接再次获取,因为后端有校验 

因为刷新网站后,倒计时就没有了

 13-10 完善登录注册校验功能

增加手机号后端校验

1.会员登录注册功能开发,注册时,发送短信前,先校验会员是否存在

MemberService.java

MemberController.java

login.vue

测试

增加前端正则校验

1.会员登录注册功能开发,注册时发送验证码前,增加手机号校验;手机号输入框失去焦点时,校验手机号

2.增加正则表达式校验工具类

上一版本的校验是后端的校验,这个版本是前端的校验,校验手机号是否符合规则。

pattern.js

Pattern = {// 用户名正则,2到16位(字母,数字,下划线)loginNamePattern: /^[a-zA-Z0-9_]{6,16}$/,// 昵称正则,6到20位中文,字母,数字,下划线namePattern: /^[\w\u4e00-\u9fa5]{2,20}$/,// 强密码强度正则,最少8位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符passwordStrongPattern: /^.*(?=.{8,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/,// 弱密码强度正则,最少6位,包括至少1字母,1个数字passwordWeakPattern: /^.*(?=.{6,})(?=.*\d)(?=.*[A-Za-z]).*$/,// 手机号正则,11位数字,1开头mobilePattern: /^1\d{10}$/,// 图片验证码正则,4位字母,数字imageCodePattern: /^[a-zA-Z0-9]{4}$/,// 手机验证码正则,6位数字mobileCodePattern: /^[0-9]{6}$/,validateLoginName: function (str) {if (Tool.isEmpty(str)) {return false;}return this.loginNamePattern.test(str);},validateName: function (str) {if (Tool.isEmpty(str)) {return false;}return this.namePattern.test(str);},validatePasswordStrong: function (str) {if (Tool.isEmpty(str)) {return false;}return this.passwordStrongPattern.test(str);},validatePasswordWeak: function (str) {if (Tool.isEmpty(str)) {return false;}return this.passwordWeakPattern.test(str);},validateMobile: function (str) {if (Tool.isEmpty(str)) {return false;}return this.mobilePattern.test(str);},validateImageCode: function (str) {if (Tool.isEmpty(str)) {return false;}return this.imageCodePattern.test(str);},validateMobileCode: function (str) {if (Tool.isEmpty(str)) {return false;}return this.mobileCodePattern.test(str);}
};

Pattern 里面都是一些常用的正则表达式校验,关于正则表达式是程序员比较不喜欢的一块,学了不常用容易忘,主要是靠平常的积累。

index.html

login.vue

blur事件,当文本框失去焦点(光标从文件框内跳出)会触发blur事件

测试

完善注册输入框校验

1.会员登录注册功能开发,完善注册输入框校验

login.vue

测试

提交注册之前,先校验所有注册输入框,当有一个文本框校验为false时,其它不校验

1.会员登录注册功能开发,提交注册之前,先校验所有注册输入框

当有一个文本框校验为false时,其它不校验

login.vue

只要有一个是false,后面的都不再计算

接下来实现效果:点击注册的时候,所有的输入框都校验一遍

原来的写法是一边校验,一边计算结果,当有一个为false时,就不再校验后边的。现在改为:先所有的文本框校验一遍,再计算最终校验结果。

login.vue

增加短信验证码校验

1.会员登录注册功能开发,注册时增加短信验证码校验功能

短信验证码5分钟内有效,只能验证一次

MemberDto.java

login.vue

BusinessExceptionCode.java

SmsService.java

MemberController.java

测试

故意填错验证码

正确的

增加忘记密码功能

1.会员登录注册功能开发,增加忘记密码功能,包含短信验证,输入框校验等

BusinessExceptionCode.java

login.vue

注册和忘记密码对手机是否存在的校验是反的。注册时,要求手机是不存在的;忘记密码时,要求手机是已存在的。 

MemberService.java

MemberController.java

 测试


13-11 增加立即报名功能

新增报名表并生成基本代码

1.会员报名课程功能开发,新增报名表,生成服务端和持久层代码

all.sql

一个人对同一门课程只能报名一次,所以加唯一键约束

generatorConfig.xml

登录后可报名课程

1.会员报名课程功能开发,登录后可报名课程

MemberCourseService.java

真实项目中,报名时,需要对接第三方支付,比如支付宝,还要另外做一个支付回调接口,让支付宝告诉你会员支付是否成功,如果支付成功了,再插入报名表

 MemberCourseController.java

报名功能就是保存会员课程关联表,相当于生成的save方法

detail.vue

多次用到了互斥显示

测试

已报名的才可播放收费视频

1.会员报名课程功能开发,已报名的才可播放视频

detail.vue

测试

进入课程详情页面时加载报名信息

1.会员报名课程功能开发,加载完课程信息后,如果已登录,则加载报名信息。

刷新页面也会导致前端报名信息丢失

detail.vue

MemberCourseService.java

MemberCourseController.java

测试

这一章算是对前面知识的一个综合考查,整个网站的功能较多,不过按我的步骤一步一步的将功能点拆解,完成起来不算太难。大家在实际工作中也是这样,把大功能分解成一个一个的小功能,再来开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浪花 - 后端接口完善

一、队伍已加入用户数量 1. 封装的响应对象 UserTeamVO 新增字段 hasJoinNum 2. 查询队伍 id 列表 3. 分组过滤&#xff0c;将 team_id 相同的 userTeam 分到同一组 4. 获取每一组的 userTeam 数量&#xff0c;即一个 team_id 对应几个userTeam&#xff08;用户数量&#x…

Qt两种定时器

QT 中使用定时器&#xff0c;有两种方式&#xff1a; 定时器类&#xff1a;QTimer定时器事件&#xff1a;QEvent::Timer&#xff0c;对应的子类是 QTimerEvent 简单讲一下两种用法&#xff1a; QTimer&#xff1a; QTimer 需要创建QTimer对象 然后需要给定时器绑定 定时器超…

Qt : Style Sheet

When a style sheet is active, the QStyle returned by QWidget::style() is a wrapper “style sheet” style, not the platform-specific style. The wrapper style ensures that any active style sheet is respected and otherwise forwards the drawing operations to t…

消息中间件之RocketMQ(四)

RocketMQ高性能背后的核心原理 1.读队列与写队列 在RocketMQ的管理控制台创建Topic时&#xff0c;可以看到要单独设置读队列和写队列。 通常在运行时&#xff0c;都需要设置读队列写队列。perm字段表示Topic的权限&#xff0c;有三个可选项 2&#xff1a;禁写禁订阅 4: 可订阅…

【前端web入门第二天】01 html语法实现列表与表格

html语法实现列表与表格 文章目录: 1.列表 1.1 无序列表1.2 有序列表1.3 定义列表 2.表格 2.1 表格基本结构2.2 表格结构标签 写在最前,第二天学习目标: 列表 表格 表单 元素为嵌套关系 1.列表 作用:布局内容排列整齐的区域。 列表分类:无序列表、有序列表、定义列表。 1…

pysot中eval多种算法比较和画图

安装miktex和Texwork&#xff0c;记得更新miktex&#xff0c;链接https://miktex.org/download&#xff0c; 参考https://blog.csdn.net/weixin_42495721/article/details/110855071 我用的是pysot官方的库&#xff0c;里面包括eval和test、train等py文件。 路径结构为&#x…

大数据期望最大化(EM)算法:从理论到实战全解析

文章目录 大数据期望最大化&#xff08;EM&#xff09;算法&#xff1a;从理论到实战全解析一、引言概率模型与隐变量极大似然估计&#xff08;MLE&#xff09;Jensen不等式 二、基础数学原理条件概率与联合概率似然函数Kullback-Leibler散度贝叶斯推断 三、EM算法的核心思想期…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Swiper容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Swiper容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Swiper容器组件 滑块视图容器&#xff0c;提供子组件滑动轮播显示的能力。…

【网站项目】基于SSM的228图书商城网站

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

openssl3.2 - 测试程序的学习 - 准备openssl测试专用工程的模板

文章目录 openssl3.2 - 测试程序的学习 - 准备openssl测试专用工程的模板概述笔记工程中需要的openssl的库实现END openssl3.2 - 测试程序的学习 - 准备openssl测试专用工程的模板 概述 openssl3.2 - 测试程序的学习 整了几个test.c, 每开一个新的测试工程, 总要改这改那的. …

Linux——进程间通信(共享内存)

目录 system V共享内存 ​编辑 共享内存函数 共享内存的建立过程 shmget函数 shmctl函数 shmat函数 shmdt函数 实例代码 共享内存的特点 system V共享内存 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间&#xff08;即内存通过某种映射关…

OpenCV-28 全局二值化

一、形态学概念 什么是形态学&#xff1f; 1&#xff09;指一系列处理图像型状特征的图像处理技术 2&#xff09;形态学的基本思想是利用一直特殊的结构元&#xff08;本质上是卷积核&#xff0c;且这个卷积核的值只有1和0&#xff09;来测量或提取输入图像中相应的型状或特…

深入了解Go语言中的HTTP代理处理机制

Go语言&#xff0c;也被称为Golang&#xff0c;自2009年诞生以来&#xff0c;凭借其简洁、高效的特点&#xff0c;逐渐在系统编程、Web开发等多个领域崭露头角。在Web开发中&#xff0c;HTTP代理处理是常见的需求之一。本文将深入探讨Go语言中的HTTP代理处理机制。 HTTP代理是…

【Java 数据结构】LinkedList与链表

LinkedList与链表 1. ArrayList的缺陷2. 链表2.1 链表的概念及结构2.2 链表的实现 3. LinkedList的模拟实现4.LinkedList的使用4.1 什么是LinkedList4.2LinkedList的使用 5. ArrayList和LinkedList的区别 1. ArrayList的缺陷 上节课已经熟悉了ArrayList的使用&#xff0c;并且…

【linux】Linux项目自动化构建工具-make/Makefile

make/makefile 背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0…

Java笔记 --- 二、Stream流

二、Stream流 结合Lambda表达式&#xff0c;简化集合、数组的操作 获取Stream流对象 单列集合获取Stream流 双列集合获取Stream流 数组获取Stream流 一堆零散的数据获取Stream流 Stream流的静态方法of的形参是一个可变参数&#xff0c;可以传递零散数据&#xff0c;也可以传递…

【计算机网络】IP协议及动态路由算法

对应代码包传送门 IP协议及动态路由算法代码包及思科模拟器资料说明 相关文章 【计算机网络】中小型校园网构建与配置 【计算机网络】Socket通信编程与传输协议分析 【计算机网络】网络应用通信基本原理 目的&#xff1a; 1、掌握IP协议&#xff0c;IP分片&#xff0c;DH…

04. OSPF

文章目录 一. 初识OSPF1.1. OSPF概述1.2. OSPF特性1.3. OSPF的专业术语1.4. OSPF维护的3张表1.5. OSPF报文类型1.6. OSPF的邻居状态 二. 实验题2.1. 实验1&#xff1a;点到点链路上的OSPF2.1.1. 实验目的2.1.2. 实验拓扑图2.1.3. 实验步骤&#xff08;1&#xff09;IP地址配置&…

【无标题】Gateway API 实践之(五)FSM Gateway 的会话保持功能

网关的会话保持功能是一种网络技术&#xff0c;旨在确保用户的连续请求在一段时间内被定向到同一台后端服务器。这种功能在需要保持用户状态或进行连续交互的场景中特别重要&#xff0c;例如在维护在线购物车、保持用户登录状态或处理多步骤事务时。 会话保持通过提供一致的用…

java判空工具类 java判断对象属性是否全部为空

java判空工具类 java判断参数是否为空工具类 java判断对象属性是否全部为空 。 可以判断字符串数组&#xff0c;字符串类型&#xff0c;List对象集合&#xff0c;Map集合map<String,object>&#xff0c;map和多个String参数&#xff0c;对象类型&#xff0c;是否为空。…