目录
1.1 verifySlipping.component.html
1.2 verifySlipping.component.css
1.3 verifySlipping.component.ts
2.1 保持 loginService 服务为单例,避免路由守卫出 bug
3. 修改密码,传参格式 FORM['USER_ID'] 的写法
验证码校验功能参考开源项目 AJ-Captcha
将验证码功能,嵌入你的项目里,需要添加以下依赖:
yarn add crypto-js
yarn add jwt-decode
yarn add jquery
三点说明:
下面介绍下验证码组件相关内容
注释掉的部分:验证码提示框的头部,一般情况下用不上,会导致显示混乱
此处需要注意两个ID,这俩 ID 在组件中会被 jquery 操作控制显隐:
文件地址:src\app\components\verify\verifySlipping.component.html
- <div
- class="modal fade"
- id="slipping"
- tabindex="-1"
- role="dialog"
- aria-labelledby="myLargeModalLabel"
- >
- <div class="modal-dialog modal-dialog-centered" role="document">
- <div class="modal-content">
-
-
- <div class="modal-body">
- <div class="box">
- <div class="verifybox-top">
- <div class="box">
- <div id="mpanel1">div>
- div>
- div>
- div>
- div>
- div>
- div>
- div>
验证码弹框组件的基本样式
注意:验证码弹框的宽度是写死的,这会导致屏幕小的手机,显示不全验证码弹框
由于时间紧促,我没研究改哪些宽度百分比,而是直接使用了 scale(0.85) 缩小移动端弹框
文件地址:src\app\components\verify\verifySlipping.component.css
- .btn {
- border: none;
- outline: none;
- width: 300px;
- height: 40px;
- line-height: 40px;
- text-align: center;
- cursor: pointer;
- background-color: #409eff;
- color: #fff;
- font-size: 16px;
- letter-spacing: 1em;
- }
-
- .modal-dialog {
- width: 466px;
- }
global.scss 下,补充了全局验证码弹框样式
之前打算使用 important 强行覆盖 jq 动态写入的验证码弹框宽度,后来因为要修改的地方太多,懒得改了,所以采用了 transform: translate(-50%, -50%) scale(0.85);
文件地址:src\global.scss
- /**
- * 安全验证
- */
- .verify-code {
- font-size: 20px;
- text-align: center;
- cursor: pointer;
- margin-bottom: 5px;
- border: 1px solid #ddd;
- }
-
- .cerify-code-panel {
- height: 100%;
- overflow: hidden;
- }
-
- .verify-code-area {
- float: left;
- }
-
- .verify-input-area {
- float: left;
- width: 60%;
- padding-right: 10px;
- }
-
- .verify-change-area {
- line-height: 30px;
- float: left;
- }
-
- .varify-input-code {
- display: inline-block;
- width: 100%;
- height: 25px;
- }
-
- .verify-change-code {
- color: #337ab7;
- cursor: pointer;
- }
-
- .verify-btn {
- width: 200px;
- height: 30px;
- background-color: #337ab7;
- color: #ffffff;
- border: none;
- margin-top: 10px;
- }
-
- .verifybox {
- position: relative;
- box-sizing: border-box;
- border-radius: 2px;
- background-color: #fff;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%) scale(0.85);
- }
- .verifybox-top {
- padding: 0 15px;
- /* height: 50px; */
- line-height: 50px;
- text-align: left;
- font-size: 16px;
- color: #45494c;
- box-sizing: border-box;
- }
- .verifybox-bottom {
- padding: 15px;
- box-sizing: border-box;
- }
- .verifybox-close {
- position: absolute;
- top: 13px;
- right: 9px;
- width: 24px;
- height: 24px;
- line-height: 24px;
- text-align: center;
- cursor: pointer;
- }
- .mask {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1001;
- width: 100%;
- height: 100vh;
- background: rgba(0, 0, 0, 0.3);
- /* display: none; */
- transition: all 0.5s;
- display: none;
- }
-
- .verify-tips {
- position: absolute;
- display: none;
- left: 0px;
- bottom: -35px;
- width: 100%;
- height: 30px;
- /* transition: all .5s; */
- line-height: 30px;
- color: #fff;
- /* animation:move 1.5s linear; */
- }
-
- @keyframes move {
- 0% {
- bottom: -35px;
- }
- 50%,
- 80% {
- bottom: 0px;
- }
- 100% {
- bottom: -35px;
- }
- }
-
- .suc-bg {
- background-color: rgba(92, 184, 92, 0.5);
- filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7f5CB85C, endcolorstr=#7f5CB85C);
- }
- .err-bg {
- background-color: rgba(217, 83, 79, 0.5);
- filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7fD9534F, endcolorstr=#7fD9534F);
- }
-
- /*滑动验证码*/
- .verify-bar-area {
- position: relative;
- background: #ffffff;
- text-align: center;
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- border: 1px solid #ddd;
- -webkit-border-radius: 4px;
- }
-
- .verify-bar-area .verify-move-block {
- position: absolute;
- top: 0px;
- left: 0;
- background: #fff;
- cursor: pointer;
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- box-shadow: 0 0 2px #888888;
- -webkit-border-radius: 1px;
- }
-
- .verify-bar-area .verify-move-block:hover {
- background-color: #337ab7;
- color: #ffffff;
- }
-
- .verify-bar-area .verify-left-bar {
- position: absolute;
- top: -1px;
- left: -1px;
- background: #f0fff0;
- cursor: pointer;
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- border: 1px solid #ddd;
- }
-
- .verify-img-panel {
- margin: 0;
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- border: 1px solid #ddd;
- border-radius: 3px;
- position: relative;
- }
-
- .verify-img-panel .verify-refresh {
- width: 25px;
- height: 25px;
- text-align: center;
- padding: 5px;
- cursor: pointer;
- position: absolute;
- top: 0;
- right: 0;
- z-index: 2;
- }
-
- .verify-img-panel .icon-refresh {
- font-size: 20px;
- color: #fff;
- }
-
- .verify-img-panel .verify-gap {
- background-color: #fff;
- position: relative;
- z-index: 2;
- border: 1px solid #fff;
- }
-
- .verify-bar-area .verify-move-block .verify-sub-block {
- position: absolute;
- text-align: center;
- z-index: 3;
- /* border: 1px solid #fff; */
- }
-
- .verify-bar-area .verify-move-block .verify-icon {
- font-size: 18px;
- }
-
- .verify-bar-area .verify-msg {
- z-index: 3;
- }
-
- /* 字体图标的css */
- @font-face {
- font-family: "iconfont";
- src: url("./assets/font/iconfont.eot?t=1508229193188"); /* IE9*/
- src: url("./assets/font/iconfont.eot?t=1508229193188#iefix")
- format("embedded-opentype"),
- /* IE6-IE8 */
- url("data:application/x-font-woff;charset=utf-8;base64,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")
- format("woff"),
- url("./assets/font/iconfont.ttf?t=1508229193188") format("truetype"),
- /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url("./assets/font/iconfont.svg?t=1508229193188#iconfont") format("svg"); /* iOS 4.1- */
- }
-
- .iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
-
- .icon-check:before {
- content: "\e645";
- }
-
- .icon-close:before {
- content: "\e646";
- }
-
- .icon-right:before {
- content: "\e6a3";
- }
-
- .icon-refresh:before {
- content: "\e6a4";
- }
官网示例中,初始化验证码是在组件里进行的
但是,我的项目在 初始化验证码请求图片 时,需要传入额外的参数;因此,我注释了组件内的 初始化验证码 逻辑
文件地址:src\app\components\verify\verifySlipping.component.ts
- import { Component } from '@angular/core';
- import './verify/verify.js';
-
- @Component({
- selector: 'verify-slipping',
- templateUrl: './verifySlipping.component.html',
- styleUrls: ['./verifySlipping.component.css'],
- })
- export class verifySlippingComponent {
- /**
- * 页面初始化
- */
- ngOnInit(): void {
- // 引入 promise
- if (!window.Promise) {
- document.writeln(
- '