33 * Copyright (c) <spug.dev@gmail.com>
44 * Released under the AGPL-3.0 License.
55 */
6- import React , { useState } from 'react' ;
6+ import React , { useState , useMemo } from 'react' ;
77import { observer } from 'mobx-react' ;
8- import { Modal , Form , Input , Tooltip , message } from 'antd' ;
8+ import { Modal , Form , Input , Tooltip , Checkbox , Divider , message } from 'antd' ;
99import { ThunderboltOutlined , LoadingOutlined } from '@ant-design/icons' ;
1010import http from 'libs/http' ;
1111import store from './store' ;
1212
13+ const channelConfig = [
14+ {
15+ key : 'email' ,
16+ label : '邮箱' ,
17+ fields : [
18+ { name : 'email' , label : '邮箱地址' , placeholder : '请输入邮箱地址' , testMode : '4' }
19+ ]
20+ } ,
21+ {
22+ key : 'ding' ,
23+ label : '钉钉' ,
24+ fields : [
25+ { name : 'ding' , label : 'Webhook' , placeholder : 'https://oapi.dingtalk.com/robot/send?access_token=xxx' , testMode : '3' } ,
26+ { name : 'ding_secret' , label : 'Secret' , placeholder : 'SECxxxxxxxx' , extra : '可选,机器人安全设置中的加签密钥' }
27+ ] ,
28+ help : { text : '钉钉收不到通知?请参考' , link : 'https://ops.spug.cc/docs/use-problem#use-dd' , linkText : '官方文档' }
29+ } ,
30+ {
31+ key : 'feishu' ,
32+ label : '飞书' ,
33+ fields : [
34+ { name : 'feishu' , label : 'Webhook' , placeholder : 'https://open.feishu.cn/open-apis/bot/v2/hook/xxx' , testMode : '7' } ,
35+ { name : 'feishu_secret' , label : 'Secret' , placeholder : 'xxxxxxxx' , extra : '可选,机器人安全设置中的签名校验密钥' }
36+ ]
37+ } ,
38+ {
39+ key : 'qy_wx' ,
40+ label : '企业微信' ,
41+ fields : [
42+ { name : 'qy_wx' , label : 'Webhook' , placeholder : 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx' , testMode : '5' }
43+ ]
44+ }
45+ ] ;
46+
1347export default observer ( function ( ) {
1448 const [ form ] = Form . useForm ( ) ;
1549 const [ loading , setLoading ] = useState ( false ) ;
1650 const [ testLoading , setTestLoading ] = useState ( '0' ) ;
1751
52+ const initialChannels = useMemo ( ( ) => ( {
53+ email : ! ! store . record . email ,
54+ ding : ! ! store . record . ding ,
55+ feishu : ! ! store . record . feishu ,
56+ qy_wx : ! ! store . record . qy_wx ,
57+ } ) , [ ] ) ;
58+
59+ const [ channels , setChannels ] = useState ( initialChannels ) ;
60+
61+ function handleChannelToggle ( key , checked ) {
62+ setChannels ( prev => ( { ...prev , [ key ] : checked } ) ) ;
63+ if ( ! checked ) {
64+ const channel = channelConfig . find ( c => c . key === key ) ;
65+ if ( channel ) {
66+ const resetFields = channel . fields . map ( f => f . name ) ;
67+ form . resetFields ( resetFields ) ;
68+ }
69+ }
70+ }
71+
1872 function handleSubmit ( ) {
1973 setLoading ( true ) ;
2074 const formData = form . getFieldsValue ( ) ;
2175 formData [ 'id' ] = store . record . id ;
76+ const secret = { } ;
77+ if ( formData . ding_secret ) secret . ding = formData . ding_secret ;
78+ if ( formData . feishu_secret ) secret . feishu = formData . feishu_secret ;
79+ delete formData . ding_secret ;
80+ delete formData . feishu_secret ;
81+ formData . secret = Object . keys ( secret ) . length ? JSON . stringify ( secret ) : null ;
2282 http . post ( '/api/alarm/contact/' , formData )
2383 . then ( res => {
2484 message . success ( '操作成功' ) ;
@@ -39,19 +99,15 @@ export default observer(function () {
3999 }
40100
41101 function Test ( props ) {
42- return (
43- < div style = { { position : 'absolute' , right : - 30 , top : 8 } } >
44- { testLoading === props . mode ? (
45- < LoadingOutlined style = { { fontSize : 18 , color : '#faad14' } } />
46- ) : (
47- < Tooltip title = "执行测试" >
48- < ThunderboltOutlined
49- style = { { fontSize : 18 , color : '#faad14' } }
50- onClick = { ( ) => handleTest ( props . mode , props . name ) } />
51- </ Tooltip >
52- ) }
53- </ div >
54- )
102+ return testLoading === props . mode ? (
103+ < LoadingOutlined style = { { fontSize : 16 , color : '#faad14' } } />
104+ ) : (
105+ < Tooltip title = "执行测试" >
106+ < ThunderboltOutlined
107+ style = { { fontSize : 16 , color : '#faad14' , cursor : 'pointer' } }
108+ onClick = { ( ) => handleTest ( props . mode , props . name ) } />
109+ </ Tooltip >
110+ ) ;
55111 }
56112
57113 return (
@@ -63,36 +119,47 @@ export default observer(function () {
63119 onCancel = { ( ) => store . formVisible = false }
64120 confirmLoading = { loading }
65121 onOk = { handleSubmit } >
66- < Form form = { form } initialValues = { store . record } labelCol = { { span : 6 } } wrapperCol = { { span : 14 } } >
122+ < Form form = { form } initialValues = { {
123+ ...store . record ,
124+ ding_secret : store . record . secret ? JSON . parse ( store . record . secret ) . ding : undefined ,
125+ feishu_secret : store . record . secret ? JSON . parse ( store . record . secret ) . feishu : undefined ,
126+ } } labelCol = { { span : 6 } } wrapperCol = { { span : 14 } } >
67127 < Form . Item required name = "name" label = "姓名" >
68128 < Input placeholder = "请输入联系人姓名" />
69129 </ Form . Item >
70130 < Form . Item name = "phone" label = "手机号" >
71131 < Input placeholder = "请输入手机号" />
72132 </ Form . Item >
73- < Form . Item label = "邮箱" >
74- < Form . Item noStyle name = "email" >
75- < Input placeholder = "请输入邮箱地址" />
76- </ Form . Item >
77- < Test mode = "4" name = "email" />
78- </ Form . Item >
79- < Form . Item label = "钉钉" extra = { < span >
80- 钉钉收不到通知?请参考
81- < a target = "_blank" rel = "noopener noreferrer"
82- href = "https://ops.spug.cc/docs/use-problem#use-dd" > 官方文档</ a >
83- </ span > } >
84- < Form . Item noStyle name = "ding" >
85- < Input placeholder = "https://oapi.dingtalk.com/robot/send?access_token=xxx" />
86- </ Form . Item >
87- < Test mode = "3" name = "ding" />
88- </ Form . Item >
89- < Form . Item label = "企业微信" >
90- < Form . Item noStyle name = "qy_wx" >
91- < Input placeholder = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" />
92- </ Form . Item >
93- < Test mode = "5" name = "qy_wx" />
94- </ Form . Item >
133+ < Divider orientation = "left" style = { { margin : '8px 0 16px' } } > 通知渠道</ Divider >
134+ { channelConfig . map ( channel => (
135+ < div key = { channel . key } style = { { marginBottom : channels [ channel . key ] ? 16 : 4 } } >
136+ < Form . Item wrapperCol = { { offset : 6 , span : 14 } } style = { { marginBottom : 0 } } >
137+ < Checkbox
138+ checked = { channels [ channel . key ] }
139+ onChange = { e => handleChannelToggle ( channel . key , e . target . checked ) }
140+ >
141+ { channel . label }
142+ </ Checkbox >
143+ </ Form . Item >
144+ { channels [ channel . key ] && channel . fields . map ( field => {
145+ const extra = field . extra || ( channel . help && field === channel . fields [ 0 ] ? (
146+ < span >
147+ { channel . help . text }
148+ < a target = "_blank" rel = "noopener noreferrer" href = { channel . help . link } > { channel . help . linkText } </ a >
149+ </ span >
150+ ) : undefined ) ;
151+ return (
152+ < Form . Item key = { field . name } name = { field . name } label = { field . label } extra = { extra } >
153+ < Input
154+ placeholder = { field . placeholder }
155+ suffix = { field . testMode ? < Test mode = { field . testMode } name = { field . name } /> : < span /> }
156+ />
157+ </ Form . Item >
158+ ) ;
159+ } ) }
160+ </ div >
161+ ) ) }
95162 </ Form >
96163 </ Modal >
97164 )
98- } )
165+ } )
0 commit comments