index.tsx 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. import {
  2. ArrayItems,
  3. ArrayTable,
  4. Editable,
  5. Form,
  6. FormButtonGroup,
  7. FormGrid,
  8. FormItem,
  9. Input,
  10. NumberPicker,
  11. PreviewText,
  12. Radio,
  13. Select,
  14. Space,
  15. Switch,
  16. } from '@formily/antd';
  17. import type { Field } from '@formily/core';
  18. import {
  19. createForm,
  20. FormPath,
  21. onFieldInit,
  22. onFieldReact,
  23. onFieldValueChange,
  24. registerValidateRules,
  25. } from '@formily/core';
  26. import { createSchemaField, observer } from '@formily/react';
  27. import type { ISchema } from '@formily/json-schema';
  28. import styles from './index.less';
  29. import { useEffect, useMemo, useRef, useState } from 'react';
  30. import FUpload from '@/components/Upload';
  31. import { useParams } from 'umi';
  32. import { PageContainer } from '@ant-design/pro-layout';
  33. import { Card, Col, message, Row } from 'antd';
  34. import { typeList } from '@/pages/notice';
  35. import { configService, service, state } from '@/pages/notice/Template';
  36. import FBraftEditor from '@/components/FBraftEditor';
  37. import { useAsyncDataSource } from '@/utils/util';
  38. import WeixinCorp from '@/pages/notice/Template/Detail/doc/WeixinCorp';
  39. import WeixinApp from '@/pages/notice/Template/Detail/doc/WeixinApp';
  40. import DingTalk from '@/pages/notice/Template/Detail/doc/DingTalk';
  41. import DingTalkRebot from '@/pages/notice/Template/Detail/doc/DingTalkRebot';
  42. import AliyunVoice from '@/pages/notice/Template/Detail/doc/AliyunVoice';
  43. import AliyunSms from '@/pages/notice/Template/Detail/doc/AliyunSms';
  44. import Email from '@/pages/notice/Template/Detail/doc/Email';
  45. import { Store } from 'jetlinks-store';
  46. import FAutoComplete from '@/components/FAutoComplete';
  47. import { PermissionButton } from '@/components';
  48. import usePermissions from '@/hooks/permission';
  49. export const docMap = {
  50. weixin: {
  51. corpMessage: <WeixinCorp />,
  52. officialMessage: <WeixinApp />,
  53. },
  54. dingTalk: {
  55. dingTalkMessage: <DingTalk />,
  56. dingTalkRobotWebHook: <DingTalkRebot />,
  57. },
  58. voice: {
  59. aliyun: <AliyunVoice />,
  60. },
  61. sms: {
  62. aliyunSms: <AliyunSms />,
  63. },
  64. email: {
  65. embedded: <Email />,
  66. },
  67. };
  68. const Detail = observer(() => {
  69. const { id } = useParams<{ id: string }>();
  70. const [provider, setProvider] = useState<string>('embedded');
  71. // 正则提取${}里面的值
  72. const pattern = /(?<=\$\{).*?(?=\})/g;
  73. // 提取微信服务号里面的值 {{}}
  74. const weixinPattern = /(?<=\{\{).*?(?=\.DATA}})/g;
  75. const getConfig = (provider1: string) =>
  76. configService
  77. .queryNoPagingPost({
  78. terms: [
  79. { column: 'type$IN', value: id },
  80. { column: 'provider', value: provider1 },
  81. ],
  82. })
  83. .then((resp: any) => {
  84. return resp.result?.map((item: any) => ({
  85. label: item.name,
  86. value: item.id,
  87. }));
  88. });
  89. //需要复杂联动才可以完成
  90. const getWeixinDept = (configId: string) => service.weixin.getDepartments(configId);
  91. const getWeixinTags = (configId: string) => service.weixin.getTags(configId);
  92. const getWeixinUser = (configId: string) => service.weixin.getUser(configId);
  93. const getDingTalkDept = (configId: string) => service.dingTalk.getDepartments(configId);
  94. const getDingTalkDeptTree = (configId: string) => service.dingTalk.getDepartmentsTree(configId);
  95. const getDingTalkUser = (configId: string) => service.dingTalk.getUser(configId);
  96. const getWeixinOfficialTags = (configId: string) => service.weixin.getOfficialTags(configId);
  97. const getWeixinOfficialTemplates = (configId: string) =>
  98. service.weixin.getOfficialTemplates(configId);
  99. const getAliyunSigns = (configId: string) => service.aliyun.getSigns(configId);
  100. const getAliyunTemplates = (configId: string) => service.aliyun.getTemplates(configId);
  101. const variableDefinitionsRef =
  102. useRef<{ id: string; name: string; type: string; format: string }[]>();
  103. const form = useMemo(
  104. () =>
  105. createForm({
  106. validateFirst: true,
  107. effects() {
  108. onFieldInit('template.message', (field) => {
  109. if (id === 'email') {
  110. field.setComponent(FBraftEditor, {
  111. placeholder:
  112. '变量格式:${name};\n 示例:尊敬的${name},${time}有设备触发告警,请注意处理',
  113. height: '100px',
  114. });
  115. }
  116. const _provider = field.query('provider').value();
  117. if (_provider === 'corpMessage') {
  118. field.componentProps = {
  119. disabled: true,
  120. rows: 5,
  121. placeholder:
  122. '变量格式:${name};\n 示例:尊敬的${name},${time}有设备触发告警,请注意处理',
  123. };
  124. }
  125. });
  126. onFieldValueChange('provider', (field, form1) => {
  127. const value = field.value;
  128. setProvider(value);
  129. if (field.modified) {
  130. form1.setValuesIn('configId', null);
  131. form1.setValuesIn('template', null);
  132. }
  133. // 设置绑定配置的数据
  134. form1.setFieldState('configId', async (state1) => {
  135. state1.dataSource = await getConfig(value);
  136. });
  137. if (value === 'officialMessage') {
  138. form1.setFieldState('template.message', (state5) => {
  139. state5.decoratorProps = {
  140. tooltip: '服务号模版消息内容',
  141. };
  142. });
  143. }
  144. });
  145. onFieldValueChange('configId', (field, form1) => {
  146. const value = field.value;
  147. // 判断provider
  148. if (!value) return;
  149. switch (form1.values.provider) {
  150. case 'corpMessage':
  151. form1.setFieldState('template.toUser', async (state8) => {
  152. state8.dataSource = await getWeixinUser(value);
  153. });
  154. form1.setFieldState('template.toParty', async (state9) => {
  155. state9.dataSource = await getWeixinDept(value);
  156. });
  157. form1.setFieldState('template.toTag', async (state10) => {
  158. state10.dataSource = await getWeixinTags(value);
  159. });
  160. break;
  161. case 'officialMessage':
  162. form1.setFieldState('template.tagid', async (state1) => {
  163. state1.dataSource = await getWeixinOfficialTags(value);
  164. });
  165. form1.setFieldState('template.wxTemplateId', async (state2) => {
  166. const list = await getWeixinOfficialTemplates(value);
  167. Store.set('wxTemplate', list);
  168. state2.dataSource = list;
  169. });
  170. break;
  171. case 'dingTalkMessage':
  172. form1.setFieldState('template.userIdList', async (state3) => {
  173. state3.dataSource = await getDingTalkUser(value);
  174. });
  175. form1.setFieldState('template.departmentIdList', async (state4) => {
  176. const list = await getDingTalkDept(value);
  177. Store.set('wxTemplate', list);
  178. state4.dataSource = list;
  179. });
  180. break;
  181. case 'aliyun':
  182. // 阿里云语音
  183. form1.setFieldState('template.ttsCode', async (state5) => {
  184. const list = await getAliyunTemplates(value);
  185. Store.set('AliyunTemplate', list);
  186. state5.dataSource = list;
  187. });
  188. break;
  189. case 'aliyunSms':
  190. // 阿里云短信
  191. form1.setFieldState('template.code', async (state6) => {
  192. const list = await getAliyunTemplates(value);
  193. Store.set('AliyunTemplate', list);
  194. state6.dataSource = list;
  195. });
  196. form1.setFieldState('template.signName', async (state7) => {
  197. // const list =
  198. // Store.set('AliyunTemplate', list);
  199. state7.dataSource = await getAliyunSigns(value);
  200. });
  201. break;
  202. default:
  203. break;
  204. }
  205. });
  206. onFieldValueChange('template.wxTemplateId', (field, form1) => {
  207. const value = field.value;
  208. // 处理消息模版。
  209. const template = Store.get('wxTemplate');
  210. const data = template?.find((i: { id: any }) => i.id === value);
  211. if (data) {
  212. form1.setFieldState('template.title', (state1) => {
  213. state1.value = data.title;
  214. state1.disabled = true;
  215. });
  216. form1.setFieldState('template.message', (state1) => {
  217. state1.value = data.content;
  218. state1.disabled = true;
  219. });
  220. }
  221. });
  222. onFieldValueChange('template.code', (field, form1) => {
  223. const value = field.value;
  224. const template = Store.get('AliyunTemplate');
  225. const data = template?.find((i: { templateCode: any }) => i.templateCode === value);
  226. if (data) {
  227. form1.setFieldState('template.message', (state1) => {
  228. state1.value = data.templateContent;
  229. state1.disabled = true;
  230. });
  231. }
  232. });
  233. onFieldValueChange('template.*(subject,markdown.title)', (field, form1) => {
  234. const value = (field as Field).value;
  235. const _message = field.query('template.message').value();
  236. const titleList =
  237. (typeof value === 'string' &&
  238. (value + _message)?.match(pattern)?.filter((i: string) => i)) ||
  239. // .map((item: string) => ({id: item, type: 'string', format: '--'}))) ||
  240. [];
  241. // 拼接message的内容
  242. form1.setFieldState('variableDefinitions', (state1) => {
  243. state1.visible = !!titleList && titleList.length > 0;
  244. });
  245. if (form1.modified) {
  246. const oldKey = variableDefinitionsRef.current?.map((i) => i.id);
  247. const newKey = [...new Set(titleList)];
  248. const _result = newKey.map((item) =>
  249. oldKey?.includes(item)
  250. ? variableDefinitionsRef.current?.find((i) => i.id === item)
  251. : {
  252. id: item,
  253. type: 'string',
  254. format: '%s',
  255. },
  256. );
  257. form1.setValuesIn('variableDefinitions', _result);
  258. }
  259. });
  260. onFieldValueChange('template.message', (field, form1) => {
  261. const _provider = field.query('provider').value();
  262. const value = (field as Field).value;
  263. const idList =
  264. (typeof value === 'string' &&
  265. value
  266. ?.match(_provider === 'officialMessage' ? weixinPattern : pattern)
  267. ?.filter((i: string) => i)) ||
  268. [];
  269. if (id === 'email') {
  270. const subject = field.query('template.subject');
  271. const title = subject.value();
  272. const titleList = title?.match(pattern)?.filter((i: string) => i);
  273. // .map((item: string) => ({id: item, type: 'string', format: '--'}));
  274. if (idList && titleList?.length > 0) {
  275. idList.unshift(...titleList);
  276. }
  277. }
  278. if (_provider === 'dingTalkRobotWebHook') {
  279. const title = field.query('template.markdown.title').value();
  280. const titleList = title?.match(pattern)?.filter((i: string) => i);
  281. // .map((item: string) => ({id: item, type: 'string', format: '--'}));
  282. if (idList && titleList?.length > 0) {
  283. idList.unshift(...titleList);
  284. }
  285. }
  286. form1.setFieldState('variableDefinitions', (state1) => {
  287. state1.visible = !!idList && idList.length > 0;
  288. });
  289. if (form1.modified) {
  290. // 获取缓存的KEY;
  291. const oldKey = variableDefinitionsRef.current?.map((i) => i.id);
  292. const newKey = [...new Set(idList)];
  293. const _result = newKey.map((item) =>
  294. oldKey?.includes(item)
  295. ? variableDefinitionsRef.current?.find((i) => i.id === item)
  296. : {
  297. id: item,
  298. type: 'string',
  299. format: '%s',
  300. },
  301. );
  302. form1.setValuesIn('variableDefinitions', _result);
  303. }
  304. });
  305. onFieldValueChange('variableDefinitions.*.*', (field) => {
  306. // 缓存编辑后的数据
  307. variableDefinitionsRef.current = field.query('variableDefinitions').value();
  308. });
  309. onFieldReact('variableDefinitions.*.type', (field) => {
  310. const value = (field as Field).value;
  311. const formatPath = FormPath.transform(
  312. field.path,
  313. /\d+/,
  314. (index) => `variableDefinitions.${parseInt(index)}.format`,
  315. );
  316. const format = field.query(formatPath).take() as any;
  317. const fieldModified = field && (field as Field).modified;
  318. if (!format) return;
  319. if (fieldModified) {
  320. format.setValue(undefined);
  321. }
  322. switch (value) {
  323. case 'date':
  324. format.setComponent(FAutoComplete);
  325. format.setDataSource([
  326. { label: 'timestamp', value: 'timestamp' },
  327. { label: 'yyyy-MM-dd', value: 'yyyy-MM-dd' },
  328. { label: 'yyyy-MM-dd HH:mm:ss', value: 'yyyy-MM-dd HH:mm:ss' },
  329. { label: 'yyyy-MM-dd HH:mm:ss EE', value: 'yyyy-MM-dd HH:mm:ss EE' },
  330. { label: 'yyyy-MM-dd HH:mm:ss zzz', value: 'yyyy-MM-dd HH:mm:ss zzz' },
  331. ]);
  332. if (fieldModified) {
  333. format.setValue('timestamp');
  334. }
  335. break;
  336. case 'string':
  337. format.setComponent(PreviewText.Input);
  338. if (fieldModified) {
  339. format.setValue('%s');
  340. }
  341. break;
  342. case 'number':
  343. format.setComponent(Input);
  344. if (fieldModified) {
  345. format.setValue('%.xf');
  346. }
  347. break;
  348. // case 'file':
  349. // format.setComponent(Select);
  350. // format.setDataSource([
  351. // {label: '视频', value: 'video'},
  352. // {label: '图片', value: 'img'},
  353. // {label: '全部', value: 'any'},
  354. // ]);
  355. // format.setValue('any');
  356. // break;
  357. // case 'other':
  358. // format.setComponent(PreviewText.Input);
  359. // format.setValue('--');
  360. // break;
  361. }
  362. });
  363. },
  364. }),
  365. [id],
  366. );
  367. useEffect(() => {
  368. if (state.current) {
  369. form.setValues(state.current);
  370. }
  371. }, []);
  372. const SchemaField = createSchemaField({
  373. components: {
  374. FormItem,
  375. Input,
  376. Select,
  377. Switch,
  378. Radio,
  379. Editable,
  380. PreviewText,
  381. Space,
  382. FUpload,
  383. NumberPicker,
  384. FBraftEditor,
  385. ArrayItems,
  386. FormGrid,
  387. ArrayTable,
  388. FAutoComplete,
  389. },
  390. });
  391. const handleSave = async () => {
  392. const data: TemplateItem = await form.submit();
  393. // dingTalkRobotWebHook
  394. // 提交的时候处理内容
  395. // 钉钉机器人-->dingTalkRobotWebHook
  396. // r如果是text 的话。template.message=>template.text.content
  397. // 如果是markdown 的话。 template.message=>template.markdown.text
  398. // 如果是link的话。 template.message =>template.markdown.text
  399. // 微信服务号: template.message =>template.content
  400. if (data.provider === 'dingTalkRobotWebHook') {
  401. const type = data.template.messageType;
  402. // emplate.messageType
  403. switch (type) {
  404. case 'text':
  405. data.template.text = {
  406. content: data.template.message,
  407. };
  408. // data.template.text.content = data.template.message;
  409. break;
  410. case 'markdown':
  411. data.template.markdown.text = data.template.message;
  412. break;
  413. case 'link':
  414. data.template.link.text = data.template.message;
  415. }
  416. }
  417. if (id === 'email') {
  418. data.provider = 'embedded';
  419. data.template.text = data.template.message;
  420. }
  421. let response;
  422. if (data.id) {
  423. response = await service.update(data);
  424. } else {
  425. response = await service.save(data);
  426. }
  427. if (response?.status === 200) {
  428. message.success('保存成功');
  429. history.back();
  430. }
  431. };
  432. registerValidateRules({
  433. batchCheckEmail(value) {
  434. const regEmail = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
  435. let error;
  436. value.some((item: string) => {
  437. if (!regEmail.test(item)) {
  438. error = item;
  439. return true;
  440. }
  441. return false;
  442. });
  443. return error ? `${error}邮件格式错误` : '';
  444. },
  445. });
  446. const schema: ISchema = {
  447. type: 'object',
  448. properties: {
  449. name: {
  450. title: '名称',
  451. type: 'string',
  452. 'x-decorator': 'FormItem',
  453. 'x-component': 'Input',
  454. 'x-component-props': {
  455. placeholder: '请输入名称',
  456. },
  457. name: 'name',
  458. 'x-validator': [
  459. {
  460. max: 64,
  461. message: '最多可输入64个字符',
  462. },
  463. {
  464. required: true,
  465. message: '请输入名称',
  466. },
  467. ],
  468. },
  469. type: {
  470. title: '类型',
  471. 'x-value': id,
  472. 'x-hidden': true,
  473. },
  474. provider: {
  475. title: '类型',
  476. type: 'string',
  477. 'x-decorator': 'FormItem',
  478. 'x-component': 'Radio.Group',
  479. 'x-component-props': {
  480. optionType: 'button',
  481. placeholder: '请选择类型',
  482. },
  483. required: true,
  484. 'x-visible': typeList[id]?.length > 0,
  485. 'x-hidden': id === 'email',
  486. 'x-value': typeList[id][0]?.value,
  487. enum: typeList[id] || [],
  488. },
  489. configId: {
  490. title: '绑定配置',
  491. type: 'string',
  492. 'x-decorator': 'FormItem',
  493. 'x-component': 'Select',
  494. 'x-component-props': {
  495. placeholder: '请选择绑定配置',
  496. },
  497. required: true,
  498. 'x-decorator-props': {
  499. tooltip: '使用固定的通知配置来发送此通知模版',
  500. },
  501. 'x-visible': id !== 'email',
  502. },
  503. template: {
  504. type: 'object',
  505. properties: {
  506. weixin: {
  507. type: 'void',
  508. 'x-visible': id === 'weixin',
  509. properties: {
  510. corpMessage: {
  511. type: 'void',
  512. properties: {
  513. agentId: {
  514. title: 'AgentId',
  515. 'x-component': 'Input',
  516. 'x-decorator': 'FormItem',
  517. 'x-decorator-props': {
  518. tooltip: '应用唯一标识',
  519. },
  520. required: true,
  521. 'x-component-props': {
  522. placeholder: '请输入AgentID',
  523. },
  524. },
  525. layout: {
  526. type: 'void',
  527. 'x-decorator': 'FormGrid',
  528. 'x-decorator-props': {
  529. maxColumns: 2,
  530. minColumns: 2,
  531. },
  532. properties: {
  533. toUser: {
  534. title: '收信人',
  535. 'x-component': 'Select',
  536. 'x-decorator': 'FormItem',
  537. 'x-decorator-props': {
  538. tooltip: '如果不填写该字段,将在使用此模版发送通知时进行指定。',
  539. gridSpan: 1,
  540. },
  541. 'x-component-props': {
  542. placeholder: '请选择收信人',
  543. },
  544. },
  545. toParty: {
  546. title: '收信部门',
  547. 'x-component': 'Select',
  548. 'x-decorator': 'FormItem',
  549. 'x-decorator-props': {
  550. tooltip: '如果不填写该字段,将在使用此模版发送通知时进行指定。',
  551. gridSpan: 1,
  552. },
  553. 'x-component-props': {
  554. placeholder: '请选择收信部门',
  555. },
  556. },
  557. },
  558. },
  559. toTag: {
  560. title: '标签推送',
  561. 'x-component': 'Select',
  562. 'x-decorator': 'FormItem',
  563. 'x-decorator-props': {
  564. tooltip:
  565. '本企业微信的标签ID列表,最多支持100个,如果不填写该字段,将在使用此模版发送通知时进行指定',
  566. },
  567. 'x-component-props': {
  568. placeholder: '请输入标签推送,多个标签用,号分隔',
  569. },
  570. },
  571. },
  572. 'x-reactions': {
  573. dependencies: ['provider'],
  574. fulfill: {
  575. state: {
  576. visible: '{{$deps[0]==="corpMessage"}}',
  577. },
  578. },
  579. },
  580. },
  581. officialMessage: {
  582. type: 'void',
  583. properties: {
  584. tagid: {
  585. title: '用户标签',
  586. type: 'string',
  587. 'x-decorator': 'FormItem',
  588. 'x-component': 'Select',
  589. 'x-component-props': {
  590. placeholder: '请选择用户标签',
  591. },
  592. 'x-decorator-props': {
  593. tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
  594. },
  595. },
  596. layout: {
  597. type: 'void',
  598. 'x-decorator': 'FormGrid',
  599. 'x-decorator-props': {
  600. maxColumns: 2,
  601. minColumns: 2,
  602. },
  603. properties: {
  604. wxTemplateId: {
  605. title: '消息模版',
  606. type: 'string',
  607. 'x-decorator': 'FormItem',
  608. 'x-component': 'Select',
  609. 'x-component-props': {
  610. placeholder: '请选择消息模版',
  611. },
  612. required: true,
  613. 'x-decorator-props': {
  614. gridSpan: 1,
  615. tooltip: '微信公众号中配置的消息模版',
  616. },
  617. },
  618. url: {
  619. title: '模版跳转链接',
  620. type: 'string',
  621. 'x-decorator': 'FormItem',
  622. 'x-component': 'Input',
  623. 'x-component-props': {
  624. placeholder: '请输入模版跳转链接',
  625. },
  626. 'x-decorator-props': {
  627. gridSpan: 1,
  628. tooltip: '用于点击消息后进行页面跳转',
  629. },
  630. },
  631. },
  632. },
  633. toMiniProgram: {
  634. title: '跳转小程序',
  635. type: 'string',
  636. 'x-decorator': 'FormItem',
  637. 'x-component': 'Radio.Group',
  638. 'x-component-props': {
  639. // optionType: 'button'
  640. },
  641. 'x-decorator-props': {
  642. tooltip: '配置后点击通知消息将跳转到对应小程序',
  643. },
  644. default: false,
  645. enum: [
  646. { label: '是', value: true },
  647. { label: '否', value: false },
  648. ],
  649. },
  650. miniProgram: {
  651. type: 'void',
  652. properties: {
  653. layout: {
  654. type: 'void',
  655. 'x-decorator': 'FormGrid',
  656. 'x-decorator-props': {
  657. maxColumns: 2,
  658. minColumns: 2,
  659. },
  660. properties: {
  661. miniProgramId: {
  662. title: '跳转小程序AppId',
  663. type: 'string',
  664. 'x-decorator': 'FormItem',
  665. 'x-component': 'Input',
  666. 'x-component-props': {
  667. placeholder: '请输入跳转小程序AppId',
  668. },
  669. 'x-decorator-props': {
  670. gridSpan: 1,
  671. tooltip: '小程序唯一性id',
  672. },
  673. },
  674. miniProgramPath: {
  675. title: '跳转小程序具体路径',
  676. type: 'string',
  677. 'x-decorator': 'FormItem',
  678. 'x-component': 'Input',
  679. 'x-component-props': {
  680. placeholder: '请输入跳转小程序具体路径',
  681. },
  682. 'x-decorator-props': {
  683. gridSpan: 1,
  684. tooltip: '用于点击消息之后跳转到小程序的具体页面',
  685. },
  686. },
  687. },
  688. },
  689. },
  690. 'x-reactions': {
  691. dependencies: ['.toMiniProgram'],
  692. fulfill: {
  693. state: {
  694. visible: '{{$deps[0]===true}}',
  695. },
  696. },
  697. },
  698. },
  699. title: {
  700. title: '模版标题',
  701. type: 'string',
  702. 'x-decorator': 'FormItem',
  703. 'x-component': 'Input',
  704. 'x-component-props': {
  705. placeholder: '这里是回显内容',
  706. },
  707. 'x-decorator-props': {
  708. tooltip: '服务号消息模版标题',
  709. },
  710. 'x-disabled': true,
  711. },
  712. },
  713. 'x-reactions': {
  714. dependencies: ['provider'],
  715. fulfill: {
  716. state: {
  717. visible: '{{$deps[0]==="officialMessage"}}',
  718. },
  719. },
  720. },
  721. },
  722. },
  723. },
  724. dingTalk: {
  725. type: 'void',
  726. 'x-visible': id === 'dingTalk',
  727. properties: {
  728. dingTalkMessage: {
  729. type: 'void',
  730. properties: {
  731. agentId: {
  732. title: 'AgentID',
  733. required: true,
  734. 'x-component': 'Input',
  735. 'x-decorator': 'FormItem',
  736. 'x-decorator-props': {
  737. tooltip: '应用唯一标识',
  738. },
  739. 'x-component-props': {
  740. placeholder: '请输入AgentID',
  741. },
  742. },
  743. layout: {
  744. type: 'void',
  745. 'x-decorator': 'FormGrid',
  746. 'x-decorator-props': {
  747. maxColumns: 2,
  748. minColumns: 2,
  749. },
  750. properties: {
  751. userIdList: {
  752. title: '收信人',
  753. 'x-component': 'Select',
  754. 'x-decorator': 'FormItem',
  755. 'x-decorator-props': {
  756. tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
  757. gridSpan: 1,
  758. },
  759. 'x-component-props': {
  760. placeholder: '请选择收信人',
  761. },
  762. // 'x-reactions': {
  763. // dependencies: ['configId'],
  764. // fulfill: {
  765. // run: '{{useAsyncDataSource(getDingTalkUser($deps[0]))}}',
  766. // },
  767. // },
  768. },
  769. departmentIdList: {
  770. title: '收信部门',
  771. 'x-component': 'Select',
  772. 'x-decorator': 'FormItem',
  773. 'x-decorator-props': {
  774. tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
  775. gridSpan: 1,
  776. },
  777. 'x-component-props': {
  778. placeholder: '请选择收信部门',
  779. },
  780. // 'x-reactions': {
  781. // dependencies: ['configId'],
  782. // fulfill: {
  783. // run: '{{useAsyncDataSource(getDingTalkDept($deps[0]))}}',
  784. // },
  785. // },
  786. },
  787. },
  788. },
  789. },
  790. 'x-reactions': {
  791. dependencies: ['provider'],
  792. fulfill: {
  793. state: {
  794. visible: '{{$deps[0]==="dingTalkMessage"}}',
  795. },
  796. },
  797. },
  798. },
  799. dingTalkRobotWebHook: {
  800. type: 'void',
  801. properties: {
  802. messageType: {
  803. title: '消息类型',
  804. 'x-component': 'Select',
  805. 'x-decorator': 'FormItem',
  806. required: true,
  807. 'x-component-props': {
  808. placeholder: '请选择消息类型',
  809. },
  810. enum: [
  811. { label: 'markdown', value: 'markdown' },
  812. { label: 'text', value: 'text' },
  813. { label: 'link', value: 'link' },
  814. ],
  815. },
  816. markdown: {
  817. type: 'object',
  818. properties: {
  819. title: {
  820. required: true,
  821. title: '标题',
  822. 'x-component': 'Input',
  823. 'x-decorator': 'FormItem',
  824. 'x-component-props': {
  825. placeholder: '请输入标题',
  826. },
  827. },
  828. },
  829. 'x-reactions': {
  830. dependencies: ['.messageType'],
  831. fulfill: {
  832. state: {
  833. visible: '{{$deps[0]==="markdown"}}',
  834. },
  835. },
  836. },
  837. },
  838. link: {
  839. type: 'object',
  840. properties: {
  841. title: {
  842. required: true,
  843. title: '标题',
  844. 'x-component': 'Input',
  845. 'x-decorator': 'FormItem',
  846. 'x-component-props': {
  847. placeholder: '请输入标题',
  848. },
  849. },
  850. '{url:picUrl}': {
  851. title: '图片链接',
  852. 'x-component': 'FUpload',
  853. 'x-decorator': 'FormItem',
  854. 'x-component-props': {
  855. type: 'file',
  856. placeholder: '请输入图片链接',
  857. },
  858. },
  859. messageUrl: {
  860. title: '内容链接',
  861. 'x-component': 'Input',
  862. 'x-decorator': 'FormItem',
  863. 'x-component-props': {
  864. placeholder: '请输入内容链接',
  865. },
  866. },
  867. },
  868. 'x-reactions': {
  869. dependencies: ['.messageType'],
  870. fulfill: {
  871. state: {
  872. visible: '{{$deps[0]==="link"}}',
  873. },
  874. },
  875. },
  876. },
  877. },
  878. 'x-reactions': {
  879. dependencies: ['provider'],
  880. fulfill: {
  881. state: {
  882. visible: '{{$deps[0]==="dingTalkRobotWebHook"}}',
  883. },
  884. },
  885. },
  886. },
  887. },
  888. // 钉钉群机器人配置参数名 类型 说明
  889. // messageType String 钉钉-消息类型 markdown、text、link
  890. // ${messageType} String 钉钉-内容
  891. },
  892. aliyun: {
  893. type: 'void',
  894. properties: {
  895. voice: {
  896. 'x-visible': id === 'voice',
  897. type: 'void',
  898. properties: {
  899. layout: {
  900. type: 'void',
  901. 'x-decorator': 'FormGrid',
  902. 'x-decorator-props': {
  903. maxColumns: 2,
  904. minColumns: 2,
  905. },
  906. properties: {
  907. ttsCode: {
  908. title: '模版ID',
  909. 'x-component': 'Select',
  910. 'x-decorator': 'FormItem',
  911. 'x-decorator-props': {
  912. tooltip: '阿里云内部分配的唯一ID标识',
  913. gridSpan: 1,
  914. },
  915. required: true,
  916. 'x-component-props': {
  917. placeholder: '请输入模版ID',
  918. },
  919. },
  920. calledShowNumbers: {
  921. title: '被叫号码',
  922. 'x-component': 'Input',
  923. 'x-decorator': 'FormItem',
  924. 'x-decorator-props': {
  925. tooltip: '仅支持中国大陆号码',
  926. gridSpan: 1,
  927. },
  928. 'x-component-props': {
  929. placeholder: '请输入被叫号码',
  930. },
  931. },
  932. },
  933. },
  934. calledNumber: {
  935. title: '被叫显号',
  936. 'x-component': 'Input',
  937. 'x-decorator': 'FormItem',
  938. 'x-decorator-props': {
  939. tooltip: '必须是已购买的号码,用于呼叫号码显示',
  940. },
  941. 'x-component-props': {
  942. placeholder: '请输入被叫显号',
  943. },
  944. },
  945. PlayTimes: {
  946. title: '播放次数',
  947. 'x-component': 'NumberPicker',
  948. 'x-decorator': 'FormItem',
  949. 'x-decorator-props': {
  950. tooltip: '语音文件的播放次数',
  951. },
  952. default: 1,
  953. 'x-validator': [
  954. {
  955. min: 1,
  956. max: 3,
  957. message: '仅支持1~3次',
  958. },
  959. ],
  960. 'x-component-props': {
  961. placeholder: '请输入播放次数',
  962. },
  963. },
  964. },
  965. },
  966. sms: {
  967. 'x-visible': id === 'sms',
  968. type: 'void',
  969. properties: {
  970. layout: {
  971. type: 'void',
  972. 'x-decorator': 'FormGrid',
  973. 'x-decorator-props': {
  974. maxColumns: 2,
  975. minColumns: 2,
  976. },
  977. properties: {
  978. code: {
  979. title: '模版',
  980. 'x-component': 'Select',
  981. 'x-decorator': 'FormItem',
  982. 'x-decorator-props': {
  983. tooltip: '阿里云短信平台自定义的模版名称',
  984. gridSpan: 1,
  985. },
  986. 'x-component-props': {
  987. placeholder: '请选择模版',
  988. },
  989. 'x-reactions': {
  990. dependencies: ['configId'],
  991. fulfill: {
  992. run: '{{useAsyncDataSource(getAliyunTemplates($deps[0]))}}',
  993. },
  994. },
  995. },
  996. phoneNumber: {
  997. title: '收信人',
  998. 'x-component': 'Input',
  999. 'x-decorator': 'FormItem',
  1000. 'x-decorator-props': {
  1001. tooltip: '仅支持中国大陆号码',
  1002. gridSpan: 1,
  1003. },
  1004. 'x-validator': ['phone'],
  1005. 'x-component-props': {
  1006. placeholder: '请输入收信人',
  1007. },
  1008. },
  1009. },
  1010. },
  1011. signName: {
  1012. title: '签名',
  1013. 'x-component': 'Select',
  1014. 'x-decorator': 'FormItem',
  1015. 'x-decorator-props': {
  1016. tooltip: '用于短信内容签名信息显示',
  1017. },
  1018. 'x-component-props': {
  1019. placeholder: '请输入签名',
  1020. },
  1021. 'x-reactions': {
  1022. dependencies: ['configId'],
  1023. fulfill: {
  1024. run: '{{useAsyncDataSource(getAliyunSigns($deps[0]))}}',
  1025. },
  1026. },
  1027. },
  1028. },
  1029. },
  1030. },
  1031. },
  1032. email: {
  1033. type: 'void',
  1034. 'x-visible': id === 'email',
  1035. properties: {
  1036. subject: {
  1037. 'x-component': 'Input',
  1038. 'x-decorator': 'FormItem',
  1039. title: '标题',
  1040. 'x-decorator-props': {
  1041. tooltip: '邮件标题',
  1042. },
  1043. required: true,
  1044. 'x-component-props': {
  1045. placeholder: '请输入标题',
  1046. },
  1047. },
  1048. sendTo: {
  1049. 'x-component': 'Select',
  1050. 'x-decorator': 'FormItem',
  1051. title: '收件人',
  1052. 'x-decorator-props': {
  1053. tooltip: '多个收件人用换行分隔 \n最大支持1000个号码',
  1054. },
  1055. 'x-component-props': {
  1056. mode: 'tags',
  1057. placeholder: '请输入收件人邮箱,多个收件人用换行分隔',
  1058. },
  1059. 'x-validator': {
  1060. batchCheckEmail: true,
  1061. },
  1062. },
  1063. attachments: {
  1064. type: 'array',
  1065. title: '附件信息',
  1066. 'x-decorator': 'FormItem',
  1067. 'x-component': 'ArrayItems',
  1068. 'x-decorator-props': {
  1069. style: {
  1070. width: '100%',
  1071. },
  1072. tooltip: '附件只输入文件名称将在发送邮件时进行文件上传',
  1073. },
  1074. items: {
  1075. type: 'object',
  1076. 'x-decorator': 'FormGrid',
  1077. 'x-decorator-props': {
  1078. maxColumns: 24,
  1079. minColumns: 24,
  1080. },
  1081. properties: {
  1082. '{url:location,name:name}': {
  1083. 'x-component': 'FUpload',
  1084. 'x-decorator': 'FormItem',
  1085. 'x-decorator-props': {
  1086. style: {
  1087. width: '100%',
  1088. },
  1089. gridSpan: 23,
  1090. },
  1091. 'x-component-props': {
  1092. type: 'file',
  1093. display: 'name',
  1094. placeholder: '请上传文件或输入文件名称',
  1095. },
  1096. },
  1097. remove: {
  1098. type: 'void',
  1099. 'x-decorator': 'FormItem',
  1100. 'x-component': 'ArrayItems.Remove',
  1101. 'x-decorator-props': {
  1102. gridSpan: 1,
  1103. },
  1104. },
  1105. },
  1106. },
  1107. properties: {
  1108. add: {
  1109. type: 'void',
  1110. 'x-component': 'ArrayItems.Addition',
  1111. title: '添加附件',
  1112. },
  1113. },
  1114. },
  1115. },
  1116. },
  1117. },
  1118. },
  1119. 'template.message': {
  1120. title: '模版内容',
  1121. 'x-component': 'Input.TextArea',
  1122. 'x-decorator': 'FormItem',
  1123. 'x-decorator-props': {
  1124. tooltip: '发送的内容,支持录入变量',
  1125. },
  1126. required: true,
  1127. 'x-reactions': {
  1128. dependencies: ['provider'],
  1129. fulfill: {
  1130. state: {
  1131. hidden: '{{$deps[0]==="aliyun"}}',
  1132. disabled: '{{["aliyunSms","aliyun"].includes($deps[0])}}',
  1133. },
  1134. },
  1135. },
  1136. 'x-component-props': {
  1137. rows: 5,
  1138. placeholder: '变量格式:${name};\n 示例:尊敬的${name},${time}有设备触发告警,请注意处理',
  1139. },
  1140. },
  1141. variableDefinitions: {
  1142. type: 'array',
  1143. title: '变量列表',
  1144. 'x-decorator': 'FormItem',
  1145. 'x-component': 'ArrayTable',
  1146. 'x-component-props': {
  1147. pagination: { pageSize: 9999 },
  1148. scroll: { x: '100%' },
  1149. },
  1150. 'x-decorator-props': {
  1151. style: {
  1152. zIndex: 999,
  1153. },
  1154. },
  1155. 'x-visible': false,
  1156. items: {
  1157. type: 'object',
  1158. properties: {
  1159. column1: {
  1160. type: 'void',
  1161. 'x-component': 'ArrayTable.Column',
  1162. 'x-component-props': { title: '变量', width: '120px' },
  1163. properties: {
  1164. id: {
  1165. type: 'string',
  1166. 'x-decorator': 'FormItem',
  1167. 'x-component': 'PreviewText.Input',
  1168. 'x-disabled': true,
  1169. },
  1170. },
  1171. },
  1172. column2: {
  1173. type: 'void',
  1174. 'x-component': 'ArrayTable.Column',
  1175. 'x-component-props': { title: '名称', width: '120px' },
  1176. properties: {
  1177. name: {
  1178. type: 'string',
  1179. 'x-decorator': 'FormItem',
  1180. required: true,
  1181. 'x-component': 'Input',
  1182. },
  1183. },
  1184. },
  1185. column3: {
  1186. type: 'void',
  1187. 'x-component': 'ArrayTable.Column',
  1188. 'x-component-props': { title: '类型', width: '120px' },
  1189. properties: {
  1190. type: {
  1191. type: 'string',
  1192. 'x-decorator': 'FormItem',
  1193. 'x-component': 'Select',
  1194. required: true,
  1195. enum: [
  1196. { label: '字符串', value: 'string' },
  1197. { label: '时间', value: 'date' },
  1198. { label: '数字', value: 'number' },
  1199. ],
  1200. },
  1201. },
  1202. },
  1203. column4: {
  1204. type: 'void',
  1205. 'x-component': 'ArrayTable.Column',
  1206. 'x-component-props': { title: '格式', width: '300px' },
  1207. required: true,
  1208. properties: {
  1209. format: {
  1210. type: 'string',
  1211. 'x-decorator': 'FormItem',
  1212. 'x-component': 'Input',
  1213. },
  1214. },
  1215. },
  1216. },
  1217. },
  1218. },
  1219. description: {
  1220. title: '说明',
  1221. 'x-decorator': 'FormItem',
  1222. 'x-component': 'Input.TextArea',
  1223. 'x-component-props': {
  1224. rows: 4,
  1225. },
  1226. },
  1227. },
  1228. };
  1229. const { permission } = usePermissions('notice');
  1230. return (
  1231. <PageContainer>
  1232. <Card>
  1233. <Row>
  1234. <Col span={10}>
  1235. <Form className={styles.form} form={form} layout={'vertical'}>
  1236. <SchemaField
  1237. schema={schema}
  1238. scope={{
  1239. getConfig,
  1240. getDingTalkDept,
  1241. getDingTalkDeptTree,
  1242. getDingTalkUser,
  1243. getWeixinDept,
  1244. getWeixinTags,
  1245. getWeixinUser,
  1246. getAliyunSigns,
  1247. getAliyunTemplates,
  1248. useAsyncDataSource,
  1249. getWeixinOfficialTags,
  1250. getWeixinOfficialTemplates,
  1251. }}
  1252. />
  1253. <FormButtonGroup.Sticky>
  1254. <FormButtonGroup.FormItem>
  1255. <PermissionButton
  1256. type="primary"
  1257. isPermission={permission.add || permission.update}
  1258. onClick={handleSave}
  1259. >
  1260. 保存
  1261. </PermissionButton>
  1262. </FormButtonGroup.FormItem>
  1263. </FormButtonGroup.Sticky>
  1264. </Form>
  1265. </Col>
  1266. <Col span={12} push={2}>
  1267. {docMap[id][provider]}
  1268. </Col>
  1269. </Row>
  1270. </Card>
  1271. </PageContainer>
  1272. );
  1273. });
  1274. export default Detail;