EditChannel.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. import React, { useEffect, useState } from 'react';
  2. import { Button, Form, Header, Message, Segment } from 'semantic-ui-react';
  3. import { useParams } from 'react-router-dom';
  4. import { API, showError, showSuccess } from '../../helpers';
  5. import { CHANNEL_OPTIONS } from '../../constants';
  6. import axios from 'axios';
  7. const EditChannel = () => {
  8. const params = useParams();
  9. const channelId = params.id;
  10. const isEditing = channelId !== undefined;
  11. const [loading, setLoading] = useState(isEditing);
  12. const originInputs = {
  13. type: 'none',
  14. name: '',
  15. description: '',
  16. secret: '',
  17. app_id: '',
  18. account_id: '',
  19. url: '',
  20. other: '',
  21. corp_id: '', // only for corp_app
  22. agent_id: '', // only for corp_app
  23. };
  24. const [inputs, setInputs] = useState(originInputs);
  25. const { type, name, description, secret, app_id, account_id, url, other } =
  26. inputs;
  27. const handleInputChange = (e, { name, value }) => {
  28. setInputs((inputs) => ({ ...inputs, [name]: value }));
  29. };
  30. const loadChannel = async () => {
  31. let res = await API.get(`/api/channel/${channelId}`);
  32. const { success, message, data } = res.data;
  33. if (success) {
  34. if (data.type === 'corp_app') {
  35. const [corp_id, agent_id] = data.app_id.split('|');
  36. data.corp_id = corp_id;
  37. data.agent_id = agent_id;
  38. }
  39. setInputs(data);
  40. } else {
  41. showError(message);
  42. }
  43. setLoading(false);
  44. };
  45. useEffect(() => {
  46. if (isEditing) {
  47. loadChannel().then();
  48. }
  49. }, []);
  50. const submit = async () => {
  51. if (!name) return;
  52. let res = undefined;
  53. let localInputs = { ...inputs };
  54. switch (inputs.type) {
  55. case 'corp_app':
  56. localInputs.app_id = `${inputs.corp_id}|${inputs.agent_id}`;
  57. break;
  58. case 'bark':
  59. if (localInputs.url === '') {
  60. localInputs.url = 'https://api.day.app';
  61. }
  62. break;
  63. case 'one_bot':
  64. if (localInputs.url.endsWith('/')) {
  65. localInputs.url = localInputs.url.slice(0, -1);
  66. }
  67. break;
  68. case 'group':
  69. let channels = localInputs.app_id.split('|');
  70. let targets = localInputs.account_id.split('|');
  71. if (localInputs.account_id === '') {
  72. for (let i = 0; i < channels.length - 1; i++) {
  73. localInputs.account_id += '|';
  74. }
  75. } else if (channels.length !== targets.length) {
  76. showError(
  77. '群组通道的子通道数量与目标数量不匹配,对于不需要指定的目标请直接留空'
  78. );
  79. return;
  80. }
  81. break;
  82. case 'custom':
  83. if (!localInputs.url.startsWith('https://')) {
  84. showError('自定义通道的 URL 必须以 https:// 开头!');
  85. return;
  86. }
  87. try {
  88. JSON.parse(localInputs.other);
  89. }catch (e) {
  90. showError('JSON 格式错误:' + e.message);
  91. return;
  92. }
  93. break;
  94. }
  95. if (isEditing) {
  96. res = await API.put(`/api/channel/`, {
  97. ...localInputs,
  98. id: parseInt(channelId),
  99. });
  100. } else {
  101. res = await API.post(`/api/channel`, localInputs);
  102. }
  103. const { success, message } = res.data;
  104. if (success) {
  105. if (isEditing) {
  106. showSuccess('通道信息更新成功!');
  107. } else {
  108. showSuccess('通道创建成功!');
  109. setInputs(originInputs);
  110. }
  111. } else {
  112. showError(message);
  113. }
  114. };
  115. const getTelegramChatId = async () => {
  116. if (inputs.telegram_bot_token === '') {
  117. showError('请先输入 Telegram 机器人令牌!');
  118. return;
  119. }
  120. let res = await axios.get(
  121. `https://api.telegram.org/bot${inputs.secret}/getUpdates`
  122. );
  123. const { ok } = res.data;
  124. if (ok) {
  125. let result = res.data.result;
  126. if (result.length === 0) {
  127. showError(`请先向你的机器人发送一条任意消息!`);
  128. } else {
  129. let id = result[0]?.message?.chat?.id;
  130. id = id.toString();
  131. setInputs((inputs) => ({ ...inputs, account_id: id }));
  132. showSuccess('会话 ID 获取成功!');
  133. }
  134. } else {
  135. showError(`发生错误:${res.description}`);
  136. }
  137. };
  138. const renderChannelForm = () => {
  139. switch (type) {
  140. case 'email':
  141. return (
  142. <>
  143. <Message>
  144. 邮件推送方式(email)需要设置邮箱,请前往个人设置页面绑定邮箱地址,之后系统将自动为你创建邮箱推送通道。
  145. </Message>
  146. </>
  147. );
  148. case 'test':
  149. return (
  150. <>
  151. <Message>
  152. 通过微信测试号进行推送,点击前往配置:
  153. <a
  154. target='_blank'
  155. href='https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index'
  156. >
  157. 微信公众平台接口测试帐号
  158. </a>
  159. <br />
  160. 需要新增测试模板,模板标题推荐填写为「消息推送」,模板内容填写为:
  161. <br/>标题:{' {{'}title.DATA{'}}'}
  162. <br/>描述:{' {{'}description.DATA{'}}'}
  163. <br/>内容:{' {{'}content.DATA{'}}'}
  164. </Message>
  165. <Form.Group widths={3}>
  166. <Form.Input
  167. label='测试号 ID'
  168. name='app_id'
  169. onChange={handleInputChange}
  170. autoComplete='new-password'
  171. value={inputs.app_id}
  172. placeholder='测试号信息 -> appID'
  173. />
  174. <Form.Input
  175. label='测试号密钥'
  176. name='secret'
  177. type='password'
  178. onChange={handleInputChange}
  179. autoComplete='new-password'
  180. value={inputs.secret}
  181. placeholder='测试号信息 -> appsecret'
  182. />
  183. <Form.Input
  184. label='测试模板 ID'
  185. name='other'
  186. onChange={handleInputChange}
  187. autoComplete='new-password'
  188. value={inputs.other}
  189. placeholder='模板消息接口 -> 模板 ID'
  190. />
  191. </Form.Group>
  192. <Form.Group widths={3}>
  193. <Form.Input
  194. label='用户 Open ID'
  195. name='account_id'
  196. onChange={handleInputChange}
  197. autoComplete='new-password'
  198. value={inputs.account_id}
  199. placeholder='扫描测试号二维码 -> 用户列表 -> 微信号'
  200. />
  201. </Form.Group>
  202. </>
  203. );
  204. case 'tencent_alarm':
  205. return (
  206. <>
  207. <Message>
  208. 通过腾讯云自定义消息告警进行推送,
  209. <a
  210. target='_blank'
  211. href='https://github.com/songquanpeng/message-pusher/issues/87#issuecomment-1547971847'
  212. >
  213. 配置教程
  214. </a>
  215. </Message>
  216. <Form.Group widths={3}>
  217. <Form.Input
  218. label='SecretId'
  219. name='app_id'
  220. onChange={handleInputChange}
  221. autoComplete='new-password'
  222. value={inputs.app_id}
  223. placeholder='子账号的 SecretId'
  224. />
  225. <Form.Input
  226. label='SecretKey'
  227. name='secret'
  228. type='password'
  229. onChange={handleInputChange}
  230. autoComplete='new-password'
  231. value={inputs.secret}
  232. placeholder='子账号的 SecretKey'
  233. />
  234. <Form.Input
  235. label='消息策略 ID'
  236. name='account_id'
  237. onChange={handleInputChange}
  238. autoComplete='new-password'
  239. value={inputs.account_id}
  240. placeholder='例如:cm-6gl3pq19'
  241. />
  242. </Form.Group>
  243. <Form.Group widths={3}>
  244. <Form.Input
  245. label='区域'
  246. name='other'
  247. onChange={handleInputChange}
  248. autoComplete='new-password'
  249. value={inputs.other}
  250. placeholder='例如:ap-shanghai'
  251. />
  252. </Form.Group>
  253. </>
  254. );
  255. case 'corp_app':
  256. return (
  257. <>
  258. <Message>
  259. 通过企业微信应用号进行推送,点击前往配置:
  260. <a
  261. target='_blank'
  262. href='https://work.weixin.qq.com/wework_admin/frame#apps'
  263. >
  264. 企业微信应用管理
  265. </a>
  266. <br />
  267. <br />
  268. 注意,企业微信要求配置可信 IP,步骤:应用管理 -> 自建 -> 创建应用
  269. -> 应用设置页面下拉中找到「企业可信 IP」,点击配置 -> 设置可信域名
  270. -> 在「可调用
  271. JS-SDK、跳转小程序的可信域名」下面填写一个域名,然后点击「申请校验域名」,根据提示完成校验
  272. -> 之后填写服务器 IP 地址(此 IP
  273. 地址是消息推送服务所部署在的服务器的 IP
  274. 地址,未必是上面校验域名中记录的 IP 地址)。
  275. </Message>
  276. <Form.Group widths={3}>
  277. <Form.Input
  278. label='企业 ID'
  279. name='corp_id'
  280. onChange={handleInputChange}
  281. autoComplete='new-password'
  282. value={inputs.corp_id}
  283. placeholder='我的企业 -> 企业信息 -> 企业 ID'
  284. />
  285. <Form.Input
  286. label='应用 AgentId'
  287. name='agent_id'
  288. onChange={handleInputChange}
  289. autoComplete='new-password'
  290. value={inputs.agent_id}
  291. placeholder='应用管理 -> 自建 -> 创建应用 -> AgentId'
  292. />
  293. <Form.Input
  294. label='应用 Secret'
  295. name='secret'
  296. type='password'
  297. onChange={handleInputChange}
  298. autoComplete='new-password'
  299. value={inputs.secret}
  300. placeholder='应用管理 -> 自建 -> 创建应用 -> Secret'
  301. />
  302. </Form.Group>
  303. <Form.Group widths={3}>
  304. <Form.Input
  305. label='用户账号'
  306. name='account_id'
  307. onChange={handleInputChange}
  308. autoComplete='new-password'
  309. value={inputs.account_id}
  310. placeholder='通讯录 -> 点击姓名 -> 账号'
  311. />
  312. <Form.Select
  313. label='微信企业号客户端类型'
  314. name='other'
  315. options={[
  316. {
  317. key: 'plugin',
  318. text: '微信中的企业微信插件',
  319. value: 'plugin',
  320. },
  321. { key: 'app', text: '企业微信 APP', value: 'app' },
  322. ]}
  323. value={inputs.other}
  324. onChange={handleInputChange}
  325. />
  326. </Form.Group>
  327. </>
  328. );
  329. case 'corp':
  330. return (
  331. <>
  332. <Message>
  333. 通过企业微信群机器人进行推送,配置流程:选择一个群聊 -> 设置 ->
  334. 群机器人 -> 添加 -> 新建 -> 输入名字,点击添加 -> 点击复制 Webhook
  335. 地址
  336. </Message>
  337. <Form.Group widths={2}>
  338. <Form.Input
  339. label='Webhook 地址'
  340. name='url'
  341. onChange={handleInputChange}
  342. autoComplete='new-password'
  343. value={inputs.url}
  344. placeholder='在此填写企业微信提供的 Webhook 地址'
  345. />
  346. </Form.Group>
  347. </>
  348. );
  349. case 'lark':
  350. return (
  351. <>
  352. <Message>
  353. 通过飞书群机器人进行推送,飞书桌面客户端的配置流程:选择一个群聊
  354. -> 设置 -> 群机器人 -> 添加机器人 -> 自定义机器人 -> 添加(
  355. <strong>注意选中「签名校验」</strong>)。具体参见:
  356. <a
  357. target='_blank'
  358. href='https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN'
  359. >
  360. 飞书开放文档
  361. </a>
  362. </Message>
  363. <Form.Group widths={2}>
  364. <Form.Input
  365. label='Webhook 地址'
  366. name='url'
  367. onChange={handleInputChange}
  368. autoComplete='new-password'
  369. value={inputs.url}
  370. placeholder='在此填写飞书提供的 Webhook 地址'
  371. />
  372. <Form.Input
  373. label='签名校验密钥'
  374. name='secret'
  375. type='password'
  376. onChange={handleInputChange}
  377. autoComplete='new-password'
  378. value={inputs.secret}
  379. placeholder='在此填写飞书提供的签名校验密钥'
  380. />
  381. </Form.Group>
  382. </>
  383. );
  384. case 'ding':
  385. return (
  386. <>
  387. <Message>
  388. 通过钉钉群机器人进行推送,钉钉桌面客户端的配置流程:选择一个群聊
  389. -> 群设置 -> 智能群助手 -> 添加机器人(点击右侧齿轮图标) ->
  390. 自定义 -> 添加(
  391. <strong>注意选中「加密」</strong>)。具体参见:
  392. <a
  393. target='_blank'
  394. href='https://open.dingtalk.com/document/robots/custom-robot-access'
  395. >
  396. 钉钉开放文档
  397. </a>
  398. </Message>
  399. <Form.Group widths={2}>
  400. <Form.Input
  401. label='Webhook 地址'
  402. name='url'
  403. onChange={handleInputChange}
  404. autoComplete='new-password'
  405. value={inputs.url}
  406. placeholder='在此填写钉钉提供的 Webhook 地址'
  407. />
  408. <Form.Input
  409. label='签名校验密钥'
  410. name='secret'
  411. type='password'
  412. onChange={handleInputChange}
  413. autoComplete='new-password'
  414. value={inputs.secret}
  415. placeholder='在此填写钉钉提供的签名校验密钥'
  416. />
  417. </Form.Group>
  418. </>
  419. );
  420. case 'bark':
  421. return (
  422. <>
  423. <Message>
  424. 通过 Bark 进行推送,下载 Bark 后按提示注册设备,之后会看到一个
  425. URL,例如 <code>https://api.day.app/wrsVSDRANDOM/Body Text</code>
  426. ,其中 <code>wrsVSDRANDOM</code> 就是你的推送 key。
  427. </Message>
  428. <Form.Group widths={2}>
  429. <Form.Input
  430. label='服务器地址'
  431. name='url'
  432. onChange={handleInputChange}
  433. autoComplete='new-password'
  434. value={inputs.url}
  435. placeholder='在此填写 Bark 服务器地址,不填则使用默认值'
  436. />
  437. <Form.Input
  438. label='推送 key'
  439. name='secret'
  440. type='password'
  441. onChange={handleInputChange}
  442. autoComplete='new-password'
  443. value={inputs.secret}
  444. placeholder='在此填写 Bark 推送 key'
  445. />
  446. </Form.Group>
  447. </>
  448. );
  449. case 'client':
  450. return (
  451. <>
  452. <Message>
  453. 通过 WebSocket
  454. 客户端进行推送,可以使用官方客户端实现,或者根据协议自行实现。官方客户端
  455. <a
  456. target='_blank'
  457. href='https://github.com/songquanpeng/personal-assistant'
  458. >
  459. 详见此处
  460. </a>
  461. </Message>
  462. <Form.Group widths={2}>
  463. <Form.Input
  464. label='客户端连接密钥'
  465. name='secret'
  466. type='password'
  467. onChange={handleInputChange}
  468. autoComplete='new-password'
  469. value={inputs.secret}
  470. placeholder='在此设置客户端连接密钥'
  471. />
  472. </Form.Group>
  473. </>
  474. );
  475. case 'telegram':
  476. return (
  477. <>
  478. <Message>
  479. 通过 Telegram 机器人进行消息推送。首先向
  480. <a href='https://t.me/botfather' target='_blank'>
  481. {' '}
  482. Bot Father{' '}
  483. </a>
  484. 申请创建一个新的机器人,之后在下方输入获取到的令牌,然后点击你的机器人,随便发送一条消息,之后点击下方的「获取会话
  485. ID」按钮,系统将自动为你填写会话
  486. ID,最后点击保存按钮保存设置即可。
  487. </Message>
  488. <Form.Group widths={2}>
  489. <Form.Input
  490. label='Telegram 机器人令牌'
  491. name='secret'
  492. type='password'
  493. onChange={handleInputChange}
  494. autoComplete='new-password'
  495. value={inputs.secret}
  496. placeholder='在此设置 Telegram 机器人令牌'
  497. />
  498. <Form.Input
  499. label='Telegram 会话 ID'
  500. name='account_id'
  501. type='text'
  502. onChange={handleInputChange}
  503. autoComplete='new-password'
  504. value={inputs.account_id}
  505. placeholder='在此设置 Telegram 会话 ID'
  506. />
  507. </Form.Group>
  508. <Button onClick={getTelegramChatId} loading={loading}>
  509. 获取会话 ID
  510. </Button>
  511. </>
  512. );
  513. case 'discord':
  514. return (
  515. <>
  516. <Message>
  517. 通过 Discord 群机器人进行推送,配置流程:选择一个 channel -> 设置
  518. -> 整合 -> 创建 Webhook -> 点击复制 Webhook URL
  519. </Message>
  520. <Form.Group widths={2}>
  521. <Form.Input
  522. label='Webhook 地址'
  523. name='url'
  524. onChange={handleInputChange}
  525. autoComplete='new-password'
  526. value={inputs.url}
  527. placeholder='在此填写 Discord 提供的 Webhook 地址'
  528. />
  529. </Form.Group>
  530. </>
  531. );
  532. case 'one_bot':
  533. return (
  534. <>
  535. <Message>
  536. 通过 OneBot 协议进行推送,可以使用{' '}
  537. <a href='https://github.com/Mrs4s/go-cqhttp' target='_blank'>
  538. cqhttp
  539. </a>{' '}
  540. 等实现。 利用 OneBot 协议可以实现推送 QQ 消息。
  541. <br/>
  542. 注意,如果推送目标是群号则前面必须加上群号前缀,例如 group_123456789。
  543. </Message>
  544. <Form.Group widths={3}>
  545. <Form.Input
  546. label='服务器地址'
  547. name='url'
  548. onChange={handleInputChange}
  549. autoComplete='new-password'
  550. value={inputs.url}
  551. placeholder='在此填写服务器地址'
  552. />
  553. <Form.Input
  554. label='推送 key'
  555. name='secret'
  556. type='password'
  557. onChange={handleInputChange}
  558. autoComplete='new-password'
  559. value={inputs.secret}
  560. placeholder='在此填写服务器的 access token'
  561. />
  562. <Form.Input
  563. label='默认推送目标'
  564. name='account_id'
  565. onChange={handleInputChange}
  566. autoComplete='new-password'
  567. value={inputs.account_id}
  568. placeholder='在此填写默认推送目标,例如 QQ 号'
  569. />
  570. </Form.Group>
  571. </>
  572. );
  573. case 'group':
  574. return (
  575. <>
  576. <Message>
  577. 对渠道进行分组,然后在推送时选择分组进行推送,可以实现一次性推送到多个渠道的功能。
  578. <br />
  579. <br />
  580. 推送目标如若不填,则使用子渠道的默认推送目标。如果填写,请务必全部按顺序填写,对于不需要指定的直接留空即可,例如{' '}
  581. <code>123456789||@wechat</code>,两个连续的分隔符表示跳过该渠道。
  582. </Message>
  583. <Form.Group widths={2}>
  584. <Form.Input
  585. label='渠道列表'
  586. name='app_id'
  587. onChange={handleInputChange}
  588. autoComplete='new-password'
  589. value={inputs.app_id}
  590. placeholder='在此填写渠道列表,使用 | 分割,例如 bark|telegram|wechat'
  591. />
  592. <Form.Input
  593. label='默认推送目标'
  594. name='account_id'
  595. onChange={handleInputChange}
  596. autoComplete='new-password'
  597. value={inputs.account_id}
  598. placeholder='在此填写默认推送目标,使用 | 分割,例如 123456789|@wechat|@wechat'
  599. />
  600. </Form.Group>
  601. </>
  602. );
  603. case 'lark_app':
  604. return (
  605. <>
  606. <Message>
  607. 通过飞书自建应用进行推送,点击前往配置:
  608. <a target='_blank' href='https://open.feishu.cn/app'>
  609. 飞书开放平台
  610. </a>
  611. <br />
  612. 需要为应用添加机器人能力:应用能力->添加应用能力—>机器人。
  613. <br />
  614. 需要为应用添加消息发送权限:开发配置->权限管理->权限配置->搜索「获取与发送单聊、群组消息」->开通权限。
  615. <br />
  616. 注意,添加完成权限后需要发布版本提交审核才能见效。
  617. <br />
  618. 注意,推送目标的格式为:
  619. <strong>
  620. <code>类型:ID</code>
  621. </strong>
  622. ,详见飞书
  623. <a
  624. href='https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create#bc6d1214'
  625. target='_blank'
  626. >
  627. 开发文档
  628. </a>
  629. 中查询参数一节。
  630. </Message>
  631. <Form.Group widths={3}>
  632. <Form.Input
  633. label='App ID'
  634. name='app_id'
  635. onChange={handleInputChange}
  636. autoComplete='new-password'
  637. value={inputs.app_id}
  638. placeholder='应用凭证 -> App ID'
  639. />
  640. <Form.Input
  641. label='App Secret'
  642. name='secret'
  643. onChange={handleInputChange}
  644. autoComplete='new-password'
  645. value={inputs.secret}
  646. placeholder='应用凭证 -> App Secret'
  647. />
  648. <Form.Input
  649. label='默认推送目标'
  650. name='account_id'
  651. onChange={handleInputChange}
  652. autoComplete='new-password'
  653. value={inputs.account_id}
  654. placeholder='格式必须为:<类型>:<ID>,例如 open_id:123456'
  655. />
  656. </Form.Group>
  657. </>
  658. );
  659. case 'custom':
  660. return (
  661. <>
  662. <Message>
  663. 自定义推送,目前仅支持 POST 请求,请求体为 JSON 格式。
  664. <br/>
  665. 支持以下模板变量:<code>$title</code>,<code>$description</code>,<code>$content</code>,<code>$url</code>,<code>$to</code>。
  666. <br/>
  667. <a href="https://iamazing.cn/page/message-pusher-common-custom-templates" target='_blank'>这个页面</a>给出了常见的第三方平台的配置实例,你可以参考这些示例进行配置。
  668. <br/>
  669. 注意,为了防止攻击者利用本功能访问内部网络,也为了你的信息安全,请求地址必须使用 HTTPS 协议。
  670. </Message>
  671. <Form.Group widths={2}>
  672. <Form.Input
  673. label='请求地址'
  674. name='url'
  675. onChange={handleInputChange}
  676. autoComplete='new-password'
  677. value={inputs.url}
  678. placeholder='在此填写完整的请求地址,必须使用 HTTPS 协议'
  679. />
  680. </Form.Group>
  681. <Form.Group widths='equal'>
  682. <Form.TextArea
  683. label='请求体'
  684. placeholder='在此输入请求体,支持模板变量,必须为合法的 JSON 格式'
  685. value={inputs.other}
  686. name='other'
  687. onChange={handleInputChange}
  688. style={{ minHeight: 200, fontFamily: 'JetBrains Mono, Consolas' }}
  689. />
  690. </Form.Group>
  691. </>
  692. );
  693. case 'none':
  694. return (
  695. <>
  696. <Message>
  697. 仅保存消息,不做推送,可以在 Web
  698. 端查看,需要用户具有消息持久化的权限。
  699. </Message>
  700. </>
  701. );
  702. default:
  703. return (
  704. <>
  705. <Message>未知通道类型!</Message>
  706. </>
  707. );
  708. }
  709. };
  710. return (
  711. <>
  712. <Segment loading={loading}>
  713. <Header as='h3'>{isEditing ? '更新通道配置' : '新建消息通道'}</Header>
  714. <Form autoComplete='new-password'>
  715. <Form.Field>
  716. <Form.Input
  717. label='名称'
  718. name='name'
  719. placeholder={
  720. '请输入通道名称,请仅使用英文字母和下划线,该名称必须唯一'
  721. }
  722. onChange={handleInputChange}
  723. value={name}
  724. autoComplete='new-password'
  725. required
  726. />
  727. </Form.Field>
  728. <Form.Field>
  729. <Form.Input
  730. label='备注'
  731. name='description'
  732. type={'text'}
  733. placeholder={'请输入备注信息'}
  734. onChange={handleInputChange}
  735. value={description}
  736. autoComplete='new-password'
  737. />
  738. </Form.Field>
  739. <Form.Select
  740. label='通道类型'
  741. name='type'
  742. options={CHANNEL_OPTIONS}
  743. value={type}
  744. onChange={handleInputChange}
  745. />
  746. {renderChannelForm()}
  747. <Button disabled={type === 'email'} onClick={submit}>
  748. 提交
  749. </Button>
  750. </Form>
  751. </Segment>
  752. </>
  753. );
  754. };
  755. export default EditChannel;