message-verify 1.0.1-beta.69 → 1.0.1-beta.7
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 +0 -1
- package/dist/index.d.ts +2 -6
- package/dist/index.js +30 -65
- package/dist/locales/en.d.ts +0 -5
- package/dist/locales/en.js +1 -6
- package/dist/locales/id.d.ts +0 -5
- package/dist/locales/id.js +1 -6
- package/dist/locales/zh.d.ts +0 -5
- package/dist/locales/zh.js +1 -6
- package/dist/main.js +22 -15
- package/dist/relogin-modal.d.ts +2 -2
- package/dist/relogin-modal.js +7 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/type.d.ts +4 -14
- package/dist/verify-modal.d.ts +2 -2
- package/dist/verify-modal.js +22 -40
- package/package.json +2 -3
- package/src/index.tsx +44 -81
- package/src/locales/en.ts +1 -6
- package/src/locales/id.ts +1 -6
- package/src/locales/zh.ts +1 -6
- package/src/main.tsx +21 -18
- package/src/{modal/relogin-modal.tsx → relogin-modal.tsx} +11 -13
- package/src/utils/type.ts +4 -13
- package/src/{modal/verify-modal.tsx → verify-modal.tsx} +32 -66
- package/dist/modal/relogin-modal.d.ts +0 -5
- package/dist/modal/relogin-modal.js +0 -24
- package/dist/modal/verify-modal.d.ts +0 -5
- package/dist/modal/verify-modal.js +0 -129
- package/dist/utils/eventEmitter.d.ts +0 -10
- package/dist/utils/eventEmitter.js +0 -28
- package/dist/utils/resource.d.ts +0 -2
- package/dist/utils/resource.js +0 -7
- package/src/utils/eventEmitter.ts +0 -33
- /package/src/{utils/resource.ts → resource.ts} +0 -0
package/.arcconfig
CHANGED
package/dist/index.d.ts
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
import {
|
1
|
+
import { ModalConfig } from './utils/type.js';
|
2
2
|
export declare const initFingerprint: () => Promise<string>;
|
3
|
-
export declare const
|
4
|
-
export declare const VerifyHandler: {
|
5
|
-
({ data, config, http, verifyPromise, sendApi }: VerifyModalConfig): Promise<any>;
|
6
|
-
getResponse(response: any): any;
|
7
|
-
};
|
3
|
+
export declare const createMessageVerifyModal: (modalProps: ModalConfig) => void;
|
package/dist/index.js
CHANGED
@@ -1,81 +1,46 @@
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
2
2
|
import ReactDOM from 'react-dom/client';
|
3
|
-
import VerifyModal from './
|
3
|
+
import VerifyModal from './verify-modal.js';
|
4
4
|
import Fingerprint2 from 'fingerprintjs2';
|
5
|
-
|
6
|
-
import eventEmitter from './utils/eventEmitter';
|
7
|
-
let fingerprintPromise = null; // 用 Promise 本身作为缓存
|
8
|
-
let modalRoot = null;
|
5
|
+
let cachedFingerprint = null;
|
9
6
|
export const initFingerprint = async () => {
|
10
|
-
|
11
|
-
|
12
|
-
|
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;
|
7
|
+
if (cachedFingerprint) {
|
8
|
+
// 已有缓存,直接返回 Promise
|
9
|
+
return Promise.resolve(cachedFingerprint);
|
34
10
|
}
|
11
|
+
// 首次异步获取并缓存
|
12
|
+
return new Promise((resolve) => {
|
13
|
+
Fingerprint2.get(function (components) {
|
14
|
+
console.log('components', components);
|
15
|
+
const includeKeys = ['userAgent', 'deviceMemory', 'cpuClass', 'hardwareConcurrency', 'platform'];
|
16
|
+
const values = components
|
17
|
+
.filter(component => includeKeys.includes(component.key))
|
18
|
+
.map(component => component.value)
|
19
|
+
.join('###');
|
20
|
+
const fingerprint = Fingerprint2.x64hash128(values, 31);
|
21
|
+
cachedFingerprint = fingerprint;
|
22
|
+
resolve(fingerprint);
|
23
|
+
});
|
24
|
+
});
|
35
25
|
};
|
36
|
-
|
37
|
-
|
26
|
+
let modalRoot = null;
|
27
|
+
export const createMessageVerifyModal = (modalProps) => {
|
38
28
|
const container = document.createElement('div');
|
39
|
-
container.id =
|
29
|
+
container.id = 'antd-modal-container';
|
40
30
|
document.body.appendChild(container);
|
41
31
|
modalRoot = ReactDOM.createRoot(container);
|
42
|
-
modalRoot.render(_jsx(
|
32
|
+
modalRoot.render(_jsx(VerifyModal, { props: {
|
43
33
|
...modalProps,
|
44
34
|
onClose: () => {
|
45
|
-
|
35
|
+
modalProps.onClose?.();
|
36
|
+
destroyModal();
|
46
37
|
}
|
47
38
|
} }));
|
48
39
|
};
|
49
|
-
const
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
export const createReLoginModal = (modalProps) => {
|
55
|
-
const reLoginModalId = 'relogin-modal-container';
|
56
|
-
creatModal(modalProps, reLoginModalId, ReLoginModal);
|
57
|
-
};
|
58
|
-
export const VerifyHandler = ({ data, config, http, verifyPromise, sendApi }) => {
|
59
|
-
const toEmpty = () => {
|
60
|
-
verifyPromise.current = null;
|
61
|
-
};
|
62
|
-
if (!verifyPromise.current) {
|
63
|
-
verifyPromise.current = new Promise((resolve, reject) => {
|
64
|
-
createMessageVerifyModal({
|
65
|
-
data,
|
66
|
-
config,
|
67
|
-
http,
|
68
|
-
apiResolve: resolve,
|
69
|
-
apiReject: reject,
|
70
|
-
apiPromiseToEmpty: toEmpty,
|
71
|
-
sendApi
|
72
|
-
});
|
73
|
-
}).finally(toEmpty);
|
40
|
+
const destroyModal = () => {
|
41
|
+
if (modalRoot) {
|
42
|
+
modalRoot.unmount();
|
43
|
+
document.getElementById('antd-modal-container')?.remove();
|
44
|
+
modalRoot = null;
|
74
45
|
}
|
75
|
-
return verifyPromise.current;
|
76
|
-
};
|
77
|
-
// 获取当前触发接口response
|
78
|
-
VerifyHandler.getResponse = (response) => {
|
79
|
-
eventEmitter.emit('response', response?.data?.status?.code ?? -1);
|
80
|
-
return response;
|
81
46
|
};
|
package/dist/locales/en.d.ts
CHANGED
package/dist/locales/en.js
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: 'Successfully sent',
|
7
7
|
sendFailed: 'Send failed',
|
8
8
|
reSend: 'Resend',
|
9
|
-
countDownSecound: '{countdown} seconds'
|
10
|
-
accountAlert: 'Account Security Alert',
|
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'
|
9
|
+
countDownSecound: '{countdown} seconds'
|
15
10
|
};
|
package/dist/locales/id.d.ts
CHANGED
package/dist/locales/id.js
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: 'Berhasil dikirim',
|
7
7
|
sendFailed: 'Gagal mengirim',
|
8
8
|
reSend: 'Kirim Ulang',
|
9
|
-
countDownSecound: '{countdown} detik'
|
10
|
-
accountAlert: 'Peringatan Keamanan Akun',
|
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'
|
9
|
+
countDownSecound: '{countdown} detik'
|
15
10
|
};
|
package/dist/locales/zh.d.ts
CHANGED
package/dist/locales/zh.js
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: '发送成功',
|
7
7
|
sendFailed: '发送失败',
|
8
8
|
reSend: '重新发送',
|
9
|
-
countDownSecound: '{countdown}秒'
|
10
|
-
accountAlert: '账号安全提醒',
|
11
|
-
alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
|
12
|
-
ok: '好的',
|
13
|
-
chidoriOpenPro: 'chidori已为您开启安全防护',
|
14
|
-
verifyFail: '验证失败'
|
9
|
+
countDownSecound: '{countdown}秒'
|
15
10
|
};
|
package/dist/main.js
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
2
2
|
import { createRoot } from 'react-dom/client';
|
3
|
-
import {
|
3
|
+
import { createMessageVerifyModal, initFingerprint } 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
|
-
|
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": "
|
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,12 +43,12 @@ const config = {
|
|
36
43
|
"data": "{\"id\":1,\"classificationName\":\"个人基本概况信息\",\"classificationLevel\":2}",
|
37
44
|
"baseURL": "http://localhost:62888"
|
38
45
|
};
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
createRoot(document.getElementById('root')).render(_jsx(_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" }) }));
|
package/dist/relogin-modal.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { ModalConfig } from './utils/type.js';
|
2
2
|
declare const ReLoginModal: ({ props }: {
|
3
|
-
props:
|
3
|
+
props: ModalConfig;
|
4
4
|
}) => import("react/jsx-runtime.js").JSX.Element;
|
5
5
|
export default ReLoginModal;
|
package/dist/relogin-modal.js
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
2
|
import { useState } from 'react';
|
3
3
|
import { Modal, Button } from './compoments/index.js';
|
4
|
-
import t from './utils/i18n.js';
|
5
4
|
const ReLoginModal = ({ props }) => {
|
6
|
-
|
5
|
+
console.log('props', props);
|
7
6
|
const [visible, setVisible] = useState(true);
|
8
7
|
const onClose = () => {
|
9
8
|
setVisible(false);
|
10
|
-
goLogin();
|
11
9
|
};
|
12
|
-
return (_jsxs(Modal, { visible: visible, onClose: onClose, width: 420, children: [_jsx("div", { style: { fontWeight: 500, fontSize: 20, marginBottom: 24 }, children:
|
10
|
+
return (_jsxs(Modal, { visible: visible, onClose: onClose, width: 420, children: [_jsx("div", { style: { fontWeight: 500, fontSize: 20, marginBottom: 24 }, children: "\u8D26\u53F7\u5B89\u5168\u63D0\u9192" }), _jsx("div", { style: { color: '#222', fontSize: 16, marginBottom: 40, lineHeight: '24px' }, children: "\u68C0\u6D4B\u5230\u60A8\u7684\u767B\u5F55\u73AF\u5883\u6216\u7F51\u7EDC\u4FE1\u606F\u6709\u53D8\u52A8\uFF0C\u4E3A\u4FDD\u969C\u8D26\u6237\u5B89\u5168\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u9A8C\u8BC1\u8EAB\u4EFD" }), _jsx("div", { style: { display: 'flex', justifyContent: 'center' }, children: _jsx(Button, { style: {
|
13
11
|
background: '#1677ff',
|
14
12
|
color: '#fff',
|
15
13
|
border: 'none',
|
16
|
-
width:
|
17
|
-
height:
|
18
|
-
fontSize:
|
14
|
+
width: 160,
|
15
|
+
height: 44,
|
16
|
+
fontSize: 18,
|
17
|
+
fontWeight: 500,
|
19
18
|
borderRadius: 8,
|
20
19
|
boxShadow: '0 2px 8px rgba(22,119,255,0.08)',
|
21
|
-
}, onClick: onClose, children:
|
20
|
+
}, onClick: onClose, children: "\u597D\u7684" }) })] }));
|
22
21
|
};
|
23
22
|
export default ReLoginModal;
|
@@ -1 +1 @@
|
|
1
|
-
{"root":["../src/index.tsx","../src/main.tsx","../src/
|
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"}
|
package/dist/utils/type.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
export type
|
1
|
+
export type ModalConfig = {
|
2
2
|
data: string;
|
3
3
|
onClose?: () => void;
|
4
4
|
config: {
|
@@ -7,7 +7,7 @@ export type VerifyModalConfig = {
|
|
7
7
|
};
|
8
8
|
[key: string]: unknown;
|
9
9
|
};
|
10
|
-
lang
|
10
|
+
lang: 'zh' | 'en' | 'id';
|
11
11
|
http: {
|
12
12
|
(params: object): Promise<unknown>;
|
13
13
|
defaults: {
|
@@ -18,16 +18,6 @@ export type VerifyModalConfig = {
|
|
18
18
|
};
|
19
19
|
};
|
20
20
|
};
|
21
|
-
|
22
|
-
|
23
|
-
apiResolve?: any;
|
24
|
-
apiPromiseToEmpty?: () => void;
|
25
|
-
verifyPromise: {
|
26
|
-
current: Promise<any> | null;
|
27
|
-
};
|
28
|
-
};
|
29
|
-
export type LoginModalConfig = {
|
30
|
-
goLogin: () => void;
|
31
|
-
onClose?: () => void;
|
32
|
-
lang?: 'zh' | 'en' | 'id';
|
21
|
+
api?: string;
|
22
|
+
response: any;
|
33
23
|
};
|
package/dist/verify-modal.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { ModalConfig } from './utils/type.js';
|
2
2
|
declare const CreateMessageVerifyModal: ({ props }: {
|
3
|
-
props:
|
3
|
+
props: ModalConfig;
|
4
4
|
}) => import("react/jsx-runtime.js").JSX.Element;
|
5
5
|
export default CreateMessageVerifyModal;
|
package/dist/verify-modal.js
CHANGED
@@ -11,9 +11,10 @@ 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,
|
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
|
+
console.log('response===>', response);
|
17
18
|
const getKey = async () => {
|
18
19
|
try {
|
19
20
|
const res = await Api.getCaptchaKey();
|
@@ -51,65 +52,46 @@ const CreateMessageVerifyModal = ({ props }) => {
|
|
51
52
|
formData.append('captchaKey', captchaKey);
|
52
53
|
formData.append('captcha', captchCode);
|
53
54
|
// 重新发送验证码
|
54
|
-
const res = await axios.post(
|
55
|
+
const res = await axios.post(api, formData);
|
55
56
|
const { code, detail } = res.data?.status || {};
|
56
57
|
if (code !== 0) {
|
57
58
|
message.error(detail || t('sendFailed', lang));
|
58
59
|
return;
|
59
60
|
}
|
60
61
|
message.success(t('sendSuccess', lang));
|
61
|
-
// 重置倒计时、验证码
|
62
62
|
setCountdown(60);
|
63
|
-
setOtp('');
|
64
63
|
};
|
65
64
|
return (_jsxs(Modal, { width: 420, visible: visible, onClose: () => {
|
66
65
|
setVisible(false);
|
67
|
-
apiPromiseToEmpty?.();
|
68
66
|
props.onClose?.();
|
69
67
|
}, 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
68
|
setOtp(val);
|
71
69
|
if (val.length === 6) {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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('');
|
70
|
+
const { data: { status: { code, detail } = {} } } = response || {};
|
71
|
+
http({
|
72
|
+
...response?.config,
|
73
|
+
headers: {
|
74
|
+
...http.defaults.headers.common,
|
75
|
+
'sms-code': val,
|
76
|
+
'sms-code-key': verifyCodeKey,
|
77
|
+
'Content-Type': response?.config.headers['Content-Type'],
|
102
78
|
}
|
79
|
+
});
|
80
|
+
console.log(response, code, 'config');
|
81
|
+
if (code !== 0) {
|
82
|
+
const detailObj = JSON.parse(detail || '{}');
|
83
|
+
message.error(detailObj?.message || t('verifyFailed', lang));
|
84
|
+
return;
|
103
85
|
}
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
setOtp('');
|
86
|
+
else {
|
87
|
+
message.success(t('verifySuccess', lang));
|
88
|
+
// setVisible(false);
|
108
89
|
}
|
90
|
+
setVisible(false);
|
109
91
|
}
|
110
92
|
} }), _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
93
|
_jsxs("div", { style: { marginTop: 12, display: 'flex', alignItems: 'center' }, children: [_jsx(Input, { placeholder: t('pleaseEnterPicVerifyCode', lang), onChange: (e) => {
|
112
94
|
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) })
|
95
|
+
}, 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
96
|
};
|
115
97
|
export default CreateMessageVerifyModal;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "message-verify",
|
3
|
-
"version": "1.0.1-beta.
|
3
|
+
"version": "1.0.1-beta.7",
|
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
|
}
|
package/src/index.tsx
CHANGED
@@ -1,92 +1,55 @@
|
|
1
1
|
import ReactDOM from 'react-dom/client'
|
2
|
-
import VerifyModal from './
|
2
|
+
import VerifyModal from './verify-modal.js'
|
3
3
|
import Fingerprint2 from 'fingerprintjs2';
|
4
|
-
import
|
5
|
-
import { VerifyModalConfig, LoginModalConfig } from './utils/type.js';
|
6
|
-
import eventEmitter from './utils/eventEmitter';
|
4
|
+
import { ModalConfig } from './utils/type.js';
|
7
5
|
|
8
|
-
let
|
9
|
-
let modalRoot: ReactDOM.Root | null = null
|
6
|
+
let cachedFingerprint: string | null = null;
|
10
7
|
export const initFingerprint = async (): Promise<string> => {
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
});
|
8
|
+
if (cachedFingerprint) {
|
9
|
+
// 已有缓存,直接返回 Promise
|
10
|
+
return Promise.resolve(cachedFingerprint);
|
27
11
|
}
|
28
|
-
|
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
|
+
});
|
29
26
|
};
|
30
27
|
|
31
|
-
|
32
|
-
if (modalRoot) {
|
33
|
-
modalRoot.unmount()
|
34
|
-
document.getElementById(modalId)?.remove()
|
35
|
-
modalRoot = null
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
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)
|
28
|
+
let modalRoot: ReactDOM.Root | null = null
|
44
29
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
...modalProps,
|
50
|
-
onClose: () => {
|
51
|
-
destroyModal(modalId);
|
52
|
-
}
|
53
|
-
}}
|
54
|
-
/>
|
55
|
-
)
|
56
|
-
}
|
30
|
+
export const createMessageVerifyModal = (modalProps: ModalConfig) => {
|
31
|
+
const container = document.createElement('div')
|
32
|
+
container.id = 'antd-modal-container'
|
33
|
+
document.body.appendChild(container)
|
57
34
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
35
|
+
modalRoot = ReactDOM.createRoot(container)
|
36
|
+
modalRoot.render(
|
37
|
+
<VerifyModal
|
38
|
+
props={{
|
39
|
+
...modalProps,
|
40
|
+
onClose: () => {
|
41
|
+
modalProps.onClose?.()
|
42
|
+
destroyModal()
|
43
|
+
}
|
44
|
+
}}
|
45
|
+
/>
|
46
|
+
)
|
66
47
|
}
|
67
48
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
data,
|
76
|
-
config,
|
77
|
-
http,
|
78
|
-
apiResolve: resolve,
|
79
|
-
apiReject: reject,
|
80
|
-
apiPromiseToEmpty: toEmpty,
|
81
|
-
sendApi
|
82
|
-
} as VerifyModalConfig);
|
83
|
-
}).finally(toEmpty);
|
84
|
-
}
|
85
|
-
return verifyPromise.current;
|
86
|
-
};
|
87
|
-
|
88
|
-
// 获取当前触发接口response
|
89
|
-
VerifyHandler.getResponse = (response: any) => {
|
90
|
-
eventEmitter.emit('response', response?.data?.status?.code ?? -1);
|
91
|
-
return response;
|
92
|
-
};
|
49
|
+
const destroyModal = () => {
|
50
|
+
if (modalRoot) {
|
51
|
+
modalRoot.unmount()
|
52
|
+
document.getElementById('antd-modal-container')?.remove()
|
53
|
+
modalRoot = null
|
54
|
+
}
|
55
|
+
}
|
package/src/locales/en.ts
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: 'Successfully sent',
|
7
7
|
sendFailed: 'Send failed',
|
8
8
|
reSend: 'Resend',
|
9
|
-
countDownSecound: '{countdown} seconds'
|
10
|
-
accountAlert: 'Account Security Alert',
|
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'
|
9
|
+
countDownSecound: '{countdown} seconds'
|
15
10
|
};
|
package/src/locales/id.ts
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: 'Berhasil dikirim',
|
7
7
|
sendFailed: 'Gagal mengirim',
|
8
8
|
reSend: 'Kirim Ulang',
|
9
|
-
countDownSecound: '{countdown} detik'
|
10
|
-
accountAlert: 'Peringatan Keamanan Akun',
|
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'
|
9
|
+
countDownSecound: '{countdown} detik'
|
15
10
|
};
|
package/src/locales/zh.ts
CHANGED
@@ -6,10 +6,5 @@ export default {
|
|
6
6
|
sendSuccess: '发送成功',
|
7
7
|
sendFailed: '发送失败',
|
8
8
|
reSend: '重新发送',
|
9
|
-
countDownSecound: '{countdown}秒'
|
10
|
-
accountAlert: '账号安全提醒',
|
11
|
-
alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
|
12
|
-
ok: '好的',
|
13
|
-
chidoriOpenPro: 'chidori已为您开启安全防护',
|
14
|
-
verifyFail: '验证失败'
|
9
|
+
countDownSecound: '{countdown}秒'
|
15
10
|
};
|
package/src/main.tsx
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import { createRoot } from 'react-dom/client';
|
2
|
-
import {
|
2
|
+
import { createMessageVerifyModal, initFingerprint } 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
|
-
|
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": "
|
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,22 +44,18 @@ 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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
<button onClick={() => createReLoginModal({
|
51
|
-
goLogin: () => {
|
52
|
-
console.log('goLogin');
|
50
|
+
<button onClick={() => createMessageVerifyModal({
|
51
|
+
data,
|
52
|
+
config,
|
53
|
+
response: {
|
54
|
+
config
|
53
55
|
},
|
54
|
-
lang: '
|
56
|
+
lang: 'en',
|
57
|
+
http: axios,
|
55
58
|
})}>
|
56
|
-
Show
|
59
|
+
Show verify Modal
|
57
60
|
</button>
|
58
61
|
</>);
|
@@ -1,23 +1,20 @@
|
|
1
1
|
import { useState } from 'react'
|
2
|
-
import { Modal, Button } from '
|
3
|
-
import {
|
4
|
-
import t from '../utils/i18n.js'
|
2
|
+
import { Modal, Button } from './compoments/index.js'
|
3
|
+
import { ModalConfig } from './utils/type.js';
|
5
4
|
|
6
|
-
const ReLoginModal = ({ props }: { props:
|
7
|
-
|
5
|
+
const ReLoginModal = ({ props }: { props: ModalConfig }) => {
|
6
|
+
console.log('props', props);
|
8
7
|
const [visible, setVisible] = useState(true);
|
9
8
|
const onClose = () => {
|
10
9
|
setVisible(false);
|
11
|
-
goLogin();
|
12
|
-
destroyModal?.();
|
13
10
|
};
|
14
11
|
return (
|
15
12
|
<Modal visible={visible} onClose={onClose} width={420}>
|
16
13
|
<div style={{ fontWeight: 500, fontSize: 20, marginBottom: 24 }}>
|
17
|
-
|
14
|
+
账号安全提醒
|
18
15
|
</div>
|
19
16
|
<div style={{ color: '#222', fontSize: 16, marginBottom: 40, lineHeight: '24px' }}>
|
20
|
-
|
17
|
+
检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份
|
21
18
|
</div>
|
22
19
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
23
20
|
<Button
|
@@ -25,15 +22,16 @@ const ReLoginModal = ({ props }: { props: LoginModalConfig }) => {
|
|
25
22
|
background: '#1677ff',
|
26
23
|
color: '#fff',
|
27
24
|
border: 'none',
|
28
|
-
width:
|
29
|
-
height:
|
30
|
-
fontSize:
|
25
|
+
width: 160,
|
26
|
+
height: 44,
|
27
|
+
fontSize: 18,
|
28
|
+
fontWeight: 500,
|
31
29
|
borderRadius: 8,
|
32
30
|
boxShadow: '0 2px 8px rgba(22,119,255,0.08)',
|
33
31
|
}}
|
34
32
|
onClick={onClose}
|
35
33
|
>
|
36
|
-
|
34
|
+
好的
|
37
35
|
</Button>
|
38
36
|
</div>
|
39
37
|
</Modal>
|
package/src/utils/type.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
export type
|
2
|
+
export type ModalConfig = {
|
3
3
|
data: string;
|
4
4
|
onClose?: () => void
|
5
5
|
config: {
|
@@ -8,7 +8,7 @@ export type VerifyModalConfig = {
|
|
8
8
|
};
|
9
9
|
[key: string]: unknown;
|
10
10
|
};
|
11
|
-
lang
|
11
|
+
lang: 'zh' | 'en' | 'id';
|
12
12
|
http: {
|
13
13
|
(params: object): Promise<unknown>;
|
14
14
|
defaults: {
|
@@ -19,15 +19,6 @@ export type VerifyModalConfig = {
|
|
19
19
|
}
|
20
20
|
}
|
21
21
|
};
|
22
|
-
|
23
|
-
|
24
|
-
apiResolve?: any;
|
25
|
-
apiPromiseToEmpty?: () => void;
|
26
|
-
verifyPromise: {current: Promise<any> | null}
|
27
|
-
}
|
28
|
-
|
29
|
-
export type LoginModalConfig = {
|
30
|
-
goLogin: () => void;
|
31
|
-
onClose?: () => void
|
32
|
-
lang?: 'zh' | 'en' | 'id';
|
22
|
+
api?: string;
|
23
|
+
response: any;
|
33
24
|
}
|
@@ -1,23 +1,21 @@
|
|
1
|
-
import {
|
2
|
-
import { Modal, Input, Button, message } from '
|
3
|
-
import Api from '
|
4
|
-
import t from '
|
5
|
-
import {
|
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';
|
8
7
|
|
9
|
-
const CreateMessageVerifyModal = ({ props }: { props:
|
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,
|
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
|
-
|
20
|
-
const rejectRef = useRef<any>(null);
|
18
|
+
console.log('response===>', response);
|
21
19
|
|
22
20
|
const getKey = async () => {
|
23
21
|
try {
|
@@ -28,11 +26,10 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
|
28
26
|
const image = Api.getCaptchaImgUrl(key) as unknown;
|
29
27
|
setCaptchaImage(image as string);
|
30
28
|
}
|
31
|
-
} catch (err) {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
: String(err);
|
29
|
+
} catch (err) {const msg =
|
30
|
+
typeof err === 'object' && err !== null && 'message' in err
|
31
|
+
? (err as {message: string}).message
|
32
|
+
: String(err);
|
36
33
|
message.error(msg as React.ReactNode);
|
37
34
|
}
|
38
35
|
}
|
@@ -52,42 +49,19 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
|
52
49
|
};
|
53
50
|
}, [visible, countdown]);
|
54
51
|
|
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
52
|
const handleResend = async () => {
|
77
53
|
const formData = new FormData();
|
78
54
|
formData.append('captchaKey', captchaKey);
|
79
55
|
formData.append('captcha', captchCode);
|
80
56
|
// 重新发送验证码
|
81
|
-
const res = await axios.post(
|
57
|
+
const res = await axios.post(api, formData);
|
82
58
|
const { code, detail } = res.data?.status || {};
|
83
59
|
if (code !== 0) {
|
84
60
|
message.error(detail || t('sendFailed', lang));
|
85
61
|
return;
|
86
62
|
}
|
87
63
|
message.success(t('sendSuccess', lang));
|
88
|
-
// 重置倒计时、验证码
|
89
64
|
setCountdown(60);
|
90
|
-
setOtp('');
|
91
65
|
}
|
92
66
|
|
93
67
|
return (
|
@@ -96,8 +70,7 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
|
96
70
|
visible={visible}
|
97
71
|
onClose={() => {
|
98
72
|
setVisible(false);
|
99
|
-
|
100
|
-
onClose?.();
|
73
|
+
props.onClose?.();
|
101
74
|
}}
|
102
75
|
>
|
103
76
|
<div style={{ fontSize: 14, color: '#666' }}>{t('alreadySend', lang, { phone: mobile })}</div>
|
@@ -109,32 +82,26 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
|
109
82
|
onChange={async (val) => {
|
110
83
|
setOtp(val);
|
111
84
|
if (val.length === 6) {
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
http
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
'sms-code': val,
|
121
|
-
'sms-code-key': verifyCodeKey,
|
122
|
-
'Content-Type': config.headers['Content-Type'],
|
123
|
-
}
|
124
|
-
});
|
125
|
-
}) as any;
|
126
|
-
if (res?.code === 0 || res?.data?.status?.code === 0) {
|
127
|
-
apiResolve(res);
|
128
|
-
setVisible(false);
|
129
|
-
} else {
|
130
|
-
message.error(t('verifyFail', lang));
|
131
|
-
setOtp('');
|
85
|
+
const {data: {status: {code, detail} = {}}} = response || {};
|
86
|
+
http({
|
87
|
+
...response?.config,
|
88
|
+
headers: {
|
89
|
+
...http.defaults.headers.common,
|
90
|
+
'sms-code': val,
|
91
|
+
'sms-code-key': verifyCodeKey,
|
92
|
+
'Content-Type': response?.config.headers['Content-Type'],
|
132
93
|
}
|
133
|
-
}
|
134
|
-
|
135
|
-
|
136
|
-
|
94
|
+
}) as any;
|
95
|
+
console.log(response, code, 'config');
|
96
|
+
if(code !== 0) {
|
97
|
+
const detailObj = JSON.parse(detail || '{}');
|
98
|
+
message.error(detailObj?.message || t('verifyFailed', lang));
|
99
|
+
return;
|
100
|
+
} else {
|
101
|
+
message.success(t('verifySuccess', lang));
|
102
|
+
// setVisible(false);
|
137
103
|
}
|
104
|
+
setVisible(false);
|
138
105
|
}
|
139
106
|
}} />
|
140
107
|
<Button
|
@@ -158,7 +125,6 @@ const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
|
158
125
|
{captchaImage && <img src={captchaImage} alt={t('verifyCode', lang)} style={{ width: 100, height: 30, marginLeft: 8 }} onClick={getKey} />}
|
159
126
|
</div>}
|
160
127
|
{(countdown > 0 || captchCode) ? null : <div style={{ fontSize: 14, color: 'red' }}>{t('pleaseEnterAndSend', lang)}</div>}
|
161
|
-
<div style={{ fontSize: 14, color: '#666', marginTop: 12 }}>{t('chidoriOpenPro', lang)}</div>
|
162
128
|
</Modal>
|
163
129
|
);
|
164
130
|
};
|
@@ -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,129 +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';
|
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, detail } = res.data?.status || {};
|
79
|
-
if (code !== 0) {
|
80
|
-
message.error(detail || t('sendFailed', lang));
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
message.success(t('sendSuccess', lang));
|
84
|
-
// 重置倒计时、验证码
|
85
|
-
setCountdown(60);
|
86
|
-
setOtp('');
|
87
|
-
};
|
88
|
-
return (_jsxs(Modal, { width: 420, visible: visible, onClose: () => {
|
89
|
-
setVisible(false);
|
90
|
-
apiPromiseToEmpty?.();
|
91
|
-
onClose?.();
|
92
|
-
}, 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) => {
|
93
|
-
setOtp(val);
|
94
|
-
if (val.length === 6) {
|
95
|
-
try {
|
96
|
-
const res = await new Promise((resolve, reject) => {
|
97
|
-
resolveRef.current = resolve;
|
98
|
-
rejectRef.current = reject;
|
99
|
-
http({
|
100
|
-
...config,
|
101
|
-
headers: {
|
102
|
-
...http.defaults.headers.common,
|
103
|
-
'sms-code': val,
|
104
|
-
'sms-code-key': verifyCodeKey,
|
105
|
-
'Content-Type': config.headers['Content-Type'],
|
106
|
-
}
|
107
|
-
});
|
108
|
-
});
|
109
|
-
if (res?.code === 0 || res?.data?.status?.code === 0) {
|
110
|
-
apiResolve(res);
|
111
|
-
setVisible(false);
|
112
|
-
}
|
113
|
-
else {
|
114
|
-
message.error(t('verifyFail', lang));
|
115
|
-
setOtp('');
|
116
|
-
}
|
117
|
-
}
|
118
|
-
catch (error) {
|
119
|
-
console.warn('catch=>error', error);
|
120
|
-
message.error(t('verifyFail', lang));
|
121
|
-
setOtp('');
|
122
|
-
}
|
123
|
-
}
|
124
|
-
} }), _jsx(Button, { disabled: countdown > 0 || !captchCode, onClick: handleResend, style: { padding: '8px' }, children: countdown > 0 ? t('countDownSecound', lang, { countdown }) : t('reSend', lang) })] }), countdown > 0 ? null :
|
125
|
-
_jsxs("div", { style: { marginTop: 12, display: 'flex', alignItems: 'center' }, children: [_jsx(Input, { placeholder: t('pleaseEnterPicVerifyCode', lang), onChange: (e) => {
|
126
|
-
setCaptchCode(e);
|
127
|
-
}, 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) })] }));
|
128
|
-
};
|
129
|
-
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;
|
package/dist/utils/resource.d.ts
DELETED
package/dist/utils/resource.js
DELETED
@@ -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
|