项目初始化

This commit is contained in:
Taric Xin
2021-11-26 16:34:35 +08:00
parent 66644bcf0a
commit 5287578452
354 changed files with 45736 additions and 0 deletions

View File

@ -0,0 +1 @@
<div id="captcha"></div>

View File

@ -0,0 +1,6 @@
:host {
::ng-deep {
.captcha-box {
}
}
}

View File

@ -0,0 +1,79 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { keysConf } from '@conf/keys.conf';
import { Subject } from 'rxjs';
import { EACaptchaService } from '../../services';
import { initNECaptchaWithFallback } from './dun';
@Component({
selector: 'app-captcha',
templateUrl: './captcha.component.html',
styleUrls: ['./captcha.component.less'],
})
export class CaptchaComponent implements OnInit {
@Input() phone!: string; // 手机号
@Input() url!: string; // api地址
@Output() done = new EventEmitter<any>();
captchaIns: any;
initSubject = new Subject<any>();
constructor(public captchaService: EACaptchaService) {}
ngOnInit() {}
init() {
const _this = this;
if (this.captchaIns) {
return this.initSubject;
}
initNECaptchaWithFallback(
{
element: '#captcha',
captchaId: keysConf.yidun_capcha_id,
mode: 'popup',
width: '320px',
onClose: () => {
// 弹出关闭结束后将会触发该函数
},
onVerify: (err: any, data: any) => {
// console.log('🚀 ~ init ~ data', data);
if (data?.validate) {
// 验证通过,获取验证码
_this.captchaDone(data?.validate);
}
},
},
(instance: any) => {
// console.log('🚀 ~ initCaptcha ~ instance', instance);
// 初始化成功后得到验证实例instance可以调用实例的方法
_this.captchaIns = instance;
this.initSubject.next(_this.captchaIns);
},
(err: any) => {
// 初始化失败后触发该函数err对象描述当前错误信息
},
);
return this.initSubject;
}
/* 网易盾验证通过 */
captchaDone(validate: any) {
this.captchaService.getCaptchaByDun(this.phone, validate, this.url || undefined).subscribe((res: any) => {
// console.log('🚀 ~ 验证通过发送验证码=>', res);
if (res) {
this.captchaService.msgSrv.success('验证码发送成功!');
this.done.emit(null);
} else {
this.captchaService.msgSrv.warning(res.msg);
}
});
}
popUp() {
console.log(222222222222222222222222222222222222222);
if (!this.captchaIns) {
this.init();
}
this.captchaIns.refresh();
this.captchaIns.popUp();
}
}

View File

@ -0,0 +1,84 @@
import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { ComponentRef, Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { CaptchaComponent } from './captcha.component';
@Injectable({
providedIn: 'root',
})
export class DunHelper {
captchacontainerRef!: ComponentRef<CaptchaComponent>;
userInfo;
constructor(private overlay: Overlay) {
this.userInfo = JSON.parse(localStorage.getItem('user') || '');
}
/**
* 组件初始化
* @param phone 手机号
* @param url 发送验证码请求地址
*/
init(phone: string, url?: string): Subject<any> {
const overlayRef = this.createOverlay();
const containerPortal = new ComponentPortal(CaptchaComponent);
this.captchacontainerRef = overlayRef.attach<CaptchaComponent>(containerPortal);
this.captchacontainerRef.instance.phone = phone;
this.captchacontainerRef.instance.url = url || '';
return this.captchacontainerRef.instance.init();
}
/**
* 弹出滑块验证
* @param phone 手机号
* @param url 发送验证码请求地址
*/
popUp(phone?: string, url?: string): Observable<any> {
if (this.captchacontainerRef) {
this.destory();
}
this.init(phone || this.userInfo?.phone, url).subscribe((instance) => {
if (instance) {
this.captchacontainerRef.instance.popUp();
}
});
/* if (!this.captchacontainerRef) {
this.init(phone || this.userInfo?.phone, url).subscribe(instance => {
if (instance) {
this.captchacontainerRef.instance.popUp();
}
});
} else {
if (!!phone && !!url) {
this.init(phone || this.userInfo?.phone, url).subscribe(instance => {
if (instance) {
this.captchacontainerRef.instance.popUp();
}
});
} else {
this.captchacontainerRef.instance.popUp();
}
} */
return this.captchacontainerRef.instance.done;
}
/** 组件销毁 */
destory() {
this.captchacontainerRef.destroy();
}
private createOverlay(): OverlayRef {
const overlayConfig = new OverlayConfig({
hasBackdrop: false,
scrollStrategy: this.overlay.scrollStrategies.block(),
positionStrategy: this.overlay.position().global(),
backdropClass: 'captcha-back-drop',
panelClass: 'captcha-overlay',
});
return this.overlay.create(overlayConfig);
}
}

View File

@ -0,0 +1,353 @@
/* eslint-disable no-undef */
let errorCallbackCount: any = 0;
// 常量
const DEFAULT_VALIDATE =
'QjGAuvoHrcpuxlbw7cp4WnIbbjzG4rtSlpc7EDovNHQS._ujzPZpeCInSxIT4WunuDDh8dRZYF2GbBGWyHlC6q5uEi9x-TXT9j7J705vSsBXyTar7aqFYyUltKYJ7f4Y2TXm_1Mn6HFkb4M7URQ_rWtpxQ5D6hCgNJYC0HpRE7.2sttqYKLoi7yP1KHzK-PptdHHkVwb77cwS2EJW7Mj_PsOtnPBubTmTZLpnRECJR99dWTVC11xYG0sx8dJNLUxUFxEyzTfX4nSmQz_T5sXATRKHtVAz7nmV0De5unmflfAlUwMGKlCT1khBtewlgN5nHvyxeD8Z1_fPVzi9oznl-sbegj6lKfCWezmLcwft8.4yaVh6SlzXJq-FnSK.euq9OBd5jYc82ge2_hEca1fGU--SkPRzgwkzew4O4qjdS2utdPwFONnhKAIMJRPUmCV4lPHG1OeRDvyNV8sCnuFMw7leasxIhPoycl4pm5bNy70Z1laozEGJgItVNr3'; // 默认validate
const FALLBACK_LANG: any = {
'zh-CN': '前方拥堵,已自动跳过验证',
en: 'captcha errorVerified automatically',
};
const CACHE_MIN = 1000 * 60; // 缓存时长单位1分钟
const REQUEST_SCRIPT_ERROR = 502;
const RESOURCE_CACHE: any = {};
// 工具函数
function loadScript(src: any, cb: any) {
const head: any = document.head || document.getElementsByTagName('head')[0];
const script: any = document.createElement('script');
cb = cb || function () {};
script.type = 'text/javascript';
script.charset = 'utf8';
script.async = true;
script.src = src;
if (!('onload' in script)) {
script.onreadystatechange = function () {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') {
return;
}
this.onreadystatechange = null;
cb(null, script); // there is no way to catch loading errors in IE8
};
}
script.onload = function () {
this.onerror = this.onload = null;
cb(null, script);
};
script.onerror = function () {
// because even IE9 works not like others
this.onerror = this.onload = null;
cb(new Error('Failed to load ' + this.src), script);
};
head.appendChild(script);
}
function joinUrl(protocol: any, host: any, path: any) {
protocol = protocol || '';
host = host || '';
path = path || '';
if (protocol) {
protocol = protocol.replace(/:?\/{0,2}$/, '://');
}
if (host) {
const matched = host.match(/^([-0-9a-zA-Z.:]*)(\/.*)?/);
host = matched[1];
path = (matched[2] || '') + '/' + path;
}
!host && (protocol = '');
return protocol + host + path;
}
function setDomText(el: any, value: any) {
if (value === undefined) {
return;
}
const nodeType = el.nodeType;
if (nodeType === 1 || nodeType === 11 || nodeType === 9) {
if (typeof el.textContent === 'string') {
el.textContent = value;
} else {
el.innerText = value;
}
}
}
function queryAllByClassName(selector: any, node: any) {
node = node || document;
if (node.querySelectorAll) {
return node.querySelectorAll(selector);
}
if (!/^\.[^.]+$/.test(selector)) {
return [];
}
if (node.getElementsByClassName) {
return node.getElementsByClassName(selector);
}
const children = node.getElementsByTagName('*');
let current;
const result = [];
const className = selector.slice(1);
for (let i = 0, l = children.length; i < l; i++) {
current = children[i];
if (~(' ' + current.className + ' ').indexOf(' ' + className + ' ')) {
result.push(current);
}
}
return result;
}
function assert(condition: any, msg: any) {
if (!condition) {
throw new Error('[NECaptcha] ' + msg);
}
}
function isInteger(val: any) {
if (Number.isInteger) {
return Number.isInteger(val);
}
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
}
function isArray(val: any) {
if (Array.isArray) {
return Array.isArray(val);
}
return Object.prototype.toString.call(val) === '[object Array]';
}
function ObjectAssign(a: any, b: any, c: any) {
if (Object.assign) {
// return Object.assign.apply(null, arguments);
return Object.assign.apply(null, arguments as any);
}
const target: any = {};
for (let index = 1; index < arguments.length; index++) {
const source = arguments[index];
if (source != null) {
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
}
function getTimestamp(msec: any) {
msec = !msec && msec !== 0 ? msec : 1;
return parseInt((new Date().valueOf() / msec).toString(), 10);
}
// 降级方案
function normalizeFallbackConfig(customConfig: any) {
const siteProtocol = window.location.protocol.replace(':', '');
const defaultConf: any = {
protocol: siteProtocol === 'http' ? 'http' : 'https',
lang: 'zh-CN',
errorFallbackCount: 3,
};
const config: any = ObjectAssign({}, defaultConf, customConfig);
const errorFallbackCount: any = config.errorFallbackCount;
assert(
errorFallbackCount === undefined || (isInteger(errorFallbackCount) && errorFallbackCount >= 1),
"errorFallbackCount must be an integer, and it's value greater than or equal one",
);
return config;
}
function loadResource(config: any, cb: any) {
if ((window as any).initNECaptcha) {
return cb(null);
}
function genUrl(server: any) {
const path = 'load.min.js';
let _urls = [];
if (isArray(server)) {
for (let i = 0, len = server.length; i < len; i++) {
_urls.push(joinUrl(config.protocol, server[i], path));
}
} else {
const url = joinUrl(config.protocol, server, path);
_urls = [url, url];
}
return _urls;
}
const urls = genUrl(config.staticServer || ['cstaticdun.126.net', 'cstaticdun1.126.net', 'cstatic.dun.163yun.com']);
function step(i: any) {
const url = urls[i] + '?v=' + getTimestamp(CACHE_MIN);
loadScript(url, function (err: any) {
if (err || !(window as any).initNECaptcha) {
// loadjs的全局变量
i = i + 1;
if (i === urls.length) {
return cb(new Error('Failed to load script(' + url + ').' + (err ? err.message : 'unreliable script')));
}
return step(i);
}
return cb(null);
});
}
step(0);
}
/*
* entry: initNECaptchaWithFallback
* options:
* errorFallbackCount: 触发降级的错误次数,默认第三次错误降级
* defaultFallback: 是否开启默认降级
* onFallback: 自定义降级方案参数为默认validate
*/
export function initNECaptchaWithFallback(options: any, onload: any, onerror: any) {
let captchaIns: any = null;
const config = normalizeFallbackConfig(options);
const defaultFallback = config.defaultFallback !== false;
const langPkg = FALLBACK_LANG[config.lang === 'zh-CN' ? config.lang : 'en'];
const storeKey = window.location.pathname + '_' + config.captchaId + '_NECAPTCHA_ERROR_COUNTS';
try {
errorCallbackCount = parseInt(localStorage.getItem(storeKey)?.toString() || '0', 10);
} catch (error) {}
const fallbackFn = !defaultFallback
? config.onFallback || function () {}
: (validate: any) => {
function setFallbackTip(instance: any) {
if (!instance) {
return;
}
setFallbackTip(instance._captchaIns);
if (!instance.$el) {
return;
}
const tipEles = queryAllByClassName('.yidun-fallback__tip', instance.$el);
if (!tipEles.length) {
return;
}
// 确保在队列的最后
setTimeout(() => {
for (let i = 0, l = tipEles.length; i < l; i++) {
setDomText(tipEles[i], langPkg);
}
}, 0);
}
setFallbackTip(captchaIns);
config.onVerify && config.onVerify(null, { validate: validate });
};
const noFallback = !defaultFallback && !config.onFallback;
const proxyOnError = (error: any) => {
errorCallbackCount++;
if (errorCallbackCount < config.errorFallbackCount) {
try {
localStorage.setItem(storeKey, errorCallbackCount);
} catch (err) {}
onerror(error);
} else {
fallbackFn(DEFAULT_VALIDATE);
proxyRefresh();
noFallback && onerror(error);
}
};
const proxyRefresh = () => {
errorCallbackCount = 0;
try {
localStorage.setItem(storeKey, '0');
} catch (err) {}
};
const triggerInitError = (error: any) => {
if (initialTimer && initialTimer.isError()) {
initialTimer.resetError();
return;
}
initialTimer && initialTimer.resetTimer();
noFallback ? onerror(error) : proxyOnError(error);
};
config.onError = (error: any) => {
if (initialTimer && initialTimer.isError()) {
initialTimer.resetError();
}
proxyOnError(error);
};
config.onDidRefresh = () => {
if (initialTimer && initialTimer.isError()) {
initialTimer.resetError();
}
proxyRefresh();
};
const initialTimer = options.initTimeoutError ? options.initTimeoutError(proxyOnError) : null; // initialTimer is only for mobile.html
const loadResolve = () => {
(window as any).initNECaptcha(
config,
(instance: any) => {
if (initialTimer && initialTimer.isError()) {
return;
}
initialTimer && initialTimer.resetTimer();
captchaIns = instance;
onload && onload(instance);
},
triggerInitError,
);
};
const cacheId = 'load-queue';
if (!RESOURCE_CACHE[cacheId]) {
RESOURCE_CACHE[cacheId] = {
rejects: [],
resolves: [],
status: 'error',
};
}
if (RESOURCE_CACHE[cacheId].status === 'error') {
RESOURCE_CACHE[cacheId].status = 'pending';
loadResource(config, (error: any) => {
if (error) {
const err: any = new Error();
err.code = REQUEST_SCRIPT_ERROR;
err.message = config.staticServer + '/load.min.js error';
const rejects = RESOURCE_CACHE[cacheId].rejects;
for (let i = 0, iLen = rejects.length; i < iLen; i++) {
rejects.pop()(err);
}
RESOURCE_CACHE[cacheId].status = 'error';
} else {
RESOURCE_CACHE[cacheId].status = 'done';
const resolves = RESOURCE_CACHE[cacheId].resolves;
for (let j = 0, jLen = resolves.length; j < jLen; j++) {
resolves.pop()();
}
}
});
} else if (RESOURCE_CACHE[cacheId].status === 'done') {
loadResolve();
}
if (RESOURCE_CACHE[cacheId].status === 'pending') {
RESOURCE_CACHE[cacheId].rejects.push(function loadReject(err: any) {
triggerInitError(err);
});
RESOURCE_CACHE[cacheId].resolves.push(loadResolve);
}
}

View File

@ -0,0 +1,2 @@
export * from './captcha.component';
export * from './dun.helper';