业务
coderljw 2024-10-13 大约 3 分钟
# 1. 扫码盒微信/支付宝支付
import React, { useEffect, useRef, useState } from 'react'
import { Input, message, Modal } from 'antd'
import type { InputRef } from 'antd'
import { useRequest } from 'ahooks'
import { OrderPay } from '@/services/OrderService'
import styles from './index.less'
type ScanPayProps = {
visible: true
hidden: () => void
refresh: () => void
currentRow: API.Order
}
const ScanPay: React.FC<ScanPayProps> = ({
visible,
hidden,
refresh,
currentRow,
}) => {
const inputRef = useRef<InputRef>(null)
const [payOrderCode, setPayOrderCode] = useState('')
const [timestamp, setTimestamp] = useState(0)
const timer = useRef<NodeJS.Timer>()
const { run: pay, loading: payLoading } = useRequest(OrderPay, {
manual: true,
onSuccess: () => {
message.success('支付成功')
hidden()
refresh()
},
onFinally: () => {
message.destroy()
},
})
const { orderId, orderNo, actualAmount } = currentRow || {}
useEffect(() => {
setTimeout(() => inputRef?.current?.focus(), 300)
}, [])
useEffect(() => {
clearInterval(timer.current)
timer.current = setInterval(async () => {
if (timestamp && +new Date() - timestamp > 500 && payOrderCode) {
clearInterval(timer.current)
message.loading('提交中', 0)
await pay({ orderId, payCode: payOrderCode })
}
}, 500)
return () => {
clearInterval(timer.current)
}
}, [timestamp])
return (
<Modal
title={`${orderNo}(支付金额:${actualAmount})`}
visible={visible}
maskClosable={false}
footer={null}
onCancel={hidden}
destroyOnClose
width={600}
>
<Input
ref={inputRef}
value={payOrderCode}
onBlur={_ => inputRef.current?.focus?.()}
onChange={({ target }) => {
if (payLoading) return
setTimestamp(+new Date())
// 扫码机30ms内延迟
if (timestamp && +new Date() - timestamp > 30 && payOrderCode) {
setPayOrderCode(target.value.slice(-1))
} else {
setPayOrderCode(target.value)
}
}}
className={styles['pay-input']}
/>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div className={styles['qr-scanner']}>
<div className={styles.box}>
<div className={styles.line}></div>
<div className={styles.angle}></div>
</div>
</div>
</div>
</Modal>
)
}
export default ScanPay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 2. SKU 排列组合
const combine = (...chunks) => {
const result = [],
chunkQuantity = chunks.length
function recursion(chunkIndex, prev) {
const chunk = chunks[chunkIndex],
isLast = chunkIndex === chunkQuantity - 1
chunk.forEach(i => {
if (isLast) return result.push([...prev, i])
return recursion(chunkIndex + 1, [...prev, i])
})
}
recursion(0, [])
return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3. 禁止打开 DevTools
import devtools from 'devtools-detect'
// 禁止右键菜单
window.oncontextmenu = _ => false
// 禁止打开DevTools
document.onkeydown = event => {
const { keyCode, ctrlKey, shiftKey } = event
if (keyCode === 123 || (ctrlKey && shiftKey && keyCode === 73)) {
return false
}
}
/**
* 监听打开DevTools,跳转about:blank(空白页)
* Bug:根据可视宽高判断,如果改为手机模式是区分不了的
* Tips:window.close() 只能关闭通过JS打开的窗口
*/
if (devtools.isOpen) window.location.href = 'about:blank'
window.addEventListener('devtoolschange', event => {
if (event.detail.isOpen) window.location.href = 'about:blank'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24