Event 发布者订阅模式
type ICallback = (data: any) => void;
export default class Event {
listener: { [key in string]: any[] } = {};
on(event: string, fn: ICallback) {
if (!this.listener[event]) {
this.listener[event] = [];
}
this.listener[event].push(fn);
return true;
}
off(event: string, fn: ICallback) {
if (this.listener[event]) {
var index = this.listener[event].indexOf(fn);
if (index > -1) {
this.listener[event].splice(index, 1);
}
return true;
}
return false;
}
offAll() {
this.listener = {};
}
dispatch(event: string, data: any) {
if (this.listener[event]) {
this.listener[event].map(each => {
each.call(null, data);
});
return true;
}
return false;
}
}
SmPhotoVerify.ts页面
import Event from './Event';
interface ISmCaptchaOptions {
// 数美分配的公司标识,数美后台可以查看看
organization?: string;
// 应用标识,区分不同应用,数美后台可以管理
appId?: string;
channel?: string; // 推广渠道,可自定义
product?: string; // embed(嵌入式) 展现形式,支持:embed(嵌入式)、float(浮动式)、popup(弹出层)
mode?: string; // 模式:支持slide(滑动验证码)、auto_slide(无图片滑动验证码)、select(文字点选验证码)、 icon_select(图标点选验证码)、 seq_select(成语语序验证码)、spatial_select(空间逻辑)
appendTo?: string; // 验证码dom元素的id。embed(嵌入式)、float(浮动式)必须配置,popup(弹出层)不需要
lang?: string; // zh-cn(简体中文) 模式:支持zh-cn(简体中文)、en(英文)、ph(菲律宾语)、ina(印尼语)、tha(泰语)、vn(越南语)、mys(马来语)、jp(日语)、kr(韩语)、西班牙语(es)、孟加拉语(bn)、葡萄牙语(pt)、德语(de)、法语(fr)、印地语(hi)、意大利语(it)、乌尔都语(ur)、俄语(ru)、瑞典语(sv)、土耳其语(tr)、中文繁体 (zh-tw)、阿拉伯语(ar)。注意:成语语序和文字点选模式下,验证码图片内容默认为中文,不随语言调整而变化。
useBrowserLang?: boolean; // false 是否高优使用浏览器设置的语言作为验证码的语言
customData?: { [key: string]: any }; // 自定义数据
tipsMessage?: {
slidePlaceholder?: string; // 向右滑动滑块填充拼图 自定义滑块默认文案,仅滑块验证支持自定义设置
};
disabled?: boolean; // false 初始状态是否禁用验证码
https?: boolean; // 加载资源是否是https
width?: number | string; // 验证码宽度,单位支持数字、%、px,建议最小宽度为300px,宽高比建议为2:1
debug?: boolean; // 开发过程中可以开启,出现异常会打印log,方便开发;上线后,务必关闭
maskBindClose?: boolean; // popup模式有效,是否可以通过点击遮罩层来关闭弹框
onError?: () => void; // 监听配置获取接口异常,参数: errType, errMsg
onInit?: () => void; // 开始加载验证码的回调,其中有个参数是captchaUuid
style?: { [key: string]: any }; // 支持自定义样式及风格设置,详细配置请见3.12
captchaUuid?: string; // 32位随机字符串 支持业务侧传入一个32位随机字符串,与业务方自身埋点数据配合,便于后续定位问题或进行数据统计
}
export class SmPhotoVerify extends Event {
static isCreatedScript = false;
private static url = 'https://castatic.fengkongcloud.cn/pr/v1.0.4/smcp.min.js';
// 加载sdk
static async createSmsSdk(): Promise<void> {
if (this.isCreatedScript) {
console.warn('sdk已经加载,已阻止重复加载');
return;
}
const sm = document.createElement('script');
const s = document.getElementsByTagName('script')[0];
sm.src = this.url;
if (s.parentNode) {
s.parentNode.insertBefore(sm, s);
} else {
document.body.appendChild(sm);
}
this.isCreatedScript = true;
return new Promise(resolve => {
sm.onload = () => {
resolve();
};
});
}
options = {
organization: 'FOIj56LX23Xlg5Bvosc3',
};
initSMCaptcha = (options: ISmCaptchaOptions) => {
if (!SmPhotoVerify.isCreatedScript) {
console.warn('验证码初始化失败,请检查是否引入验证码sdk');
return;
}
window.initSMCaptcha(Object.assign(this.options, options), (SMCaptcha: any) => {
this.dispatch('message', {
type: 'INSTANCE',
data: {
instance: SMCaptcha,
},
});
SMCaptcha.onReady((dom: any, params: any) => {
// params中有type字段,'init'-启动后 | 'refresh'-手动刷新图片后 | 'afterFail'-验证失败后
this.dispatch('message', {
type: 'READY',
data: {
dom,
params,
},
});
});
// 验证码校验情况回调
SMCaptcha.onSuccess((data: any) => {
this.dispatch('message', {
type: 'SMS_VERIFY_SUCCESS',
data: data,
});
});
SMCaptcha.onError((errType: any, errMsg: any) => {
// errType格式: 'SERVER_ERROR'
// errMsg格式: {code: 2002, message: " 服 务 器 异 常 : 无 权 限 操 作 (invalid organization)"}
this.dispatch('message', {
type: 'SMS_VERIFY_ERROR',
data: {
errType,
errMsg,
},
});
});
SMCaptcha.onClose(() => {
this.dispatch('message', {
type: 'SMS_VERIFY_CLOSE',
});
});
});
};
}
export default new SmPhotoVerify();
mixin/smCaptchaMixin.ts
import { Vue, Component } from 'vue-property-decorator';
import smPhoneVerifyInstace, { SmPhotoVerify } from '@/utils/SmPhotoVerify';
@Component
export default class captchaMixin extends Vue {
waitingRid: any = null;
instance: any = null;
isOpenSmVerify(retry: boolean = false): Promise<string> {
return new Promise((resolve, reject) => {
this.waitingRid = resolve;
if (!this.instance) {
reject('实例没加载完成');
}
this.instance.verify();
});
}
getVerifyMessage({ type, data }: any) {
if (!type) {
return;
}
switch (type) {
case 'INSTANCE': {
this.instance = data.instance;
break;
}
// 添加块作用域
case 'SMS_VERIFY_SUCCESS': {
const { rid, pass } = data;
if (pass) {
this.waitingRid && this.waitingRid(rid);
this.instance.reset();
}
break;
}
case 'SMS_VERIFY_ERROR':
console.log(data.errMsg.message || '验证失败');
break;
}
}
beforeUnmount() {
smPhoneVerifyInstace.offAll();
}
mounted() {
smPhoneVerifyInstace.offAll();
SmPhotoVerify.createSmsSdk().then(() => {
smPhoneVerifyInstace.initSMCaptcha({
product: 'popup',
width: 300,
https: false,
});
});
smPhoneVerifyInstace.on('message', this.getVerifyMessage.bind(this));
}
}
页面调用
import SmCaptchaMixin from '@/mixin/smCaptchaMixin';
export default class extends Mixins(SmCaptchaMixin)
this.rid = await this.isOpenSmVerify();