Forráskód Böngészése

fix: 合并冲突

sun-chaochao 3 éve
szülő
commit
f6e3029b5e

+ 1 - 1
src/components/FBraftEditor/index.tsx

@@ -23,7 +23,7 @@ const FBraftEditor = connect((props: Props) => {
           value={editorState}
           onChange={(state) => {
             setEditorState(state);
-            props.onChange(state.toHTML());
+            props.onChange(state.toHTML() === '<p></p>' ? undefined : state.toHTML());
           }}
         />
       }

+ 1 - 1
src/components/ProTableCard/CardItems/noticeConfig.tsx

@@ -15,7 +15,7 @@ export default (props: NoticeCardProps) => {
     <TableCard detail={props.detail} actions={props.actions} showStatus={false} showMask={false}>
       <div className={'pro-table-card-item'}>
         <div className={'card-item-avatar'}>
-          <img width={88} height={88} src={imgMap[props.type]} alt={props.type} />
+          <img width={88} height={88} src={imgMap[props.type][props.provider]} alt={props.type} />
         </div>
         <div className={'card-item-body'}>
           <div className={'card-item-header'}>

+ 18 - 6
src/components/ProTableCard/CardItems/noticeTemplate.tsx

@@ -10,11 +10,23 @@ export interface NoticeCardProps extends TemplateItem {
 }
 
 export const imgMap = {
-  dingTalk: require('/public/images/notice/dingtalk.png'),
-  weixin: require('/public/images/notice/wechat.png'),
-  email: require('/public/images/notice/email.png'),
-  voice: require('/public/images/notice/voice.png'),
-  sms: require('/public/images/notice/sms.png'),
+  dingTalk: {
+    dingTalkMessage: require('/public/images/notice/dingtalk.png'),
+    dingTalkRobotWebHook: require('/public/images/notice/dingTalk-rebot.png'),
+  },
+  weixin: {
+    corpMessage: require('/public/images/notice/wechat.png'),
+    officialMessage: require('/public/images/notice/weixin-official.png'),
+  },
+  email: {
+    embedded: require('/public/images/notice/email.png'),
+  },
+  voice: {
+    aliyun: require('/public/images/notice/voice.png'),
+  },
+  sms: {
+    aliyunSms: require('/public/images/notice/sms.png'),
+  },
 };
 
 export const typeList = {
@@ -42,7 +54,7 @@ export default (props: NoticeCardProps) => {
     <TableCard actions={props.actions} showStatus={false} detail={props.detail} showMask={false}>
       <div className={'pro-table-card-item'}>
         <div className={'card-item-avatar'}>
-          <img width={88} height={88} src={imgMap[props.type]} alt={props.type} />
+          <img width={88} height={88} src={imgMap[props.type][props.provider]} alt={props.type} />
         </div>
         <div className={'card-item-body'}>
           <div className={'card-item-header'}>

+ 35 - 2
src/pages/device/Instance/Detail/index.tsx

@@ -1,7 +1,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { InstanceModel } from '@/pages/device/Instance';
 import { history, useParams } from 'umi';
-import { Badge, Card, Descriptions, Divider, Tooltip } from 'antd';
+import { Badge, Card, Descriptions, Divider, message, Space, Tooltip } from 'antd';
 import type { ReactNode } from 'react';
 import { useEffect, useState } from 'react';
 import { observer } from '@formily/react';
@@ -35,6 +35,7 @@ const InstanceDetail = observer(() => {
   const [tab, setTab] = useState<string>('detail');
   const params = useParams<{ id: string }>();
   const service = new Service('device-instance');
+  const { permission } = PermissionButton.usePermission('device/Instance');
 
   // const resetMetadata = async () => {
   //   const resp = await service.deleteMetadata(params.id);
@@ -260,7 +261,39 @@ const InstanceDetail = observer(() => {
         <>
           {InstanceModel.detail?.name}
           <Divider type="vertical" />
-          {deviceStatus.get(InstanceModel.detail?.state?.value)}
+          <Space>
+            {deviceStatus.get(InstanceModel.detail?.state?.value)}
+            <PermissionButton
+              type={'link'}
+              key={'state'}
+              popConfirm={{
+                title:
+                  InstanceModel.detail?.state?.value !== 'notActive'
+                    ? '确认断开连接'
+                    : '确认启用设备',
+                onConfirm: async () => {
+                  if (InstanceModel.detail?.state?.value !== 'notActive') {
+                    await service.undeployDevice(params.id);
+                  } else {
+                    await service.deployDevice(params.id);
+                  }
+                  message.success(
+                    intl.formatMessage({
+                      id: 'pages.data.option.success',
+                      defaultMessage: '操作成功!',
+                    }),
+                  );
+                  getDetail(params.id);
+                },
+              }}
+              isPermission={permission.action}
+              tooltip={{
+                title: InstanceModel.detail?.state?.value !== 'notActive' ? '断开连接' : '启用设备',
+              }}
+            >
+              {InstanceModel.detail?.state?.value !== 'notActive' ? '断开连接' : '启用设备'}
+            </PermissionButton>
+          </Space>
         </>
       }
       // extra={[

+ 5 - 3
src/pages/device/Instance/Export/index.tsx

@@ -49,9 +49,10 @@ const Export = (props: Props) => {
         type: 'void',
         'x-component': 'FormLayout',
         'x-component-props': {
-          labelCol: 4,
-          wrapperCol: 18,
-          labelAlign: 'right',
+          // labelCol: 4,
+          // wrapperCol: 18,
+          // labelAlign: 'right',
+          layout: 'vertical',
         },
         properties: {
           product: {
@@ -103,6 +104,7 @@ const Export = (props: Props) => {
     } else {
       downloadFile(`/${SystemConst.API_BASE}/device/instance/export.${values.fileType}`, params);
     }
+    close();
   };
   return (
     <Modal

+ 6 - 5
src/pages/device/Instance/Import/index.tsx

@@ -88,7 +88,7 @@ const NormalUpload = (props: any) => {
           dt += temp;
           setCount(dt);
         } else {
-          setErrMessage(res.message);
+          setErrMessage(res.message || '失败');
         }
       };
       source.onerror = () => {
@@ -221,9 +221,10 @@ const Import = (props: Props) => {
         type: 'void',
         'x-component': 'FormLayout',
         'x-component-props': {
-          labelCol: 4,
-          wrapperCol: 18,
-          labelAlign: 'right',
+          // labelCol: 6,
+          // wrapperCol: 18,
+          // labelAlign: 'right',
+          layout: 'vertical',
         },
         properties: {
           product: {
@@ -264,7 +265,7 @@ const Import = (props: Props) => {
       visible={visible}
       onCancel={() => close()}
       width="35vw"
-      title="导"
+      title="导"
       onOk={() => close()}
       footer={[
         <Button key="cancel" onClick={() => close()}>

+ 4 - 3
src/pages/device/Product/Detail/PropertyImport/index.tsx

@@ -138,9 +138,10 @@ const PropertyImport = (props: Props) => {
         type: 'void',
         'x-component': 'FormLayout',
         'x-component-props': {
-          labelCol: 4,
-          wrapperCol: 18,
-          labelAlign: 'right',
+          // labelCol: 4,
+          // wrapperCol: 18,
+          // labelAlign: 'right',
+          layout: 'vertical',
         },
         properties: {
           fileType: {

+ 1 - 3
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -60,10 +60,7 @@ const Edit = observer((props: Props) => {
         initialValues: MetadataModel.item as Record<string, unknown>,
         effects: () => {
           onFieldReact('expands.metrics.*.*', (field, form1) => {
-            console.log('指标配置');
             const type = field.query('valueType.type').take() as Field;
-            console.log(type.value, 'value');
-
             const componentMap = {
               int: 'NumberPicker',
               long: 'NumberPicker',
@@ -459,6 +456,7 @@ const Edit = observer((props: Props) => {
             'x-decorator': 'FormItem',
             'x-component': 'Select',
             enum: PropertySource,
+            'x-visible': props.type === 'product',
           },
           'virtualRule.type': {
             type: 'string',

+ 4 - 6
src/pages/notice/Config/Debug/index.tsx

@@ -34,12 +34,10 @@ const Debug = observer(() => {
             const list = Store.get('notice-template-list');
 
             const _template = list.find((item: any) => item.id === value);
-            if (_template?.variableDefinitions?.length > 0) {
-              form1.setFieldState('variableDefinitions', (_state) => {
-                _state.visible = true;
-                _state.value = _template.variableDefinitions;
-              });
-            }
+            form1.setFieldState('variableDefinitions', (_state) => {
+              _state.visible = _template?.variableDefinitions?.length > 0;
+              _state.value = _template.variableDefinitions;
+            });
           });
           onFieldReact('variableDefinitions.*.type', (field) => {
             const value = (field as Field).value;

+ 74 - 3
src/pages/notice/Config/Detail/index.tsx

@@ -33,6 +33,7 @@ import AliyunVoice from '@/pages/notice/Config/Detail/doc/AliyunVoice';
 import Email from '@/pages/notice/Config/Detail/doc/Email';
 import { PermissionButton } from '@/components';
 import usePermissions from '@/hooks/permission';
+import FAutoComplete from '@/components/FAutoComplete';
 
 export const docMap = {
   weixin: {
@@ -105,6 +106,7 @@ const Detail = observer(() => {
       FUpload,
       Checkbox,
       NumberPicker,
+      FAutoComplete,
     },
   });
 
@@ -127,6 +129,12 @@ const Detail = observer(() => {
         'x-component-props': {
           placeholder: '请输入名称',
         },
+        'x-validator': [
+          {
+            max: 64,
+            message: '最多可输入64个字符',
+          },
+        ],
       },
       type: {
         title: '分类',
@@ -336,9 +344,23 @@ const Detail = observer(() => {
                     required: true,
                     'x-component-props': {
                       placeholder: '请输入服务器地址',
+                      style: {
+                        width: '200px',
+                      },
                     },
-                    'x-component': 'Input',
+                    'x-component': 'FAutoComplete',
                     'x-decorator': 'FormItem',
+                    enum: [
+                      { label: 'smtp.163.com', value: 'smtp.163.com' },
+                      { label: 'pop.163.com', value: 'pop.163.com' },
+                      { label: 'smtp.exmail.qq.com', value: 'smtp.exmail.qq.com' },
+                      { label: 'pop.exmail.qq.com', value: 'pop.exmail.qq.com' },
+                      { label: 'smtp.qq.com', value: 'smtp.qq.com' },
+                      { label: 'pop.qq.com', value: 'pop.qq.com' },
+                      { label: 'smtpdm.aliyun.com', value: 'smtpdm.aliyun.com' },
+                      { label: 'smtp.126.com', value: 'smtp.126.com' },
+                      { label: 'pop.126.com', value: 'pop.126.com' },
+                    ],
                   },
                   port: {
                     // title: '端口',
@@ -346,15 +368,40 @@ const Detail = observer(() => {
                     'x-component-props': {
                       placeholder: '请输入端口',
                     },
+                    default: 25,
+                    'x-validator': [
+                      {
+                        min: 1,
+                        max: 65535,
+                        message: '请输入1~65535之间的正整数',
+                      },
+                    ],
                     'x-component': 'NumberPicker',
                     'x-decorator': 'FormItem',
+                    'x-reactions': {
+                      dependencies: ['.enableSSL'],
+                      when: '{{$deps[0]}}',
+                      fulfill: {
+                        state: {
+                          value: 465,
+                        },
+                      },
+                      otherwise: {
+                        state: {
+                          value: 25,
+                        },
+                      },
+                    },
                   },
                   enableSSL: {
                     // title: '开启SSL',
                     type: 'boolean',
-                    'x-component': 'Checkbox.Group',
+                    'x-component': 'Checkbox',
                     'x-decorator': 'FormItem',
-                    enum: [{ label: '开启SSL', value: true }],
+                    'x-component-props': {
+                      children: '开启SSL',
+                    },
+                    // enum: [{label: '开启SSL', value: true}],
                   },
                 },
               },
@@ -366,6 +413,12 @@ const Detail = observer(() => {
                 'x-component-props': {
                   placeholder: '请输入发件人',
                 },
+                'x-validator': [
+                  {
+                    max: 64,
+                    message: '最多可输入64个字符',
+                  },
+                ],
               },
               username: {
                 title: '用户名',
@@ -375,6 +428,12 @@ const Detail = observer(() => {
                   placeholder: '请输入用户名',
                 },
                 'x-decorator': 'FormItem',
+                'x-validator': [
+                  {
+                    max: 64,
+                    message: '最多可输入64个字符',
+                  },
+                ],
               },
               password: {
                 title: '密码',
@@ -384,6 +443,12 @@ const Detail = observer(() => {
                 },
                 'x-component': 'Input',
                 'x-decorator': 'FormItem',
+                'x-validator': [
+                  {
+                    max: 64,
+                    message: '最多可输入64个字符',
+                  },
+                ],
               },
             },
           },
@@ -396,6 +461,12 @@ const Detail = observer(() => {
         'x-component-props': {
           rows: 4,
         },
+        'x-validator': [
+          {
+            max: 200,
+            message: '最多可输入200个字符',
+          },
+        ],
       },
     },
   };

+ 3 - 3
src/pages/notice/Config/SyncUser/index.tsx

@@ -166,14 +166,14 @@ const SyncUser = observer(() => {
                   )
                   .then((resp: any) => {
                     return {
-                      code: resp?.message,
+                      code: '',
                       result: {
-                        data: resp?.result || [],
+                        data: resp || [],
                         pageIndex: 0,
                         pageSize: 0,
                         total: 0,
                       },
-                      status: resp?.status,
+                      status: 200,
                     };
                   })
               }

+ 11 - 13
src/pages/notice/Config/service.ts

@@ -1,7 +1,7 @@
 import BaseService from '@/utils/BaseService';
 import { request } from 'umi';
 import SystemConst from '@/utils/const';
-import { from, map, zip } from 'rxjs';
+import { from, lastValueFrom, map, mergeMap, toArray, zip } from 'rxjs';
 
 class Service extends BaseService<ConfigItem> {
   public getTypes = () =>
@@ -84,15 +84,15 @@ class Service extends BaseService<ConfigItem> {
     configId: string,
     departmentId: string,
   ) =>
-    new Promise((resolve) => {
+    lastValueFrom(
       zip(
         from(this.syncUser.getDeptUser(type, configId, departmentId)),
         from(this.syncUser.bindUserThirdParty(_type, provider, configId)),
         from(this.syncUser.noBindUser({ paging: false })),
-      )
-        .pipe(map((resp) => resp.map((item) => item.result)))
-        .subscribe((resp) => {
-          const [resp1, resp2, resp3] = resp;
+      ).pipe(
+        map((resp) => resp.map((i) => i.result)),
+        mergeMap((res) => {
+          const [resp1, resp2, resp3] = res;
           const list = resp1.map((item: { id: string; name: string }) => {
             const data =
               resp2.find(
@@ -112,13 +112,11 @@ class Service extends BaseService<ConfigItem> {
               userName: _user?.name,
             };
           });
-          resolve({
-            message: 'success',
-            result: list,
-            status: 200,
-          });
-        });
-    });
+          return list;
+        }),
+        toArray(),
+      ),
+    );
 }
 
 export default Service;

+ 156 - 40
src/pages/notice/Template/Detail/index.tsx

@@ -16,7 +16,14 @@ import {
   Switch,
 } from '@formily/antd';
 import type { Field } from '@formily/core';
-import { createForm, FormPath, onFieldInit, onFieldReact, onFieldValueChange } from '@formily/core';
+import {
+  createForm,
+  FormPath,
+  onFieldInit,
+  onFieldReact,
+  onFieldValueChange,
+  registerValidateRules,
+} from '@formily/core';
 import { createSchemaField, observer } from '@formily/react';
 import type { ISchema } from '@formily/json-schema';
 import styles from './index.less';
@@ -119,6 +126,14 @@ const Detail = observer(() => {
             form1.setFieldState('configId', async (state1) => {
               state1.dataSource = await getConfig(value);
             });
+
+            if (value === 'officialMessage') {
+              form1.setFieldState('template.message', (state5) => {
+                state5.decoratorProps = {
+                  tooltip: '服务号模版消息内容',
+                };
+              });
+            }
           });
           onFieldValueChange('configId', (field, form1) => {
             const value = field.value;
@@ -210,14 +225,70 @@ const Detail = observer(() => {
               });
             }
           });
+          onFieldValueChange('template.subject', (field, form1) => {
+            const value = (field as Field).value;
+            const titleList =
+              (typeof value === 'string' &&
+                value
+                  ?.match(pattern)
+                  ?.filter((i: string) => i)
+                  .map((item: string) => ({ id: item, type: 'string', format: '--' }))) ||
+              [];
+            form1.setFieldState('variableDefinitions', (state1) => {
+              state1.visible = !!titleList && titleList.length > 0;
+            });
+            if (form1.modified) {
+              form1.setValuesIn('variableDefinitions', titleList);
+            }
+          });
+          onFieldValueChange('template.markdown.title', (field, form1) => {
+            const value = (field as Field).value;
+            const titleList =
+              (typeof value === 'string' &&
+                value
+                  ?.match(pattern)
+                  ?.filter((i: string) => i)
+                  .map((item: string) => ({ id: item, type: 'string', format: '--' }))) ||
+              [];
+            form1.setFieldState('variableDefinitions', (state1) => {
+              state1.visible = !!titleList && titleList.length > 0;
+            });
+            if (form1.modified) {
+              form1.setValuesIn('variableDefinitions', titleList);
+            }
+          });
           onFieldValueChange('template.message', (field, form1) => {
             const value = (field as Field).value;
             const idList =
-              typeof value === 'string' &&
-              value
+              (typeof value === 'string' &&
+                value
+                  ?.match(pattern)
+                  ?.filter((i: string) => i)
+                  .map((item: string) => ({ id: item, type: 'string', format: '--' }))) ||
+              [];
+
+            if (id === 'email') {
+              const subject = field.query('template.subject');
+              const title = subject.value();
+              const titleList = title
                 ?.match(pattern)
                 ?.filter((i: string) => i)
                 .map((item: string) => ({ id: item, type: 'string', format: '--' }));
+              if (idList && titleList?.length > 0) {
+                idList.unshift(...titleList);
+              }
+            }
+            const _provider = field.query('provider').value();
+            if (_provider === 'dingTalkRobotWebHook') {
+              const title = field.query('template.markdown.title').value();
+              const titleList = title
+                ?.match(pattern)
+                ?.filter((i: string) => i)
+                .map((item: string) => ({ id: item, type: 'string', format: '--' }));
+              if (idList && titleList?.length > 0) {
+                idList.unshift(...titleList);
+              }
+            }
             form1.setFieldState('variableDefinitions', (state1) => {
               state1.visible = !!idList && idList.length > 0;
             });
@@ -244,30 +315,29 @@ const Detail = observer(() => {
                   { label: 'yyyy-MM-dd HH:mm:ss EE', value: 'yyyy-MM-dd HH:mm:ss EE' },
                   { label: 'yyyy-MM-dd HH:mm:ss zzz', value: 'yyyy-MM-dd HH:mm:ss zzz' },
                 ]);
-                format.setValue('string');
+                // format.setValue('string');
                 break;
               case 'string':
-                console.log('string');
                 format.setComponent(PreviewText.Input);
-                format.setValue('%s');
+                format.setValue('s%');
                 break;
               case 'number':
                 format.setComponent(Input);
                 format.setValue('%.xf');
                 break;
-              case 'file':
-                format.setComponent(Select);
-                format.setDataSource([
-                  { label: '视频', value: 'video' },
-                  { label: '图片', value: 'img' },
-                  { label: '全部', value: 'any' },
-                ]);
-                format.setValue('any');
-                break;
-              case 'other':
-                format.setComponent(PreviewText.Input);
-                format.setValue('--');
-                break;
+              // case 'file':
+              //   format.setComponent(Select);
+              //   format.setDataSource([
+              //     {label: '视频', value: 'video'},
+              //     {label: '图片', value: 'img'},
+              //     {label: '全部', value: 'any'},
+              //   ]);
+              //   format.setValue('any');
+              //   break;
+              // case 'other':
+              //   format.setComponent(PreviewText.Input);
+              //   format.setValue('--');
+              //   break;
             }
           });
         },
@@ -347,7 +417,20 @@ const Detail = observer(() => {
     }
   };
 
-  console.log(typeList[id][0]);
+  registerValidateRules({
+    batchCheckEmail(value) {
+      const regEmail = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
+      let error;
+      value.some((item: string) => {
+        if (!regEmail.test(item)) {
+          error = item;
+          return true;
+        }
+        return false;
+      });
+      return error ? `${error}邮件格式错误` : '';
+    },
+  });
   const schema: ISchema = {
     type: 'object',
     properties: {
@@ -399,6 +482,10 @@ const Detail = observer(() => {
         'x-component-props': {
           placeholder: '请选择绑定配置',
         },
+        required: true,
+        'x-decorator-props': {
+          tooltip: '使用固定的通知配置来发送此通知模版',
+        },
         'x-visible': id !== 'email',
       },
       template: {
@@ -416,8 +503,9 @@ const Detail = observer(() => {
                     'x-component': 'Input',
                     'x-decorator': 'FormItem',
                     'x-decorator-props': {
-                      tooltip: '请输入AgentID',
+                      tooltip: '应用唯一标识',
                     },
+                    required: true,
                     'x-component-props': {
                       placeholder: '请输入AgentID',
                     },
@@ -435,11 +523,11 @@ const Detail = observer(() => {
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '请输入收信人ID',
+                          tooltip: '如果不填写该字段,将在使用此模版发送通知时进行指定。',
                           gridSpan: 1,
                         },
                         'x-component-props': {
-                          placeholder: '请输入收信人ID',
+                          placeholder: '请选择收信人',
                         },
                       },
                       toParty: {
@@ -447,11 +535,11 @@ const Detail = observer(() => {
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '请输入收信部门ID',
+                          tooltip: '如果不填写该字段,将在使用此模版发送通知时进行指定。',
                           gridSpan: 1,
                         },
                         'x-component-props': {
-                          placeholder: '请输入收信部门ID',
+                          placeholder: '请选择收信部门',
                         },
                       },
                     },
@@ -461,7 +549,8 @@ const Detail = observer(() => {
                     'x-component': 'Select',
                     'x-decorator': 'FormItem',
                     'x-decorator-props': {
-                      tooltip: '标签推送',
+                      tooltip:
+                        '本企业微信的标签ID列表,最多支持100个,如果不填写该字段,将在使用此模版发送通知时进行指定',
                     },
                     'x-component-props': {
                       placeholder: '请输入标签推送,多个标签用,号分隔',
@@ -488,6 +577,9 @@ const Detail = observer(() => {
                     'x-component-props': {
                       placeholder: '请选择用户标签',
                     },
+                    'x-decorator-props': {
+                      tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
+                    },
                   },
                   layout: {
                     type: 'void',
@@ -507,6 +599,7 @@ const Detail = observer(() => {
                         },
                         'x-decorator-props': {
                           gridSpan: 1,
+                          tooltip: '微信公众号中配置的消息模版',
                         },
                       },
                       url: {
@@ -519,6 +612,7 @@ const Detail = observer(() => {
                         },
                         'x-decorator-props': {
                           gridSpan: 1,
+                          tooltip: '用于点击消息后进行页面跳转',
                         },
                       },
                     },
@@ -531,6 +625,9 @@ const Detail = observer(() => {
                     'x-component-props': {
                       // optionType: 'button'
                     },
+                    'x-decorator-props': {
+                      tooltip: '配置后点击通知消息将跳转到对应小程序',
+                    },
                     default: false,
                     enum: [
                       { label: '是', value: true },
@@ -558,6 +655,7 @@ const Detail = observer(() => {
                             },
                             'x-decorator-props': {
                               gridSpan: 1,
+                              tooltip: '小程序唯一性id',
                             },
                           },
                           miniProgramPath: {
@@ -570,6 +668,7 @@ const Detail = observer(() => {
                             },
                             'x-decorator-props': {
                               gridSpan: 1,
+                              tooltip: '用于点击消息之后跳转到小程序的具体页面',
                             },
                           },
                         },
@@ -592,6 +691,9 @@ const Detail = observer(() => {
                     'x-component-props': {
                       placeholder: '这里是回显内容',
                     },
+                    'x-decorator-props': {
+                      tooltip: '服务号消息模版标题',
+                    },
                   },
                 },
                 'x-reactions': {
@@ -618,7 +720,7 @@ const Detail = observer(() => {
                     'x-component': 'Input',
                     'x-decorator': 'FormItem',
                     'x-decorator-props': {
-                      tooltip: '请输入AgentID',
+                      tooltip: '应用唯一标识',
                     },
                     'x-component-props': {
                       placeholder: '请输入AgentID',
@@ -637,11 +739,11 @@ const Detail = observer(() => {
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '请输入收信人ID',
+                          tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
                           gridSpan: 1,
                         },
                         'x-component-props': {
-                          placeholder: '请输入收信人ID',
+                          placeholder: '请选择收信人',
                         },
                         'x-reactions': {
                           dependencies: ['configId'],
@@ -655,7 +757,7 @@ const Detail = observer(() => {
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '收信部门ID',
+                          tooltip: '如果不填写该字段,将在使用此模板发送通知时进行指定',
                           gridSpan: 1,
                         },
                         'x-component-props': {
@@ -794,9 +896,10 @@ const Detail = observer(() => {
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '请输入模版ID',
+                          tooltip: '阿里云内部分配的唯一ID标识',
                           gridSpan: 1,
                         },
+                        required: true,
                         'x-component-props': {
                           placeholder: '请输入模版ID',
                         },
@@ -806,7 +909,7 @@ const Detail = observer(() => {
                         'x-component': 'Input',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          tooltip: '请输入被叫号码',
+                          tooltip: '仅支持中国大陆号码',
                           gridSpan: 1,
                         },
                         'x-component-props': {
@@ -820,7 +923,7 @@ const Detail = observer(() => {
                     'x-component': 'Input',
                     'x-decorator': 'FormItem',
                     'x-decorator-props': {
-                      tooltip: '请输入被叫显号',
+                      tooltip: '必须是已购买的号码,用于呼叫号码显示',
                     },
                     'x-component-props': {
                       placeholder: '请输入被叫显号',
@@ -828,11 +931,19 @@ const Detail = observer(() => {
                   },
                   PlayTimes: {
                     title: '播放次数',
-                    'x-component': 'Input',
+                    'x-component': 'NumberPicker',
                     'x-decorator': 'FormItem',
                     'x-decorator-props': {
-                      tooltip: '请输入播放次数',
+                      tooltip: '语音文件的播放次数',
                     },
+                    default: 1,
+                    'x-validator': [
+                      {
+                        min: 1,
+                        max: 3,
+                        message: '仅支持1~3次',
+                      },
+                    ],
                     'x-component-props': {
                       placeholder: '请输入播放次数',
                     },
@@ -877,6 +988,7 @@ const Detail = observer(() => {
                           tooltip: '请输入收信人',
                           gridSpan: 1,
                         },
+                        'x-validator': ['phone'],
                         'x-component-props': {
                           placeholder: '请输入收信人',
                         },
@@ -913,22 +1025,27 @@ const Detail = observer(() => {
                 'x-decorator': 'FormItem',
                 title: '标题',
                 'x-decorator-props': {
-                  tip: '邮件标题',
+                  tooltip: '邮件标题',
                 },
+                required: true,
                 'x-component-props': {
                   placeholder: '请输入标题',
                 },
               },
               sendTo: {
-                'x-component': 'Input.TextArea',
+                'x-component': 'Select',
                 'x-decorator': 'FormItem',
                 title: '收件人',
                 'x-decorator-props': {
-                  tip: '多个收件人用换行分隔 \n最大支持1000个号码',
+                  tooltip: '多个收件人用换行分隔 \n最大支持1000个号码',
                 },
                 'x-component-props': {
+                  mode: 'tags',
                   placeholder: '请输入收件人邮箱,多个收件人用换行分隔',
                 },
+                'x-validator': {
+                  batchCheckEmail: true,
+                },
               },
               attachments: {
                 type: 'array',
@@ -939,6 +1056,7 @@ const Detail = observer(() => {
                   style: {
                     width: '100%',
                   },
+                  tooltip: '附件只输入文件名称将在发送邮件时进行文件上传',
                 },
                 items: {
                   type: 'object',
@@ -1061,8 +1179,6 @@ const Detail = observer(() => {
                     { label: '字符串', value: 'string' },
                     { label: '时间', value: 'date' },
                     { label: '数字', value: 'number' },
-                    { label: '文件', value: 'file' },
-                    { label: '其他', value: 'other' },
                   ],
                 },
               },