message-verify 1.0.1-beta.74 → 1.0.1-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.arcconfig CHANGED
@@ -1,5 +1,4 @@
1
1
  {
2
- "project.name": "message-verify",
3
2
  "phabricator.uri": "https://code.yangqianguan.com/",
4
3
  "lint.engine": "YqgWebDiffLintEngine"
5
4
  }
@@ -3,6 +3,8 @@ const Button = ({ disabled, onClick, children, style }) => {
3
3
  const handleClick = (e) => {
4
4
  if (!disabled) {
5
5
  onClick?.(e);
6
+ // 这里可以记录点击事件
7
+ console.log('按钮被点击');
6
8
  }
7
9
  };
8
10
  return (_jsx("button", { disabled: disabled, onClick: handleClick, style: {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,4 @@
1
- import { VerifyModalConfig, LoginModalConfig } from './utils/type.js';
1
+ import { ModalConfig, LoginModalConfig } from './utils/type.js';
2
2
  export declare const initFingerprint: () => Promise<string>;
3
+ export declare const createMessageVerifyModal: (modalProps: ModalConfig) => void;
3
4
  export declare const createReLoginModal: (modalProps: LoginModalConfig) => void;
4
- export declare const VerifyHandler: {
5
- _currentUrl: string;
6
- createModal: ({ data, config, http, verifyPromise, sendApi }: VerifyModalConfig) => Promise<any>;
7
- getResponse: (response: any) => any;
8
- };
package/dist/index.js CHANGED
@@ -1,86 +1,57 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import ReactDOM from 'react-dom/client';
3
- import VerifyModal from './modal/verify-modal.js';
3
+ import VerifyModal from './verify-modal.js';
4
4
  import Fingerprint2 from 'fingerprintjs2';
5
- import ReLoginModal from './modal/relogin-modal.js';
6
- import eventEmitter from './utils/eventEmitter.js';
7
- let fingerprintPromise = null; // 用 Promise 本身作为缓存
8
- let modalRoot = null;
5
+ import ReLoginModal from './relogin-modal.js';
6
+ let cachedFingerprint = null;
9
7
  export const initFingerprint = async () => {
10
- console.log('fingerprintPromise:', fingerprintPromise);
11
- if (!fingerprintPromise) {
12
- fingerprintPromise = new Promise((resolve) => {
13
- Fingerprint2.get(components => {
14
- const includeKeys = ['userAgent', 'cpuClass', 'hardwareConcurrency', 'platform'];
15
- console.log('components:', components);
16
- const values = components
17
- .filter(c => includeKeys.includes(c.key))
18
- .map(c => c.value)
19
- .join('###');
20
- console.log('values:', values);
21
- const fingerprint = Fingerprint2.x64hash128(values, 31);
22
- console.log('fingerprint:', fingerprint);
23
- resolve(fingerprint);
24
- });
25
- });
26
- }
27
- return fingerprintPromise;
28
- };
29
- const destroyModal = (modalId) => {
30
- if (modalRoot) {
31
- modalRoot.unmount();
32
- document.getElementById(modalId)?.remove();
33
- modalRoot = null;
8
+ if (cachedFingerprint) {
9
+ // 已有缓存,直接返回 Promise
10
+ return Promise.resolve(cachedFingerprint);
34
11
  }
12
+ // 首次异步获取并缓存
13
+ return new Promise((resolve) => {
14
+ Fingerprint2.get(function (components) {
15
+ console.log('components', components);
16
+ const includeKeys = ['userAgent', 'deviceMemory', 'cpuClass', 'hardwareConcurrency', 'platform'];
17
+ const values = components
18
+ .filter(component => includeKeys.includes(component.key))
19
+ .map(component => component.value)
20
+ .join('###');
21
+ const fingerprint = Fingerprint2.x64hash128(values, 31);
22
+ cachedFingerprint = fingerprint;
23
+ resolve(fingerprint);
24
+ });
25
+ });
35
26
  };
36
- const creatModal = (modalProps, modalId, ModalDom) => {
37
- destroyModal(modalId);
27
+ let modalRoot = null;
28
+ export const createMessageVerifyModal = (modalProps) => {
38
29
  const container = document.createElement('div');
39
- container.id = modalId;
30
+ container.id = 'antd-modal-container';
40
31
  document.body.appendChild(container);
41
32
  modalRoot = ReactDOM.createRoot(container);
42
- modalRoot.render(_jsx(ModalDom, { props: {
33
+ modalRoot.render(_jsx(VerifyModal, { props: {
43
34
  ...modalProps,
44
35
  onClose: () => {
45
- destroyModal(modalId);
36
+ modalProps.onClose?.();
37
+ destroyModal();
46
38
  }
47
39
  } }));
48
40
  };
49
- const createMessageVerifyModal = (modalProps) => {
50
- const verifyModalId = 'verify-modal-container';
51
- creatModal(modalProps, verifyModalId, VerifyModal);
52
- };
53
41
  // 新增 relogin 弹窗创建方法
54
42
  export const createReLoginModal = (modalProps) => {
55
- const reLoginModalId = 'relogin-modal-container';
56
- creatModal(modalProps, reLoginModalId, ReLoginModal);
43
+ const container = document.createElement('div');
44
+ container.id = 'antd-modal-container';
45
+ document.body.appendChild(container);
46
+ modalRoot = ReactDOM.createRoot(container);
47
+ modalRoot.render(_jsx(ReLoginModal, { props: {
48
+ ...modalProps,
49
+ } }));
57
50
  };
58
- export const VerifyHandler = {
59
- _currentUrl: '',
60
- createModal: ({ data, config, http, verifyPromise, sendApi }) => {
61
- VerifyHandler._currentUrl = config?.url || '';
62
- const toEmpty = () => {
63
- verifyPromise.current = null;
64
- };
65
- if (!verifyPromise.current) {
66
- verifyPromise.current = new Promise((resolve, reject) => {
67
- createMessageVerifyModal({
68
- data,
69
- config,
70
- http,
71
- apiResolve: resolve,
72
- apiReject: reject,
73
- apiPromiseToEmpty: toEmpty,
74
- sendApi
75
- });
76
- }).finally(toEmpty);
77
- }
78
- return verifyPromise.current;
79
- },
80
- getResponse: (response) => {
81
- if (VerifyHandler._currentUrl === response?.config?.url) {
82
- eventEmitter.emit('response', response?.data?.status?.code ?? -1);
83
- }
84
- return response;
51
+ const destroyModal = () => {
52
+ if (modalRoot) {
53
+ modalRoot.unmount();
54
+ document.getElementById('antd-modal-container')?.remove();
55
+ modalRoot = null;
85
56
  }
86
57
  };
@@ -10,7 +10,5 @@ declare const _default: {
10
10
  accountAlert: string;
11
11
  alertContent: string;
12
12
  ok: string;
13
- chidoriOpenPro: string;
14
- verifyFail: string;
15
13
  };
16
14
  export default _default;
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown} seconds',
10
10
  accountAlert: 'Account Security Alert',
11
11
  alertContent: 'Changes have been detected in your login environment or network information.To ensure account security, please log in again to verify your identity.',
12
- ok: 'OK',
13
- chidoriOpenPro: 'Chidori has activated security protection for you',
14
- verifyFail: 'Validation failed'
12
+ ok: 'OK'
15
13
  };
@@ -10,7 +10,5 @@ declare const _default: {
10
10
  accountAlert: string;
11
11
  alertContent: string;
12
12
  ok: string;
13
- chidoriOpenPro: string;
14
- verifyFail: string;
15
13
  };
16
14
  export default _default;
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown} detik',
10
10
  accountAlert: 'Peringatan Keamanan Akun',
11
11
  alertContent: 'Terdeteksi perubahan pada lingkungan login atau informasi jaringan Anda.Untuk menjaga keamanan akun, silakan login kembali untuk memverifikasi identitas Anda.',
12
- ok: 'Baiklah',
13
- chidoriOpenPro: 'chidori telah mengaktifkan keamanan untuk Anda',
14
- verifyFail: 'Autentikasi gagal'
12
+ ok: 'Baiklah'
15
13
  };
@@ -10,7 +10,5 @@ declare const _default: {
10
10
  accountAlert: string;
11
11
  alertContent: string;
12
12
  ok: string;
13
- chidoriOpenPro: string;
14
- verifyFail: string;
15
13
  };
16
14
  export default _default;
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown}秒',
10
10
  accountAlert: '账号安全提醒',
11
11
  alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
12
- ok: '好的',
13
- chidoriOpenPro: 'chidori已为您开启安全防护',
14
- verifyFail: '验证失败'
12
+ ok: '好的'
15
13
  };
package/dist/main.js CHANGED
@@ -1,17 +1,22 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createRoot } from 'react-dom/client';
3
- import { initFingerprint, createReLoginModal, VerifyHandler } from './index.js'; // 或 './index'
3
+ import { createMessageVerifyModal, initFingerprint, createReLoginModal } from './index.js'; // 或 './index'
4
4
  import { axios } from '@yqg/resource';
5
5
  const data = "{\"mobile\":\"188****4035\",\"verifyCodeKey\":\"3f025b33-988e-47c0-950d-6beb776d043f\",\"needValid\":true,\"step\":2,\"message\":\"需要填写验证码\"}";
6
6
  const fp = await initFingerprint();
7
- const fp2 = await initFingerprint();
8
- console.log(fp, fp2);
7
+ console.log(fp);
9
8
  const config = {
10
9
  "transitional": {
11
10
  "silentJSONParsing": true,
12
11
  "forcedJSONParsing": true,
13
12
  "clarifyTimeoutError": false
14
13
  },
14
+ "transformRequest": [
15
+ null
16
+ ],
17
+ "transformResponse": [
18
+ null
19
+ ],
15
20
  "timeout": 0,
16
21
  "xsrfCookieName": "XSRF-TOKEN",
17
22
  "xsrfHeaderName": "X-XSRF-TOKEN",
@@ -19,8 +24,10 @@ const config = {
19
24
  "maxBodyLength": -1,
20
25
  "headers": {
21
26
  "Accept": "application/json, text/plain, */*",
22
- "X-Device-Fingerprint": "531f14c7176b16ebeb18e2b23d6bd124",
23
- "Content-Type": "application/json"
27
+ "X-Device-Fingerprint": "185fcce88c629725321adf29daa5e444",
28
+ "Content-Type": "application/json",
29
+ "sms-code": "121212",
30
+ "sms-code-key": "212190c7-2f5e-4cc1-82ec-a242c2ab5425"
24
31
  },
25
32
  "url": "/classification/admin/level/edit",
26
33
  "method": "post",
@@ -36,10 +43,15 @@ const config = {
36
43
  "data": "{\"id\":1,\"classificationName\":\"个人基本概况信息\",\"classificationLevel\":2}",
37
44
  "baseURL": "http://localhost:62888"
38
45
  };
39
- const promise = { current: null };
40
- createRoot(document.getElementById('root')).render(_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => {
41
- return VerifyHandler.createModal({ data, config, http: axios, verifyPromise: promise });
42
- }, children: "Show verify Modal" }), _jsx("button", { onClick: () => createReLoginModal({
46
+ createRoot(document.getElementById('root')).render(_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => createMessageVerifyModal({
47
+ data,
48
+ config,
49
+ response: {
50
+ config
51
+ },
52
+ lang: 'en',
53
+ http: axios,
54
+ }), children: "Show verify Modal" }), _jsx("button", { onClick: () => createReLoginModal({
43
55
  goLogin: () => {
44
56
  console.log('goLogin');
45
57
  },
@@ -1 +1 @@
1
- {"root":["../src/index.tsx","../src/main.tsx","../src/vite-env.d.ts","../src/compoments/button.tsx","../src/compoments/index.ts","../src/compoments/input.tsx","../src/compoments/message.tsx","../src/compoments/modal.tsx","../src/locales/en.ts","../src/locales/id.ts","../src/locales/zh.ts","../src/modal/relogin-modal.tsx","../src/modal/verify-modal.tsx","../src/utils/eventemitter.ts","../src/utils/i18n.ts","../src/utils/resource.ts","../src/utils/status.ts","../src/utils/type.ts"],"version":"5.7.3"}
1
+ {"root":["../src/index.tsx","../src/main.tsx","../src/relogin-modal.tsx","../src/resource.ts","../src/verify-modal.tsx","../src/vite-env.d.ts","../src/compoments/button.tsx","../src/compoments/index.ts","../src/compoments/input.tsx","../src/compoments/message.tsx","../src/compoments/modal.tsx","../src/locales/en.ts","../src/locales/id.ts","../src/locales/zh.ts","../src/utils/i18n.ts","../src/utils/status.ts","../src/utils/type.ts"],"version":"5.7.3"}
@@ -1,4 +1,4 @@
1
- export type VerifyModalConfig = {
1
+ export type ModalConfig = {
2
2
  data: string;
3
3
  onClose?: () => void;
4
4
  config: {
@@ -18,16 +18,10 @@ export type VerifyModalConfig = {
18
18
  };
19
19
  };
20
20
  };
21
- sendApi?: string;
22
- apiReject?: any;
23
- apiResolve?: any;
24
- apiPromiseToEmpty?: () => void;
25
- verifyPromise: {
26
- current: Promise<any> | null;
27
- };
21
+ api?: string;
22
+ response: any;
28
23
  };
29
24
  export type LoginModalConfig = {
30
25
  goLogin: () => void;
31
- onClose?: () => void;
32
26
  lang?: 'zh' | 'en' | 'id';
33
27
  };
@@ -1,5 +1,5 @@
1
- import { VerifyModalConfig } from './utils/type.js';
1
+ import { ModalConfig } from './utils/type.js';
2
2
  declare const CreateMessageVerifyModal: ({ props }: {
3
- props: VerifyModalConfig;
3
+ props: ModalConfig;
4
4
  }) => import("react/jsx-runtime.js").JSX.Element;
5
5
  export default CreateMessageVerifyModal;
@@ -11,7 +11,7 @@ const CreateMessageVerifyModal = ({ props }) => {
11
11
  const [captchaImage, setCaptchaImage] = useState('');
12
12
  const [captchCode, setCaptchCode] = useState('');
13
13
  const [captchaKey, setCaptchaKey] = useState('');
14
- const { data, http, lang, sendApi = '/admin/sms/send', config, apiResolve, apiPromiseToEmpty } = props || {};
14
+ const { data, http, lang, api = '/admin/sms/send', response } = props || {};
15
15
  const dataObj = JSON.parse(data || '{}');
16
16
  const { mobile, verifyCodeKey } = dataObj || {};
17
17
  const getKey = async () => {
@@ -51,65 +51,46 @@ const CreateMessageVerifyModal = ({ props }) => {
51
51
  formData.append('captchaKey', captchaKey);
52
52
  formData.append('captcha', captchCode);
53
53
  // 重新发送验证码
54
- const res = await axios.post(sendApi, formData);
54
+ const res = await axios.post(api, formData);
55
55
  const { code, detail } = res.data?.status || {};
56
56
  if (code !== 0) {
57
57
  message.error(detail || t('sendFailed', lang));
58
58
  return;
59
59
  }
60
60
  message.success(t('sendSuccess', lang));
61
- // 重置倒计时、验证码
62
61
  setCountdown(60);
63
- setOtp('');
64
62
  };
65
63
  return (_jsxs(Modal, { width: 420, visible: visible, onClose: () => {
66
64
  setVisible(false);
67
- apiPromiseToEmpty?.();
68
65
  props.onClose?.();
69
66
  }, children: [_jsx("div", { style: { fontSize: 14, color: '#666' }, children: t('alreadySend', lang, { phone: mobile }) }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12 }, children: [_jsx(Input.OTP, { length: 6, value: otp, onChange: async (val) => {
70
67
  setOtp(val);
71
68
  if (val.length === 6) {
72
- try {
73
- const res = await Promise.race([
74
- new Promise((resolve, reject) => {
75
- setTimeout(() => {
76
- reject(new Error('Timeout'));
77
- }, 1000);
78
- }),
79
- new Promise((resolve, reject) => {
80
- http({
81
- ...config,
82
- headers: {
83
- ...http.defaults.headers.common,
84
- 'sms-code': val,
85
- 'sms-code-key': verifyCodeKey,
86
- 'Content-Type': config.headers['Content-Type'],
87
- }
88
- }).then(res => {
89
- resolve(res);
90
- }).catch(err => {
91
- reject(err);
92
- });
93
- })
94
- ]);
95
- if (res?.data?.status?.code === 0) {
96
- apiResolve(res);
97
- setVisible(false);
98
- }
99
- else {
100
- message.error('验证失败');
101
- setOtp('');
69
+ const { data: { status: { code, detail } = {} } } = response || {};
70
+ http({
71
+ ...response?.config,
72
+ headers: {
73
+ ...http.defaults.headers.common,
74
+ 'sms-code': val,
75
+ 'sms-code-key': verifyCodeKey,
76
+ 'Content-Type': response?.config.headers['Content-Type'],
102
77
  }
78
+ });
79
+ console.log('=>response===>', response);
80
+ if (code !== 0) {
81
+ const detailObj = JSON.parse(detail || '{}');
82
+ message.error(detailObj?.message || t('verifyFailed', lang));
83
+ return;
103
84
  }
104
- catch (error) {
105
- console.warn('catch', error);
106
- message.error('验证失败');
107
- setOtp('');
85
+ else {
86
+ message.success(t('verifySuccess', lang));
87
+ // setVisible(false);
108
88
  }
89
+ setVisible(false);
109
90
  }
110
91
  } }), _jsx(Button, { disabled: countdown > 0 || !captchCode, onClick: handleResend, style: { padding: '8px' }, children: countdown > 0 ? t('countDownSecound', lang, { countdown }) : t('reSend', lang) })] }), countdown > 0 ? null :
111
92
  _jsxs("div", { style: { marginTop: 12, display: 'flex', alignItems: 'center' }, children: [_jsx(Input, { placeholder: t('pleaseEnterPicVerifyCode', lang), onChange: (e) => {
112
93
  setCaptchCode(e);
113
- }, style: { width: 300 } }), captchaImage && _jsx("img", { src: captchaImage, alt: t('verifyCode', lang), style: { width: 100, height: 30, marginLeft: 8 }, onClick: getKey })] }), (countdown > 0 || captchCode) ? null : _jsx("div", { style: { fontSize: 14, color: 'red' }, children: t('pleaseEnterAndSend', lang) }), _jsx("div", { style: { fontSize: 14, color: '#666', marginTop: 12 }, children: t('chidoriOpenPro', lang) })] }));
94
+ }, style: { width: 300 } }), captchaImage && _jsx("img", { src: captchaImage, alt: t('verifyCode', lang), style: { width: 100, height: 30, marginLeft: 8 }, onClick: getKey })] }), (countdown > 0 || captchCode) ? null : _jsx("div", { style: { fontSize: 14, color: 'red' }, children: t('pleaseEnterAndSend', lang) })] }));
114
95
  };
115
96
  export default CreateMessageVerifyModal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "message-verify",
3
- "version": "1.0.1-beta.74",
3
+ "version": "1.0.1-beta.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "dependencies": {
@@ -29,7 +29,6 @@
29
29
  "build": "tsc -b",
30
30
  "analyze": "vite build",
31
31
  "lint": "eslint .",
32
- "preview": "vite preview",
33
- "release:beta": "pnpm version prerelease --preid=beta && pnpm run build && pnpm publish"
32
+ "preview": "vite preview"
34
33
  }
35
34
  }
@@ -11,6 +11,8 @@ const Button: React.FC<CustomButtonProps> = ({ disabled, onClick, children, styl
11
11
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
12
12
  if (!disabled) {
13
13
  onClick?.(e);
14
+ // 这里可以记录点击事件
15
+ console.log('按钮被点击');
14
16
  }
15
17
  };
16
18
 
package/src/index.tsx CHANGED
@@ -1,96 +1,72 @@
1
1
  import ReactDOM from 'react-dom/client'
2
- import VerifyModal from './modal/verify-modal.js'
2
+ import VerifyModal from './verify-modal.js'
3
3
  import Fingerprint2 from 'fingerprintjs2';
4
- import ReLoginModal from './modal/relogin-modal.js';
5
- import { VerifyModalConfig, LoginModalConfig } from './utils/type.js';
6
- import eventEmitter from './utils/eventEmitter.js';
4
+ import ReLoginModal from './relogin-modal.js';
5
+ import { ModalConfig, LoginModalConfig } from './utils/type.js';
7
6
 
8
- let fingerprintPromise: Promise<string> | null = null; // 用 Promise 本身作为缓存
9
- let modalRoot: ReactDOM.Root | null = null
7
+ let cachedFingerprint: string | null = null;
10
8
  export const initFingerprint = async (): Promise<string> => {
11
- console.log('fingerprintPromise:', fingerprintPromise);
12
- if (!fingerprintPromise) {
13
- fingerprintPromise = new Promise((resolve) => {
14
- Fingerprint2.get(components => {
15
- const includeKeys = ['userAgent', 'cpuClass', 'hardwareConcurrency', 'platform'];
16
- console.log('components:', components);
17
- const values = components
18
- .filter(c => includeKeys.includes(c.key))
19
- .map(c => c.value)
20
- .join('###');
21
- console.log('values:', values);
22
- const fingerprint = Fingerprint2.x64hash128(values, 31);
23
- console.log('fingerprint:', fingerprint);
24
- resolve(fingerprint);
25
- });
26
- });
9
+ if (cachedFingerprint) {
10
+ // 已有缓存,直接返回 Promise
11
+ return Promise.resolve(cachedFingerprint);
27
12
  }
28
- return fingerprintPromise;
13
+ // 首次异步获取并缓存
14
+ return new Promise((resolve) => {
15
+ Fingerprint2.get(function (components) {
16
+ console.log('components', components);
17
+ const includeKeys = ['userAgent', 'deviceMemory', 'cpuClass', 'hardwareConcurrency', 'platform'];
18
+ const values = components
19
+ .filter(component => includeKeys.includes(component.key))
20
+ .map(component => component.value)
21
+ .join('###');
22
+ const fingerprint = Fingerprint2.x64hash128(values, 31);
23
+ cachedFingerprint = fingerprint;
24
+ resolve(fingerprint);
25
+ });
26
+ });
29
27
  };
30
28
 
31
- const destroyModal = (modalId: string) => {
32
- if (modalRoot) {
33
- modalRoot.unmount()
34
- document.getElementById(modalId)?.remove()
35
- modalRoot = null
36
- }
37
- }
29
+ let modalRoot: ReactDOM.Root | null = null
38
30
 
39
- const creatModal = (modalProps: VerifyModalConfig | LoginModalConfig, modalId: string, ModalDom: any) => {
40
- destroyModal(modalId);
41
- const container = document.createElement('div')
42
- container.id = modalId
43
- document.body.appendChild(container)
31
+ export const createMessageVerifyModal = (modalProps: ModalConfig) => {
32
+ const container = document.createElement('div')
33
+ container.id = 'antd-modal-container'
34
+ document.body.appendChild(container)
44
35
 
45
- modalRoot = ReactDOM.createRoot(container)
46
- modalRoot.render(
47
- <ModalDom
48
- props={{
49
- ...modalProps,
50
- onClose: () => {
51
- destroyModal(modalId);
52
- }
53
- }}
54
- />
55
- )
36
+ modalRoot = ReactDOM.createRoot(container)
37
+ modalRoot.render(
38
+ <VerifyModal
39
+ props={{
40
+ ...modalProps,
41
+ onClose: () => {
42
+ modalProps.onClose?.()
43
+ destroyModal()
44
+ }
45
+ }}
46
+ />
47
+ )
56
48
  }
57
49
 
58
- const createMessageVerifyModal = (modalProps: VerifyModalConfig) => {
59
- const verifyModalId = 'verify-modal-container';
60
- creatModal(modalProps, verifyModalId, VerifyModal);
61
- }
62
50
  // 新增 relogin 弹窗创建方法
63
51
  export const createReLoginModal = (modalProps: LoginModalConfig) => {
64
- const reLoginModalId = 'relogin-modal-container';
65
- creatModal(modalProps, reLoginModalId, ReLoginModal);
52
+ const container = document.createElement('div')
53
+ container.id = 'antd-modal-container'
54
+ document.body.appendChild(container)
55
+
56
+ modalRoot = ReactDOM.createRoot(container)
57
+ modalRoot.render(
58
+ <ReLoginModal
59
+ props={{
60
+ ...modalProps,
61
+ }}
62
+ />
63
+ )
66
64
  }
67
65
 
68
- export const VerifyHandler = {
69
- _currentUrl: '',
70
- createModal: ({ data, config, http, verifyPromise, sendApi }: VerifyModalConfig) => {
71
- VerifyHandler._currentUrl = config?.url as string || '';
72
- const toEmpty = () => {
73
- verifyPromise.current = null;
74
- };
75
- if (!verifyPromise.current) {
76
- verifyPromise.current = new Promise((resolve, reject) => {
77
- createMessageVerifyModal({
78
- data,
79
- config,
80
- http,
81
- apiResolve: resolve,
82
- apiReject: reject,
83
- apiPromiseToEmpty: toEmpty,
84
- sendApi
85
- } as VerifyModalConfig);
86
- }).finally(toEmpty);
87
- }
88
- return verifyPromise.current;
89
- },
90
- getResponse: (response: any) => {
91
- if (VerifyHandler._currentUrl === response?.config?.url) {
92
- eventEmitter.emit('response', response?.data?.status?.code ?? -1);
93
- }
94
- return response;
95
- }
96
- };
66
+ const destroyModal = () => {
67
+ if (modalRoot) {
68
+ modalRoot.unmount()
69
+ document.getElementById('antd-modal-container')?.remove()
70
+ modalRoot = null
71
+ }
72
+ }
package/src/locales/en.ts CHANGED
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown} seconds',
10
10
  accountAlert: 'Account Security Alert',
11
11
  alertContent: 'Changes have been detected in your login environment or network information.To ensure account security, please log in again to verify your identity.',
12
- ok: 'OK',
13
- chidoriOpenPro: 'Chidori has activated security protection for you',
14
- verifyFail: 'Validation failed'
12
+ ok: 'OK'
15
13
  };
package/src/locales/id.ts CHANGED
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown} detik',
10
10
  accountAlert: 'Peringatan Keamanan Akun',
11
11
  alertContent: 'Terdeteksi perubahan pada lingkungan login atau informasi jaringan Anda.Untuk menjaga keamanan akun, silakan login kembali untuk memverifikasi identitas Anda.',
12
- ok: 'Baiklah',
13
- chidoriOpenPro: 'chidori telah mengaktifkan keamanan untuk Anda',
14
- verifyFail: 'Autentikasi gagal'
12
+ ok: 'Baiklah'
15
13
  };
package/src/locales/zh.ts CHANGED
@@ -9,7 +9,5 @@ export default {
9
9
  countDownSecound: '{countdown}秒',
10
10
  accountAlert: '账号安全提醒',
11
11
  alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
12
- ok: '好的',
13
- chidoriOpenPro: 'chidori已为您开启安全防护',
14
- verifyFail: '验证失败'
12
+ ok: '好的'
15
13
  };
package/src/main.tsx CHANGED
@@ -1,12 +1,11 @@
1
1
  import { createRoot } from 'react-dom/client';
2
- import { initFingerprint, createReLoginModal, VerifyHandler } from './index.js'; // 或 './index'
2
+ import { createMessageVerifyModal, initFingerprint, createReLoginModal } from './index.js'; // 或 './index'
3
3
  import { axios } from '@yqg/resource';
4
4
 
5
5
  const data = "{\"mobile\":\"188****4035\",\"verifyCodeKey\":\"3f025b33-988e-47c0-950d-6beb776d043f\",\"needValid\":true,\"step\":2,\"message\":\"需要填写验证码\"}";
6
6
 
7
7
  const fp = await initFingerprint();
8
- const fp2 = await initFingerprint();
9
- console.log(fp, fp2);
8
+ console.log(fp);
10
9
 
11
10
  const config = {
12
11
  "transitional": {
@@ -14,6 +13,12 @@ const config = {
14
13
  "forcedJSONParsing": true,
15
14
  "clarifyTimeoutError": false
16
15
  },
16
+ "transformRequest": [
17
+ null
18
+ ],
19
+ "transformResponse": [
20
+ null
21
+ ],
17
22
  "timeout": 0,
18
23
  "xsrfCookieName": "XSRF-TOKEN",
19
24
  "xsrfHeaderName": "X-XSRF-TOKEN",
@@ -21,8 +26,10 @@ const config = {
21
26
  "maxBodyLength": -1,
22
27
  "headers": {
23
28
  "Accept": "application/json, text/plain, */*",
24
- "X-Device-Fingerprint": "531f14c7176b16ebeb18e2b23d6bd124",
25
- "Content-Type": "application/json"
29
+ "X-Device-Fingerprint": "185fcce88c629725321adf29daa5e444",
30
+ "Content-Type": "application/json",
31
+ "sms-code": "121212",
32
+ "sms-code-key": "212190c7-2f5e-4cc1-82ec-a242c2ab5425"
26
33
  },
27
34
  "url": "/classification/admin/level/edit",
28
35
  "method": "post",
@@ -37,15 +44,19 @@ const config = {
37
44
  "formInstance": {},
38
45
  "data": "{\"id\":1,\"classificationName\":\"个人基本概况信息\",\"classificationLevel\":2}",
39
46
  "baseURL": "http://localhost:62888"
40
- }
41
-
42
- const promise = {current: null};
47
+ };
43
48
  createRoot(document.getElementById('root')!).render(
44
49
  <>
45
- <button onClick={() => {
46
- return VerifyHandler.createModal({data, config, http: axios, verifyPromise: promise});
47
- }}>
48
- Show verify Modal
50
+ <button onClick={() => createMessageVerifyModal({
51
+ data,
52
+ config,
53
+ response: {
54
+ config
55
+ },
56
+ lang: 'en',
57
+ http: axios,
58
+ })}>
59
+ Show verify Modal
49
60
  </button>
50
61
  <button onClick={() => createReLoginModal({
51
62
  goLogin: () => {
@@ -1,15 +1,14 @@
1
1
  import { useState } from 'react'
2
- import { Modal, Button } from '../compoments/index.js'
3
- import { LoginModalConfig } from '../utils/type.js';
4
- import t from '../utils/i18n.js'
2
+ import { Modal, Button } from './compoments/index.js'
3
+ import { LoginModalConfig } from './utils/type.js';
4
+ import t from './utils/i18n.js'
5
5
 
6
6
  const ReLoginModal = ({ props }: { props: LoginModalConfig }) => {
7
- const { goLogin, lang, onClose: destroyModal } = props || {};
7
+ const { goLogin, lang } = props || {};
8
8
  const [visible, setVisible] = useState(true);
9
9
  const onClose = () => {
10
10
  setVisible(false);
11
11
  goLogin();
12
- destroyModal?.();
13
12
  };
14
13
  return (
15
14
  <Modal visible={visible} onClose={onClose} width={420}>
package/src/utils/type.ts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- export type VerifyModalConfig = {
2
+ export type ModalConfig = {
3
3
  data: string;
4
4
  onClose?: () => void
5
5
  config: {
@@ -19,15 +19,11 @@ export type VerifyModalConfig = {
19
19
  }
20
20
  }
21
21
  };
22
- sendApi?: string;
23
- apiReject?: any;
24
- apiResolve?: any;
25
- apiPromiseToEmpty?: () => void;
26
- verifyPromise: {current: Promise<any> | null}
22
+ api?: string;
23
+ response: any;
27
24
  }
28
25
 
29
26
  export type LoginModalConfig = {
30
27
  goLogin: () => void;
31
- onClose?: () => void
32
28
  lang?: 'zh' | 'en' | 'id';
33
29
  }
@@ -1,23 +1,20 @@
1
- import { useRef, useEffect, useState } from 'react'
2
- import { Modal, Input, Button, message } from '../compoments/index.js'
3
- import Api from '../utils/resource.js'
4
- import t from '../utils/i18n.js'
5
- import { VerifyModalConfig } from '../utils/type.js';
1
+ import { useEffect, useState } from 'react'
2
+ import { Modal, Input, Button, message } from './compoments/index.js'
3
+ import Api from './resource.js'
4
+ import t from './utils/i18n.js'
5
+ import { ModalConfig } from './utils/type.js';
6
6
  import { axios } from '@yqg/resource';
7
- import eventEmitter from '../utils/eventEmitter.js';
8
7
 
9
- const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
8
+ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
10
9
  const [visible, setVisible] = useState(true);
11
10
  const [countdown, setCountdown] = useState(60);
12
11
  const [otp, setOtp] = useState('');
13
12
  const [captchaImage, setCaptchaImage] = useState<string>('');
14
13
  const [captchCode, setCaptchCode] = useState<string>('');
15
14
  const [captchaKey, setCaptchaKey] = useState<string>('');
16
- const { data, http, lang, sendApi = '/admin/sms/send', config, apiResolve, apiPromiseToEmpty, onClose } = props || {};
15
+ const { data, http, lang, api='/admin/sms/send', response } = props || {};
17
16
  const dataObj = JSON.parse(data || '{}');
18
17
  const { mobile, verifyCodeKey } = dataObj || {};
19
- const resolveRef = useRef<any>(null);
20
- const rejectRef = useRef<any>(null);
21
18
 
22
19
  const getKey = async () => {
23
20
  try {
@@ -28,11 +25,10 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
28
25
  const image = Api.getCaptchaImgUrl(key) as unknown;
29
26
  setCaptchaImage(image as string);
30
27
  }
31
- } catch (err) {
32
- const msg =
33
- typeof err === 'object' && err !== null && 'message' in err
34
- ? (err as { message: string }).message
35
- : String(err);
28
+ } catch (err) {const msg =
29
+ typeof err === 'object' && err !== null && 'message' in err
30
+ ? (err as {message: string}).message
31
+ : String(err);
36
32
  message.error(msg as React.ReactNode);
37
33
  }
38
34
  }
@@ -52,41 +48,19 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
52
48
  };
53
49
  }, [visible, countdown]);
54
50
 
55
-
56
- useEffect(() => {
57
- const handler = (data: any) => {
58
- // 这里处理收到的数据
59
- console.log('收到 response 事件:', data);
60
- if (data === 0) {
61
- resolveRef.current({
62
- code: data,
63
- });
64
- } else {
65
- rejectRef.current({
66
- code: data,
67
- });
68
- }
69
- };
70
- eventEmitter.on('response', handler);
71
- return () => {
72
- eventEmitter.off('response', handler);
73
- };
74
- }, []);
75
-
76
51
  const handleResend = async () => {
77
52
  const formData = new FormData();
78
53
  formData.append('captchaKey', captchaKey);
79
54
  formData.append('captcha', captchCode);
80
55
  // 重新发送验证码
81
- const res = await axios.post(sendApi, formData) || {};
82
- const { code } = res.data?.status || {};
56
+ const res = await axios.post(api, formData);
57
+ const { code, detail } = res.data?.status || {};
83
58
  if (code !== 0) {
59
+ message.error(detail || t('sendFailed', lang));
84
60
  return;
85
61
  }
86
62
  message.success(t('sendSuccess', lang));
87
- // 重置倒计时、验证码
88
63
  setCountdown(60);
89
- setOtp('');
90
64
  }
91
65
 
92
66
  return (
@@ -95,8 +69,7 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
95
69
  visible={visible}
96
70
  onClose={() => {
97
71
  setVisible(false);
98
- apiPromiseToEmpty?.();
99
- onClose?.();
72
+ props.onClose?.();
100
73
  }}
101
74
  >
102
75
  <div style={{ fontSize: 14, color: '#666' }}>{t('alreadySend', lang, { phone: mobile })}</div>
@@ -108,32 +81,26 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
108
81
  onChange={async (val) => {
109
82
  setOtp(val);
110
83
  if (val.length === 6) {
111
- try {
112
- const res = await new Promise((resolve, reject) => {
113
- resolveRef.current = resolve;
114
- rejectRef.current = reject;
115
- http({
116
- ...config,
117
- headers: {
118
- ...http.defaults.headers.common,
119
- 'sms-code': val,
120
- 'sms-code-key': verifyCodeKey,
121
- 'Content-Type': config.headers['Content-Type'],
122
- }
123
- });
124
- }) as any;
125
- if (res?.code === 0 || res?.data?.status?.code === 0) {
126
- apiResolve(res);
127
- setVisible(false);
128
- } else {
129
- message.error(t('verifyFail', lang));
130
- setOtp('');
84
+ const {data: {status: {code, detail} = {}}} = response || {};
85
+ http({
86
+ ...response?.config,
87
+ headers: {
88
+ ...http.defaults.headers.common,
89
+ 'sms-code': val,
90
+ 'sms-code-key': verifyCodeKey,
91
+ 'Content-Type': response?.config.headers['Content-Type'],
131
92
  }
132
- } catch (error) {
133
- console.warn('catch=>error', error);
134
- message.error(t('verifyFail', lang));
135
- setOtp('');
93
+ }) as any;
94
+ console.log('=>response===>', response);
95
+ if(code !== 0) {
96
+ const detailObj = JSON.parse(detail || '{}');
97
+ message.error(detailObj?.message || t('verifyFailed', lang));
98
+ return;
99
+ } else {
100
+ message.success(t('verifySuccess', lang));
101
+ // setVisible(false);
136
102
  }
103
+ setVisible(false);
137
104
  }
138
105
  }} />
139
106
  <Button
@@ -157,7 +124,6 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
157
124
  {captchaImage && <img src={captchaImage} alt={t('verifyCode', lang)} style={{ width: 100, height: 30, marginLeft: 8 }} onClick={getKey} />}
158
125
  </div>}
159
126
  {(countdown > 0 || captchCode) ? null : <div style={{ fontSize: 14, color: 'red' }}>{t('pleaseEnterAndSend', lang)}</div>}
160
- <div style={{ fontSize: 14, color: '#666', marginTop: 12 }}>{t('chidoriOpenPro', lang)}</div>
161
127
  </Modal>
162
128
  );
163
129
  };
@@ -1,5 +0,0 @@
1
- import { LoginModalConfig } from '../utils/type.js';
2
- declare const ReLoginModal: ({ props }: {
3
- props: LoginModalConfig;
4
- }) => import("react/jsx-runtime.js").JSX.Element;
5
- export default ReLoginModal;
@@ -1,24 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Modal, Button } from '../compoments/index.js';
4
- import t from '../utils/i18n.js';
5
- const ReLoginModal = ({ props }) => {
6
- const { goLogin, lang, onClose: destroyModal } = props || {};
7
- const [visible, setVisible] = useState(true);
8
- const onClose = () => {
9
- setVisible(false);
10
- goLogin();
11
- destroyModal?.();
12
- };
13
- return (_jsxs(Modal, { visible: visible, onClose: onClose, width: 420, children: [_jsx("div", { style: { fontWeight: 500, fontSize: 20, marginBottom: 24 }, children: t('accountAlert', lang) }), _jsx("div", { style: { color: '#222', fontSize: 16, marginBottom: 40, lineHeight: '24px' }, children: t('alertContent', lang) }), _jsx("div", { style: { display: 'flex', justifyContent: 'center' }, children: _jsx(Button, { style: {
14
- background: '#1677ff',
15
- color: '#fff',
16
- border: 'none',
17
- width: 80,
18
- height: 36,
19
- fontSize: 14,
20
- borderRadius: 8,
21
- boxShadow: '0 2px 8px rgba(22,119,255,0.08)',
22
- }, onClick: onClose, children: t('ok', lang) }) })] }));
23
- };
24
- export default ReLoginModal;
@@ -1,5 +0,0 @@
1
- import { VerifyModalConfig } from '../utils/type.js';
2
- declare const CreateMessageVerifyModal: ({ props }: {
3
- props: VerifyModalConfig;
4
- }) => import("react/jsx-runtime.js").JSX.Element;
5
- export default CreateMessageVerifyModal;
@@ -1,128 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef, useEffect, useState } from 'react';
3
- import { Modal, Input, Button, message } from '../compoments/index.js';
4
- import Api from '../utils/resource.js';
5
- import t from '../utils/i18n.js';
6
- import { axios } from '@yqg/resource';
7
- import eventEmitter from '../utils/eventEmitter.js';
8
- const CreateMessageVerifyModal = ({ props }) => {
9
- const [visible, setVisible] = useState(true);
10
- const [countdown, setCountdown] = useState(60);
11
- const [otp, setOtp] = useState('');
12
- const [captchaImage, setCaptchaImage] = useState('');
13
- const [captchCode, setCaptchCode] = useState('');
14
- const [captchaKey, setCaptchaKey] = useState('');
15
- const { data, http, lang, sendApi = '/admin/sms/send', config, apiResolve, apiPromiseToEmpty, onClose } = props || {};
16
- const dataObj = JSON.parse(data || '{}');
17
- const { mobile, verifyCodeKey } = dataObj || {};
18
- const resolveRef = useRef(null);
19
- const rejectRef = useRef(null);
20
- const getKey = async () => {
21
- try {
22
- const res = await Api.getCaptchaKey();
23
- const { body: key } = res.data || {};
24
- setCaptchaKey(key);
25
- if (key) {
26
- const image = Api.getCaptchaImgUrl(key);
27
- setCaptchaImage(image);
28
- }
29
- }
30
- catch (err) {
31
- const msg = typeof err === 'object' && err !== null && 'message' in err
32
- ? err.message
33
- : String(err);
34
- message.error(msg);
35
- }
36
- };
37
- useEffect(() => {
38
- let timer = null;
39
- if (visible && countdown > 0) {
40
- timer = setInterval(() => {
41
- setCountdown((prev) => prev - 1);
42
- }, 1000);
43
- }
44
- if (countdown === 0) {
45
- getKey();
46
- }
47
- return () => {
48
- if (timer)
49
- clearInterval(timer);
50
- };
51
- }, [visible, countdown]);
52
- useEffect(() => {
53
- const handler = (data) => {
54
- // 这里处理收到的数据
55
- console.log('收到 response 事件:', data);
56
- if (data === 0) {
57
- resolveRef.current({
58
- code: data,
59
- });
60
- }
61
- else {
62
- rejectRef.current({
63
- code: data,
64
- });
65
- }
66
- };
67
- eventEmitter.on('response', handler);
68
- return () => {
69
- eventEmitter.off('response', handler);
70
- };
71
- }, []);
72
- const handleResend = async () => {
73
- const formData = new FormData();
74
- formData.append('captchaKey', captchaKey);
75
- formData.append('captcha', captchCode);
76
- // 重新发送验证码
77
- const res = await axios.post(sendApi, formData) || {};
78
- const { code } = res.data?.status || {};
79
- if (code !== 0) {
80
- return;
81
- }
82
- message.success(t('sendSuccess', lang));
83
- // 重置倒计时、验证码
84
- setCountdown(60);
85
- setOtp('');
86
- };
87
- return (_jsxs(Modal, { width: 420, visible: visible, onClose: () => {
88
- setVisible(false);
89
- apiPromiseToEmpty?.();
90
- onClose?.();
91
- }, children: [_jsx("div", { style: { fontSize: 14, color: '#666' }, children: t('alreadySend', lang, { phone: mobile }) }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12 }, children: [_jsx(Input.OTP, { length: 6, value: otp, onChange: async (val) => {
92
- setOtp(val);
93
- if (val.length === 6) {
94
- try {
95
- const res = await new Promise((resolve, reject) => {
96
- resolveRef.current = resolve;
97
- rejectRef.current = reject;
98
- http({
99
- ...config,
100
- headers: {
101
- ...http.defaults.headers.common,
102
- 'sms-code': val,
103
- 'sms-code-key': verifyCodeKey,
104
- 'Content-Type': config.headers['Content-Type'],
105
- }
106
- });
107
- });
108
- if (res?.code === 0 || res?.data?.status?.code === 0) {
109
- apiResolve(res);
110
- setVisible(false);
111
- }
112
- else {
113
- message.error(t('verifyFail', lang));
114
- setOtp('');
115
- }
116
- }
117
- catch (error) {
118
- console.warn('catch=>error', error);
119
- message.error(t('verifyFail', lang));
120
- setOtp('');
121
- }
122
- }
123
- } }), _jsx(Button, { disabled: countdown > 0 || !captchCode, onClick: handleResend, style: { padding: '8px' }, children: countdown > 0 ? t('countDownSecound', lang, { countdown }) : t('reSend', lang) })] }), countdown > 0 ? null :
124
- _jsxs("div", { style: { marginTop: 12, display: 'flex', alignItems: 'center' }, children: [_jsx(Input, { placeholder: t('pleaseEnterPicVerifyCode', lang), onChange: (e) => {
125
- setCaptchCode(e);
126
- }, style: { width: 300 } }), captchaImage && _jsx("img", { src: captchaImage, alt: t('verifyCode', lang), style: { width: 100, height: 30, marginLeft: 8 }, onClick: getKey })] }), (countdown > 0 || captchCode) ? null : _jsx("div", { style: { fontSize: 14, color: 'red' }, children: t('pleaseEnterAndSend', lang) }), _jsx("div", { style: { fontSize: 14, color: '#666', marginTop: 12 }, children: t('chidoriOpenPro', lang) })] }));
127
- };
128
- export default CreateMessageVerifyModal;
@@ -1,10 +0,0 @@
1
- type Listener = (...args: any[]) => void;
2
- declare class EventEmitter {
3
- private events;
4
- on(event: string, listener: Listener): void;
5
- off(event: string, listener: Listener): void;
6
- emit(event: string, ...args: any[]): void;
7
- once(event: string, listener: Listener): void;
8
- }
9
- declare const eventEmitter: EventEmitter;
10
- export default eventEmitter;
@@ -1,28 +0,0 @@
1
- class EventEmitter {
2
- events = {};
3
- on(event, listener) {
4
- if (!this.events[event]) {
5
- this.events[event] = [];
6
- }
7
- this.events[event].push(listener);
8
- }
9
- off(event, listener) {
10
- if (!this.events[event])
11
- return;
12
- this.events[event] = this.events[event].filter(l => l !== listener);
13
- }
14
- emit(event, ...args) {
15
- if (!this.events[event])
16
- return;
17
- this.events[event].forEach(listener => listener(...args));
18
- }
19
- once(event, listener) {
20
- const onceListener = (...args) => {
21
- listener(...args);
22
- this.off(event, onceListener);
23
- };
24
- this.on(event, onceListener);
25
- }
26
- }
27
- const eventEmitter = new EventEmitter();
28
- export default eventEmitter;
@@ -1,2 +0,0 @@
1
- declare const _default: import("@yqg/resource").default & Record<"getCaptchaKey" | "reSendCode" | "getCaptchaImgUrl", import("@yqg/resource").CustomMethod>;
2
- export default _default;
@@ -1,7 +0,0 @@
1
- import { assign, customPost } from '@yqg/resource';
2
- const api = {
3
- getCaptchaKey: customPost('/messageVerify/admin/captcha/key'),
4
- reSendCode: customPost('/admin/sms/send'),
5
- getCaptchaImgUrl: (captchaKey) => { return `/messageVerify/admin/captcha/image/${captchaKey}`; },
6
- };
7
- export default assign(api);
@@ -1,33 +0,0 @@
1
- type Listener = (...args: any[]) => void;
2
-
3
- class EventEmitter {
4
- private events: Record<string, Listener[]> = {};
5
-
6
- on(event: string, listener: Listener) {
7
- if (!this.events[event]) {
8
- this.events[event] = [];
9
- }
10
- this.events[event].push(listener);
11
- }
12
-
13
- off(event: string, listener: Listener) {
14
- if (!this.events[event]) return;
15
- this.events[event] = this.events[event].filter(l => l !== listener);
16
- }
17
-
18
- emit(event: string, ...args: any[]) {
19
- if (!this.events[event]) return;
20
- this.events[event].forEach(listener => listener(...args));
21
- }
22
-
23
- once(event: string, listener: Listener) {
24
- const onceListener: Listener = (...args) => {
25
- listener(...args);
26
- this.off(event, onceListener);
27
- };
28
- this.on(event, onceListener);
29
- }
30
- }
31
-
32
- const eventEmitter = new EventEmitter();
33
- export default eventEmitter;
File without changes