Day 10(07.08)
删除视频接口
后端
controller
//根据视频id删除阿里云视频
@DeleteMapping("removeAlyVideo/{id}")
public R removeAlyVideo(@PathVariable String id){
try{
//初始化对象
DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET);
//创建删除视频的request对象
DeleteVideoRequest request=new DeleteVideoRequest();
//向request设置视频id
request.setVideoIds(id);
//调用初始化对象的方法实现删除
client.getAcsResponse(request);
return R.ok();
}catch (Exception e){
e.printStackTrace();
throw new GuliException(20001,"删除视频失败");
}
}
前端
video.js
//删除阿里云视频
deleteAliyunvod(id){
return request({
url: `/eduvod/video/removeAlyVideo/`+id,//接口地址
method: 'delete'
})
}
chapter.vue
//点击确定调用这个方法
handleVodRemove(){
//调用接口删除视频方法
video.deleteAliyunvod(this.video.videoSourceId)
.then(response =>{
//提示信息
this.$message({
type: 'success',
message: '删除成功!'
});
//把文件列表清空
this.fileList=[]
//把video视频id和视频名称值清空
//上传视频id赋值
this.video.videoSourceId = ''
//上传视频名称赋值
this.video.videoOriginalName = ''
})
},
//点击×调用这个方法
beforeVodRemove(file,fileList){
return this.$confirm(`'确定删除' ${ file.name}?`)
},
效果
微服务
微服务是一种架构风格
把一个项目拆分成独立的多个服务,多个服务独立运行,每个服务占用独立进程
微服务每个模块都可以使用不同的存储方式(比如有的用redis,有的用mysql等),数据库也是单个模块对应自己的数据库。
微服务每个模块都可以使用不同的开发技术,开发模式更灵活。
Spring Cloud
Spring Cloud不是一种技术,是很多技术的总称,是很多框架的集合
使用 Spring Cloud 里面的这些框架实现微服务操作
使用Spring Cloud需要依赖技术 Spring Boot,但Spring Boot可以单独使用,它就是Spring的升级版
Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架; Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,必须基于Spring Boot开发。可以单独使用Spring Boot开发项目,但是Spring Cloud离不开 Spring Boot。
NACOS
简介
Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现、配置和管理。
Nacos主要提供以下四大功能:
服务发现和服务健康监测
动态配置服务
动态DNS服务
服务及其元数据管理
安装
访问:http://localhost:8848/nacos
用户名密码:nacos/nacos
删除小节删除阿里云视频
把service_edu 和service_vod服务在Nacos注册
-
引入依赖
<!--服务注册--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
-
在要注册的服务的配置文件中 application进行配置
# nacos服务地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
-
在启动类添加注解
@EnableDiscoveryClient//Nacos注册
Feign
用来调用服务
-
引入依赖
!--服务调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
调用
在调用端写代码,即在service_edu 中,在启动类加注解
@EnableFeignClients
-
在调用端创建 interface,使用注解指定调用服务名称,定义调用方法的路径
@FeignClient("service-vod") @Component public interface VodClient { //定义调用方法的路径 //根据视频id删除阿里云视频 @DeleteMapping("/eduvod/video/removeAlyVideo/{id}") public R removeAlyVideo(@PathVariable("id") String id); }
-
实现
//删除小节时,同时把里面的视频也删除 @DeleteMapping("{id}") public R deleteVideo(@PathVariable String id){ //根据小节id得到视频id,调用方法实现删除 EduVideo eduVideo = videoService.getById(id); String videoSourceId=eduVideo.getVideoSourceId(); //判断小节是否有视频id if (!StringUtils.isEmpty(videoSourceId)){ //根据视频id,远程调用service_vod中的方法实现删除 vodClient.removeAlyVideo(videoSourceId); } //删除小节 videoService.removeById(id); return R.ok(); }
删除课程同时删除视频
一个课程有多个章节,一个章节爷多个小节,每个小节都有视频
删除课程的时候,删除的视频有多个
-
在service_vod创建接口,删除多个视频
controller
//删除多个阿里云视频的方法 //参数是多个视频id @DeleteMapping("delete-batch") public R deleteBatch(@RequestParam("videoIdList") List videoIdList){ vodService.removeMoreAlyVideo(videoIdList); return R.ok(); }
service
@Override public void removeMoreAlyVideo(List videoIdList) { try{ //初始化对象 DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET); //创建删除视频的request对象 DeleteVideoRequest request=new DeleteVideoRequest(); //videoList值转换成 1,12,35 String videoIds = StringUtils.join(videoIdList.toArray(), ","); //向request设置视频id request.setVideoIds(videoIds); //调用初始化对象的方法实现删除 client.getAcsResponse(request); }catch (Exception e){ e.printStackTrace(); throw new GuliException(20001,"删除视频失败"); } }
-
在service_edu中调用
EduVideoServiceImpl代码
//1 根据课程id删除小节 @Override public void removeVideoByCourseId(String courseId) { //根据课程id查询出课程所有的视频id QueryWrapper<EduVideo> wrapperVideo=new QueryWrapper<>(); wrapperVideo.eq("course_id",courseId); wrapperVideo.select("video_source_id"); List<EduVideo> eduVideoList = baseMapper.selectList(wrapperVideo); //把List<EduVideo>变成List<String> List<String> videoIds=new ArrayList<>(); for (int i = 0; i < eduVideoList.size(); i++) { EduVideo eduVideo=eduVideoList.get(i); String videoSourceId=eduVideo.getVideoSourceId(); if (!StringUtils.isEmpty(videoSourceId)){ //如果视频id为空就不放到集合里面 videoIds.add(videoSourceId); } } //根据多个视频id删除多个视频 if (videoIds.size()>0){ //如果集合为空,不做删除操作 vodClient.deleteBatch(videoIds); } QueryWrapper<EduVideo> wrapper=new QueryWrapper<>(); wrapper.eq("course_id",courseId); baseMapper.delete(wrapper); }
Hystrix
Spring Cloud 过程
Spring Cloud 在接口调用上,大致会经过如下几个组件配合:
Feign
----->Hystrix
—>Ribbon
—>Http Client(apache http components 或者 Okhttp)
Feign:服务发现。根据名字找到服务并调用
Hystrix:熔断器。如果Feign能够调到服务,则不做操作,执行调用,如果调不到服务,则执行熔断,切断调用过程
Ribbon:负载均衡。将请求平均分摊到集群的服务器中
Http CLient:真正的调用。
Hystrix概念与使用
概念
Hystrix 是一个供分布式系统使用,提供延迟和容错功能,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。
使用
-
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <!--hystrix依赖,主要是用 @HystrixCommand --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
修改配置文件
#开启熔断机制 feign.hystrix.enabled=true # 设置hystrix超时时间,默认1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
-
在创建interface之后,需要创建对应的实现类
@Component public class VodFileDegradeFeignClient implements VodClient{ //出错之后会执行 @Override public R removeAlyVideo(String id) { return R.error().message("删除视频出错"); } @Override public R deleteBatch(List<String> videoIdList) { return R.error().message("删除多个视频出错"); } }
-
在interface添加注解
@FeignClient(name = "service-vod",fallback = VodFileDegradeFeignClient.class)//调用服务的名字和失败的实现类
Day 11(07.09)
搭建前台环境
NUXT
是node.js的一个框架,把数据一次性返回到浏览器中
框架目录结构
先加载layouts的default.vue页面再通过nuxt标签加载到pages的index.vue页面
路由
固定路由:路径是固定地址,不发生变化
通过to设置路由跳转地址
做法:在pages文件夹course文件夹,创建index.vue
动态路由:每次生成路由地址不一样,如每个课程详情页面,每个课程id不同
做法:NUXT的动态路由是以下划线开头的vue文件,参数名为下划线后边的文件名
整合前台页面
-
安装组件
npm install vue-awesome-swiper@3.1.3
-
配置幻灯片插件
-
复制项目中使用的静态资源到assets中
首页显示banner数据
创建新模块service_cms
后台管理banner接口
@RestController
@RequestMapping("/educms/banneradmin")
@CrossOrigin
public class BannerAdminController {
@Autowired
private CrmBannerService bannerService;
//1 分页查询banner
@GetMapping("pageBanner/{page}/{limit}")
public R pageBanner(@PathVariable long page,@PathVariable long limit){
Page<CrmBanner> pageBanner=new Page<>(page,limit);
bannerService.page(pageBanner,null);
return R.ok().data("items",pageBanner.getRecords()).data("total",pageBanner.getTotal());
}
//2 添加banner
@PostMapping("addBanner")
public R addBanner(@RequestBody CrmBanner crmBanner){
bannerService.save(crmBanner);
return R.ok();
}
@ApiOperation(value = "获取Banner")
@GetMapping("get/{id}")
public R get(@PathVariable String id) {
CrmBanner banner = bannerService.getById(id);
return R.ok().data("item", banner);
}
@ApiOperation(value = "修改Banner")
@PutMapping("update")
public R updateById(@RequestBody CrmBanner banner) {
bannerService.updateById(banner);
return R.ok();
}
@ApiOperation(value = "删除Banner")
@DeleteMapping("remove/{id}")
public R remove(@PathVariable String id) {
bannerService.removeById(id);
return R.ok();
}
}
前台显示banner接口
@RestController
@RequestMapping("/educms/bannerfront")
@CrossOrigin
public class BannerFrontController {
@Autowired
private CrmBannerService bannerService;
//查询所有banner
@ApiOperation(value = "获取首页banner")
@GetMapping("getAllBanner")
public R index() {
List<CrmBanner> list = bannerService.selectAllBanner();
return R.ok().data("list", list);
}
}
@Service
public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
//查询所有banner
@Override
public List<CrmBanner> selectAllBanner() {
//根据id降序排列,显示排列后的前两条记录
QueryWrapper<CrmBanner> wrapper=new QueryWrapper<>();
wrapper.orderByDesc("id");
//last方法,拼接SQL语句
wrapper.last("limit 2");
List<CrmBanner> list=baseMapper.selectList(null);
return list;
}
}
首页热门课程名师
@RestController
@RequestMapping("/eduservice/indexfront")
@CrossOrigin
public class IndexFrontController {
@Autowired
private EduCourseService courseService;
@Autowired
private EduTeacherService teacherService;
//查询前8条热门课程,查询前4条名师
@GetMapping("index")
public R index() {
//查询前8条热门课程
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
wrapper.last("limit 8");
List<EduCourse> eduList = courseService.list(wrapper);
//查询前4条名师
QueryWrapper<EduTeacher> wrapperTeacher = new QueryWrapper<>();
wrapperTeacher.orderByDesc("id");
wrapperTeacher.last("limit 4");
List<EduTeacher> teacherList = teacherService.list(wrapperTeacher);
return R.ok().data("eduList", eduList).data("teacherList", teacherList);
}
}
前端页面准备工作
下载axios依赖
npm install axios
封装axios
首页banner数据显示
-
创建api文件夹,创建js文件,定义调用接口路径
import request from '@/utils/request' export default { getListBanner() {//查询前2条banner信息 return request({ url: '/educms/bannerfront/getAllBanner', method: 'get' }) } }
-
在index.vue进行调用
created(){ //调用查询banner数据的方法 this.getBannerList() //调用查询热门课程和讲师 this.getHotCourseTeacher() }, methods:{ //查询热门课程和讲师 getHotCourseTeacher(){ index.getIndexData() .then(response =>{ this.eduList=response.data.data.eduList this.teacherList=response.data.data.teacherList }) }, //查询banner数据 getBannerList(){ banner.getListBanner() .then(response =>{ this.bannerList=response.data.data.list }) } } }
-
nginx添加访问配置
首页数据使用Redis
Redis
基于key-value进行存储
支持多种数据结构:string;list;hash;set;zset(有序集合)
支持持久化,通过内存进行存储,可以存到硬盘里面
支持过期时间,支持事务
一般把经常进行查询,不经常修改,不是特别重要的数据放到Redis作为缓存
-
创建Redis配置类,写到common里面
-
在查询所有banner方法上天极爱注解@Cacheable
连接Redis
-
关闭iunx防火墙
-
找到redis配置文件, 注释一行配置
#bind 127.0.0.1
-
修改 protected-mode yes为no
在service_cms配置Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
存在问题
- 搭建前台环境出错
解决方案,输入以下命令
npm install @babel/core @babel/preset-env
接着报下一个错误
原因是@babel/preset-env的版本原因,解决方案,输入以下命令
npm i @babel/preset-env@7.12.17
成功解决问题
Day 12(07.09)
登录
单一服务器登录
使用session对象实现,把用户数据放到session里面,判断是否登录,从session获取数据,可以获取到登录
session.setAttribute(“user”,user)
session.getAttribute(user)
单点登录(Single Sign On)
概念:项目中多个模块,在任何一个模块登录,其他任何模块就不用再登录了
实现方式:
-
session广播机制
session的复制,把登录模块的session复制到所有模块
缺点:当模块过多时造成资源浪费,数据重复
-
cookie+Redis实现
cookie是客户端机制,每次发送请求,带着cookie进行发送
-
在项目中任何一个模块进行登录,登陆之后,把数据放到两地方
Redis:key:生成唯一随机值(id、ip等等) value:用户数据
cookie:把Redis生成的key放到cookie中
-
访问项目中其他模块,发送请求带着cookie发送,获取cookie值,拿着cookie做事情:
把cookie获取值放到Redis中根据key查询,查得到数据就是登录
-
-
token实现
token:按照一定规则生成字符串,字符串可以包含用户信息
- 在项目某个模块进行登录,登录之后,按照规则生成字符串,把登录之后用户包含到生成字符串里面,把字符串返回
- 可以把字符串通过cookie返回
- 把字符串通过地址栏返回
- 再去访问其他模块,每次访问在地址栏获取字符串,根据字符串获取用户信息,如果可以获取,就是一个登录
- 在项目某个模块进行登录,登录之后,按照规则生成字符串,把登录之后用户包含到生成字符串里面,把字符串返回
JWT
JWT规定好了token规则,可以删除字符串,包含用户信息
三部分:
第一部分:JWT 头信息
第二部分:有效载荷,包含主题信息(用户信息)
第三部分:签名哈希 防伪标志
使用
-
引入依赖
<dependencies> <!-- JWT--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> </dependencies>
短信服务
阿里云短信服务
申请模板管理
申请签名管理
创建service_msm模块
创建包结构,创建controller和service,配置类,启动类
controller
@RestController
@RequestMapping("/edumsm/msm")
@CrossOrigin
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@GetMapping("send/{phone}")
public R sendMsm(@PathVariable String phone){
//1 从Redis中获取验证码,获取的到直接返回
String code = redisTemplate.opsForValue().get(phone);
if (!StringUtils.isEmpty(code)){
return R.ok();
}
//2 Redis获取不到,进行阿里云发送
//生成随机值,传递阿里云进行发送
code= RandomUtil.getFourBitRandom();
Map<String,Object> param=new HashMap<>();
param.put("code",code);
//调用service发送短信的方法
boolean isSend=msmService.send(param,phone);
if (isSend){
//发送成功,把发送成功验证码放到Redis里面
//设置有效时间
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
return R.ok();
}else {
return R.error().message("短信发送失败");
}
}
}
service
@Service
public class MsmServiceImpl implements MsmService {
//发送短信验证码
@Override
public boolean send(Map<String, Object> param, String phone) {
if(StringUtils.isEmpty(phone)) return false;
DefaultProfile profile =
DefaultProfile.getProfile("default", "LTAI4FvvVEWiTJ3GNJJqJnk7", "9st82dv7EvFk9mTjY01XXbM632fRbG");
IAcsClient client = new DefaultAcsClient(profile);
//设置相关固定的参数
CommonRequest request = new CommonRequest();
//request.setProtocol(ProtocolType.HTTPS);
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2017-05-25");
request.setAction("SendSms");
//设置发送相关参数
request.putQueryParameter("PhoneNumbers", phone);//手机号
request.putQueryParameter("SignName", "我的谷粒在线教育网站");//签名名称
request.putQueryParameter("TemplateCode", "SMS_180051135");//模板code
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));//验证码数据,转换成json数据
//最终发送
try {
CommonResponse response = client.getCommonResponse(request);
boolean success=response.getHttpResponse().isSuccess();
return success;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Redis解决验证码的有效时间
登录注册接口
创建service_ucenter模块
数据库创建用户表,使用代码生成器
启动类
@ComponentScan("com.lujin")
@SpringBootApplication
@MapperScan("com.lujin.educenter.mapper")
public class UcenterApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterApplication.class,args);
}
}
登录
-
controller
//登录 @PostMapping("login") public R loginUser(@RequestBody UcenterMember member){ //调用service方法实现登录 //返回token值 String token= memberService.login(member); return R.ok().data("token",token); }
-
service
//登录的方法 @Override public String login(UcenterMember member) { //获取登录的手机号和密码 String mobile=member.getMobile(); String password = member.getPassword(); //手机号和密码非空判断 if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){ throw new GuliException(20001,"登录失败"); } //判断手机号是否正确 QueryWrapper<UcenterMember> wrapper=new QueryWrapper<>(); wrapper.eq("monbile",mobile); UcenterMember mobileMember = baseMapper.selectOne(wrapper); //判断查询对象是否为空 if (mobileMember==null){ throw new GuliException(20001,"登录失败"); } //判断密码是否正确 //因为存储到数据库中的密码是加密的,先将输入的密码进行加密再和数据库比较 //加密方式MD5(只能加密,本身不能解密) if (!MD5.encrypt(password).equals(mobileMember.getPassword())){ throw new GuliException(20001,"登录失败"); } //判断用户是否被禁用 if (mobileMember.getIsDisabled()){ throw new GuliException(20001,"登录失败"); } //登录成功 //生成token字符串,使用JWT工具 String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname()); return jwtToken; }
注册
-
创建实体类用于封装注册的数据,包含验证码属性
@Data @ApiModel(value="注册对象", description="注册对象") public class RegisterVo { @ApiModelProperty(value = "昵称") private String nickname; @ApiModelProperty(value = "手机号") private String mobile; @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "验证码") private String code; }
-
controller
//注册 @PostMapping("register") public R registerUser(@RequestBody RegisterVo registerVo){ memberService.register(registerVo); return R.ok(); }
-
service
//注册的方法 @Override public void register(RegisterVo registerVo) { //获取注册的数据 String code = registerVo.getCode();//验证码 String mobile = registerVo.getMobile();//手机号 String nickname = registerVo.getNickname();//昵称 String password = registerVo.getPassword();//密码 //非空判断 if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password) || StringUtils.isEmpty(code) || StringUtils.isEmpty(nickname)){ throw new GuliException(20001,"登录失败"); } //判断手机验证码是否正确 //获取Redis验证码 String redisCode = redisTemplate.opsForValue().get(mobile); if (!code.equals(redisCode)){ throw new GuliException(20001,"注册失败"); } //判断手机号是否重复,表中存在相同手机号,不进行添加 QueryWrapper<UcenterMember> wrapper=new QueryWrapper<>(); wrapper.eq("mobile",mobile); Integer count = baseMapper.selectCount(wrapper); if (count>0){ throw new GuliException(20001,"注册失败"); } //将数据添加到数据库中 UcenterMember member=new UcenterMember(); member.setMobile(mobile); member.setNickname(nickname); member.setPassword(MD5.encrypt(password));//密码需要加密 member.setIsDisabled(false);//默认不禁用 member.setAvatar("");//头像 baseMapper.insert(member); }
-
创建token获取用户信息
//根据token获取用户信息 @GetMapping("getMenberInfo") public R getMenberInfo(HttpServletRequest request){ //调用JWT工具类方法,得到request对象的头信息,返回用户id String memberId = JwtUtils.getMemberIdByJwtToken(request); //查询数据库,根据用户id获取用户信息 UcenterMember member = memberService.getById(memberId); return R.ok().data("userInfo",member); }
整合页面
在nuxt环境安装element-ui
npm install element-ui
修改配置文件
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
import VueQriously from 'vue-qriously'
import ElementUI from 'element-ui' //element-ui的全部组件
import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
Vue.use(ElementUI) //使用elementUI
Vue.use(VueQriously)
Vue.use(VueAwesomeSwiper)
整合注册页面
-
在layouts文件夹创建登录注册布局页面sign.vue
<template> <div class="sign"> <!--标题--> <div class="logo"> <img src="~/assets/img/logo.png" alt="logo"> </div> <!--表单--> <nuxt/> </div> </template>
-
修改layouts文件夹里面default.vue页面,修改登录和注册超链接地址
-
在pages文件夹下创建注册页面
-
创建登录页面
前端
验证码倒计时通过js 定时器的方法,每个一段时间执行一次js方法
<script>
setInterval("alert('test')",3000);
</script>
前端代码
//注册提交方法
submitRegister(){
registerApi.registerMember(this.params)
.then(response =>{
//提示信息
this.$message({
type: 'success',
message: "注册成功"
})
//跳转到登录页面
this.$router.push({path: '/login'})
})
},
//通过输入手机号发送验证码
getCodeFun(){
registerApi.sendCode(this.params.mobile)
.then(response =>{
this.sending=false
//调用倒计时的方法
this.timeDown()
})
},
//倒计时
timeDown() {
let result = setInterval(() => {
--this.second;
this.codeTest = this.second
if (this.second < 1) {
clearInterval(result);
this.sending = true;
//this.disabled = false;
this.second = 60;
this.codeTest = "获取验证码"
}
}, 1000);
},
checkPhone (rule, value, callback) {
//debugger
if (!(/^1[34578]\d{9}$/.test(value))) {
return callback(new Error('手机号码格式不正确'))
}
return callback()
}
}
自动校验,失去焦点触发规则
:rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]//validator为自己写的规则
})
//跳转到登录页面
this.$router.push({path: '/login'})
})
},
//通过输入手机号发送验证码
getCodeFun(){
registerApi.sendCode(this.params.mobile)
.then(response =>{
this.sending=false
//调用倒计时的方法
this.timeDown()
})
},
//倒计时
timeDown() {
let result = setInterval(() => {
--this.second;
this.codeTest = this.second
if (this.second < 1) {
clearInterval(result);
this.sending = true;
//this.disabled = false;
this.second = 60;
this.codeTest = "获取验证码"
}
}, 1000);
},
checkPhone (rule, value, callback) {
//debugger
if (!(/^1[34578]\d{9}$/.test(value))) {
return callback(new Error('手机号码格式不正确'))
}
return callback()
}
}
自动校验,失去焦点触发规则
```js
:rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]//validator为自己写的规则