message-verify 1.0.1-beta.4 → 1.0.1-beta.41
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/dist/index.d.ts +3 -2
- package/dist/index.js +49 -20
- package/dist/locales/en.d.ts +4 -0
- package/dist/locales/en.js +5 -1
- package/dist/locales/id.d.ts +4 -0
- package/dist/locales/id.js +5 -1
- package/dist/locales/zh.d.ts +4 -0
- package/dist/locales/zh.js +5 -1
- package/dist/main.js +14 -21
- package/dist/relogin-modal.d.ts +2 -2
- package/dist/relogin-modal.js +8 -7
- package/dist/utils/type.d.ts +12 -3
- package/dist/verify-modal.d.ts +2 -2
- package/dist/verify-modal.js +39 -18
- package/package.json +3 -2
- package/src/index.tsx +79 -42
- package/src/locales/en.ts +5 -1
- package/src/locales/id.ts +5 -1
- package/src/locales/zh.ts +5 -1
- package/src/main.tsx +16 -19
- package/src/relogin-modal.tsx +11 -10
- package/src/utils/type.ts +11 -3
- package/src/verify-modal.tsx +45 -23
package/dist/index.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { VerifyModalConfig, LoginModalConfig } from './utils/type.js';
|
2
2
|
export declare const initFingerprint: () => Promise<string>;
|
3
|
-
export declare const
|
3
|
+
export declare const createReLoginModal: (modalProps: LoginModalConfig) => void;
|
4
|
+
export declare const VerifyHandler: (modalProps: VerifyModalConfig) => Promise<any>;
|
package/dist/index.js
CHANGED
@@ -2,31 +2,31 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import ReactDOM from 'react-dom/client';
|
3
3
|
import VerifyModal from './verify-modal.js';
|
4
4
|
import Fingerprint2 from 'fingerprintjs2';
|
5
|
-
|
5
|
+
import ReLoginModal from './relogin-modal.js';
|
6
|
+
let fingerprintPromise = null; // 用 Promise 本身作为缓存
|
6
7
|
export const initFingerprint = async () => {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
.
|
18
|
-
.
|
19
|
-
.
|
20
|
-
|
21
|
-
|
22
|
-
resolve(fingerprint);
|
8
|
+
console.log('fingerprintPromise:', fingerprintPromise);
|
9
|
+
if (!fingerprintPromise) {
|
10
|
+
fingerprintPromise = new Promise((resolve) => {
|
11
|
+
Fingerprint2.get(components => {
|
12
|
+
const includeKeys = ['userAgent', 'cpuClass', 'hardwareConcurrency', 'platform'];
|
13
|
+
console.log('components:', components);
|
14
|
+
const values = components
|
15
|
+
.filter(c => includeKeys.includes(c.key))
|
16
|
+
.map(c => c.value)
|
17
|
+
.join('###');
|
18
|
+
console.log('values:', values);
|
19
|
+
const fingerprint = Fingerprint2.x64hash128(values, 31);
|
20
|
+
console.log('fingerprint:', fingerprint);
|
21
|
+
resolve(fingerprint);
|
22
|
+
});
|
23
23
|
});
|
24
|
-
}
|
24
|
+
}
|
25
|
+
return fingerprintPromise;
|
25
26
|
};
|
26
27
|
let modalRoot = null;
|
27
|
-
|
28
|
+
const createMessageVerifyModal = (modalProps) => {
|
28
29
|
const container = document.createElement('div');
|
29
|
-
container.id = 'antd-modal-container';
|
30
30
|
document.body.appendChild(container);
|
31
31
|
modalRoot = ReactDOM.createRoot(container);
|
32
32
|
modalRoot.render(_jsx(VerifyModal, { props: {
|
@@ -37,6 +37,16 @@ export const createMessageVerifyModal = (modalProps) => {
|
|
37
37
|
}
|
38
38
|
} }));
|
39
39
|
};
|
40
|
+
// 新增 relogin 弹窗创建方法
|
41
|
+
export const createReLoginModal = (modalProps) => {
|
42
|
+
const container = document.createElement('div');
|
43
|
+
container.id = 'antd-modal-container';
|
44
|
+
document.body.appendChild(container);
|
45
|
+
modalRoot = ReactDOM.createRoot(container);
|
46
|
+
modalRoot.render(_jsx(ReLoginModal, { props: {
|
47
|
+
...modalProps,
|
48
|
+
} }));
|
49
|
+
};
|
40
50
|
const destroyModal = () => {
|
41
51
|
if (modalRoot) {
|
42
52
|
modalRoot.unmount();
|
@@ -44,3 +54,22 @@ const destroyModal = () => {
|
|
44
54
|
modalRoot = null;
|
45
55
|
}
|
46
56
|
};
|
57
|
+
export const VerifyHandler = (modalProps) => {
|
58
|
+
const { data, config, http, verifyPromise } = modalProps || {};
|
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
|
+
});
|
72
|
+
}).finally(toEmpty);
|
73
|
+
}
|
74
|
+
return verifyPromise.current;
|
75
|
+
};
|
package/dist/locales/en.d.ts
CHANGED
package/dist/locales/en.js
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: 'Successfully sent',
|
7
7
|
sendFailed: 'Send failed',
|
8
8
|
reSend: 'Resend',
|
9
|
-
countDownSecound: '{countdown} seconds'
|
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'
|
10
14
|
};
|
package/dist/locales/id.d.ts
CHANGED
package/dist/locales/id.js
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: 'Berhasil dikirim',
|
7
7
|
sendFailed: 'Gagal mengirim',
|
8
8
|
reSend: 'Kirim Ulang',
|
9
|
-
countDownSecound: '{countdown} detik'
|
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'
|
10
14
|
};
|
package/dist/locales/zh.d.ts
CHANGED
package/dist/locales/zh.js
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: '发送成功',
|
7
7
|
sendFailed: '发送失败',
|
8
8
|
reSend: '重新发送',
|
9
|
-
countDownSecound: '{countdown}秒'
|
9
|
+
countDownSecound: '{countdown}秒',
|
10
|
+
accountAlert: '账号安全提醒',
|
11
|
+
alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
|
12
|
+
ok: '好的',
|
13
|
+
chidoriOpenPro: 'chidori已为您开启安全防护'
|
10
14
|
};
|
package/dist/main.js
CHANGED
@@ -1,22 +1,17 @@
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
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 {
|
3
|
+
import { initFingerprint, createReLoginModal, VerifyHandler } 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
|
-
|
7
|
+
const fp2 = await initFingerprint();
|
8
|
+
console.log(fp, fp2);
|
8
9
|
const config = {
|
9
10
|
"transitional": {
|
10
11
|
"silentJSONParsing": true,
|
11
12
|
"forcedJSONParsing": true,
|
12
13
|
"clarifyTimeoutError": false
|
13
14
|
},
|
14
|
-
"transformRequest": [
|
15
|
-
null
|
16
|
-
],
|
17
|
-
"transformResponse": [
|
18
|
-
null
|
19
|
-
],
|
20
15
|
"timeout": 0,
|
21
16
|
"xsrfCookieName": "XSRF-TOKEN",
|
22
17
|
"xsrfHeaderName": "X-XSRF-TOKEN",
|
@@ -25,9 +20,7 @@ const config = {
|
|
25
20
|
"headers": {
|
26
21
|
"Accept": "application/json, text/plain, */*",
|
27
22
|
"X-Device-Fingerprint": "185fcce88c629725321adf29daa5e444",
|
28
|
-
"Content-Type": "application/json"
|
29
|
-
"sms-code": "121212",
|
30
|
-
"sms-code-key": "212190c7-2f5e-4cc1-82ec-a242c2ab5425"
|
23
|
+
"Content-Type": "application/json"
|
31
24
|
},
|
32
25
|
"url": "/classification/admin/level/edit",
|
33
26
|
"method": "post",
|
@@ -43,12 +36,12 @@ const config = {
|
|
43
36
|
"data": "{\"id\":1,\"classificationName\":\"个人基本概况信息\",\"classificationLevel\":2}",
|
44
37
|
"baseURL": "http://localhost:62888"
|
45
38
|
};
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
39
|
+
const promise = { current: null };
|
40
|
+
createRoot(document.getElementById('root')).render(_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => {
|
41
|
+
return VerifyHandler({ data, config, http: axios, verifyPromise: promise });
|
42
|
+
}, children: "Show verify Modal" }), _jsx("button", { onClick: () => createReLoginModal({
|
43
|
+
goLogin: () => {
|
44
|
+
console.log('goLogin');
|
45
|
+
},
|
46
|
+
lang: 'zh',
|
47
|
+
}), children: "Show login Modal" })] }));
|
package/dist/relogin-modal.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { LoginModalConfig } from './utils/type.js';
|
2
2
|
declare const ReLoginModal: ({ props }: {
|
3
|
-
props:
|
3
|
+
props: LoginModalConfig;
|
4
4
|
}) => import("react/jsx-runtime.js").JSX.Element;
|
5
5
|
export default ReLoginModal;
|
package/dist/relogin-modal.js
CHANGED
@@ -1,22 +1,23 @@
|
|
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';
|
4
5
|
const ReLoginModal = ({ props }) => {
|
5
|
-
|
6
|
+
const { goLogin, lang } = props || {};
|
6
7
|
const [visible, setVisible] = useState(true);
|
7
8
|
const onClose = () => {
|
8
9
|
setVisible(false);
|
10
|
+
goLogin();
|
9
11
|
};
|
10
|
-
return (_jsxs(Modal, { visible: visible, onClose: onClose, width: 420, children: [_jsx("div", { style: { fontWeight: 500, fontSize: 20, marginBottom: 24 }, children:
|
12
|
+
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: {
|
11
13
|
background: '#1677ff',
|
12
14
|
color: '#fff',
|
13
15
|
border: 'none',
|
14
|
-
width:
|
15
|
-
height:
|
16
|
-
fontSize:
|
17
|
-
fontWeight: 500,
|
16
|
+
width: 80,
|
17
|
+
height: 36,
|
18
|
+
fontSize: 14,
|
18
19
|
borderRadius: 8,
|
19
20
|
boxShadow: '0 2px 8px rgba(22,119,255,0.08)',
|
20
|
-
}, onClick: onClose, children:
|
21
|
+
}, onClick: onClose, children: t('ok', lang) }) })] }));
|
21
22
|
};
|
22
23
|
export default ReLoginModal;
|
package/dist/utils/type.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
export type
|
1
|
+
export type VerifyModalConfig = {
|
2
2
|
data: string;
|
3
3
|
onClose?: () => void;
|
4
4
|
config: {
|
@@ -7,7 +7,7 @@ export type ModalConfig = {
|
|
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: {
|
@@ -19,5 +19,14 @@ export type ModalConfig = {
|
|
19
19
|
};
|
20
20
|
};
|
21
21
|
api?: string;
|
22
|
-
|
22
|
+
apiReject?: any;
|
23
|
+
apiResolve?: any;
|
24
|
+
apiPromiseToEmpty?: () => void;
|
25
|
+
verifyPromise: {
|
26
|
+
current: Promise<any> | null;
|
27
|
+
};
|
28
|
+
};
|
29
|
+
export type LoginModalConfig = {
|
30
|
+
goLogin: () => void;
|
31
|
+
lang?: 'zh' | 'en' | 'id';
|
23
32
|
};
|
package/dist/verify-modal.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { VerifyModalConfig } from './utils/type.js';
|
2
2
|
declare const CreateMessageVerifyModal: ({ props }: {
|
3
|
-
props:
|
3
|
+
props: VerifyModalConfig;
|
4
4
|
}) => import("react/jsx-runtime.js").JSX.Element;
|
5
5
|
export default CreateMessageVerifyModal;
|
package/dist/verify-modal.js
CHANGED
@@ -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, api = '/admin/sms/send',
|
14
|
+
const { data, http, lang, api = '/admin/sms/send', config, apiResolve, apiPromiseToEmpty } = props || {};
|
15
15
|
const dataObj = JSON.parse(data || '{}');
|
16
16
|
const { mobile, verifyCodeKey } = dataObj || {};
|
17
17
|
const getKey = async () => {
|
@@ -58,37 +58,58 @@ const CreateMessageVerifyModal = ({ props }) => {
|
|
58
58
|
return;
|
59
59
|
}
|
60
60
|
message.success(t('sendSuccess', lang));
|
61
|
+
// 重置倒计时、验证码
|
61
62
|
setCountdown(60);
|
63
|
+
setOtp('');
|
62
64
|
};
|
63
65
|
return (_jsxs(Modal, { width: 420, visible: visible, onClose: () => {
|
64
66
|
setVisible(false);
|
67
|
+
apiPromiseToEmpty?.();
|
65
68
|
props.onClose?.();
|
66
69
|
}, 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) => {
|
67
70
|
setOtp(val);
|
68
71
|
if (val.length === 6) {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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('');
|
77
102
|
}
|
78
|
-
});
|
79
|
-
console.log(response, code, 'config');
|
80
|
-
if (code !== 0) {
|
81
|
-
message.error(detail || t('verifyFailed', lang));
|
82
|
-
return;
|
83
103
|
}
|
84
|
-
|
85
|
-
|
86
|
-
|
104
|
+
catch (error) {
|
105
|
+
console.warn('catch', error);
|
106
|
+
message.error('验证失败');
|
107
|
+
setOtp('');
|
87
108
|
}
|
88
109
|
}
|
89
110
|
} }), _jsx(Button, { disabled: countdown > 0 || !captchCode, onClick: handleResend, style: { padding: '8px' }, children: countdown > 0 ? t('countDownSecound', lang, { countdown }) : t('reSend', lang) })] }), countdown > 0 ? null :
|
90
111
|
_jsxs("div", { style: { marginTop: 12, display: 'flex', alignItems: 'center' }, children: [_jsx(Input, { placeholder: t('pleaseEnterPicVerifyCode', lang), onChange: (e) => {
|
91
112
|
setCaptchCode(e);
|
92
|
-
}, 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) })] }));
|
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) })] }));
|
93
114
|
};
|
94
115
|
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.41",
|
4
4
|
"type": "module",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"dependencies": {
|
@@ -29,6 +29,7 @@
|
|
29
29
|
"build": "tsc -b",
|
30
30
|
"analyze": "vite build",
|
31
31
|
"lint": "eslint .",
|
32
|
-
"preview": "vite preview"
|
32
|
+
"preview": "vite preview",
|
33
|
+
"release:beta": "pnpm version prerelease --preid=beta && pnpm run build && pnpm publish"
|
33
34
|
}
|
34
35
|
}
|
package/src/index.tsx
CHANGED
@@ -1,55 +1,92 @@
|
|
1
1
|
import ReactDOM from 'react-dom/client'
|
2
2
|
import VerifyModal from './verify-modal.js'
|
3
3
|
import Fingerprint2 from 'fingerprintjs2';
|
4
|
-
import
|
4
|
+
import ReLoginModal from './relogin-modal.js';
|
5
|
+
import { VerifyModalConfig, LoginModalConfig } from './utils/type.js';
|
6
|
+
|
7
|
+
let fingerprintPromise: Promise<string> | null = null; // 用 Promise 本身作为缓存
|
5
8
|
|
6
|
-
let cachedFingerprint: string | null = null;
|
7
9
|
export const initFingerprint = async (): Promise<string> => {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
.
|
19
|
-
.
|
20
|
-
.
|
21
|
-
|
22
|
-
|
23
|
-
resolve(fingerprint);
|
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
|
+
});
|
24
25
|
});
|
25
|
-
}
|
26
|
+
}
|
27
|
+
return fingerprintPromise;
|
26
28
|
};
|
27
29
|
|
28
30
|
let modalRoot: ReactDOM.Root | null = null
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
32
|
+
const createMessageVerifyModal = (modalProps: VerifyModalConfig) => {
|
33
|
+
const container = document.createElement('div')
|
34
|
+
document.body.appendChild(container)
|
35
|
+
|
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
|
+
)
|
48
|
+
}
|
49
|
+
|
50
|
+
// 新增 relogin 弹窗创建方法
|
51
|
+
export const createReLoginModal = (modalProps: LoginModalConfig) => {
|
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
|
+
)
|
47
64
|
}
|
48
65
|
|
49
66
|
const destroyModal = () => {
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
}
|
67
|
+
if (modalRoot) {
|
68
|
+
modalRoot.unmount()
|
69
|
+
document.getElementById('antd-modal-container')?.remove()
|
70
|
+
modalRoot = null
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
export const VerifyHandler = (modalProps: VerifyModalConfig): Promise<any> => {
|
75
|
+
const {data, config, http, verifyPromise} = modalProps || {};
|
76
|
+
const toEmpty = () => {
|
77
|
+
verifyPromise.current = null;
|
78
|
+
};
|
79
|
+
if (!verifyPromise.current) {
|
80
|
+
verifyPromise.current = new Promise((resolve, reject) => {
|
81
|
+
createMessageVerifyModal({
|
82
|
+
data,
|
83
|
+
config,
|
84
|
+
http,
|
85
|
+
apiResolve: resolve,
|
86
|
+
apiReject: reject,
|
87
|
+
apiPromiseToEmpty: toEmpty,
|
88
|
+
} as any);
|
89
|
+
}).finally(toEmpty);
|
90
|
+
}
|
91
|
+
return verifyPromise.current;
|
92
|
+
};
|
package/src/locales/en.ts
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: 'Successfully sent',
|
7
7
|
sendFailed: 'Send failed',
|
8
8
|
reSend: 'Resend',
|
9
|
-
countDownSecound: '{countdown} seconds'
|
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'
|
10
14
|
};
|
package/src/locales/id.ts
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: 'Berhasil dikirim',
|
7
7
|
sendFailed: 'Gagal mengirim',
|
8
8
|
reSend: 'Kirim Ulang',
|
9
|
-
countDownSecound: '{countdown} detik'
|
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'
|
10
14
|
};
|
package/src/locales/zh.ts
CHANGED
@@ -6,5 +6,9 @@ export default {
|
|
6
6
|
sendSuccess: '发送成功',
|
7
7
|
sendFailed: '发送失败',
|
8
8
|
reSend: '重新发送',
|
9
|
-
countDownSecound: '{countdown}秒'
|
9
|
+
countDownSecound: '{countdown}秒',
|
10
|
+
accountAlert: '账号安全提醒',
|
11
|
+
alertContent: '检测到您的登录环境或网络信息有变动,为保障账户安全,请重新登录验证身份',
|
12
|
+
ok: '好的',
|
13
|
+
chidoriOpenPro: 'chidori已为您开启安全防护'
|
10
14
|
};
|
package/src/main.tsx
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
import { createRoot } from 'react-dom/client';
|
2
|
-
import {
|
2
|
+
import { initFingerprint, createReLoginModal, VerifyHandler } 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
|
-
|
8
|
+
const fp2 = await initFingerprint();
|
9
|
+
console.log(fp, fp2);
|
9
10
|
|
10
11
|
const config = {
|
11
12
|
"transitional": {
|
@@ -13,12 +14,6 @@ const config = {
|
|
13
14
|
"forcedJSONParsing": true,
|
14
15
|
"clarifyTimeoutError": false
|
15
16
|
},
|
16
|
-
"transformRequest": [
|
17
|
-
null
|
18
|
-
],
|
19
|
-
"transformResponse": [
|
20
|
-
null
|
21
|
-
],
|
22
17
|
"timeout": 0,
|
23
18
|
"xsrfCookieName": "XSRF-TOKEN",
|
24
19
|
"xsrfHeaderName": "X-XSRF-TOKEN",
|
@@ -27,9 +22,7 @@ const config = {
|
|
27
22
|
"headers": {
|
28
23
|
"Accept": "application/json, text/plain, */*",
|
29
24
|
"X-Device-Fingerprint": "185fcce88c629725321adf29daa5e444",
|
30
|
-
"Content-Type": "application/json"
|
31
|
-
"sms-code": "121212",
|
32
|
-
"sms-code-key": "212190c7-2f5e-4cc1-82ec-a242c2ab5425"
|
25
|
+
"Content-Type": "application/json"
|
33
26
|
},
|
34
27
|
"url": "/classification/admin/level/edit",
|
35
28
|
"method": "post",
|
@@ -45,17 +38,21 @@ const config = {
|
|
45
38
|
"data": "{\"id\":1,\"classificationName\":\"个人基本概况信息\",\"classificationLevel\":2}",
|
46
39
|
"baseURL": "http://localhost:62888"
|
47
40
|
};
|
41
|
+
|
42
|
+
const promise = {current: null};
|
48
43
|
createRoot(document.getElementById('root')!).render(
|
49
44
|
<>
|
50
|
-
<button onClick={() =>
|
51
|
-
data,
|
52
|
-
|
53
|
-
|
54
|
-
|
45
|
+
<button onClick={() => {
|
46
|
+
return VerifyHandler({data, config, http: axios, verifyPromise: promise});
|
47
|
+
}}>
|
48
|
+
Show verify Modal
|
49
|
+
</button>
|
50
|
+
<button onClick={() => createReLoginModal({
|
51
|
+
goLogin: () => {
|
52
|
+
console.log('goLogin');
|
55
53
|
},
|
56
|
-
lang: '
|
57
|
-
http: axios,
|
54
|
+
lang: 'zh',
|
58
55
|
})}>
|
59
|
-
Show
|
56
|
+
Show login Modal
|
60
57
|
</button>
|
61
58
|
</>);
|
package/src/relogin-modal.tsx
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
import { useState } from 'react'
|
2
2
|
import { Modal, Button } from './compoments/index.js'
|
3
|
-
import {
|
3
|
+
import { LoginModalConfig } from './utils/type.js';
|
4
|
+
import t from './utils/i18n.js'
|
4
5
|
|
5
|
-
const ReLoginModal = ({ props }: { props:
|
6
|
-
|
6
|
+
const ReLoginModal = ({ props }: { props: LoginModalConfig }) => {
|
7
|
+
const { goLogin, lang } = props || {};
|
7
8
|
const [visible, setVisible] = useState(true);
|
8
9
|
const onClose = () => {
|
9
10
|
setVisible(false);
|
11
|
+
goLogin();
|
10
12
|
};
|
11
13
|
return (
|
12
14
|
<Modal visible={visible} onClose={onClose} width={420}>
|
13
15
|
<div style={{ fontWeight: 500, fontSize: 20, marginBottom: 24 }}>
|
14
|
-
|
16
|
+
{t('accountAlert', lang)}
|
15
17
|
</div>
|
16
18
|
<div style={{ color: '#222', fontSize: 16, marginBottom: 40, lineHeight: '24px' }}>
|
17
|
-
|
19
|
+
{t('alertContent', lang)}
|
18
20
|
</div>
|
19
21
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
20
22
|
<Button
|
@@ -22,16 +24,15 @@ const ReLoginModal = ({ props }: { props: ModalConfig }) => {
|
|
22
24
|
background: '#1677ff',
|
23
25
|
color: '#fff',
|
24
26
|
border: 'none',
|
25
|
-
width:
|
26
|
-
height:
|
27
|
-
fontSize:
|
28
|
-
fontWeight: 500,
|
27
|
+
width: 80,
|
28
|
+
height: 36,
|
29
|
+
fontSize: 14,
|
29
30
|
borderRadius: 8,
|
30
31
|
boxShadow: '0 2px 8px rgba(22,119,255,0.08)',
|
31
32
|
}}
|
32
33
|
onClick={onClose}
|
33
34
|
>
|
34
|
-
|
35
|
+
{t('ok', lang)}
|
35
36
|
</Button>
|
36
37
|
</div>
|
37
38
|
</Modal>
|
package/src/utils/type.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
export type
|
2
|
+
export type VerifyModalConfig = {
|
3
3
|
data: string;
|
4
4
|
onClose?: () => void
|
5
5
|
config: {
|
@@ -8,7 +8,7 @@ export type ModalConfig = {
|
|
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: {
|
@@ -20,5 +20,13 @@ export type ModalConfig = {
|
|
20
20
|
}
|
21
21
|
};
|
22
22
|
api?: string;
|
23
|
-
|
23
|
+
apiReject?: any;
|
24
|
+
apiResolve?: any;
|
25
|
+
apiPromiseToEmpty?: () => void;
|
26
|
+
verifyPromise: {current: Promise<any> | null}
|
27
|
+
}
|
28
|
+
|
29
|
+
export type LoginModalConfig = {
|
30
|
+
goLogin: () => void;
|
31
|
+
lang?: 'zh' | 'en' | 'id';
|
24
32
|
}
|
package/src/verify-modal.tsx
CHANGED
@@ -2,17 +2,17 @@ import { useEffect, useState } from 'react'
|
|
2
2
|
import { Modal, Input, Button, message } from './compoments/index.js'
|
3
3
|
import Api from './resource.js'
|
4
4
|
import t from './utils/i18n.js'
|
5
|
-
import {
|
5
|
+
import { VerifyModalConfig } from './utils/type.js';
|
6
6
|
import { axios } from '@yqg/resource';
|
7
7
|
|
8
|
-
const CreateMessageVerifyModal = ({ props }: { props:
|
8
|
+
const CreateMessageVerifyModal = ({ props }: { props: VerifyModalConfig }) => {
|
9
9
|
const [visible, setVisible] = useState(true);
|
10
10
|
const [countdown, setCountdown] = useState(60);
|
11
11
|
const [otp, setOtp] = useState('');
|
12
12
|
const [captchaImage, setCaptchaImage] = useState<string>('');
|
13
13
|
const [captchCode, setCaptchCode] = useState<string>('');
|
14
14
|
const [captchaKey, setCaptchaKey] = useState<string>('');
|
15
|
-
const { data, http, lang, api='/admin/sms/send',
|
15
|
+
const { data, http, lang, api = '/admin/sms/send', config, apiResolve, apiPromiseToEmpty } = props || {};
|
16
16
|
const dataObj = JSON.parse(data || '{}');
|
17
17
|
const { mobile, verifyCodeKey } = dataObj || {};
|
18
18
|
|
@@ -25,10 +25,11 @@ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
|
|
25
25
|
const image = Api.getCaptchaImgUrl(key) as unknown;
|
26
26
|
setCaptchaImage(image as string);
|
27
27
|
}
|
28
|
-
} catch (err) {
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
} catch (err) {
|
29
|
+
const msg =
|
30
|
+
typeof err === 'object' && err !== null && 'message' in err
|
31
|
+
? (err as { message: string }).message
|
32
|
+
: String(err);
|
32
33
|
message.error(msg as React.ReactNode);
|
33
34
|
}
|
34
35
|
}
|
@@ -60,7 +61,9 @@ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
|
|
60
61
|
return;
|
61
62
|
}
|
62
63
|
message.success(t('sendSuccess', lang));
|
64
|
+
// 重置倒计时、验证码
|
63
65
|
setCountdown(60);
|
66
|
+
setOtp('');
|
64
67
|
}
|
65
68
|
|
66
69
|
return (
|
@@ -69,6 +72,7 @@ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
|
|
69
72
|
visible={visible}
|
70
73
|
onClose={() => {
|
71
74
|
setVisible(false);
|
75
|
+
apiPromiseToEmpty?.();
|
72
76
|
props.onClose?.();
|
73
77
|
}}
|
74
78
|
>
|
@@ -81,23 +85,40 @@ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
|
|
81
85
|
onChange={async (val) => {
|
82
86
|
setOtp(val);
|
83
87
|
if (val.length === 6) {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
try {
|
89
|
+
const res = await Promise.race([
|
90
|
+
new Promise((resolve, reject) => {
|
91
|
+
setTimeout(() => {
|
92
|
+
reject(new Error('Timeout'));
|
93
|
+
}, 1000);
|
94
|
+
}),
|
95
|
+
new Promise((resolve, reject) => {
|
96
|
+
http({
|
97
|
+
...config,
|
98
|
+
headers: {
|
99
|
+
...http.defaults.headers.common,
|
100
|
+
'sms-code': val,
|
101
|
+
'sms-code-key': verifyCodeKey,
|
102
|
+
'Content-Type': config.headers['Content-Type'],
|
103
|
+
}
|
104
|
+
}).then(res => {
|
105
|
+
resolve(res);
|
106
|
+
}).catch(err => {
|
107
|
+
reject(err);
|
108
|
+
})
|
109
|
+
})
|
110
|
+
]) as any;
|
111
|
+
if (res?.data?.status?.code === 0) {
|
112
|
+
apiResolve(res);
|
113
|
+
setVisible(false);
|
114
|
+
} else {
|
115
|
+
message.error('验证失败');
|
116
|
+
setOtp('');
|
92
117
|
}
|
93
|
-
})
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
return;
|
98
|
-
} else {
|
99
|
-
message.success(t('verifySuccess', lang));
|
100
|
-
setVisible(false);
|
118
|
+
} catch (error) {
|
119
|
+
console.warn('catch', error);
|
120
|
+
message.error('验证失败');
|
121
|
+
setOtp('');
|
101
122
|
}
|
102
123
|
}
|
103
124
|
}} />
|
@@ -122,6 +143,7 @@ const CreateMessageVerifyModal = ({ props }: { props: ModalConfig }) => {
|
|
122
143
|
{captchaImage && <img src={captchaImage} alt={t('verifyCode', lang)} style={{ width: 100, height: 30, marginLeft: 8 }} onClick={getKey} />}
|
123
144
|
</div>}
|
124
145
|
{(countdown > 0 || captchCode) ? null : <div style={{ fontSize: 14, color: 'red' }}>{t('pleaseEnterAndSend', lang)}</div>}
|
146
|
+
<div style={{ fontSize: 14, color: '#666', marginTop: 12 }}>{t('chidoriOpenPro', lang)}</div>
|
125
147
|
</Modal>
|
126
148
|
);
|
127
149
|
};
|