ソースを参照

feat(merge): merge xyh

Next xyh
Lind 4 年 前
コミット
93d503852b
82 ファイル変更1334 行追加2660 行削除
  1. 7 7
      package.json
  2. BIN
      public/images/alarm/background.png
  3. BIN
      public/images/alarm/device.png
  4. BIN
      public/images/alarm/log.png
  5. BIN
      public/images/alarm/org.png
  6. BIN
      public/images/alarm/other.png
  7. BIN
      public/images/alarm/product.png
  8. BIN
      public/images/metadata-map.png
  9. BIN
      public/images/notice/doc/template/weixin-official/02-mini-Program-Appid.png
  10. BIN
      public/images/scene.png
  11. 13 13
      src/components/BaseCrud/index.tsx
  12. 1 1
      src/components/CheckButton/index.less
  13. 5 5
      src/components/Metadata/ArrayParam/index.tsx
  14. 4 4
      src/components/PermissionButton/index.tsx
  15. 4 4
      src/components/ProTableCard/CardItems/AccessConfig/index.tsx
  16. 49 0
      src/components/ProTableCard/CardItems/scene.tsx
  17. 4 4
      src/hooks/permission/index.ts
  18. 8 8
      src/pages/device/Category/Save/index.tsx
  19. 4 4
      src/pages/device/Instance/Detail/Diagnose/index.tsx
  20. 8 12
      src/pages/device/Instance/Detail/Info/index.tsx
  21. 12 23
      src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx
  22. 0 160
      src/pages/device/Instance/Detail/Reation/Edit.tsx
  23. 0 88
      src/pages/device/Instance/Detail/Reation/index.tsx
  24. 7 7
      src/pages/device/Instance/Save/index.tsx
  25. 0 14
      src/pages/device/Instance/service.ts
  26. 0 1
      src/pages/device/Instance/typings.d.ts
  27. 1 224
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  28. 2 24
      src/pages/device/components/Metadata/Cat/index.tsx
  29. 1 1
      src/pages/device/components/Metadata/index.tsx
  30. 25 31
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  31. 1 1
      src/pages/link/AccessConfig/Detail/Provider/index.tsx
  32. 2 3
      src/pages/link/AccessConfig/service.ts
  33. 20 34
      src/pages/notice/Template/Debug/index.tsx
  34. 14 31
      src/pages/notice/Template/Detail/doc/WeixinApp.tsx
  35. 15 17
      src/pages/notice/Template/Detail/index.tsx
  36. 4 27
      src/pages/notice/Template/Log/index.tsx
  37. 0 1
      src/pages/notice/Template/index.tsx
  38. 0 1
      src/pages/notice/Template/typings.d.ts
  39. 28 107
      src/pages/rule-engine/Alarm/Config/index.tsx
  40. 0 26
      src/pages/rule-engine/Alarm/Config/service.ts
  41. 0 25
      src/pages/rule-engine/Alarm/Config/typing.d.ts
  42. 0 8
      src/pages/rule-engine/Alarm/Configuration/Save/index.less
  43. 0 200
      src/pages/rule-engine/Alarm/Configuration/Save/index.tsx
  44. 0 117
      src/pages/rule-engine/Alarm/Configuration/index.tsx
  45. 0 17
      src/pages/rule-engine/Alarm/Configuration/service.ts
  46. 0 9
      src/pages/rule-engine/Alarm/Configuration/typings.d.ts
  47. 0 49
      src/pages/rule-engine/Alarm/Log/Detail/Info.tsx
  48. 0 133
      src/pages/rule-engine/Alarm/Log/Detail/index.tsx
  49. 0 53
      src/pages/rule-engine/Alarm/Log/SolveComponent/index.tsx
  50. 0 95
      src/pages/rule-engine/Alarm/Log/SolveLog/index.tsx
  51. 0 135
      src/pages/rule-engine/Alarm/Log/TabComponent/index.less
  52. 0 305
      src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx
  53. 5 19
      src/pages/rule-engine/Alarm/Log/index.tsx
  54. 1 32
      src/pages/rule-engine/Alarm/Log/model.ts
  55. 0 35
      src/pages/rule-engine/Alarm/Log/service.ts
  56. 0 38
      src/pages/rule-engine/Alarm/Log/typings.d.ts
  57. 162 0
      src/pages/rule-engine/Scene/Save/action/action.tsx
  58. 18 0
      src/pages/rule-engine/Scene/Save/action/index.less
  59. 26 0
      src/pages/rule-engine/Scene/Save/action/index.tsx
  60. 60 0
      src/pages/rule-engine/Scene/Save/action/messageContent.tsx
  61. 28 0
      src/pages/rule-engine/Scene/Save/action/service.ts
  62. 7 0
      src/pages/rule-engine/Scene/Save/components/ItemGroup/index.less
  63. 10 0
      src/pages/rule-engine/Scene/Save/components/ItemGroup/index.tsx
  64. 49 0
      src/pages/rule-engine/Scene/Save/components/TimeSelect/index.tsx
  65. 89 0
      src/pages/rule-engine/Scene/Save/components/TimingTrigger/index.tsx
  66. 24 0
      src/pages/rule-engine/Scene/Save/components/TriggerWay/index.less
  67. 65 0
      src/pages/rule-engine/Scene/Save/components/TriggerWay/index.tsx
  68. 4 0
      src/pages/rule-engine/Scene/Save/components/index.ts
  69. 67 1
      src/pages/rule-engine/Scene/Save/index.tsx
  70. 10 0
      src/pages/rule-engine/Scene/Save/trigger/index.tsx
  71. 360 0
      src/pages/rule-engine/Scene/Save2/index.tsx
  72. 12 4
      src/pages/rule-engine/Scene/index.tsx
  73. 6 3
      src/pages/rule-engine/Scene/typings.d.ts
  74. 4 1
      src/pages/system/Menu/Detail/edit.tsx
  75. 0 251
      src/pages/system/Relationship/Save/index.tsx
  76. 0 146
      src/pages/system/Relationship/index.tsx
  77. 0 12
      src/pages/system/Relationship/service.ts
  78. 0 12
      src/pages/system/Relationship/typings.d.ts
  79. 3 2
      src/pages/system/User/ResetPassword/index.tsx
  80. 9 2
      src/utils/menu/index.ts
  81. 2 0
      src/utils/menu/router.ts
  82. 74 73
      yarn.lock

+ 7 - 7
package.json

@@ -62,13 +62,13 @@
     "@ant-design/pro-descriptions": "^1.6.8",
     "@ant-design/pro-form": "^1.18.3",
     "@ant-design/pro-layout": "^6.27.2",
-    "@formily/antd": "2.0.19",
-    "@formily/core": "2.0.19",
-    "@formily/json-schema": "2.0.19",
-    "@formily/react": "2.0.19",
-    "@formily/reactive": "2.0.19",
-    "@formily/reactive-react": "2.0.19",
-    "@formily/shared": "2.0.19",
+    "@formily/antd": "2.0.0-rc.17",
+    "@formily/core": "2.0.0-rc.17",
+    "@formily/json-schema": "2.0.0-rc.17",
+    "@formily/react": "2.0.0-rc.17",
+    "@formily/reactive": "2.0.0-rc.17",
+    "@formily/reactive-react": "2.0.0-rc.17",
+    "@formily/shared": "2.0.0-rc.17",
     "@jetlinks/pro-list": "^1.10.8",
     "@jetlinks/pro-table": "^2.63.11",
     "@liveqing/liveplayer": "^2.6.4",

BIN
public/images/alarm/background.png


BIN
public/images/alarm/device.png


BIN
public/images/alarm/log.png


BIN
public/images/alarm/org.png


BIN
public/images/alarm/other.png


BIN
public/images/alarm/product.png


BIN
public/images/metadata-map.png


BIN
public/images/notice/doc/template/weixin-official/02-mini-Program-Appid.png


BIN
public/images/scene.png


+ 13 - 13
src/components/BaseCrud/index.tsx

@@ -1,22 +1,22 @@
-import { useIntl } from '@@/plugin-locale/localeExports';
-import { Button, Tooltip } from 'antd';
-import type { ActionType, ProColumns, RequestData } from '@jetlinks/pro-table';
+import {useIntl} from '@@/plugin-locale/localeExports';
+import {Button, Tooltip} from 'antd';
+import type {ActionType, ProColumns, RequestData} from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 
-import { PlusOutlined } from '@ant-design/icons';
+import {PlusOutlined} from '@ant-design/icons';
 import type BaseService from '@/utils/BaseService';
 import * as React from 'react';
-import { useRef, useState } from 'react';
+import {useRef, useState} from 'react';
 import Save from '@/components/BaseCrud/save';
-import type { ISchema } from '@formily/json-schema';
-import { CurdModel } from '@/components/BaseCrud/model';
-import type { ISchemaFieldProps } from '@formily/react/lib/types';
-import type { ModalProps } from 'antd/lib/modal/Modal';
-import type { TablePaginationConfig } from 'antd/lib/table/interface';
-import type { Form } from '@formily/core';
+import type {ISchema} from '@formily/json-schema';
+import {CurdModel} from '@/components/BaseCrud/model';
+import type {ISchemaFieldProps} from '@formily/react/lib/types';
+import type {ModalProps} from 'antd/lib/modal/Modal';
+import type {TablePaginationConfig} from 'antd/lib/table/interface';
+import type {Form} from '@formily/core';
 import SearchComponent from '@/components/SearchComponent';
-import type { ProFormInstance } from '@ant-design/pro-form';
-import type { SearchConfig } from '@ant-design/pro-form/lib/components/Submitter';
+import type {ProFormInstance} from '@ant-design/pro-form';
+import type {SearchConfig} from '@ant-design/pro-form/lib/components/Submitter';
 
 export type Option = {
   model: 'edit' | 'preview' | 'add';

+ 1 - 1
src/components/CheckButton/index.less

@@ -1,4 +1,4 @@
-@import '../../../node_modules/antd/lib/style/themes/variable';
+@import '~antd/es/style/themes/default.less';
 
 .box {
   display: flex;

+ 5 - 5
src/components/Metadata/ArrayParam/index.tsx

@@ -1,9 +1,9 @@
-import { createSchemaField } from '@formily/react';
-import { Editable, FormItem, FormLayout, Input, NumberPicker, Select } from '@formily/antd';
-import type { ISchema } from '@formily/json-schema';
+import {createSchemaField} from '@formily/react';
+import {Editable, FormItem, FormLayout, Input, NumberPicker, Select} from '@formily/antd';
+import type {ISchema} from '@formily/json-schema';
 import './index.less';
-import { DataTypeList, DateTypeList, FileTypeList } from '@/pages/device/data';
-import { Store } from 'jetlinks-store';
+import {DataTypeList, DateTypeList, FileTypeList} from '@/pages/device/data';
+import {Store} from 'jetlinks-store';
 import JsonParam from '@/components/Metadata/JsonParam';
 import EnumParam from '@/components/Metadata/EnumParam';
 import BooleanEnum from '@/components/Metadata/BooleanParam';

+ 4 - 4
src/components/PermissionButton/index.tsx

@@ -1,8 +1,8 @@
-import type { ButtonProps, PopconfirmProps, TooltipProps } from 'antd';
-import { Button, Popconfirm, Tooltip } from 'antd';
+import type {ButtonProps, PopconfirmProps, TooltipProps} from 'antd';
+import {Button, Popconfirm, Tooltip} from 'antd';
 import usePermissions from '@/hooks/permission';
-import { useCallback } from 'react';
-import { useIntl } from '@@/plugin-locale/localeExports';
+import {useCallback} from 'react';
+import {useIntl} from '@@/plugin-locale/localeExports';
 
 interface PermissionButtonProps extends ButtonProps {
   tooltip?: TooltipProps;

+ 4 - 4
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
-import { StatusColorEnum } from '@/components/BadgeStatus';
-import { TableCard } from '@/components';
+import {StatusColorEnum} from '@/components/BadgeStatus';
+import {TableCard} from '@/components';
 import '@/style/common.less';
-import { Badge, Tooltip } from 'antd';
-import type { AccessItem } from '@/pages/link/AccessConfig/typings';
+import {Badge, Tooltip} from 'antd';
+import type {AccessItem} from '@/pages/link/AccessConfig/typings';
 import './index.less';
 import classNames from 'classnames';
 

+ 49 - 0
src/components/ProTableCard/CardItems/scene.tsx

@@ -0,0 +1,49 @@
+import React from 'react';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import { TableCard } from '@/components';
+import '@/style/common.less';
+import '../index.less';
+import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
+
+export interface DeviceCardProps extends SceneItem {
+  tools: React.ReactNode[];
+}
+
+const defaultImage = require('/public/images/scene.png');
+
+export default (props: DeviceCardProps) => {
+  return (
+    <TableCard
+      showMask={false}
+      actions={props.tools}
+      status={props.state.value}
+      statusText={props.state.text}
+      statusNames={{
+        online: StatusColorEnum.processing,
+        offline: StatusColorEnum.error,
+        notActive: StatusColorEnum.warning,
+      }}
+    >
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={defaultImage} alt={''} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+          </div>
+          <div className={'card-item-content'}>
+            <div>
+              <label>触发方式</label>
+              <div className={'ellipsis'}>{'test'}</div>
+            </div>
+            <div>
+              <label>说明</label>
+              <div className={'ellipsis'}>{props.describe || '--'}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </TableCard>
+  );
+};

+ 4 - 4
src/hooks/permission/index.ts

@@ -1,7 +1,7 @@
-import { useEffect, useState } from 'react';
-import type { BUTTON_PERMISSION, MENUS_CODE_TYPE } from '@/utils/menu/router';
-import { BUTTON_PERMISSION_ENUM } from '@/utils/menu/router';
-import { MENUS_BUTTONS_CACHE } from '@/utils/menu';
+import {useEffect, useState} from 'react';
+import type {BUTTON_PERMISSION, MENUS_CODE_TYPE} from '@/utils/menu/router';
+import {BUTTON_PERMISSION_ENUM} from '@/utils/menu/router';
+import {MENUS_BUTTONS_CACHE} from '@/utils/menu';
 
 export type permissionKeyType = keyof typeof BUTTON_PERMISSION_ENUM;
 export type permissionType = Record<permissionKeyType, boolean>;

+ 8 - 8
src/pages/device/Category/Save/index.tsx

@@ -13,16 +13,16 @@ import {
   Upload,
 } from '@formily/antd';
 import React from 'react';
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
+import {createForm} from '@formily/core';
+import {createSchemaField} from '@formily/react';
 import FUpload from '@/components/Upload';
 import * as ICONS from '@ant-design/icons';
-import { message, Modal } from 'antd';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import type { ISchema } from '@formily/json-schema';
-import type { CategoryItem } from '@/pages/visualization/Category/typings';
-import { service, state } from '@/pages/device/Category';
-import type { Response } from '@/utils/typings';
+import {message, Modal} from 'antd';
+import {useIntl} from '@@/plugin-locale/localeExports';
+import type {ISchema} from '@formily/json-schema';
+import type {CategoryItem} from '@/pages/visualization/Category/typings';
+import {service, state} from '@/pages/device/Category';
+import type {Response} from '@/utils/typings';
 
 interface Props {
   visible: boolean;

+ 4 - 4
src/pages/device/Instance/Detail/Diagnose/index.tsx

@@ -1,11 +1,11 @@
-import { Badge, Card, Col, Row } from 'antd';
-import type { ReactNode } from 'react';
-import { useEffect, useState } from 'react';
+import {Badge, Card, Col, Row} from 'antd';
+import type {ReactNode} from 'react';
+import {useEffect, useState} from 'react';
 import Message from './Message';
 import Status from './Status';
 import './index.less';
 import classNames from 'classnames';
-import { Store } from 'jetlinks-store';
+import {Store} from 'jetlinks-store';
 
 interface ListProps {
   key: string;

+ 8 - 12
src/pages/device/Instance/Detail/Info/index.tsx

@@ -1,16 +1,15 @@
-import { Card, Descriptions } from 'antd';
-import { InstanceModel } from '@/pages/device/Instance';
+import {Card, Descriptions} from 'antd';
+import {InstanceModel} from '@/pages/device/Instance';
 import moment from 'moment';
-import { observer } from '@formily/react';
-import { useIntl } from '@@/plugin-locale/localeExports';
+import {observer} from '@formily/react';
+import {useIntl} from '@@/plugin-locale/localeExports';
 import Config from '@/pages/device/Instance/Detail/Config';
-import Reation from '@/pages/device/Instance/Detail/Reation';
 import Save from '../../Save';
-import { useState } from 'react';
-import type { DeviceInstance } from '../../typings';
-import { EditOutlined } from '@ant-design/icons';
+import {useState} from 'react';
+import type {DeviceInstance} from '../../typings';
+import {EditOutlined} from '@ant-design/icons';
 import Tags from '@/pages/device/Instance/Detail/Tags';
-import { PermissionButton } from '@/components';
+import {PermissionButton} from '@/components';
 
 const Info = observer(() => {
   const intl = useIntl();
@@ -116,9 +115,6 @@ const Info = observer(() => {
         </Descriptions>
         <Config />
         {InstanceModel.detail?.tags && InstanceModel.detail?.tags.length > 0 && <Tags />}
-        {InstanceModel.detail?.relations && InstanceModel.detail?.relations.length > 0 && (
-          <Reation />
-        )}
       </Card>
       <Save
         model={'edit'}

+ 12 - 23
src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx

@@ -27,7 +27,6 @@ interface EditableCellProps {
   dataIndex: string;
   record: any;
   list: any[];
-  properties: any[];
   handleSave: (record: any) => void;
 }
 
@@ -38,17 +37,15 @@ const EditableCell = ({
   dataIndex,
   record,
   list,
-  properties,
   handleSave,
   ...restProps
 }: EditableCellProps) => {
   const form: any = useContext(EditableContext);
-  const [temp, setTemp] = useState<any>({});
 
   const save = async () => {
     try {
       const values = await form.validateFields();
-      handleSave({ ...record, originalId: values?.originalId });
+      handleSave({ ...record, metadataId: values?.metadataId });
     } catch (errInfo) {
       console.log('Save failed:', errInfo);
     }
@@ -57,7 +54,6 @@ const EditableCell = ({
   useEffect(() => {
     if (record) {
       form.setFieldsValue({ [dataIndex]: record[dataIndex] });
-      setTemp(properties.find((i) => i.id === record.originalId));
     }
   }, [record]);
 
@@ -75,11 +71,6 @@ const EditableCell = ({
           }
         >
           <Select.Option value={record.metadataId}>使用原始属性</Select.Option>
-          {record.originalId !== record.metadataId && (
-            <Select.Option value={record.originalId}>
-              {temp?.name}({temp?.id})
-            </Select.Option>
-          )}
           {list.length > 0 &&
             list.map((item: any) => (
               <Select.Option key={item?.id} value={item?.id}>
@@ -107,7 +98,7 @@ const EditableTable = (props: Props) => {
     },
     {
       title: '设备上报属性',
-      dataIndex: 'originalId',
+      dataIndex: 'metadataId',
       width: '30%',
       editable: true,
     },
@@ -145,7 +136,7 @@ const EditableTable = (props: Props) => {
     },
   };
 
-  const initData = async (lists: any[]) => {
+  const initData = async () => {
     let resp = null;
     if (props.type === 'device') {
       resp = await service.queryDeviceMetadata(props.data.id);
@@ -156,10 +147,10 @@ const EditableTable = (props: Props) => {
       const data = resp.result;
       const obj: any = {};
       data.map((i: any) => {
-        obj[i?.metadataId] = i;
+        obj[i?.originalId] = i;
       });
-      if (lists.length > 0) {
-        setPmList(lists.filter((i) => !_.map(data, 'originalId').includes(i.id)));
+      if (protocolMetadata.length > 0) {
+        setPmList(protocolMetadata.filter((i) => !_.map(data, 'metadataId').includes(i.id)));
       } else {
         setPmList([]);
       }
@@ -193,9 +184,8 @@ const EditableTable = (props: Props) => {
         )
         .then((resp) => {
           if (resp.status === 200) {
-            const list = JSON.parse(resp.result || '{}')?.properties || [];
-            setProtocolMetadata(list);
-            initData(list);
+            setProtocolMetadata(JSON.parse(resp.result || '{}')?.properties || []);
+            initData();
           }
         });
     }
@@ -205,21 +195,21 @@ const EditableTable = (props: Props) => {
     const newData = [...dataSource.data];
     const index = newData.findIndex((item) => row.id === item.id);
     const item = newData[index];
-    if (item?.originalId !== row?.originalId) {
+    if (item?.metadataId !== row?.metadataId) {
       const resp = await service[
         props.type === 'device' ? 'saveDeviceMetadata' : 'saveProductMetadata'
       ](props.data?.id, [
         {
           metadataType: 'property',
-          metadataId: row.metadataId,
-          originalId: row.metadataId !== row.originalId ? row.originalId : '',
+          metadataId: row.metadataId === row.id ? row.metadataId : row.id,
+          originalId: row.metadataId === row.id ? row.id : '',
           others: {},
         },
       ]);
       if (resp.status === 200) {
         message.success('操作成功!');
         // 刷新
-        initData(protocolMetadata);
+        initData();
       }
     }
   };
@@ -263,7 +253,6 @@ const EditableTable = (props: Props) => {
         dataIndex: col.dataIndex,
         title: col.title,
         list: pmList,
-        properties: protocolMetadata,
         handleSave: handleSave,
       }),
     };

+ 0 - 160
src/pages/device/Instance/Detail/Reation/Edit.tsx

@@ -1,160 +0,0 @@
-import type { Field } from '@formily/core';
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
-import { InstanceModel, service } from '@/pages/device/Instance';
-import type { ISchema } from '@formily/json-schema';
-import { Form, FormGrid, FormItem, Select, PreviewText } from '@formily/antd';
-import { useParams } from 'umi';
-import { Button, Drawer, message, Space } from 'antd';
-import { action } from '@formily/reactive';
-import type { Response } from '@/utils/typings';
-import { useEffect, useState } from 'react';
-
-interface Props {
-  close: () => void;
-  data: any[];
-}
-
-const Edit = (props: Props) => {
-  const { data } = props;
-  const params = useParams<{ id: string }>();
-  const id = InstanceModel.detail?.id || params?.id;
-  const [initData, setInitData] = useState<any>({});
-
-  const getUsers = () => service.queryUserListNopaging();
-
-  const useAsyncDataSource = (api: any) => (field: Field) => {
-    field.loading = true;
-    api(field).then(
-      action.bound!((resp: Response<any>) => {
-        field.dataSource = resp.result?.map((item: Record<string, unknown>) => ({
-          ...item,
-          label: item.name,
-          value: JSON.stringify({
-            id: item.id,
-            name: item.name,
-          }),
-        }));
-        field.loading = false;
-      }),
-    );
-  };
-
-  const form = createForm({
-    validateFirst: true,
-    initialValues: initData,
-  });
-
-  const SchemaField = createSchemaField({
-    components: {
-      FormItem,
-      Select,
-      FormGrid,
-      PreviewText,
-    },
-  });
-
-  const configToSchema = (list: any[]) => {
-    const config = {};
-    list.forEach((item) => {
-      config[item.relation] = {
-        type: 'string',
-        title: item.relationName,
-        'x-decorator': 'FormItem',
-        'x-component': 'Select',
-        'x-component-props': {
-          placeholder: '请选择关联方',
-          showSearch: true,
-          showArrow: true,
-          mode: 'multiple',
-          filterOption: (input: string, option: any) =>
-            option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
-        },
-        'x-reactions': ['{{useAsyncDataSource(getUsers)}}'],
-      };
-    });
-    return config;
-  };
-
-  const renderConfigCard = () => {
-    const itemSchema: ISchema = {
-      type: 'object',
-      properties: {
-        grid: {
-          type: 'void',
-          'x-component': 'FormGrid',
-          'x-component-props': {
-            minColumns: [1],
-            maxColumns: [1],
-          },
-          properties: configToSchema(data),
-        },
-      },
-    };
-
-    return (
-      <>
-        <PreviewText.Placeholder value="-">
-          <Form form={form} layout="vertical">
-            <SchemaField schema={itemSchema} scope={{ useAsyncDataSource, getUsers }} />
-          </Form>
-        </PreviewText.Placeholder>
-      </>
-    );
-  };
-
-  useEffect(() => {
-    const obj: any = {};
-    (props?.data || []).map((item: any) => {
-      obj[item.relation] = [...(item?.related || []).map((i: any) => JSON.stringify(i))];
-    });
-    setInitData(obj);
-  }, [props.data]);
-
-  return (
-    <Drawer
-      title="编辑"
-      placement="right"
-      onClose={() => {
-        props.close();
-      }}
-      visible
-      extra={
-        <Space>
-          <Button
-            type="primary"
-            onClick={async () => {
-              const values = (await form.submit()) as any;
-              if (Object.keys(values).length > 0) {
-                const param: any[] = [];
-                Object.keys(values).forEach((key) => {
-                  const item = data.find((i) => i.relation === key);
-                  const items = (values[key] || []).map((i: string) => JSON.parse(i));
-                  if (item) {
-                    param.push({
-                      relatedType: 'user',
-                      relation: item.relation,
-                      description: '',
-                      related: [...items],
-                    });
-                  }
-                });
-                const resp = await service.saveRelations(id || '', param);
-                if (resp.status === 200) {
-                  message.success('操作成功!');
-                  props.close();
-                }
-              }
-            }}
-          >
-            保存
-          </Button>
-        </Space>
-      }
-    >
-      {renderConfigCard()}
-    </Drawer>
-  );
-};
-
-export default Edit;

+ 0 - 88
src/pages/device/Instance/Detail/Reation/index.tsx

@@ -1,88 +0,0 @@
-import { Descriptions, Tooltip } from 'antd';
-import { InstanceModel, service } from '@/pages/device/Instance';
-import { useEffect, useState } from 'react';
-import { history, useParams } from 'umi';
-import { EditOutlined, QuestionCircleOutlined } from '@ant-design/icons';
-import Edit from './Edit';
-import { PermissionButton } from '@/components';
-import _ from 'lodash';
-
-const Reation = () => {
-  const params = useParams<{ id: string }>();
-  useEffect(() => {
-    const id = InstanceModel.current?.id || params.id;
-    if (id) {
-      service.getConfigMetadata(id).then((response) => {
-        InstanceModel.config = response?.result;
-      });
-    } else {
-      history.goBack();
-    }
-  }, []);
-
-  const [data, setData] = useState<any[]>([]);
-  const [visible, setVisible] = useState<boolean>(false);
-  const { permission } = PermissionButton.usePermission('device/Instance');
-
-  const id = InstanceModel.detail?.id || params?.id;
-
-  const getDetail = () => {
-    service.detail(id || '').then((resp) => {
-      if (resp.status === 200) {
-        InstanceModel.detail = { id, ...resp.result };
-      }
-    });
-  };
-
-  useEffect(() => {
-    if (id) {
-      setData(InstanceModel.detail?.relations || []);
-    }
-  }, [id]);
-
-  return (
-    <div style={{ width: '100%', marginTop: '20px' }}>
-      <Descriptions
-        style={{ marginBottom: 20 }}
-        bordered
-        column={3}
-        size="small"
-        title={
-          <span>
-            关系信息
-            <PermissionButton
-              isPermission={permission.update}
-              type="link"
-              onClick={async () => {
-                setVisible(true);
-              }}
-            >
-              <EditOutlined />
-              编辑
-              <Tooltip title={`管理设备与其他业务的关联关系,关系来源于关系配置`}>
-                <QuestionCircleOutlined />
-              </Tooltip>
-            </PermissionButton>
-          </span>
-        }
-      >
-        {(data || [])?.map((item: any) => (
-          <Descriptions.Item span={1} label={item.relationName} key={item.objectId}>
-            {_.map(item?.related || [], 'name').join(',')}
-          </Descriptions.Item>
-        ))}
-      </Descriptions>
-      {visible && (
-        <Edit
-          data={data || []}
-          close={() => {
-            setVisible(false);
-            getDetail();
-          }}
-        />
-      )}
-    </div>
-  );
-};
-
-export default Reation;

+ 7 - 7
src/pages/device/Instance/Save/index.tsx

@@ -1,10 +1,10 @@
-import { Col, Form, Input, message, Modal, Row, Select } from 'antd';
-import { service } from '@/pages/device/Instance';
-import type { DeviceInstance } from '../typings';
-import { useEffect, useState } from 'react';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import { UploadImage } from '@/components';
-import { debounce } from 'lodash';
+import {Col, Form, Input, message, Modal, Row, Select} from 'antd';
+import {service} from '@/pages/device/Instance';
+import type {DeviceInstance} from '../typings';
+import {useEffect, useState} from 'react';
+import {useIntl} from '@@/plugin-locale/localeExports';
+import {UploadImage} from '@/components';
+import {debounce} from 'lodash';
 
 interface Props {
   visible: boolean;

+ 0 - 14
src/pages/device/Instance/service.ts

@@ -251,20 +251,6 @@ class Service extends BaseService<DeviceInstance> {
 
   //接入方式
   public queryGatewayList = () => request(`/${SystemConst.API_BASE}/gateway/device/providers`);
-  // 保存设备关系
-  public saveRelations = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/device/instance/${id}/relations`, {
-      method: 'PATCH',
-      data,
-    });
-  // 查询用户
-  public queryUserListNopaging = () =>
-    request(`/${SystemConst.API_BASE}/user/_query/no-paging`, {
-      method: 'POST',
-      data: {
-        paging: false,
-      },
-    });
 }
 
 export default Service;

+ 0 - 1
src/pages/device/Instance/typings.d.ts

@@ -29,7 +29,6 @@ export type DeviceInstance = {
   orgId: string;
   orgName: string;
   configuration: Record<string, any>;
-  relations?: any[];
   cachedConfiguration: any;
   transport: string;
   protocol: string;

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

@@ -2,14 +2,11 @@ import { Button, Drawer, Dropdown, Menu, message } from 'antd';
 import { createSchemaField, observer } from '@formily/react';
 import MetadataModel from '../model';
 import type { Field, IFieldState } from '@formily/core';
-import { createForm, onFieldReact, registerValidateRules } from '@formily/core';
+import { createForm, registerValidateRules } from '@formily/core';
 import {
   ArrayItems,
-  Checkbox,
-  DatePicker,
   Editable,
   Form,
-  FormGrid,
   FormItem,
   FormLayout,
   Input,
@@ -57,44 +54,6 @@ const Edit = observer((props: Props) => {
     () =>
       createForm({
         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',
-              float: 'NumberPicker',
-              double: 'NumberPicker',
-              number: 'NumberPicker',
-              date: 'DatePicker',
-              boolean: 'Select',
-            };
-
-            form1.setFieldState('expands.metrics.*.edit.space.value.*', (state) => {
-              state.componentType = componentMap[type.value] || 'Input';
-              if (type.value === 'date') {
-                state.componentProps = {
-                  showTime: true,
-                };
-              } else if (type.value === 'boolean') {
-                state.componentType = 'Select';
-                // 获取 boolean配置的值
-                const values = form1.getValuesIn('valueType');
-                state.dataSource = [
-                  { label: values.trueText, value: values.trueValue },
-                  { label: values.falseText, value: values.falseValue },
-                ];
-              }
-            });
-          });
-          /// 处理Boolean 类型
-          // expands.metrics.0.edit.space.value.0 路径
-          // const metricsPath = field.query('expands.metrics.value.0');
-          // form.setValuesIn('expands.metrics.value.0', 'testtttt')
-        },
       }),
     [],
   );
@@ -143,9 +102,6 @@ const Edit = observer((props: Props) => {
       BooleanEnum,
       ConfigParam,
       FRuleEditor,
-      Checkbox,
-      FormGrid,
-      DatePicker,
     },
     scope: {
       async asyncOtherConfig(field: Field) {
@@ -660,185 +616,6 @@ const Edit = observer((props: Props) => {
             'x-component': 'ConfigParam',
             'x-reactions': '{{asyncOtherConfig}}',
           },
-          // 指标
-          metrics: {
-            type: 'array',
-            'x-component': 'ArrayItems',
-            'x-decorator': 'FormItem',
-            title: '指标配置',
-            items: {
-              type: 'object',
-              'x-decorator': 'ArrayItems.Item',
-              properties: {
-                left: {
-                  type: 'void',
-                  'x-component': 'Space',
-                  properties: {
-                    sort: {
-                      type: 'void',
-                      'x-decorator': 'FormItem',
-                      'x-component': 'ArrayItems.SortHandle',
-                    },
-                    index: {
-                      type: 'void',
-                      'x-decorator': 'FormItem',
-                      'x-component': 'ArrayItems.Index',
-                    },
-                  },
-                },
-                edit: {
-                  type: 'void',
-                  'x-component': 'Editable.Popover',
-                  title: '指标数据',
-                  properties: {
-                    id: {
-                      // 标识
-                      title: '标识',
-                      'x-decorator': 'FormItem',
-                      'x-component': 'Input',
-                      'x-decorator-props': {
-                        labelAlign: 'left',
-                        layout: 'vertical',
-                      },
-                    },
-                    name: {
-                      // 名称
-                      title: '名称',
-                      'x-decorator': 'FormItem',
-                      'x-component': 'Input',
-                      'x-decorator-props': {
-                        labelAlign: 'left',
-                        layout: 'vertical',
-                      },
-                    },
-                    space: {
-                      type: 'void',
-                      title: '指标值',
-                      'x-decorator': 'FormItem',
-                      'x-component': 'FormGrid',
-                      'x-decorator-props': {
-                        labelAlign: 'left',
-                        layout: 'vertical',
-                      },
-                      'x-component-props': {
-                        maxColumns: 12,
-                        minColumns: 12,
-                      },
-                      properties: {
-                        'value[0]': {
-                          'x-decorator': 'FormItem',
-                          'x-component': 'Input',
-                          'x-decorator-props': {
-                            gridSpan: 5,
-                          },
-                          'x-reactions': {
-                            dependencies: ['..range', 'valueType.type'],
-                            fulfill: {
-                              state: {
-                                decoratorProps: {
-                                  gridSpan: '{{!!$deps[0]?5:$deps[1]==="boolean"?12:10}}',
-                                },
-                                componentType:
-                                  '{{["int","long","double","float"].includes($deps[1])?"NumberPicker":["date"].includes($deps[1])?"DatePicker":"Input"}}',
-                              },
-                            },
-                          },
-                          // 根据数据类型来渲染不同的组件
-                        },
-                        'value[1]': {
-                          title: '~',
-                          'x-decorator': 'FormItem',
-                          'x-component': 'Input',
-                          'x-decorator-props': {
-                            gridSpan: 5,
-                          },
-                          'x-reactions': [
-                            {
-                              dependencies: ['..range', 'valueType.type'],
-                              fulfill: {
-                                state: {
-                                  visible: '{{!!$deps[0]}}',
-                                  componentType:
-                                    '{{["int","long","double","float"].includes($deps[1])?"NumberPicker":["date"].includes($deps[1])?"DatePicker":"Input"}}',
-                                },
-                              },
-                            },
-                            {
-                              dependencies: ['valueType.type'],
-                              fulfill: {
-                                state: {
-                                  visible: '{{!$deps[0]==="boolean"}}',
-                                },
-                              },
-                            },
-                          ],
-                        },
-                        // 根据数据类型来渲染不同的组件
-                        range: {
-                          type: 'boolean',
-                          default: false,
-                          'x-decorator': 'FormItem',
-                          'x-component': 'Checkbox',
-                          'x-component-props': {
-                            children: '范围',
-                          },
-                          'x-decorator-props': {
-                            gridSpan: 2,
-                          },
-                          'x-reactions': {
-                            dependencies: ['valueType.type'],
-                            when: '{{$deps[0]==="boolean"}}',
-                            fulfill: {
-                              state: {
-                                visible: false,
-                                decoratorProps: {
-                                  gridSpan: 0,
-                                },
-                              },
-                            },
-                            otherwise: {
-                              state: {
-                                visible: true,
-                                decoratorProps: {
-                                  gridSpan: 2,
-                                },
-                              },
-                            },
-                          },
-                        },
-                      },
-                    },
-                  },
-                },
-                right: {
-                  type: 'void',
-                  'x-component': 'Space',
-                  properties: {
-                    remove: {
-                      type: 'void',
-                      'x-component': 'ArrayItems.Remove',
-                    },
-                  },
-                },
-              },
-            },
-            properties: {
-              addition: {
-                type: 'void',
-                title: '添加指标',
-                'x-component': 'ArrayItems.Addition',
-              },
-            },
-            'x-reactions': {
-              dependencies: ['valueType.type'],
-              fulfill: {
-                state: {
-                  visible:
-                    "{{['int','float','double','long','date','string','boolean'].includes($deps[0])}}",
-                },
-              },
-            },
-          },
         },
       },
     },

+ 2 - 24
src/pages/device/components/Metadata/Cat/index.tsx

@@ -3,45 +3,23 @@ import { useEffect, useState } from 'react';
 import { productModel, service } from '@/pages/device/Product';
 import MonacoEditor from 'react-monaco-editor';
 import { observer } from '@formily/react';
-import { InstanceModel } from '@/pages/device/Instance';
-import { useLocation } from 'umi';
-import InstanceService from '@/pages/device/Instance/service';
 
 interface Props {
   visible: boolean;
   close: () => void;
-  type: 'product' | 'device';
 }
 
-const instanceService = new InstanceService('device-instance');
 const Cat = observer((props: Props) => {
-  const location = useLocation<{ id: string }>();
   const [codecs, setCodecs] = useState<{ id: string; name: string }[]>();
-  const metadataMap = {
-    product: productModel.current?.metadata as string,
-    device: InstanceModel.current?.metadata as string, // 有问题
-  };
-  const metadata = metadataMap[props.type];
+  const metadata = productModel.current?.metadata as string;
   const [value, setValue] = useState(metadata);
-  const _path = location.pathname.split('/');
-  const id = _path[_path.length - 1];
   useEffect(() => {
     service.codecs().subscribe({
       next: (data) => {
         setCodecs([{ id: 'jetlinks', name: 'jetlinks' }].concat(data));
       },
     });
-
-    if (props.type === 'device' && id) {
-      instanceService.detail(id).then((resp) => {
-        if (resp.status === 200) {
-          InstanceModel.current = resp.result;
-          const _metadata = resp.result?.metadata;
-          setValue(_metadata);
-        }
-      });
-    }
-  }, [id]);
+  }, []);
 
   const convertMetadata = (key: string) => {
     if (key === 'alink') {

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

@@ -94,7 +94,7 @@ const Metadata = observer((props: Props) => {
         </Tabs.TabPane>
       </Tabs>
       <Import visible={visible} close={() => setVisible(false)} />
-      <Cat visible={cat} close={() => setCat(false)} type={props.type} />
+      <Cat visible={cat} close={() => setCat(false)} />
     </div>
   );
 });

+ 25 - 31
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -42,7 +42,6 @@ const Access = (props: Props) => {
   const [config, setConfig] = useState<any>();
   const networkPermission = PermissionButton.usePermission('link/Type').permission;
   const protocolPermission = PermissionButton.usePermission('link/Protocol').permission;
-  const [steps, setSteps] = useState<string[]>(['网络组件', '消息协议', '完成']);
 
   const MetworkTypeMapping = new Map();
   MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
@@ -70,7 +69,7 @@ const Access = (props: Props) => {
     });
   };
 
-  const queryProcotolList = (id?: string, params?: any) => {
+  const queryProcotolList = (id: string, params?: any) => {
     service.getProtocolList(ProcotoleMapping.get(id), params).then((resp) => {
       if (resp.status === 200) {
         setProcotolList(resp.result);
@@ -80,39 +79,25 @@ const Access = (props: Props) => {
 
   useEffect(() => {
     if (props.provider?.id && !props.data?.id) {
-      if (props.provider?.id !== 'child-device') {
-        setSteps(['网络组件', '消息协议', '完成']);
-        queryNetworkList(props.provider?.id, {
-          include: networkCurrent || '',
-        });
-        setCurrent(0);
-      } else {
-        setSteps(['消息协议', '完成']);
-        setCurrent(1);
-        queryProcotolList(props.provider?.id);
-      }
+      queryNetworkList(props.provider?.id, {
+        include: networkCurrent || '',
+      });
+      setCurrent(0);
     }
   }, [props.provider]);
 
   useEffect(() => {
     if (props.data?.id) {
       setProcotolCurrent(props.data?.protocol);
+      setNetworkCurrent(props.data?.channelId);
       form.setFieldsValue({
         name: props.data?.name,
         description: props.data?.description,
       });
-      if (props.data?.provider !== 'child-device') {
-        setCurrent(0);
-        setSteps(['网络组件', '消息协议', '完成']);
-        setNetworkCurrent(props.data?.channelId);
-        queryNetworkList(props.data?.provider, {
-          include: props.data?.channelId,
-        });
-      } else {
-        setSteps(['消息协议', '完成']);
-        setCurrent(1);
-        queryProcotolList(props.data?.provider);
-      }
+      setCurrent(0);
+      queryNetworkList(props.data?.provider, {
+        include: props.data?.channelId,
+      });
     }
   }, [props.data]);
 
@@ -145,6 +130,18 @@ const Access = (props: Props) => {
     setCurrent(current - 1);
   };
 
+  const steps = [
+    {
+      title: '网络组件',
+    },
+    {
+      title: '消息协议',
+    },
+    {
+      title: '完成',
+    },
+  ];
+
   const columnsMQTT: any[] = [
     {
       title: '分组',
@@ -528,10 +525,7 @@ const Access = (props: Props) => {
                               description: values.description,
                               provider: props.provider.id,
                               protocol: procotolCurrent,
-                              transport:
-                                props.provider?.id === 'child-device'
-                                  ? 'Gateway'
-                                  : ProcotoleMapping.get(props.provider.id),
+                              transport: ProcotoleMapping.get(props.provider.id),
                               channel: 'network', // 网络组件
                               channelId: networkCurrent,
                             })
@@ -654,13 +648,13 @@ const Access = (props: Props) => {
         <div className={styles.steps}>
           <Steps size="small" current={current}>
             {steps.map((item) => (
-              <Steps.Step key={item} title={item} />
+              <Steps.Step key={item.title} title={item.title} />
             ))}
           </Steps>
         </div>
         <div className={styles.content}>{renderSteps(current)}</div>
         <div className={styles.action}>
-          {current === 1 && props.provider.id !== 'child-device' && (
+          {current === 1 && (
             <Button style={{ margin: '0 8px' }} onClick={() => prev()}>
               上一步
             </Button>

+ 1 - 1
src/pages/link/AccessConfig/Detail/Provider/index.tsx

@@ -50,7 +50,7 @@ const Provider = (props: Props) => {
                     <div className={styles.images}>{item.name}</div>
                     <div style={{ margin: '10px', width: 'calc(100% - 84px)' }}>
                       <div style={{ fontWeight: 600 }}>{item.name}</div>
-                      <div className={styles.desc}>{item.description || '--'}</div>
+                      <div className={styles.desc}>{item.description}</div>
                     </div>
                   </div>
                   <div style={{ width: '70px' }}>

+ 2 - 3
src/pages/link/AccessConfig/service.ts

@@ -31,12 +31,11 @@ class Service extends BaseService<AccessItem> {
       method: 'GET',
       params,
     });
-  public getProtocolList = (transport?: string, params?: any) => {
-    return request(`/${SystemConst.API_BASE}/protocol/supports/${transport ? transport : ''}`, {
+  public getProtocolList = (transport: string, params?: any) =>
+    request(`/${SystemConst.API_BASE}/protocol/supports/${transport}`, {
       method: 'GET',
       params,
     });
-  };
   public getConfigView = (id: string, transport: string) =>
     request(`/${SystemConst.API_BASE}/protocol/${id}/transport/${transport}`, {
       method: 'GET',

+ 20 - 34
src/pages/notice/Template/Debug/index.tsx

@@ -38,32 +38,27 @@ const Debug = observer(() => {
             }
           });
 
-          onFieldReact('variableDefinitions.*.id', (field) => {
+          onFieldReact('variableDefinitions.*.type', (field) => {
             const value = (field as Field).value;
-            const format = field.query('.value').take() as Field;
-
-            if (format) {
-              switch (value) {
-                case 'date':
-                  format.setComponent(DatePicker, {
-                    showTime: true,
-                  });
-                  break;
-                case 'string':
-                  format.setComponent(Input);
-                  break;
-                case 'number':
-                  format.setComponent(NumberPicker, {});
-                  break;
-                case 'file':
-                  format.setComponent(FUpload, {
-                    type: 'file',
-                  });
-                  break;
-                case 'other':
-                  format.setComponent(Input);
-                  break;
-              }
+            const format = field.query('.value').take() as any;
+            switch (value) {
+              case 'date':
+                format.setComponent(DatePicker);
+                break;
+              case 'string':
+                format.setComponent(Input);
+                break;
+              case 'number':
+                format.setComponent(NumberPicker);
+                break;
+              case 'file':
+                format.setComponent(FUpload, {
+                  type: 'file',
+                });
+                break;
+              case 'other':
+                format.setComponent(Input);
+                break;
             }
           });
         },
@@ -88,9 +83,6 @@ const Debug = observer(() => {
       Select,
       ArrayTable,
       PreviewText,
-      NumberPicker,
-      DatePicker,
-      FUpload,
     },
   });
 
@@ -172,12 +164,6 @@ const Debug = observer(() => {
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
                 },
-                type: {
-                  'x-hidden': true,
-                  type: 'string',
-                  'x-decorator': 'FormItem',
-                  'x-component': 'Input',
-                },
               },
             },
           },

+ 14 - 31
src/pages/notice/Template/Detail/doc/WeixinApp.tsx

@@ -2,50 +2,33 @@ import { Image } from 'antd';
 import './index.less';
 
 const WeixinApp = () => {
-  const appId = require('/public/images/notice/doc/template/weixin-official/02-mini-Program-Appid.png');
+  const agentId = require('/public/images/notice/doc/template/weixin-official/01-Agentid.jpg');
+  const appId = require('/public/images/notice/doc/template/weixin-official/02-mini-Program-Appid.jpg');
 
   return (
     <div className="doc">
       <div className="url">
-        企业微信管理后台:<a href="https://work.weixin.qq.com">https://work.weixin.qq.com</a>
+        微信公众平台:<a href="https://mp.weixin.qq.com/">https://mp.weixin.qq.com/</a>
       </div>
       <h1>1. 概述</h1>
       <div>
-        通知模板结合通知配置为告警消息通知提供支撑。通知模板只能调用同一类型的通知配置服务
+        通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用
       </div>
-      <h1>2.模板配置说明</h1>
+      <h1>2.通知配置说明</h1>
       <div>
-        <h2>1. 绑定配置</h2>
-        <div>绑定通知配置</div>
-      </div>
-      <div>
-        <h2>2. 用户标签</h2>
-        <div>以标签的维度通知该标签下所有用户</div>
-      </div>
-      <div>
-        <h2>3. 消息模板</h2>
-        <div>微信公众号中配置的消息模板</div>
-      </div>
-      <div>
-        <h2>4. 模板跳转链接</h2>
-        <div>点击消息之后进行页面跳转</div>
-      </div>
-      <div>
-        <h2>5. 跳转小程序Appid</h2>
-        <div>点击消息之后打开对应的小程序</div>
-      </div>
-      <div>
-        <h2>6. 跳转小程序具体路径</h2>
-        <div>点击消息之后跳转到小程序的具体页面</div>
+        <h2>1. AppID</h2>
+        <div>微信服务号的唯一专属编号。</div>
+        <div>获取路径:“微信公众平台”管理后台--“设置与开发”--“基本配置”</div>
         <div className="image">
-          <Image width="100%" src={appId} />
+          <Image width="100%" src={agentId} />
         </div>
       </div>
+      <h2>2. AppSecret</h2>
       <div>
-        <h2>7. 模板内容</h2>
-        <div>
-          支持填写带变量的动态模板。变量填写规范示例:${name}
-          。填写动态参数后,可对变量的名称、类型、格式进行配置,以便告警通知时填写。
+        <div>公众号开发者身份的密码</div>
+        <div>获取路径:“微信公众平台”管理后台--“设置与开发”--“基本配置”</div>
+        <div className="image">
+          <Image width="100%" src={appId} />
         </div>
       </div>
     </div>

+ 15 - 17
src/pages/notice/Template/Detail/index.tsx

@@ -16,7 +16,7 @@ import {
   Switch,
 } from '@formily/antd';
 import type { Field } from '@formily/core';
-import { createForm, FormPath, onFieldInit, onFieldReact, onFieldValueChange } from '@formily/core';
+import { createForm, onFieldInit, onFieldValueChange } from '@formily/core';
 import { createSchemaField, observer } from '@formily/react';
 import type { ISchema } from '@formily/json-schema';
 import styles from './index.less';
@@ -60,7 +60,7 @@ export const docMap = {
 
 const Detail = observer(() => {
   const { id } = useParams<{ id: string }>();
-  const [provider, setProvider] = useState<string>('embedded');
+  const [provider, setProvider] = useState<string>();
   // 正则提取${}里面的值
   const pattern = /(?<=\$\{).*?(?=\})/g;
 
@@ -227,17 +227,9 @@ const Detail = observer(() => {
               form1.setValuesIn('variableDefinitions', idList);
             }
           });
-          onFieldReact('variableDefinitions.*.type', (field) => {
+          onFieldValueChange('variableDefinitions.*.type', (field) => {
             const value = (field as Field).value;
-            const formatPath = FormPath.transform(
-              field.path,
-              /\d+/,
-              (index) => `variableDefinitions.${parseInt(index)}.format`,
-            );
-            const format = field.query(formatPath).take() as any;
-
-            console.log(format, 'format', value);
-            if (!format) return;
+            const format = field.query('.format').take() as any;
             switch (value) {
               case 'date':
                 format.setComponent(Select);
@@ -251,9 +243,8 @@ const Detail = observer(() => {
                 format.setValue('string');
                 break;
               case 'string':
-                console.log('string');
                 format.setComponent(PreviewText.Input);
-                format.setValue('%s');
+                format.setValue('--');
                 break;
               case 'number':
                 format.setComponent(Input);
@@ -482,6 +473,15 @@ const Detail = observer(() => {
               officialMessage: {
                 type: 'void',
                 properties: {
+                  agentId: {
+                    title: 'AgentId',
+                    type: 'string',
+                    'x-decorator': 'FormItem',
+                    'x-component': 'Input',
+                    'x-component-props': {
+                      placeholder: '请输入AgentId',
+                    },
+                  },
                   tagid: {
                     title: '用户标签',
                     type: 'string',
@@ -1000,11 +1000,9 @@ const Detail = observer(() => {
           fulfill: {
             state: {
               hidden: '{{$deps[0]==="aliyun"}}',
-              disabled: '{{["aliyunSms","aliyun"].includes($deps[0])}}',
             },
           },
         },
-
         'x-component-props': {
           rows: 5,
           placeholder: '变量格式:${name};\n 示例:尊敬的${name},${time}有设备触发告警,请注意处理',
@@ -1072,7 +1070,7 @@ const Detail = observer(() => {
             column4: {
               type: 'void',
               'x-component': 'ArrayTable.Column',
-              'x-component-props': { title: '格式', width: '300px' },
+              'x-component-props': { title: '格式', width: '150px' },
               required: true,
               properties: {
                 format: {

+ 4 - 27
src/pages/notice/Template/Log/index.tsx

@@ -1,4 +1,4 @@
-import { Badge, Modal } from 'antd';
+import { Modal } from 'antd';
 import { observer } from '@formily/react';
 import { service, state } from '..';
 import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
@@ -15,7 +15,6 @@ const Log = observer(() => {
     {
       dataIndex: 'id',
       title: 'id',
-      width: 200,
     },
     {
       dataIndex: 'sendTime',
@@ -24,30 +23,6 @@ const Log = observer(() => {
     {
       dataIndex: 'state',
       title: '状态',
-      renderText: (text: { value: string; text: string }, record) => {
-        return (
-          <>
-            <Badge status={text.value === 'success' ? 'success' : 'error'} text={text.text} />
-            {text.value !== 'success' && (
-              <a
-                key="info"
-                onClick={() => {
-                  Modal.info({
-                    title: '错误信息',
-                    width: '30vw',
-                    content: (
-                      <div style={{ height: '300px', overflowY: 'auto' }}>{record.errorStack}</div>
-                    ),
-                    onOk() {},
-                  });
-                }}
-              >
-                <InfoCircleOutlined />
-              </a>
-            )}
-          </>
-        );
-      },
     },
     {
       dataIndex: 'action',
@@ -59,7 +34,9 @@ const Log = observer(() => {
             Modal.info({
               title: '详情信息',
               width: '30vw',
-              content: <div style={{ height: '300px', overflowY: 'auto' }}>{record.message}</div>,
+              content: (
+                <div style={{ height: '300px', overflowY: 'auto' }}>{record.errorStack}</div>
+              ),
               onOk() {},
             });
           }}

+ 0 - 1
src/pages/notice/Template/index.tsx

@@ -315,7 +315,6 @@ const Template = observer(() => {
                     actionRef.current?.reset?.();
                   },
                 }}
-                isPermission={templatePermission.delete}
                 key="delete"
               >
                 <DeleteOutlined />

+ 0 - 1
src/pages/notice/Template/typings.d.ts

@@ -16,5 +16,4 @@ type LogItem = {
   sendTime: number;
   state: string;
   errorStack?: string;
-  message?: string;
 };

+ 28 - 107
src/pages/rule-engine/Alarm/Config/index.tsx

@@ -1,16 +1,13 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { Button, Card, Col, Divider, message, Row } from 'antd';
+import { Button, Card, Col, Row } from 'antd';
 import TitleComponent from '@/components/TitleComponent';
 import { createSchemaField } from '@formily/react';
 import { ArrayItems, Form, FormButtonGroup, FormGrid, FormItem, Input } from '@formily/antd';
 import { ISchema } from '@formily/json-schema';
 import { useMemo, useState } from 'react';
-import { createForm, onFieldReact, onFormInit } from '@formily/core';
+import { createForm } from '@formily/core';
 import FLevelInput from '@/components/FLevelInput';
-import { IOConfigItem } from '@/pages/rule-engine/Alarm/Config/typing';
-import Service from '@/pages/rule-engine/Alarm/Config/service';
 
-export const service = new Service('alarm/config');
 const Config = () => {
   const [tab, setTab] = useState<'io' | 'config' | string>('config');
   const SchemaField = createSchemaField({
@@ -23,47 +20,21 @@ const Config = () => {
     },
   });
 
-  const levelForm = useMemo(
+  const form = useMemo(
     () =>
       createForm({
-        validateFirst: true,
-        effects() {
-          onFormInit(async (form) => {
-            const resp: any = await service.queryLevel();
-            if (resp.status === 200) {
-              const _level = resp.result.levels.map(
-                (item: { level: number; title: string }) => item.title,
-              );
-              form.setValuesIn('level', _level);
-            }
-          });
-          onFieldReact('level.0.remove', (state, f1) => {
-            state.setState({ display: 'none' });
-            f1.setFieldState('level.add', (state1) => {
-              const length = f1.values.level?.length;
-              if (length > 4) {
-                state1.display = 'none';
-              } else {
-                state1.display = 'visible';
-              }
-            });
-          });
-        },
+        effects() {},
       }),
     [],
   );
-
-  const inputForm = useMemo(() => createForm(), []);
-  const outputForm = useMemo(() => createForm(), []);
-
-  const levelSchema: ISchema = {
+  const schema1: ISchema = {
     type: 'object',
     properties: {
       level: {
         type: 'array',
         'x-component': 'ArrayItems',
         'x-decorator': 'FormItem',
-        maxItems: 5,
+        maxItems: 3,
         items: {
           type: 'void',
           'x-decorator': 'FormGrid',
@@ -83,15 +54,11 @@ const Config = () => {
             },
             remove: {
               type: 'void',
-              title: <div style={{ width: '20px' }} />,
               'x-decorator': 'FormItem',
               'x-component': 'ArrayItems.Remove',
               'x-decorator-props': {
                 gridSpan: 1,
               },
-              'x-component-props': {
-                style: { marginTop: '40px' },
-              },
             },
           },
         },
@@ -106,7 +73,7 @@ const Config = () => {
     },
   };
 
-  const ioSchema: ISchema = {
+  const schema2: ISchema = {
     type: 'object',
     properties: {
       kafka: {
@@ -115,9 +82,6 @@ const Config = () => {
         required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Input',
-        'x-component-props': {
-          placeholder: '请输入kafka地址',
-        },
       },
       topic: {
         title: 'topic',
@@ -125,9 +89,6 @@ const Config = () => {
         required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Input',
-        'x-component-props': {
-          placeholder: '请输入topic',
-        },
       },
       layout2: {
         type: 'void',
@@ -144,9 +105,6 @@ const Config = () => {
             required: true,
             'x-decorator': 'FormItem',
             'x-component': 'Input',
-            'x-component-props': {
-              placeholder: '请输入用户名',
-            },
             'x-decorator-props': {
               gridSpan: 1,
             },
@@ -160,55 +118,20 @@ const Config = () => {
             'x-decorator-props': {
               gridSpan: 1,
             },
-            'x-component-props': {
-              placeholder: '请输入密码',
-            },
           },
         },
       },
     },
   };
 
-  const handleSaveIO = async () => {
-    const inputConfig: IOConfigItem = await inputForm.submit();
-    const outputConfig: IOConfigItem = await outputForm.submit();
-    const inputResp = await service.saveOutputData({
-      config: {
-        type: 'kafka',
-        config: inputConfig,
-      },
-      exchangeType: 'producer',
-    });
-    const outputResp = await service.saveOutputData({
-      config: {
-        type: 'kafka',
-        config: outputConfig,
-      },
-      exchangeType: 'consume',
-    });
-
-    if (inputResp.status === 200 && outputResp.status === 200) {
-      message.success('操作成功');
-    }
-  };
-
-  const handleSaveLevel = async () => {
-    const values: { level: string[] } = await levelForm.submit();
-    const _level = values?.level.map((l: string, i: number) => ({ level: i + 1, title: l }));
-    const resp = await service.saveLevel(_level);
-    if (resp.status === 200) {
-      message.success('操作成功');
-    }
-  };
-
   const level = (
     <Card>
       <TitleComponent data="告警级别配置" />
-      <Form form={levelForm}>
-        <SchemaField schema={levelSchema} />
+      <Form form={form}>
+        <SchemaField schema={schema1} />
         <FormButtonGroup.Sticky>
           <FormButtonGroup.FormItem>
-            <Button type="primary" onClick={handleSaveLevel}>
+            <Button type="primary" onClick={() => {}}>
               保存
             </Button>
           </FormButtonGroup.FormItem>
@@ -216,27 +139,25 @@ const Config = () => {
       </Form>
     </Card>
   );
+
   const io = (
-    <div>
-      <Card>
-        <TitleComponent data="告警数据输出" />
-        <Form form={outputForm} layout="vertical">
-          <SchemaField schema={ioSchema} />
-        </Form>
-        <Divider />
-        <TitleComponent data="告警处理结果输入" />
-        <Form form={inputForm} layout="vertical">
-          <SchemaField schema={ioSchema} />
-          <FormButtonGroup.Sticky>
-            <FormButtonGroup.FormItem>
-              <Button type="primary" onClick={handleSaveIO}>
-                保存
-              </Button>
-            </FormButtonGroup.FormItem>
-          </FormButtonGroup.Sticky>
-        </Form>
-      </Card>
-    </div>
+    <Card>
+      <TitleComponent data="告警数据输出" />
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema2} />
+      </Form>
+      <TitleComponent data="告警处理结果输入" />
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema2} />
+        <FormButtonGroup.Sticky>
+          <FormButtonGroup.FormItem>
+            <Button type="primary" onClick={() => {}}>
+              保存
+            </Button>
+          </FormButtonGroup.FormItem>
+        </FormButtonGroup.Sticky>
+      </Form>
+    </Card>
   );
 
   const list = [

+ 0 - 26
src/pages/rule-engine/Alarm/Config/service.ts

@@ -1,26 +0,0 @@
-import BaseService from '@/utils/BaseService';
-import SystemConst from '@/utils/const';
-import { request } from 'umi';
-import { IOConfigItem, LevelItem } from '@/pages/rule-engine/Alarm/Config/typing';
-
-class Service extends BaseService<IOConfigItem> {
-  saveLevel = (data: LevelItem[]) =>
-    request(`/${SystemConst.API_BASE}/alarm/config/default/level`, {
-      method: 'PATCH',
-      data,
-    });
-
-  queryLevel = () =>
-    request(`/${SystemConst.API_BASE}/alarm/config/default/level`, {
-      method: 'GET',
-    });
-
-  // 保存告警数据输出
-  saveOutputData = (data: any) =>
-    request(`/${SystemConst.API_BASE}/alarm/config/data-exchange`, {
-      method: 'PATCH',
-      data,
-    });
-}
-
-export default Service;

+ 0 - 25
src/pages/rule-engine/Alarm/Config/typing.d.ts

@@ -1,25 +0,0 @@
-import { BaseItem } from '@/utils/typings';
-
-type LevelItem = {
-  level: number;
-  title: string;
-};
-
-type IOConfigItem = {
-  address: string;
-  topic: string;
-  username: string;
-  password: string;
-};
-type IOItem = {
-  alarmConfigId: string;
-  sourceType: string;
-  config: Partial<{
-    type: string;
-    dataSourceId: string;
-    config: Partial<IOConfigItem>;
-  }>;
-  exchangeType: 'consume' | 'producer'; //订阅|推送
-  state: 'disable' | 'enabled'; //禁用|正常
-  description: string;
-} & BaseItem;

+ 0 - 8
src/pages/rule-engine/Alarm/Configuration/Save/index.less

@@ -1,8 +0,0 @@
-.form {
-  :global {
-    .ant-radio-button-wrapper {
-      height: 100%;
-      margin-right: 15px;
-    }
-  }
-}

+ 0 - 200
src/pages/rule-engine/Alarm/Configuration/Save/index.tsx

@@ -1,200 +0,0 @@
-import { message, Modal } from 'antd';
-import { useMemo } from 'react';
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
-import { Form, FormGrid, FormItem, Input, Radio, Select } from '@formily/antd';
-import { ISchema } from '@formily/json-schema';
-import { PermissionButton } from '@/components';
-import { PlusOutlined } from '@ant-design/icons';
-import Service from '@/pages/rule-engine/Alarm/Configuration/service';
-import { useAsyncDataSource } from '@/utils/util';
-import styles from './index.less';
-import { service as ConfigService } from '../../Config';
-
-interface Props {
-  visible: boolean;
-  close: () => void;
-}
-
-const alarm1 = require('/public/images/alarm/alarm1.png');
-const alarm2 = require('/public/images/alarm/alarm2.png');
-const alarm3 = require('/public/images/alarm/alarm3.png');
-const alarm4 = require('/public/images/alarm/alarm4.png');
-const alarm5 = require('/public/images/alarm/alarm5.png');
-
-const service = new Service('alarm/config');
-
-const createImageLabel = (image: string, text: string) => {
-  return (
-    <div style={{ textAlign: 'center', marginTop: 10, fontSize: '25px' }}>
-      <img alt="" height="40px" src={image} />
-      {text}
-    </div>
-  );
-};
-
-const Save = (props: Props) => {
-  const { visible, close } = props;
-
-  const LevelMap = {
-    1: alarm1,
-    2: alarm2,
-    3: alarm3,
-    4: alarm4,
-    5: alarm5,
-  };
-  const getLevel = () => {
-    return ConfigService.queryLevel().then((resp) => {
-      if (resp.status === 200) {
-        console.log(resp, 'resp');
-        return resp.result?.levels?.map((item: { level: number; title: string }) => ({
-          label: createImageLabel(LevelMap[item.level], item.title),
-          value: item.level,
-        }));
-      }
-    });
-  };
-  const form = useMemo(
-    () =>
-      createForm({
-        validateFirst: true,
-        effects() {},
-      }),
-    [],
-  );
-
-  const getSupports = () => service.getTargetTypes();
-
-  const SchemaField = createSchemaField({
-    components: {
-      FormItem,
-      Input,
-      Select,
-      FormGrid,
-      Radio,
-    },
-  });
-
-  const handleSave = async () => {
-    const data: ConfigItem = await form.submit();
-    console.log(data, 'dat');
-    const resp: any = await service.update(data);
-    if (resp.status === 200) {
-      message.success('操作成功');
-    }
-  };
-
-  const schema: ISchema = {
-    type: 'object',
-    properties: {
-      layout: {
-        type: 'void',
-        'x-decorator': 'FormGrid',
-        'x-decorator-props': {
-          maxColumns: 2,
-          minColumns: 2,
-          columnGap: 24,
-        },
-        properties: {
-          name: {
-            title: '名称',
-            'x-decorator': 'FormItem',
-            'x-component': 'Input',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            'x-component-props': {
-              placeholder: '请输入名称',
-            },
-          },
-          type: {
-            title: '类型',
-            'x-decorator': 'FormItem',
-            'x-component': 'Select',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            'x-reactions': '{{useAsyncDataSource(getSupports)}}',
-            'x-component-props': {
-              placeholder: '请选择类型',
-            },
-          },
-        },
-      },
-      level: {
-        title: '级别',
-        'x-decorator': 'FormItem',
-        'x-component': 'Radio.Group',
-        'x-component-props': {
-          optionType: 'button',
-          placeholder: '请选择类型',
-        },
-        'x-reactions': '{{useAsyncDataSource(getLevel)}}',
-        'x-decorator-props': {
-          gridSpan: 1,
-        },
-      },
-      sceneId: {
-        title: '关联触发场景',
-        'x-decorator': 'FormItem',
-        'x-component': 'Select',
-        'x-decorator-props': {
-          gridSpan: 1,
-          addonAfter: (
-            <PermissionButton
-              type="link"
-              style={{ padding: 0 }}
-              isPermission={true}
-              onClick={() => {
-                // const tab: any = window.open(`${origin}/#/system/department?save=true`);
-                // tab!.onTabSaveSuccess = (value: any) => {
-                //   form.setFieldState('orgIdList', async (state) => {
-                // state.dataSource = await getOrg().then((resp) =>
-                //   resp.result?.map((item: Record<string, unknown>) => ({
-                //     ...item,
-                //     label: item.name,
-                //     value: item.id,
-                //   })),
-                // );
-                // state.value = [...(state.value || []), value.id];
-                // });
-                // };
-              }}
-            >
-              <PlusOutlined />
-            </PermissionButton>
-          ),
-        },
-        'x-component-props': {
-          placeholder: '请输入名称',
-        },
-      },
-      description: {
-        title: '说明',
-        'x-decorator': 'FormItem',
-        'x-component': 'Input.TextArea',
-        'x-decorator-props': {
-          gridSpan: 1,
-        },
-        'x-component-props': {
-          placeholder: '请输入描述信息',
-        },
-      },
-    },
-  };
-
-  return (
-    <Modal
-      width="40vw"
-      visible={visible}
-      onOk={handleSave}
-      onCancel={() => close()}
-      title="新增告警"
-    >
-      <Form className={styles.form} form={form} layout="vertical">
-        <SchemaField schema={schema} scope={{ useAsyncDataSource, getSupports, getLevel }} />
-      </Form>
-    </Modal>
-  );
-};
-export default Save;

+ 0 - 117
src/pages/rule-engine/Alarm/Configuration/index.tsx

@@ -1,117 +0,0 @@
-import { PageContainer } from '@ant-design/pro-layout';
-import SearchComponent from '@/components/SearchComponent';
-import { ActionType, ProColumns } from '@jetlinks/pro-table';
-import { PermissionButton } from '@/components';
-import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import { useRef, useState } from 'react';
-import { Space } from 'antd';
-import ProTableCard from '@/components/ProTableCard';
-import Save from './Save';
-
-const Configuration = () => {
-  const intl = useIntl();
-
-  const columns: ProColumns<ConfigItem>[] = [
-    {
-      dataIndex: 'name',
-      title: '名称',
-    },
-    {
-      title: '类型',
-      dataIndex: 'targetType',
-    },
-    {
-      title: '告警级别',
-      dataIndex: 'level',
-    },
-    {
-      title: '关联场景联动',
-      dataIndex: 'sceneName',
-    },
-    {
-      title: '状态',
-      dataIndex: 'state',
-    },
-    {
-      title: '说明',
-      dataIndex: 'description',
-    },
-    {
-      title: '操作',
-      valueType: 'option',
-      align: 'center',
-      render: (_, record) => [
-        <PermissionButton
-          isPermission={true}
-          tooltip={{
-            title: intl.formatMessage({
-              id: 'pages.data.option.edit',
-              defaultMessage: '编辑',
-            }),
-          }}
-          onClick={() => {
-            console.log(record);
-          }}
-        >
-          <EditOutlined />
-        </PermissionButton>,
-        <PermissionButton
-          isPermission={true}
-          tooltip={{
-            title: intl.formatMessage({
-              id: 'pages.data.option.remove',
-              defaultMessage: '删除',
-            }),
-          }}
-        >
-          <DeleteOutlined />
-        </PermissionButton>,
-      ],
-    },
-  ];
-
-  const actionRef = useRef<ActionType>();
-
-  const [param, setParam] = useState({});
-
-  const [visible, setVisible] = useState<boolean>(false);
-  return (
-    <PageContainer>
-      <SearchComponent
-        field={columns}
-        onSearch={(data) => {
-          actionRef.current?.reset?.();
-          setParam(data);
-        }}
-      />
-      <ProTableCard<ConfigItem>
-        actionRef={actionRef}
-        rowKey="id"
-        search={false}
-        params={param}
-        columns={columns}
-        headerTitle={
-          <Space>
-            <PermissionButton
-              isPermission={true}
-              onClick={() => {
-                setVisible(true);
-              }}
-              key="button"
-              icon={<PlusOutlined />}
-              type="primary"
-            >
-              {intl.formatMessage({
-                id: 'pages.data.option.add',
-                defaultMessage: '新增',
-              })}
-            </PermissionButton>
-          </Space>
-        }
-      />
-      <Save visible={visible} close={() => setVisible(false)} />
-    </PageContainer>
-  );
-};
-export default Configuration;

+ 0 - 17
src/pages/rule-engine/Alarm/Configuration/service.ts

@@ -1,17 +0,0 @@
-import BaseService from '@/utils/BaseService';
-import { request } from 'umi';
-import SystemConst from '@/utils/const';
-
-class Service extends BaseService<ConfigItem> {
-  public getTargetTypes = () =>
-    request(`/${SystemConst.API_BASE}/alarm/config/target-type/supports`, {
-      method: 'GET',
-    }).then((resp) => {
-      return resp.result.map((item: { id: string; name: string }) => ({
-        label: item.name,
-        value: item.id,
-      }));
-    });
-}
-
-export default Service;

+ 0 - 9
src/pages/rule-engine/Alarm/Configuration/typings.d.ts

@@ -1,9 +0,0 @@
-type ConfigItem = {
-  name: string;
-  targetType: string;
-  level: number;
-  sceneName: string;
-  sceneId: string;
-  state: string;
-  description: string;
-};

+ 0 - 49
src/pages/rule-engine/Alarm/Log/Detail/Info.tsx

@@ -1,49 +0,0 @@
-import { Descriptions, Modal } from 'antd';
-import { useEffect, useState } from 'react';
-import moment from 'moment';
-
-interface Props {
-  data: Partial<AlarmLogHistoryItem>;
-  close: () => void;
-}
-
-const Info = (props: Props) => {
-  const [data, setDada] = useState<Partial<AlarmLogHistoryItem>>(props.data || {});
-
-  useEffect(() => {
-    setDada(props.data);
-  }, [props.data]);
-
-  return (
-    <Modal title={'详情'} visible onCancel={props.close} onOk={props.close} width={1000}>
-      <Descriptions bordered column={2}>
-        {data.targetType === 'device' && (
-          <>
-            <Descriptions.Item label="告警设备" span={1}>
-              {data?.targetName}
-            </Descriptions.Item>
-            <Descriptions.Item label="设备ID" span={1}>
-              {data?.targetId}
-            </Descriptions.Item>
-          </>
-        )}
-        <Descriptions.Item label="告警名称" span={1}>
-          {data?.alarmConfigName}
-        </Descriptions.Item>
-        <Descriptions.Item label="告警时间" span={1}>
-          {moment(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss')}
-        </Descriptions.Item>
-        <Descriptions.Item label="告警级别" span={1}>
-          {data?.level}
-        </Descriptions.Item>
-        <Descriptions.Item label="告警说明" span={1}>
-          {data?.description}
-        </Descriptions.Item>
-        <Descriptions.Item label="告警流水" span={2}>
-          {data?.alarmInfo}
-        </Descriptions.Item>
-      </Descriptions>
-    </Modal>
-  );
-};
-export default Info;

+ 0 - 133
src/pages/rule-engine/Alarm/Log/Detail/index.tsx

@@ -1,133 +0,0 @@
-import SearchComponent from '@/components/SearchComponent';
-import type { ActionType, ProColumns } from '@jetlinks/pro-table';
-import ProTable from '@jetlinks/pro-table';
-import { useEffect, useRef, useState } from 'react';
-import { service } from '@/pages/rule-engine/Alarm/Log';
-import { PageContainer } from '@ant-design/pro-layout';
-import { useParams } from 'umi';
-import { AlarmLogModel } from '../model';
-import { observer } from '@formily/reactive-react';
-import { SearchOutlined } from '@ant-design/icons';
-import Info from './Info';
-import { Button } from 'antd';
-import moment from 'moment';
-
-const Detail = observer(() => {
-  const params = useParams<{ id: string }>();
-
-  const [visible, setVisible] = useState<boolean>(false);
-  const [current, setCurrent] = useState<Partial<AlarmLogHistoryItem>>({});
-
-  const [param, setParam] = useState<any>({
-    terms: [
-      {
-        column: 'alarmRecordId',
-        termType: 'eq$not',
-        value: params.id || AlarmLogModel.current?.id,
-        type: 'and',
-      },
-    ],
-    sorts: [
-      {
-        name: 'name',
-        order: 'asc',
-      },
-    ],
-  });
-  const actionRef = useRef<ActionType>();
-
-  const initColumns: ProColumns<AlarmLogHistoryItem>[] = [
-    {
-      dataIndex: 'alarmTime',
-      title: '告警时间',
-      render: (text: any) => <span>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</span>,
-    },
-    {
-      dataIndex: 'alarmConfigName',
-      title: '告警名称',
-    },
-    {
-      dataIndex: 'description',
-      title: '说明',
-    },
-    {
-      dataIndex: 'action',
-      title: '操作',
-      render: (record: any) => (
-        <Button type="link">
-          <SearchOutlined
-            onClick={() => {
-              setVisible(true);
-              setCurrent(record);
-            }}
-          />
-        </Button>
-      ),
-    },
-  ];
-
-  useEffect(() => {
-    service.detail(params.id).then((resp) => {
-      if (resp.status === 200) {
-        AlarmLogModel.current = resp.result;
-        if (resp.result.targetType === 'device') {
-          initColumns.splice(2, 0, {
-            dataIndex: 'targetName',
-            title: '告警设备',
-          });
-        }
-        AlarmLogModel.columns = initColumns;
-      }
-    });
-  }, [params.id]);
-
-  return (
-    <PageContainer>
-      <SearchComponent<AlarmLogHistoryItem>
-        field={AlarmLogModel.columns}
-        target="alarm-log-detail"
-        enableSave={false}
-        onSearch={(data) => {
-          actionRef.current?.reload();
-          const terms = [
-            {
-              column: 'alarmRecordId',
-              termType: 'eq$not',
-              value: params.id || AlarmLogModel.current?.id,
-              type: 'and',
-            },
-          ];
-          setParam({
-            ...param,
-            terms: data?.terms ? [...data?.terms, ...terms] : [...terms],
-          });
-        }}
-      />
-      <ProTable<AlarmLogHistoryItem>
-        actionRef={actionRef}
-        params={param}
-        columns={AlarmLogModel.columns}
-        search={false}
-        headerTitle={'记录列表'}
-        request={async (data) => {
-          return service.queryHistoryList({
-            ...data,
-            sorts: [{ name: 'alarmTime', order: 'desc' }],
-          });
-        }}
-        rowKey="id"
-      />
-      {visible && (
-        <Info
-          close={() => {
-            setVisible(false);
-            setCurrent({});
-          }}
-          data={current}
-        />
-      )}
-    </PageContainer>
-  );
-});
-
-export default Detail;

+ 0 - 53
src/pages/rule-engine/Alarm/Log/SolveComponent/index.tsx

@@ -1,53 +0,0 @@
-import { service } from '@/pages/rule-engine/Alarm/Log';
-import { Form, Input, message, Modal } from 'antd';
-
-interface Props {
-  close: () => void;
-  reload: () => void;
-  data: Partial<AlarmLogItem>;
-}
-
-const SolveComponent = (props: Props) => {
-  const { data } = props;
-  const [form] = Form.useForm();
-
-  return (
-    <Modal
-      title="告警处理"
-      visible
-      onOk={form.submit}
-      onCancel={() => {
-        props.close();
-      }}
-    >
-      <Form
-        name="basic"
-        layout="vertical"
-        form={form}
-        onFinish={async (values: any) => {
-          const resp = await service.handleLog(data?.id || '', {
-            describe: values.describe,
-            type: 'user',
-            state: 'normal',
-          });
-          if (resp.status === 200) {
-            message.success('操作成功!');
-            props.reload();
-          } else {
-            message.error('操作失败!');
-          }
-        }}
-      >
-        <Form.Item
-          label="处理结果"
-          name="describe"
-          rules={[{ required: true, message: '请输入处理结果!' }]}
-        >
-          <Input.TextArea showCount maxLength={200} rows={8} placeholder="请输入处理结果" />
-        </Form.Item>
-      </Form>
-    </Modal>
-  );
-};
-
-export default SolveComponent;

+ 0 - 95
src/pages/rule-engine/Alarm/Log/SolveLog/index.tsx

@@ -1,95 +0,0 @@
-import SearchComponent from '@/components/SearchComponent';
-import type { ActionType, ProColumns } from '@jetlinks/pro-table';
-import ProTable from '@jetlinks/pro-table';
-import { Modal } from 'antd';
-import { useRef, useState } from 'react';
-import { service } from '@/pages/rule-engine/Alarm/Log';
-
-interface Props {
-  data: Partial<AlarmLogItem>;
-  close: () => void;
-}
-
-const typeMap = new Map();
-typeMap.set('system', '系统');
-typeMap.set('user', '人工');
-
-const SolveLog = (props: Props) => {
-  const [param, setParam] = useState<any>({
-    terms: [
-      {
-        column: 'alarmRecordId',
-        termType: 'eq',
-        value: props.data.id,
-        type: 'and',
-      },
-    ],
-    sorts: [
-      {
-        name: 'createTime',
-        order: 'desc',
-      },
-    ],
-  });
-  const actionRef = useRef<ActionType>();
-
-  const columns: ProColumns<AlarmLogSolveHistoryItem>[] = [
-    {
-      dataIndex: 'createTime',
-      title: '处理时间',
-    },
-    {
-      dataIndex: 'handleType',
-      title: '处理类型',
-      render: (text: any) => <span>{typeMap.get(text) || ''}</span>,
-    },
-    {
-      dataIndex: 'address',
-      title: '告警时间',
-    },
-    {
-      dataIndex: 'description',
-      title: '告警处理',
-    },
-  ];
-
-  return (
-    <Modal title={'处理记录'} visible onCancel={props.close} onOk={() => {}} width={1200}>
-      <SearchComponent<AlarmLogSolveHistoryItem>
-        field={columns}
-        target="bind-channel"
-        enableSave={false}
-        onSearch={(data) => {
-          actionRef.current?.reload();
-          const terms = [
-            {
-              column: 'alarmRecordId',
-              termType: 'eq',
-              value: props.data.id,
-              type: 'and',
-            },
-          ];
-          setParam({
-            ...param,
-            terms: data?.terms ? [...data?.terms, ...terms] : [...terms],
-          });
-        }}
-      />
-      <ProTable<AlarmLogSolveHistoryItem>
-        actionRef={actionRef}
-        params={param}
-        columns={columns}
-        search={false}
-        headerTitle={'记录列表'}
-        request={async (params) => {
-          return service.queryHandleHistory({
-            ...params,
-            sorts: [{ name: 'createTime', order: 'desc' }],
-          });
-        }}
-        rowKey="id"
-      />
-    </Modal>
-  );
-};
-export default SolveLog;

+ 0 - 135
src/pages/rule-engine/Alarm/Log/TabComponent/index.less

@@ -1,135 +0,0 @@
-@import '~antd/es/style/themes/default.less';
-
-.ellipsis {
-  width: 100%;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-}
-
-.alarm-log-card {
-  .alarm-log-item {
-    display: flex;
-    margin-bottom: 20px;
-    box-shadow: 0 2px 16px rgba(0, 0, 0, 0.1);
-
-    .alarm-log-title {
-      position: relative;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 15%;
-      padding: 10px;
-      overflow: hidden;
-      color: @primary-color;
-      background-color: #f0f2f3;
-      .alarm-log-level {
-        position: absolute;
-        top: 10px;
-        right: -12px;
-        display: flex;
-        justify-content: center;
-        width: 100px;
-        padding: 2px 0;
-        color: white;
-        background-color: red;
-        transform: skewX(45deg);
-        .alarm-log-text {
-          transform: skewX(-45deg);
-        }
-      }
-      .alarm-log-title-text {
-        margin: 0 10px;
-        .ellipsis();
-      }
-    }
-    .alarm-log-content {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      width: 87%;
-      padding: 20px;
-      background: url('/images/alarm/background.png') no-repeat;
-      background-size: 100% 100%;
-
-      .alarm-log-image {
-        display: flex;
-        align-items: center;
-
-        .alarm-type {
-          max-width: 120px;
-          padding-right: 50px;
-          border-right: 1px solid rgba(0, 0, 0, 0.09);
-          .name {
-            color: #000;
-            font-size: 18px;
-          }
-
-          .text {
-            margin-top: 8px;
-            color: #666;
-            font-size: 14px;
-            .ellipsis();
-          }
-        }
-        .alarm-log-right {
-          display: flex;
-          padding-left: 40px;
-          .alarm-log-time {
-            max-width: 165px;
-            margin: 0 10px;
-            .log-title {
-              margin-top: 8px;
-              color: #666;
-              font-size: 12px;
-            }
-
-            .context {
-              margin-top: 8px;
-              color: rgba(#000, 0.85);
-              font-size: 14px;
-              .ellipsis();
-            }
-          }
-        }
-      }
-
-      .alarm-log-actions {
-        .alarm-log-action {
-          display: flex;
-          justify-content: center;
-          width: 72px;
-          height: 72px;
-          background-color: #fff;
-          border: 1px solid @primary-color;
-
-          .btn {
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            width: 72px;
-            height: 72px;
-            .icon {
-              margin-bottom: 5px;
-              color: @primary-color;
-              font-size: 25px;
-            }
-
-            div {
-              color: @primary-color;
-              font-size: 12px;
-            }
-          }
-        }
-        .alarm-log-action:hover {
-          background-color: @primary-color;
-          .icon,
-          div {
-            color: #fff;
-          }
-        }
-      }
-    }
-  }
-}

+ 0 - 305
src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx

@@ -1,305 +0,0 @@
-import SearchComponent from '@/components/SearchComponent';
-import { FileFilled, FileTextFilled, ToolFilled } from '@ant-design/icons';
-import type { ProColumns } from '@jetlinks/pro-table';
-import { Badge, Button, Card, Col, Empty, Pagination, Row, Space } from 'antd';
-import { useEffect, useState } from 'react';
-import './index.less';
-import SolveComponent from '../SolveComponent';
-import SolveLog from '../SolveLog';
-import { AlarmLogModel } from '../model';
-import moment from 'moment';
-import { observer } from '@formily/reactive-react';
-import { service } from '@/pages/rule-engine/Alarm/Log';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
-import { useHistory } from 'umi';
-
-interface Props {
-  type: string;
-}
-
-const imgMap = new Map();
-imgMap.set('product', require('/public/images/alarm/product.png'));
-imgMap.set('device', require('/public/images/alarm/device.png'));
-imgMap.set('other', require('/public/images/alarm/other.png'));
-imgMap.set('org', require('/public/images/alarm/org.png'));
-
-const titleMap = new Map();
-titleMap.set('product', '产品');
-titleMap.set('device', '设备');
-titleMap.set('other', '其他');
-titleMap.set('org', '部门');
-
-const colorMap = new Map();
-colorMap.set(1, '#E50012');
-colorMap.set(2, '#FF9457');
-colorMap.set(3, '#FABD47');
-colorMap.set(4, '#999999');
-colorMap.set(5, '#C4C4C4');
-
-const TabComponent = observer((props: Props) => {
-  const columns: ProColumns<any>[] = [
-    {
-      title: '名称',
-      dataIndex: 'name',
-    },
-    {
-      title: '级别',
-      dataIndex: 'level',
-      valueType: 'select',
-      valueEnum: {
-        1: {
-          text: '级别一',
-          status: '1',
-        },
-        2: {
-          text: '级别二',
-          status: '2',
-        },
-        3: {
-          text: '级别三',
-          status: '3',
-        },
-        4: {
-          text: '级别四',
-          status: '4',
-        },
-        5: {
-          text: '级别五',
-          status: '5',
-        },
-      },
-    },
-    {
-      title: '状态',
-      dataIndex: 'state',
-      valueType: 'select',
-      valueEnum: {
-        warning: {
-          text: '告警中',
-          status: 'warning',
-        },
-        normal: {
-          text: '无告警',
-          status: 'normal',
-        },
-      },
-    },
-  ];
-
-  const [param, setParam] = useState<any>({ pageSize: 10, terms: [] });
-  const history = useHistory<Record<string, string>>();
-
-  const [dataSource, setDataSource] = useState<any>({
-    data: [],
-    pageSize: 10,
-    pageIndex: 0,
-    total: 0,
-  });
-
-  const handleSearch = (params: any) => {
-    setParam(params);
-    service
-      .query({
-        ...params,
-        terms: [
-          ...params.terms,
-          {
-            termType: 'eq',
-            column: 'targetType',
-            value: props.type,
-            type: 'and',
-          },
-        ],
-        sorts: [{ name: 'alarmDate', order: 'desc' }],
-      })
-      .then((resp) => {
-        if (resp.status === 200) {
-          setDataSource(resp.result);
-        }
-      });
-  };
-
-  useEffect(() => {
-    handleSearch(param);
-  }, [props.type]);
-
-  return (
-    <div className="alarm-log-card">
-      <SearchComponent<any>
-        field={columns}
-        target="alarm-log"
-        onSearch={(data) => {
-          const dt = {
-            pageSize: 10,
-            terms: [...data?.terms],
-          };
-          handleSearch(dt);
-        }}
-      />
-      <Card>
-        {dataSource?.data.length > 0 ? (
-          <Row gutter={24} style={{ marginTop: 10 }}>
-            {(dataSource?.data || []).map((item: any) => (
-              <Col key={item.id} span={24}>
-                <div className="alarm-log-item">
-                  <div className="alarm-log-title">
-                    <div
-                      className="alarm-log-level"
-                      style={{ backgroundColor: colorMap.get(item.level) }}
-                    >
-                      <div className="alarm-log-text">
-                        {AlarmLogModel.defaultLevel.find((i) => i.level === item.level)?.title ||
-                          item.level}
-                      </div>
-                    </div>
-                    <div className="alarm-log-title-text">{item.name}</div>
-                  </div>
-                  <div className="alarm-log-content">
-                    <div className="alarm-log-image">
-                      <img
-                        width={88}
-                        height={88}
-                        src={imgMap.get(props.type)}
-                        alt={''}
-                        style={{ marginRight: 20 }}
-                      />
-                      <div className="alarm-type">
-                        <div className="name">{titleMap.get(item.targetType)}</div>
-                        <div className="text">{item.targetName}</div>
-                      </div>
-                      <div className="alarm-log-right">
-                        <div className="alarm-log-time">
-                          <div className="log-title">最近告警时间</div>
-                          <div className="context">
-                            {moment(item.alarmDate).format('YYYY-MM-DD HH:mm:ss')}
-                          </div>
-                        </div>
-                        <div className="alarm-log-time" style={{ paddingLeft: 10 }}>
-                          <div className="log-title">状态</div>
-                          <div className="context">
-                            <Badge status={item.state.value === 'warning' ? 'error' : 'default'} />
-                            <span
-                              style={{
-                                color: item.state.value === 'warning' ? '#E50012' : 'black',
-                              }}
-                            >
-                              {item.state.text}
-                            </span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                    <div className="alarm-log-actions">
-                      <Space>
-                        {item.state.value === 'warning' && (
-                          <div className="alarm-log-action">
-                            <Button
-                              type={'link'}
-                              onClick={() => {
-                                AlarmLogModel.solveVisible = true;
-                                AlarmLogModel.current = item;
-                              }}
-                            >
-                              <div className="btn">
-                                <ToolFilled className="icon" />
-                                <div>告警处理</div>
-                              </div>
-                            </Button>
-                          </div>
-                        )}
-                        <div className="alarm-log-action">
-                          <Button
-                            type={'link'}
-                            onClick={() => {
-                              AlarmLogModel.current = item;
-                              const url = getMenuPathByParams(
-                                MENUS_CODE['rule-engine/Alarm/Log/Detail'],
-                                item.id,
-                              );
-                              history.push(url);
-                            }}
-                          >
-                            <div className="btn">
-                              <FileFilled className="icon" />
-                              <div>告警日志</div>
-                            </div>
-                          </Button>
-                        </div>
-                        <div className="alarm-log-action">
-                          <Button
-                            type={'link'}
-                            onClick={() => {
-                              AlarmLogModel.logVisible = true;
-                              AlarmLogModel.current = item;
-                            }}
-                          >
-                            <div className="btn">
-                              <FileTextFilled className="icon" />
-                              <div>处理记录</div>
-                            </div>
-                          </Button>
-                        </div>
-                      </Space>
-                    </div>
-                  </div>
-                </div>
-              </Col>
-            ))}
-          </Row>
-        ) : (
-          <Empty />
-        )}
-        {dataSource.data.length > 0 && (
-          <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-            <Pagination
-              showSizeChanger
-              size="small"
-              className={'pro-table-card-pagination'}
-              total={dataSource?.total || 0}
-              current={dataSource?.pageIndex + 1}
-              onChange={(page, size) => {
-                handleSearch({
-                  ...param,
-                  pageIndex: page - 1,
-                  pageSize: size,
-                });
-              }}
-              pageSizeOptions={[10, 20, 50, 100]}
-              pageSize={dataSource?.pageSize}
-              showTotal={(num) => {
-                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-              }}
-            />
-          </div>
-        )}
-      </Card>
-      {AlarmLogModel.solveVisible && (
-        <SolveComponent
-          close={() => {
-            AlarmLogModel.solveVisible = false;
-            AlarmLogModel.current = {};
-          }}
-          reload={() => {
-            AlarmLogModel.solveVisible = false;
-            AlarmLogModel.current = {};
-            handleSearch(param);
-          }}
-          data={AlarmLogModel.current}
-        />
-      )}
-      {AlarmLogModel.logVisible && (
-        <SolveLog
-          close={() => {
-            AlarmLogModel.logVisible = false;
-            AlarmLogModel.current = {};
-          }}
-          data={AlarmLogModel.current}
-        />
-      )}
-    </div>
-  );
-});
-
-export default TabComponent;

+ 5 - 19
src/pages/rule-engine/Alarm/Log/index.tsx

@@ -1,11 +1,6 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { observer } from '@formily/reactive-react';
-import { useEffect } from 'react';
 import { AlarmLogModel } from './model';
-import TabComponent from './TabComponent';
-import Service from './service';
-
-export const service = new Service('alarm/record');
 
 const Log = observer(() => {
   const list = [
@@ -18,7 +13,7 @@ const Log = observer(() => {
       tab: '设备',
     },
     {
-      key: 'org',
+      key: 'department',
       tab: '部门',
     },
     {
@@ -26,24 +21,15 @@ const Log = observer(() => {
       tab: '其他',
     },
   ];
-
-  useEffect(() => {
-    service.queryDefaultLevel().then((resp) => {
-      if (resp.status === 200) {
-        AlarmLogModel.defaultLevel = resp.result?.levels || [];
-      }
-    });
-  }, []);
-
   return (
     <PageContainer
-      onTabChange={(key: string) => {
-        AlarmLogModel.tab = key;
-      }}
+      // onTabChange={(key: 'product' | 'device' | 'department' | 'other') => {
+      //     AlarmLogModel.tab = key
+      // }}
       tabList={list}
       tabActiveKey={AlarmLogModel.tab}
     >
-      <TabComponent type={AlarmLogModel.tab} />
+      test
     </PageContainer>
   );
 });

+ 1 - 32
src/pages/rule-engine/Alarm/Log/model.ts

@@ -1,38 +1,7 @@
 import { model } from '@formily/reactive';
-import type { ProColumns } from '@jetlinks/pro-table';
 
 export const AlarmLogModel = model<{
-  tab: string;
-  current: Partial<AlarmLogItem>;
-  solveVisible: boolean;
-  logVisible: boolean;
-  defaultLevel: {
-    level: number;
-    title: string;
-  }[];
-  columns: ProColumns<AlarmLogHistoryItem>[];
+  tab: 'product' | 'device' | 'department' | 'other';
 }>({
   tab: 'product',
-  current: {},
-  solveVisible: false,
-  logVisible: false,
-  defaultLevel: [],
-  columns: [
-    {
-      dataIndex: 'alarmTime',
-      title: '告警时间',
-    },
-    {
-      dataIndex: 'alarmName',
-      title: '告警名称',
-    },
-    {
-      dataIndex: 'description',
-      title: '说明',
-    },
-    {
-      dataIndex: 'action',
-      title: '操作',
-    },
-  ],
 });

+ 0 - 35
src/pages/rule-engine/Alarm/Log/service.ts

@@ -1,35 +0,0 @@
-import BaseService from '@/utils/BaseService';
-import { request } from 'umi';
-import SystemConst from '@/utils/const';
-
-class Service extends BaseService<AlarmLogItem> {
-  getTypes = () =>
-    request(`/${SystemConst.API_BASE}/relation/types`, {
-      method: 'GET',
-    });
-
-  handleLog = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/alarm/record/${id}/_handle`, {
-      method: 'POST',
-      data,
-    });
-
-  queryDefaultLevel = () =>
-    request(`/${SystemConst.API_BASE}/alarm/config/default/level`, {
-      method: 'GET',
-    });
-
-  queryHandleHistory = (data: any) =>
-    request(`/${SystemConst.API_BASE}/alarm/record/handle-history/_query`, {
-      method: 'POST',
-      data,
-    });
-
-  queryHistoryList = (data: any) =>
-    request(`/${SystemConst.API_BASE}/alarm/history/_query`, {
-      method: 'POST',
-      data,
-    });
-}
-
-export default Service;

+ 0 - 38
src/pages/rule-engine/Alarm/Log/typings.d.ts

@@ -1,38 +0,0 @@
-type AlarmLogItem = {
-  id?: string;
-  name: string;
-  alarmConfigId: string;
-  alarmName: string;
-  targetType: string;
-  targetName: string;
-  targetTypeName: string;
-  alarmDate: number;
-  level: number;
-  description?: string;
-  state: Record<string, any>;
-};
-
-type AlarmLogSolveHistoryItem = {
-  id: string;
-  alarmId: string;
-  alarmRecordId: string;
-  handleType: string;
-  description: string;
-  creatorId?: string;
-  createTime: number;
-};
-
-type AlarmLogHistoryItem = {
-  id: string;
-  alarmId: string;
-  alarmConfigId: string;
-  alarmConfigName: string;
-  alarmRecordId: string;
-  level: number;
-  description: string;
-  alarmTime?: number;
-  targetType: string;
-  targetName: string;
-  targetId: string;
-  alarmInfo: string;
-};

+ 162 - 0
src/pages/rule-engine/Scene/Save/action/action.tsx

@@ -0,0 +1,162 @@
+import { Button, InputNumber, Select, Form } from 'antd';
+import { useEffect, useState } from 'react';
+import { useRequest } from 'umi';
+import { queryMessageType, queryMessageConfig, queryMessageTemplate } from './service';
+import MessageContent from './messageContent';
+
+interface ActionProps {
+  restField: any;
+  name: number;
+  title?: string;
+  onRemove: () => void;
+}
+
+const ActionItem = (props: ActionProps) => {
+  const { name } = props;
+  const [type1, setType1] = useState('');
+  const [templateData, setTemplateData] = useState<any>(undefined);
+
+  const { data: messageType, run: queryMessageTypes } = useRequest(queryMessageType, {
+    manual: true,
+    formatResult: (res) => res.result,
+  });
+
+  const {
+    data: messageConfig,
+    run: queryMessageConfigs,
+    loading: messageConfigLoading,
+  } = useRequest(queryMessageConfig, {
+    manual: true,
+    formatResult: (res) => res.result,
+  });
+
+  const {
+    data: messageTemplate,
+    run: queryMessageTemplates,
+    loading: messageTemplateLoading,
+  } = useRequest(queryMessageTemplate, {
+    manual: true,
+    formatResult: (res) => res.result,
+  });
+
+  const MessageNodes = (
+    <>
+      <Form.Item {...props.restField} name={[name, 'notify', 'type']}>
+        <Select
+          options={messageType}
+          fieldNames={{ value: 'id', label: 'name' }}
+          placeholder={'请选择通知方式'}
+          style={{ width: 140 }}
+          onChange={async (key: string) => {
+            setTemplateData(undefined);
+            await queryMessageConfigs({
+              terms: [{ column: 'type$IN', value: key }],
+            });
+          }}
+        />
+      </Form.Item>
+      <Form.Item {...props.restField} name={[name, 'notify', 'notifierId']}>
+        <Select
+          options={messageConfig}
+          loading={messageConfigLoading}
+          fieldNames={{ value: 'id', label: 'name' }}
+          onChange={async (key: string) => {
+            setTemplateData(undefined);
+            await queryMessageTemplates({
+              terms: [{ column: 'configId', value: key }],
+            });
+          }}
+          style={{ width: 160 }}
+          placeholder={'请选择通知配置'}
+        />
+      </Form.Item>
+      <Form.Item {...props.restField} name={[name, 'notify', 'templateId']}>
+        <Select
+          options={messageTemplate}
+          loading={messageTemplateLoading}
+          fieldNames={{ value: 'id', label: 'name' }}
+          style={{ width: 160 }}
+          placeholder={'请选择通知模板'}
+          onChange={async (key: string, nodeData: any) => {
+            setTemplateData(nodeData);
+          }}
+        />
+      </Form.Item>
+    </>
+  );
+
+  const DeviceNodes = (
+    <>
+      <Select options={[]} placeholder={'请选择产品'} style={{ width: 220 }} />
+      <Select
+        defaultValue={'1'}
+        options={[
+          { label: '固定设备', value: '1' },
+          { label: '按标签', value: '2' },
+          { label: '按关系', value: '3' },
+        ]}
+        style={{ width: 120 }}
+      />
+      <Select options={[]} placeholder={'请选择'} style={{ width: 180 }} />
+      <Select
+        defaultValue={'1'}
+        options={[
+          { label: '设置属性', value: '1' },
+          { label: '功能调用', value: '2' },
+          { label: '读取属性', value: '3' },
+        ]}
+        style={{ width: 120 }}
+      />
+    </>
+  );
+
+  useEffect(() => {
+    if (type1 === 'message') {
+      queryMessageTypes();
+    }
+  }, [type1]);
+
+  const TimeTypeAfter = (
+    <Select
+      defaultValue={'second'}
+      options={[
+        { label: '秒', value: 'second' },
+        { label: '分', value: 'minute' },
+        { label: '小时', value: 'hour' },
+      ]}
+    />
+  );
+
+  return (
+    <div className={'actions-item'}>
+      <div className={'actions-item-title'}>
+        执行动作 {props.title} <Button onClick={props.onRemove}>删除</Button>
+      </div>
+      <div style={{ display: 'flex', gap: 12 }}>
+        <Form.Item {...props.restField} name={[name, 'executor']}>
+          <Select
+            options={[
+              { label: '消息通知', value: 'message' },
+              { label: '设备输出', value: 'device' },
+              { label: '延迟执行', value: 'delay' },
+            ]}
+            style={{ width: 100 }}
+            onSelect={(key: string) => {
+              setType1(key);
+            }}
+          />
+        </Form.Item>
+        {type1 === 'message' && MessageNodes}
+        {type1 === 'device' && DeviceNodes}
+        {type1 === 'delay' && (
+          <InputNumber addonAfter={TimeTypeAfter} style={{ width: 150 }} min={0} max={9999} />
+        )}
+        {type1 === 'message' && templateData ? (
+          <MessageContent template={templateData} name={props.name} />
+        ) : null}
+      </div>
+    </div>
+  );
+};
+
+export default ActionItem;

+ 18 - 0
src/pages/rule-engine/Scene/Save/action/index.less

@@ -0,0 +1,18 @@
+.actions-items {
+  padding: 26px 24px 12px 24px;
+  background-color: #fafafa;
+
+  .actions-item {
+    .actions-item-title {
+      padding-bottom: 18px;
+    }
+
+    &:not(:first-child) {
+      margin-top: 18px;
+    }
+
+    .template-variable {
+      margin-top: 12px;
+    }
+  }
+}

+ 26 - 0
src/pages/rule-engine/Scene/Save/action/index.tsx

@@ -0,0 +1,26 @@
+import ActionItem from './action';
+import './index.less';
+import { ProFormList } from '@ant-design/pro-form';
+
+export default () => {
+  return (
+    <div className={'actions-items'}>
+      <ProFormList
+        name={'actions'}
+        creatorButtonProps={{
+          creatorButtonText: '新增',
+        }}
+      >
+        {(meta, index, action) => {
+          return (
+            <ActionItem
+              onRemove={() => {
+                action.remove?.(index);
+              }}
+            />
+          );
+        }}
+      </ProFormList>
+    </div>
+  );
+};

+ 60 - 0
src/pages/rule-engine/Scene/Save/action/messageContent.tsx

@@ -0,0 +1,60 @@
+import { Col, Form, Row, Select, TimePicker, Input } from 'antd';
+import { ItemGroup } from '@/pages/rule-engine/Scene/Save/components';
+
+interface MessageContentProps {
+  name: number;
+  template?: any;
+}
+
+const rowGutter = 12;
+
+export default (props: MessageContentProps) => {
+  const inputNodeByType = (data: any) => {
+    switch (data.type) {
+      case 'enum':
+        return <Select placeholder={`请选择${data.name}`} style={{ width: '100%' }} />;
+      case 'timmer':
+        return <TimePicker style={{ width: '100%' }} />;
+      case 'number':
+        return <Input placeholder={`请输入${data.name}`} style={{ width: '100%' }} />;
+      default:
+        return <Input placeholder={`请输入${data.name}`} />;
+    }
+  };
+
+  return (
+    <>
+      {props.template && (
+        <div className={'template-variable'}>
+          {props.template.variableDefinitions ? (
+            <Row gutter={rowGutter}>
+              {props.template.variableDefinitions.map((item: any) => {
+                // const rules = !item.required ? [{ required: true, message: '请输入'+ item.name }] : undefined
+                return (
+                  <Col span={12} key={item.id}>
+                    <Form.Item
+                      name={[props.name, 'notify', 'variables', item.id]}
+                      label={item.name}
+                    >
+                      <ItemGroup>
+                        <Select
+                          defaultValue={'1'}
+                          options={[
+                            { label: '手动输入', value: '1' },
+                            { label: '内置参数', value: '2' },
+                          ]}
+                          style={{ width: 120 }}
+                        />
+                        {inputNodeByType(item)}
+                      </ItemGroup>
+                    </Form.Item>
+                  </Col>
+                );
+              })}
+            </Row>
+          ) : null}
+        </div>
+      )}
+    </>
+  );
+};

+ 28 - 0
src/pages/rule-engine/Scene/Save/action/service.ts

@@ -0,0 +1,28 @@
+import { request } from '@@/plugin-request/request';
+import SystemConst from '@/utils/const';
+
+export const queryMessageType = () =>
+  request(`${SystemConst.API_BASE}/notifier/config/types`, { method: 'GET' });
+
+// 通知配置
+export const queryMessageConfig = (data: any) =>
+  request(`${SystemConst.API_BASE}/notifier/config/_query/no-paging?paging=false`, {
+    method: 'POST',
+    data,
+  });
+
+// 通知模板
+export const queryMessageTemplate = (data: any) =>
+  request(`${SystemConst.API_BASE}/notifier/template/_query/no-paging?paging=false`, {
+    method: 'POST',
+    data,
+  });
+
+export const queryProductList = (data?: any) =>
+  request(`${SystemConst.API_BASE}/device-product/_query/no-paging?paging=false`, {
+    method: 'POST',
+    data,
+  });
+
+export const queryDeviceSelector = () =>
+  request(`${SystemConst.API_BASE}/scene/device-selectors`, { method: 'GET' });

+ 7 - 0
src/pages/rule-engine/Scene/Save/components/ItemGroup/index.less

@@ -0,0 +1,7 @@
+.group-item-compact {
+  display: flex;
+
+  &:nth-last-child(1) {
+    flex: 1;
+  }
+}

+ 10 - 0
src/pages/rule-engine/Scene/Save/components/ItemGroup/index.tsx

@@ -0,0 +1,10 @@
+import React from 'react';
+import './index.less';
+
+interface ItemGroupProps {
+  children?: React.ReactNode;
+}
+
+export default (props: ItemGroupProps) => {
+  return <div className={'group-item-compact'}>{props.children}</div>;
+};

+ 49 - 0
src/pages/rule-engine/Scene/Save/components/TimeSelect/index.tsx

@@ -0,0 +1,49 @@
+import { TreeSelect } from 'antd';
+import React, { useCallback, useEffect, useState } from 'react';
+
+type OptionsItemType = {
+  label: string;
+  value: string | number;
+};
+
+interface TimeSelectProps {
+  options?: OptionsItemType[];
+  value?: string;
+  onChange?: (value: string[]) => void;
+  style?: React.CSSProperties;
+}
+
+export default (props: TimeSelectProps) => {
+  const [checkedKeys, setCheckedKeys] = useState<any[]>([]);
+
+  const onChange = useCallback(
+    (keys: string[], _, extra) => {
+      if (extra.triggerValue === 'all') {
+        const newKeys = extra.checked ? ['all', ...props.options!.map((item) => item.value)] : [];
+        setCheckedKeys(newKeys);
+      } else {
+        const noAllKeys = keys.filter((key) => key !== 'all');
+        const newKeys = noAllKeys.length === props.options!.length ? ['all', ...keys] : noAllKeys;
+
+        setCheckedKeys(newKeys);
+      }
+    },
+    [checkedKeys, props.options],
+  );
+
+  useEffect(() => {}, [checkedKeys]);
+
+  return (
+    <TreeSelect
+      treeCheckable
+      value={checkedKeys}
+      onChange={onChange}
+      style={props.style}
+      treeData={
+        props.options && props.options.length
+          ? [{ label: '全部', value: 'all' }, ...props.options]
+          : []
+      }
+    />
+  );
+};

+ 89 - 0
src/pages/rule-engine/Scene/Save/components/TimingTrigger/index.tsx

@@ -0,0 +1,89 @@
+import { Select, Input, TimePicker, InputNumber } from 'antd';
+import { TimeSelect } from '@/pages/rule-engine/Scene/Save/components';
+import { useState } from 'react';
+
+export default () => {
+  const [type1, setType1] = useState(1);
+  const [type2, setType2] = useState(1);
+
+  const type1Select = async (key: number) => {
+    setType1(key);
+    if (key !== 3) {
+      // TODO 请求后端返回天数
+    }
+  };
+
+  const type2Select = (key: number) => {
+    setType2(key);
+  };
+
+  const TimeTypeAfter = (
+    <Select
+      defaultValue={'second'}
+      options={[
+        { label: '秒', value: 'second' },
+        { label: '分', value: 'minute' },
+        { label: '小时', value: 'hour' },
+      ]}
+    />
+  );
+
+  const implementNode =
+    type2 === 1 ? (
+      <>
+        <TimePicker.RangePicker />
+        <span> 每 </span>
+        <InputNumber addonAfter={TimeTypeAfter} style={{ width: 150 }} min={0} max={9999} />
+        <span> 执行一次 </span>
+      </>
+    ) : (
+      <>
+        <TimePicker />
+        <InputNumber addonAfter={TimeTypeAfter} style={{ width: 150 }} min={0} max={9999} />
+        <span> 执行一次 </span>
+      </>
+    );
+
+  return (
+    <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
+      <Select
+        options={[
+          { label: '按周', value: 1 },
+          { label: '按月', value: 2 },
+          { label: 'cron表达式', value: 3 },
+        ]}
+        value={type1}
+        onSelect={type1Select}
+        style={{ width: 120 }}
+      />
+      {type1 !== 3 ? (
+        <>
+          <TimeSelect
+            options={[
+              { label: '周一', value: 1 },
+              { label: '周二', value: 2 },
+              { label: '周三', value: 3 },
+              { label: '周四', value: 4 },
+              { label: '周五', value: 5 },
+              { label: '周六', value: 6 },
+              { label: '周末', value: 7 },
+            ]}
+            style={{ width: 150 }}
+          />
+          <Select
+            options={[
+              { label: '周期执行', value: 1 },
+              { label: '执行一次', value: 2 },
+            ]}
+            value={type2}
+            style={{ width: 150 }}
+            onSelect={type2Select}
+          />
+          {implementNode}
+        </>
+      ) : (
+        <Input placeholder={'请输入cron表达式'} style={{ width: 400 }} />
+      )}
+    </div>
+  );
+};

+ 24 - 0
src/pages/rule-engine/Scene/Save/components/TriggerWay/index.less

@@ -0,0 +1,24 @@
+@import '~antd/es/style/themes/default.less';
+
+.trigger-way-warp {
+  display: flex;
+  gap: 20px;
+  width: 100%;
+
+  .trigger-way-item {
+    padding: 20px 16px;
+    border: 1px solid #e0e4e8;
+    border-radius: 2px;
+    cursor: pointer;
+    transition: all 0.3s;
+
+    &:hover {
+      color: @primary-color-hover;
+    }
+
+    &.active {
+      color: @primary-color-active;
+      border-color: @primary-color-active;
+    }
+  }
+}

+ 65 - 0
src/pages/rule-engine/Scene/Save/components/TriggerWay/index.tsx

@@ -0,0 +1,65 @@
+import { useEffect, useState } from 'react';
+import classNames from 'classnames';
+import './index.less';
+
+interface TriggerWayProps {
+  value?: string;
+  onChange?: (type: string) => void;
+}
+
+enum TriggerWayType {
+  manual = 'manual',
+  timing = 'timer',
+  device = 'device',
+}
+
+export default (props: TriggerWayProps) => {
+  const [type, setType] = useState(props.value || '');
+
+  useEffect(() => {
+    setType(props.value || '');
+  }, [props.value]);
+
+  const onSelect = (_type: string) => {
+    setType(_type);
+
+    if (props.onChange) {
+      props.onChange(_type);
+    }
+  };
+
+  return (
+    <div className={'trigger-way-warp'}>
+      <div
+        className={classNames('trigger-way-item', {
+          active: type === TriggerWayType.manual,
+        })}
+        onClick={() => {
+          onSelect(TriggerWayType.manual);
+        }}
+      >
+        手动触发
+      </div>
+      <div
+        className={classNames('trigger-way-item', {
+          active: type === TriggerWayType.timing,
+        })}
+        onClick={() => {
+          onSelect(TriggerWayType.timing);
+        }}
+      >
+        定时触发
+      </div>
+      <div
+        className={classNames('trigger-way-item', {
+          active: type === TriggerWayType.device,
+        })}
+        onClick={() => {
+          onSelect(TriggerWayType.device);
+        }}
+      >
+        设备触发
+      </div>
+    </div>
+  );
+};

+ 4 - 0
src/pages/rule-engine/Scene/Save/components/index.ts

@@ -0,0 +1,4 @@
+export { default as TimeSelect } from './TimeSelect';
+export { default as TimingTrigger } from './TimingTrigger';
+export { default as TriggerWay } from './TriggerWay';
+export { default as ItemGroup } from './ItemGroup';

+ 67 - 1
src/pages/rule-engine/Scene/Save/index.tsx

@@ -1 +1,67 @@
-export default () => {};
+import { PageContainer } from '@ant-design/pro-layout';
+import { Button, Card, Form } from 'antd';
+import { useLocation } from 'umi';
+import { useEffect } from 'react';
+import { PermissionButton } from '@/components';
+import ActionItems from './action/action';
+import { PlusOutlined } from '@ant-design/icons';
+
+export default () => {
+  const location = useLocation();
+  const [form] = Form.useForm();
+
+  const { getOtherPermission } = PermissionButton.usePermission('rule-engine/Scene');
+
+  const getDetail = async () => {
+    // TODO 回显数据
+  };
+
+  useEffect(() => {
+    const params = new URLSearchParams(location.search);
+    const id = params.get('id');
+    if (id) {
+      getDetail();
+    }
+  }, [location]);
+
+  const saveData = async () => {
+    const formData = await form.validateFields();
+    console.log(formData);
+  };
+
+  return (
+    <PageContainer>
+      <Card>
+        <Form form={form} autoComplete="off">
+          <Form.List name="users">
+            {(fields, { add, remove }) => (
+              <>
+                {fields.map(({ key, name, ...restField }) => (
+                  <ActionItems
+                    key={key}
+                    restField={restField}
+                    onRemove={() => remove(name)}
+                    name={name}
+                  />
+                ))}
+                <Form.Item>
+                  <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
+                    新增
+                  </Button>
+                </Form.Item>
+              </>
+            )}
+          </Form.List>
+          <Form.Item>
+            <Button type="primary" htmlType="submit">
+              Submit
+            </Button>
+          </Form.Item>
+        </Form>
+        <PermissionButton isPermission={getOtherPermission(['add', 'update'])} onClick={saveData}>
+          保存
+        </PermissionButton>
+      </Card>
+    </PageContainer>
+  );
+};

+ 10 - 0
src/pages/rule-engine/Scene/Save/trigger/index.tsx

@@ -0,0 +1,10 @@
+import { Form } from 'antd';
+import TriggerWay from '../components/TriggerWay';
+
+export default () => {
+  return (
+    <Form.Item>
+      <TriggerWay />
+    </Form.Item>
+  );
+};

+ 360 - 0
src/pages/rule-engine/Scene/Save2/index.tsx

@@ -0,0 +1,360 @@
+import {
+  FormItem,
+  Editable,
+  Input,
+  Select,
+  Radio,
+  DatePicker,
+  ArrayItems,
+  FormButtonGroup,
+  Submit,
+  Space,
+  FormLayout,
+  FormGrid,
+  NumberPicker,
+} from '@formily/antd';
+import { createForm, onFieldReact, FieldDataSource, onFieldValueChange } from '@formily/core';
+import type { Field } from '@formily/core';
+import { FormProvider, createSchemaField } from '@formily/react';
+import { action } from '@formily/reactive';
+import {
+  queryMessageConfig,
+  queryMessageTemplate,
+  queryMessageType,
+  queryProductList,
+} from '@/pages/rule-engine/Scene/Save/action/service';
+import { Card } from 'antd';
+import { useMemo } from 'react';
+import type { ISchema } from '@formily/json-schema';
+
+export default () => {
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Editable,
+      DatePicker,
+      Space,
+      Radio,
+      Input,
+      Select,
+      ArrayItems,
+      FormLayout,
+      FormGrid,
+      NumberPicker,
+    },
+  });
+
+  const schema: ISchema = {
+    type: 'object',
+    properties: {
+      layout: {
+        type: 'void',
+        'x-component': 'FormLayout',
+        'x-component-props': {
+          layout: 'vertical',
+        },
+        properties: {
+          actions: {
+            type: 'array',
+            'x-component': 'ArrayItems',
+            'x-decorator': 'FormItem',
+            title: '执行动作',
+            items: {
+              type: 'object',
+              title: '执行动作',
+              properties: {
+                space: {
+                  type: 'void',
+                  'x-component': 'Space',
+                  properties: {
+                    executor: {
+                      type: 'string',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'Select',
+                      'x-component-props': {
+                        style: {
+                          width: 160,
+                        },
+                      },
+                      enum: [
+                        { label: '消息通知', value: 'message' },
+                        { label: '设备输出', value: 'device' },
+                        { label: '延迟执行', value: 'delay' },
+                      ],
+                    },
+                    notify: {
+                      type: 'object',
+                      'x-decorator': 'FormItem',
+                      'x-reactions': [
+                        {
+                          dependencies: ['.executor'],
+                          fulfill: {
+                            state: {
+                              visible: "{{$deps[0] === 'message'}}",
+                            },
+                          },
+                        },
+                      ],
+                      properties: {
+                        grid: {
+                          type: 'void',
+                          'x-component': 'FormGrid',
+                          'x-component-props': {
+                            minColumns: [4, 6, 10],
+                          },
+                          properties: {
+                            messageType: {
+                              type: 'string',
+                              'x-decorator': 'FormItem',
+                              'x-component': 'Select',
+                              'x-component-props': {
+                                style: { width: 160 },
+                                fieldNames: { label: 'name', value: 'id' },
+                              },
+                              'x-reactions': ['{{useAsyncDataSource(getMessageType)}}'],
+                            },
+                            notifierId: {
+                              type: 'string',
+                              'x-decorator': 'FormItem',
+                              'x-component': 'Select',
+                              'x-component-props': {
+                                style: { width: 160 },
+                                fieldNames: { label: 'name', value: 'id' },
+                              },
+                            },
+                            templateId: {
+                              type: 'string',
+                              'x-decorator': 'FormItem',
+                              'x-component': 'Select',
+                              'x-component-props': {
+                                style: { width: 160 },
+                              },
+                            },
+                          },
+                        },
+                      },
+                    },
+                    variables: {
+                      type: 'object',
+                      'x-decorator': 'FormItem',
+                      properties: {},
+                      'x-reactions': [
+                        {
+                          dependencies: ['.executor'],
+                          fulfill: {
+                            state: {
+                              visible: "{{$deps[0] === 'message'}}",
+                            },
+                          },
+                        },
+                      ],
+                    },
+                    device: {
+                      type: 'object',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'Space',
+                      'x-reactions': [
+                        {
+                          dependencies: ['.executor'],
+                          fulfill: {
+                            state: {
+                              visible: "{{$deps[0] === 'device'}}",
+                            },
+                          },
+                        },
+                      ],
+                      properties: {
+                        productId: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Select',
+                          'x-component-props': {
+                            style: { width: 200 },
+                            fieldNames: { label: 'name', value: 'id' },
+                          },
+                          'x-reactions': ['{{useAsyncDataSource(getProductList)}}'],
+                        },
+                        selector: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Select',
+                          'x-component-props': {
+                            style: { width: 200 },
+                          },
+                          enum: [
+                            { label: '固定设备', value: 'device' },
+                            { label: '按标签', value: 'tag' },
+                            { label: '按关系', value: 'relation' },
+                          ],
+                        },
+                        'message.messageType': {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Select',
+                          'x-component-props': {
+                            style: { width: 160 },
+                          },
+                          enum: [
+                            { label: '功能调用', value: 'INVOKE_FUNCTION' },
+                            { label: '读取属性', value: 'READ_PROPERTY' },
+                            { label: '设置属性', value: 'WRITE_PROPERTY' },
+                          ],
+                        },
+                        value: {
+                          type: 'object',
+                          'x-decorator': 'FormItem',
+                        },
+                      },
+                    },
+                    delay: {
+                      type: 'object',
+                      'x-decorator': 'FormItem',
+                      'x-reactions': [
+                        {
+                          dependencies: ['.executor'],
+                          fulfill: {
+                            state: {
+                              visible: "{{$deps[0] === 'delay'}}",
+                            },
+                          },
+                        },
+                      ],
+                      properties: {
+                        time: {
+                          type: 'number',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'NumberPicker',
+                          'x-component-props': {
+                            style: {
+                              width: 240,
+                            },
+                          },
+                        },
+                        unit: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Select',
+                          'x-component-props': {
+                            style: {
+                              width: 160,
+                            },
+                          },
+                          enum: [
+                            { label: '秒', value: 'seconds' },
+                            { label: '分', value: 'minutes' },
+                            { label: '小时', value: 'hours' },
+                          ],
+                        },
+                      },
+                    },
+                  },
+                },
+                remove: {
+                  type: 'void',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'ArrayItems.Remove',
+                },
+              },
+            },
+            properties: {
+              add: {
+                type: 'void',
+                title: '添加条目',
+                'x-component': 'ArrayItems.Addition',
+              },
+            },
+          },
+        },
+      },
+    },
+  };
+
+  const form = useMemo(
+    () =>
+      createForm({
+        effects: () => {
+          onFieldReact('actions.*.notify.notifierId', async (field, f) => {
+            const key = field.query('.messageType').get('value');
+            f.clearFormGraph('.variables');
+            (field as Field).value = undefined;
+            if (key) {
+              (field as Field).loading = true;
+              const resp = await queryMessageConfig({ terms: [{ column: 'type$IN', value: key }] });
+              (field as Field).loading = false;
+              if (resp.status === 200) {
+                (field as Field).dataSource = resp.result;
+              }
+            }
+          });
+          onFieldReact('actions.*.notify.templateId', async (field) => {
+            const key = field.query('.notifierId').get('value');
+            (field as Field).value = undefined;
+            if (key) {
+              (field as Field).loading = true;
+              const resp = await queryMessageTemplate({
+                terms: [{ column: 'configId', value: key }],
+              });
+              (field as Field).loading = false;
+              if (resp.status === 200) {
+                (field as Field).dataSource = resp.result.map((item: any) => ({
+                  label: item.name,
+                  value: item.id,
+                  data: item,
+                }));
+              }
+            }
+          });
+          onFieldValueChange('actions.*.notify.templateId', async (field) => {
+            console.log(field);
+
+            const templateData = field.dataSource.find((item) => item.value === field.value);
+            if (templateData) {
+              const data = templateData.data;
+              if (data.variableDefinitions) {
+                const obj = {};
+                data.variableDefinitions.forEach((item: any) => {
+                  obj[item.id] = {
+                    title: item.name,
+                    type: 'string',
+                    'x-decorator': 'FormItem',
+                    'x-component': 'Input',
+                  };
+                });
+              }
+            }
+          });
+        },
+      }),
+    [],
+  );
+
+  const getMessageType = async () => await queryMessageType();
+
+  const getProductList = async () =>
+    await queryProductList({ sorts: [{ name: 'createTime', order: 'desc' }] });
+
+  const useAsyncDataSource =
+    (services: (arg0: Field) => Promise<FieldDataSource>) => (field: Field) => {
+      field.loading = true;
+      services(field).then(
+        action.bound!((resp: any) => {
+          field.dataSource = resp.result;
+          field.loading = false;
+        }),
+      );
+    };
+
+  return (
+    <Card>
+      <FormProvider form={form}>
+        <SchemaField
+          schema={schema}
+          scope={{ useAsyncDataSource, getMessageType, getProductList }}
+        />
+        <FormButtonGroup>
+          <Submit onSubmit={console.log}>提交</Submit>
+        </FormButtonGroup>
+      </FormProvider>
+    </Card>
+  );
+};

+ 12 - 4
src/pages/rule-engine/Scene/index.tsx

@@ -1,5 +1,5 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { useRef, useState } from 'react';
+import React, { useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import { Badge, message } from 'antd';
@@ -8,7 +8,10 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { PermissionButton, ProTableCard } from '@/components';
 import { statusMap } from '@/pages/device/Instance';
 import SearchComponent from '@/components/SearchComponent';
+import SceneCard from '@/components/ProTableCard/CardItems/scene';
 import Service from './service';
+import { useHistory } from 'umi';
+import { getMenuPathByCode } from '@/utils/menu';
 
 export const service = new Service('rule-engine/scene');
 
@@ -17,8 +20,9 @@ const Scene = () => {
   const actionRef = useRef<ActionType>();
   const { permission } = PermissionButton.usePermission('rule-engine/Scene');
   const [searchParams, setSearchParams] = useState<any>({});
+  const history = useHistory();
 
-  const Tools = (record: any, type: 'card' | 'table') => {
+  const Tools = (record: any, type: 'card' | 'table'): React.ReactNode[] => {
     return [
       <PermissionButton
         key={'update'}
@@ -189,7 +193,7 @@ const Scene = () => {
           setSearchParams(data);
         }}
       />
-      <ProTableCard
+      <ProTableCard<SceneItem>
         columns={columns}
         actionRef={actionRef}
         params={searchParams}
@@ -213,7 +217,10 @@ const Scene = () => {
             icon={<PlusOutlined />}
             type="primary"
             isPermission={permission.add}
-            onClick={() => {}}
+            onClick={() => {
+              const url = getMenuPathByCode('rule-engine/Scene/Save');
+              history.push(url);
+            }}
           >
             {intl.formatMessage({
               id: 'pages.data.option.add',
@@ -221,6 +228,7 @@ const Scene = () => {
             })}
           </PermissionButton>,
         ]}
+        cardRender={(record) => <SceneCard {...record} tools={Tools(record, 'card')} />}
       />
     </PageContainer>
   );

+ 6 - 3
src/pages/rule-engine/Scene/typings.d.ts

@@ -1,4 +1,4 @@
-import type { BaseItem, State } from '@/utils/typings';
+import type { State } from '@/utils/typings';
 
 type Action = {
   executor: string;
@@ -10,9 +10,12 @@ type Trigger = {
   device: Record<string, unknown>;
 };
 
-type SceneItem = {
+interface SceneItem {
   parallel: boolean;
   state: State;
   actions: Action[];
   triggers: Trigger[];
-} & BaseItem;
+  id: string;
+  name: string;
+  describe: string;
+}

+ 4 - 1
src/pages/system/Menu/Detail/edit.tsx

@@ -159,7 +159,10 @@ export default (props: EditProps) => {
                       defaultMessage: '名称',
                     })}
                     required={true}
-                    rules={[{ required: true, message: '请输入名称' }]}
+                    rules={[
+                      { required: true, message: '请输入名称' },
+                      { max: 64, message: '最多可输入64个字符' },
+                    ]}
                   >
                     <Input disabled={disabled} placeholder={'请输入名称'} />
                   </Form.Item>

+ 0 - 251
src/pages/system/Relationship/Save/index.tsx

@@ -1,251 +0,0 @@
-import { useIntl } from 'umi';
-import type { Field } from '@formily/core';
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
-import React from 'react';
-import * as ICONS from '@ant-design/icons';
-import { Form, FormGrid, FormItem, Input, Select } from '@formily/antd';
-import type { ISchema } from '@formily/json-schema';
-import { action } from '@formily/reactive';
-import type { Response } from '@/utils/typings';
-import { service } from '@/pages/system/Relationship';
-import { Modal } from '@/components';
-import { message } from 'antd';
-
-interface Props {
-  data: Partial<ReationItem>;
-  close: () => void;
-}
-
-const Save = (props: Props) => {
-  const intl = useIntl();
-
-  const getTypes = () => service.getTypes();
-
-  const useAsyncDataSource = (api: any) => (field: Field) => {
-    field.loading = true;
-    api(field).then(
-      action.bound!((resp: Response<any>) => {
-        field.dataSource = resp.result?.map((item: Record<string, unknown>) => ({
-          ...item,
-          label: item.name,
-          value: JSON.stringify({
-            objectType: item.id,
-            objectTypeName: item.name,
-          }),
-        }));
-        field.loading = false;
-      }),
-    );
-  };
-
-  const form = createForm({
-    validateFirst: true,
-    initialValues: props.data,
-  });
-
-  const SchemaField = createSchemaField({
-    components: {
-      FormItem,
-      Input,
-      Select,
-      FormGrid,
-    },
-    scope: {
-      icon(name: any) {
-        return React.createElement(ICONS[name]);
-      },
-    },
-  });
-
-  const schema: ISchema = {
-    type: 'object',
-    properties: {
-      layout: {
-        type: 'void',
-        'x-decorator': 'FormGrid',
-        'x-decorator-props': {
-          maxColumns: 2,
-          minColumns: 2,
-          columnGap: 24,
-        },
-        properties: {
-          name: {
-            title: '名称',
-            type: 'string',
-            'x-decorator': 'FormItem',
-            'x-component': 'Input',
-            'x-decorator-props': {
-              gridSpan: 2,
-            },
-            'x-component-props': {
-              placeholder: '请输入名称',
-            },
-            name: 'name',
-            'x-validator': [
-              {
-                max: 64,
-                message: '最多可输入64个字符',
-              },
-              {
-                required: true,
-                message: '请输入名称',
-              },
-            ],
-          },
-          relation: {
-            title: '标识',
-            'x-decorator-props': {
-              gridSpan: 2,
-            },
-            type: 'string',
-            'x-disabled': !!props.data?.id,
-            'x-decorator': 'FormItem',
-            'x-component': 'Input',
-            'x-component-props': {
-              placeholder: '请输入标识',
-            },
-            'x-validator': [
-              {
-                max: 64,
-                message: '最多可输入64个字符',
-              },
-              {
-                required: true,
-                message: '请输入标识',
-              },
-              // {
-              //   triggerType: 'onBlur',
-              //   // validator: (value: string) => {
-              //   //   return new Promise((resolve) => {
-              //   //     service
-              //   //       .validateField('username', value)
-              //   //       .then((resp) => {
-              //   //         if (resp.status === 200) {
-              //   //           if (resp.result.passed) {
-              //   //             resolve('');
-              //   //           } else {
-              //   //             resolve(model === 'edit' ? '' : resp.result.reason);
-              //   //           }
-              //   //         }
-              //   //         resolve('');
-              //   //       })
-              //   //       .catch(() => {
-              //   //         return '验证失败!';
-              //   //       });
-              //   //   });
-              //   // },
-              // },
-            ],
-            name: 'relation',
-            required: true,
-          },
-          object: {
-            title: '关联方',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            type: 'string',
-            'x-decorator': 'FormItem',
-            'x-disabled': !!props.data?.id,
-            'x-component': 'Select',
-            'x-component-props': {
-              placeholder: '请选择关联方',
-              showArrow: true,
-              filterOption: (input: string, option: any) =>
-                option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
-            },
-            required: true,
-            'x-reactions': ['{{useAsyncDataSource(getTypes)}}'],
-          },
-          target: {
-            title: '被关联方',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            type: 'string',
-            'x-decorator': 'FormItem',
-            'x-disabled': !!props.data?.id,
-            'x-component': 'Select',
-            'x-component-props': {
-              placeholder: '请选择被关联方',
-            },
-            'x-reactions': {
-              dependencies: ['..object'],
-              fulfill: {
-                state: {
-                  dataSource:
-                    '{{JSON.parse($deps[0] || "{}").objectType==="device"?[{label: "用户", value: JSON.stringify({"targetType":"user", "targetTypeName": "用户"})}] : []}}',
-                },
-              },
-            },
-            required: true,
-          },
-          description: {
-            title: '说明',
-            'x-decorator': 'FormItem',
-            'x-component': 'Input.TextArea',
-            'x-component-props': {
-              rows: 5,
-              placeholder: '请输入说明',
-            },
-            'x-decorator-props': {
-              gridSpan: 2,
-            },
-            'x-validator': [
-              {
-                max: 200,
-                message: '最多可输入200个字符',
-              },
-            ],
-          },
-        },
-      },
-    },
-  };
-
-  const save = async () => {
-    const value = await form.submit<any>();
-    const temp: any = {
-      ...props.data,
-      ...value,
-      ...JSON.parse(value?.object || '{}'),
-      ...JSON.parse(value?.target || '{}'),
-    };
-    delete temp.object;
-    delete temp.target;
-    const response: any = await service[!props.data?.id ? 'save' : 'update']({ ...temp });
-    if (response.status === 200) {
-      message.success(
-        intl.formatMessage({
-          id: 'pages.data.option.success',
-          defaultMessage: '操作成功',
-        }),
-      );
-      props.close();
-    } else {
-      message.error('操作失败!');
-    }
-  };
-
-  return (
-    <Modal
-      title={intl.formatMessage({
-        id: `pages.data.option.${props.data.id ? 'edit' : 'add'}`,
-        defaultMessage: '编辑',
-      })}
-      maskClosable={false}
-      visible
-      onCancel={props.close}
-      onOk={save}
-      width="35vw"
-      permissionCode={'system/Relationship'}
-      permission={['add', 'edit']}
-    >
-      <Form form={form} layout="vertical">
-        <SchemaField schema={schema} scope={{ useAsyncDataSource, getTypes }} />
-      </Form>
-    </Modal>
-  );
-};
-export default Save;

+ 0 - 146
src/pages/system/Relationship/index.tsx

@@ -1,146 +0,0 @@
-import SearchComponent from '@/components/SearchComponent';
-import type { ActionType, ProColumns } from '@jetlinks/pro-table';
-import ProTable from '@jetlinks/pro-table';
-import { useRef, useState } from 'react';
-import Service from '@/pages/system/Relationship/service';
-import { PageContainer } from '@ant-design/pro-layout';
-import { PermissionButton } from '@/components';
-import { useIntl } from 'umi';
-import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
-import { message } from 'antd';
-import Save from './Save';
-
-export const service = new Service('relation');
-
-const Relationship = () => {
-  const intl = useIntl();
-  const [param, setParam] = useState<any>({});
-  const [current, setCurrent] = useState<Partial<ReationItem>>({});
-  const [visible, setVisible] = useState<boolean>(false);
-  const actionRef = useRef<ActionType>();
-  const { permission } = PermissionButton.usePermission('system/Relationship');
-
-  const columns: ProColumns<ReationItem>[] = [
-    {
-      dataIndex: 'name',
-      title: '名称',
-      ellipsis: true,
-    },
-    {
-      dataIndex: 'objectTypeName',
-      title: '关联方',
-      ellipsis: true,
-    },
-    {
-      dataIndex: 'targetTypeName',
-      title: '被关联方',
-      ellipsis: true,
-    },
-    {
-      dataIndex: 'description',
-      title: '说明',
-      ellipsis: true,
-    },
-    {
-      title: '操作',
-      valueType: 'option',
-      align: 'center',
-      width: 200,
-      render: (text, record) => [
-        <PermissionButton
-          isPermission={permission.update}
-          key="warning"
-          onClick={() => {
-            setVisible(true);
-            setCurrent(record);
-          }}
-          type={'link'}
-          style={{ padding: 0 }}
-          tooltip={{
-            title: intl.formatMessage({
-              id: 'pages.data.option.edit',
-              defaultMessage: '编辑',
-            }),
-          }}
-        >
-          <EditOutlined />
-        </PermissionButton>,
-        <PermissionButton
-          isPermission={permission.delete}
-          style={{ padding: 0 }}
-          popConfirm={{
-            title: '确认删除',
-            onConfirm: async () => {
-              const resp: any = await service.remove(record.id);
-              if (resp.status === 200) {
-                message.success(
-                  intl.formatMessage({
-                    id: 'pages.data.option.success',
-                    defaultMessage: '操作成功!',
-                  }),
-                );
-                actionRef.current?.reload();
-              }
-            },
-          }}
-          key="button"
-          type="link"
-        >
-          <DeleteOutlined />
-        </PermissionButton>,
-      ],
-    },
-  ];
-
-  return (
-    <PageContainer>
-      <SearchComponent<ReationItem>
-        field={columns}
-        target="relationship"
-        onSearch={(data) => {
-          actionRef.current?.reload();
-          setParam(data);
-        }}
-      />
-      <ProTable<ReationItem>
-        actionRef={actionRef}
-        params={param}
-        columns={columns}
-        search={false}
-        rowKey="id"
-        request={async (params) => {
-          return service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] });
-        }}
-        headerTitle={[
-          <PermissionButton
-            isPermission={permission.add}
-            key="add"
-            onClick={() => {
-              setVisible(true);
-              setCurrent({});
-            }}
-            type="primary"
-            tooltip={{
-              title: intl.formatMessage({
-                id: 'pages.data.option.add',
-                defaultMessage: '新增',
-              }),
-            }}
-          >
-            新增
-          </PermissionButton>,
-        ]}
-      />
-      {visible && (
-        <Save
-          data={current}
-          close={() => {
-            setVisible(false);
-            actionRef.current?.reload();
-          }}
-        />
-      )}
-    </PageContainer>
-  );
-};
-export default Relationship;

+ 0 - 12
src/pages/system/Relationship/service.ts

@@ -1,12 +0,0 @@
-import BaseService from '@/utils/BaseService';
-import { request } from 'umi';
-import SystemConst from '@/utils/const';
-
-class Service extends BaseService<ReationItem> {
-  getTypes = () =>
-    request(`/${SystemConst.API_BASE}/relation/types`, {
-      method: 'GET',
-    });
-}
-
-export default Service;

+ 0 - 12
src/pages/system/Relationship/typings.d.ts

@@ -1,12 +0,0 @@
-type ReationItem = {
-  id: string;
-  name: string;
-  objectType: string;
-  objectTypeName: string;
-  relation: string;
-  targetType: string;
-  targetTypeName: string;
-  createTime: number;
-  description?: string;
-  expands?: Record<string, any>;
-};

+ 3 - 2
src/pages/system/User/ResetPassword/index.tsx

@@ -107,7 +107,7 @@ const ResetPassword = (props: Props) => {
     },
   };
 
-  const form = useMemo(() => createForm({}), [props.visible]);
+  const form = useMemo(() => createForm({}), []);
   return (
     <Modal
       title="重置密码"
@@ -120,8 +120,9 @@ const ResetPassword = (props: Props) => {
           if (resp.status === 200) {
             message.success('操作成功');
           }
+        } else {
+          props.close();
         }
-        props.close();
       }}
     >
       <Form form={form} layout="vertical">

+ 9 - 2
src/utils/menu/index.ts

@@ -1,7 +1,8 @@
 // 路由components映射
 import type { IRouteProps } from 'umi';
 import type { MenuItem } from '@/pages/system/Menu/typing';
-import { BUTTON_PERMISSION, getDetailNameByCode, MENUS_CODE, MENUS_CODE_TYPE } from './router';
+import { getDetailNameByCode, MENUS_CODE } from './router';
+import type { BUTTON_PERMISSION, MENUS_CODE_TYPE } from './router';
 
 /** localStorage key */
 export const MENUS_DATA_CACHE = 'MENUS_DATA_CACHE';
@@ -29,6 +30,12 @@ const extraRouteObj = {
       { code: 'Playback', name: '回放' },
     ],
   },
+  'rule-engine/Scene': {
+    children: [
+      { code: 'Save', name: '详情' },
+      { code: 'Save2', name: '测试详情' },
+    ],
+  },
 };
 
 /**
@@ -257,7 +264,7 @@ export const getButtonPermission = (
  * 通过缓存的数据取出相应的路由url
  * @param code
  */
-export const getMenuPathByCode = (code: string): string => {
+export const getMenuPathByCode = (code: MENUS_CODE_TYPE): string => {
   const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
   const menusData = JSON.parse(menusStr);
   return menusData[code];

+ 2 - 0
src/utils/menu/router.ts

@@ -63,6 +63,8 @@ export enum MENUS_CODE {
   'rule-engine/Alarm/Log' = 'rule-engine/Alarm/Log',
   'rule-engine/Alarm/Log/Detail' = 'rule-engine/Alarm/Log/Detail',
   'rule-engine/Alarm/Config' = 'rule-engine/Alarm/Config',
+  'rule-engine/Scene/Save' = 'rule-engine/Scene/Save',
+  'rule-engine/Scene/Save2' = 'rule-engine/Scene/Save2',
   'rule-engine/Alarm/Configuration' = 'rule-engine/Alarm/Configuration',
   'simulator/Device' = 'simulator/Device',
   'system/DataSource' = 'system/DataSource',

+ 74 - 73
yarn.lock

@@ -2778,85 +2778,86 @@
   resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz#2dc8c57044de0340eb53a7ba602e59abf80dc799"
   integrity sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==
 
-"@formily/antd@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/antd/-/antd-2.0.19.tgz#7419807965d5d1f39324b46e0be4f6aae04ca267"
-  integrity sha512-pxybyq2zWS4Ki56oY7227yjonVN7mnFiaIXSy/NVRD5wXxUBzOvrFA+4LiJuFGv0vzUkmSBbFCBkcDb/8TRZXQ==
-  dependencies:
-    "@formily/core" "2.0.19"
-    "@formily/grid" "2.0.19"
-    "@formily/json-schema" "2.0.19"
-    "@formily/react" "2.0.19"
-    "@formily/reactive" "2.0.19"
-    "@formily/reactive-react" "2.0.19"
-    "@formily/shared" "2.0.19"
+"@formily/antd@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/antd/-/antd-2.0.0-rc.17.tgz#fe41ded1c387a2018c292cd37c17e13350ee1dfe"
+  integrity sha512-FLUnq56b43+va3NiH2bhLPSO2BhmAMMxRWvdMX2qfV7s6SG4TQ35hEQkxDPu3YUUKKDv/75kFdgAJIaIZwHcNA==
+  dependencies:
+    "@ant-design/icons" "^4.0.0"
+    "@formily/core" "2.0.0-rc.17"
+    "@formily/grid" "2.0.0-rc.17"
+    "@formily/json-schema" "2.0.0-rc.17"
+    "@formily/react" "2.0.0-rc.17"
+    "@formily/reactive" "2.0.0-rc.17"
+    "@formily/reactive-react" "2.0.0-rc.17"
+    "@formily/shared" "2.0.0-rc.17"
+    "@juggle/resize-observer" "^3.3.1"
     classnames "^2.2.6"
     react-sortable-hoc "^1.11.0"
     react-sticky-box "^0.9.3"
 
-"@formily/core@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/core/-/core-2.0.19.tgz#359bef69964b623d8468934e4cc396de4eb03173"
-  integrity sha512-VsqWJKc2jhjzPgu4SKN5EVJeRrEwu+mAvsSo5bdDeKDTQ3b9+L9TTpUF8Q4t9NvZshK+gMAfvdCYNnb5hUqSnw==
-  dependencies:
-    "@formily/reactive" "2.0.19"
-    "@formily/shared" "2.0.19"
-    "@formily/validator" "2.0.19"
-
-"@formily/grid@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/grid/-/grid-2.0.19.tgz#b0b3aa45f027fd23c5918f1490597aeea7b2b90d"
-  integrity sha512-x2s1EVAkiGx6rdFr333gsNJjpwS9yLHIECvvStqWcTfBHlszrFxtPyAa1rYf0RCvjBMWq0EE6p2o6VIVqKVOtw==
-  dependencies:
-    "@formily/reactive" "2.0.19"
-    "@juggle/resize-observer" "^3.3.1"
-
-"@formily/json-schema@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/json-schema/-/json-schema-2.0.19.tgz#e14167060a07abd54759bb5ee17c6679156b866f"
-  integrity sha512-BTcEZwcGM/up6VKEVZ4wulD4hI5fYBb8n5SgRnaezSJbHECK23p8Yh13Qj4h1GFbQbnCWr6FVYFvqBSAc8tyOQ==
-  dependencies:
-    "@formily/core" "2.0.19"
-    "@formily/reactive" "2.0.19"
-    "@formily/shared" "2.0.19"
-
-"@formily/path@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/path/-/path-2.0.19.tgz#391abd170fd68048a4f59568b7b150c68fd36785"
-  integrity sha512-uiNyq0Vrls7ie8/odP7ZVybNBOFgwJVQ68XXIzq4ZPrki0uSyoVAn5CrCkNP94PdqOjN8/gjP4sQo6eSXvPnvQ==
-
-"@formily/react@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/react/-/react-2.0.19.tgz#44d0afeb4eff2b62f555664aa620cb9ecff1bec2"
-  integrity sha512-R6FE/pX1u06nORiWX7hNgb8idMcZdd+ozvZu1iupgDqAespWz6axl24OOKWH56+JU/uXDRXG8dvGKds5rjctvQ==
-  dependencies:
-    "@formily/core" "2.0.19"
-    "@formily/json-schema" "2.0.19"
-    "@formily/reactive" "2.0.19"
-    "@formily/reactive-react" "2.0.19"
-    "@formily/shared" "2.0.19"
-    "@formily/validator" "2.0.19"
+"@formily/core@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/core/-/core-2.0.0-rc.17.tgz#06880aa6be6f6f822050998662654748bce1627b"
+  integrity sha512-O+iahZipqv1iwqQW9KDLTSo0USVwFrwjUs3v6ToHWgaJHbOFY3rHtUjUBB00QN4cQsc7tANErx8+MawoV/fH8Q==
+  dependencies:
+    "@formily/reactive" "2.0.0-rc.17"
+    "@formily/shared" "2.0.0-rc.17"
+    "@formily/validator" "2.0.0-rc.17"
+
+"@formily/grid@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/grid/-/grid-2.0.0-rc.17.tgz#46603919f435cbf71b65f039a4f7a4c98db8cf77"
+  integrity sha512-1boauZbcyKFC+0Pom3xeOzbH5DvCBXs2u9xHklDoaQrxZZLPggtZLlo7Qxyx7zXfyKQRn910WkbzlWFIt2VaDg==
+  dependencies:
+    "@formily/reactive" "2.0.0-rc.17"
+
+"@formily/json-schema@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/json-schema/-/json-schema-2.0.0-rc.17.tgz#5638ad56c7c44da89ae33b60b8b1f78f548ed9e4"
+  integrity sha512-x7kqGGdXXS40a3xf7LvbjPvMsCEhUZCshoEfHbQaxKOq4Y+mtUCpFYwJMBr3xsJG6+Yid3IDqflL0yBHn9/SDA==
+  dependencies:
+    "@formily/core" "2.0.0-rc.17"
+    "@formily/reactive" "2.0.0-rc.17"
+    "@formily/shared" "2.0.0-rc.17"
+
+"@formily/path@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/path/-/path-2.0.0-rc.17.tgz#40ea317fc8f46fa9908c4407720d8bcfd7e5fae6"
+  integrity sha512-BOFI38udFlYC/q9DYHehwu9FfKOdW1KgIjXp0t/wFlwfiVGQ+B/KyKVSkFPzEocdK5Q3fkujs8kyGLoyJLfSHQ==
+
+"@formily/react@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/react/-/react-2.0.0-rc.17.tgz#a88d9fa4b30d08a5ac19fda7ae09841fab64d493"
+  integrity sha512-PLBZYzKHNAb8PSGJrIFBEHA8kB3+j3WN0ls6weo89RXqbcnmkLCmjs6Xa7Cx0KYFLSLkmUZySmrI+Y51w0ASJA==
+  dependencies:
+    "@formily/core" "2.0.0-rc.17"
+    "@formily/json-schema" "2.0.0-rc.17"
+    "@formily/reactive" "2.0.0-rc.17"
+    "@formily/reactive-react" "2.0.0-rc.17"
+    "@formily/shared" "2.0.0-rc.17"
+    "@formily/validator" "2.0.0-rc.17"
     hoist-non-react-statics "^3.3.2"
 
-"@formily/reactive-react@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/reactive-react/-/reactive-react-2.0.19.tgz#0526cc22346d62c1809eefbdbac988a1845e581e"
-  integrity sha512-Laz3O/oSCIA4qKQ4fIMsyUQjS4XtD00nUvXSXIZhGdTkZW09Spq8zv7wd+0V6REEKIH6urTtC8htpBQN8W3fww==
+"@formily/reactive-react@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/reactive-react/-/reactive-react-2.0.0-rc.17.tgz#fa7d86c83170f183c8180bc6a798797609e8839b"
+  integrity sha512-7rHZ1Az0cpqjLccmrwASJ68b6QxPzJ2mpTLYKf5jbmIINPB5mG0zziFPJLymY15ljAQ6jIyX15viOkDBSkedJA==
   dependencies:
-    "@formily/reactive" "2.0.19"
+    "@formily/reactive" "2.0.0-rc.17"
     hoist-non-react-statics "^3.3.2"
 
-"@formily/reactive@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/reactive/-/reactive-2.0.19.tgz#4498b4e70c466bfee9b9dda8639ffe6f10d5d7ca"
-  integrity sha512-gEpiEITdrRHGc+cf/0lalw4gTcES+8axdAxC0mZRMHfJ8iSZnFs369AGxiWdElUK9NNVLfEmSuU60op6XCQhrg==
+"@formily/reactive@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/reactive/-/reactive-2.0.0-rc.17.tgz#fcf752d2c6c14459580d08305efc0d56d9741278"
+  integrity sha512-xFLOFnd+O5t1TRmunlFJHpTTKObSjh7rxJW7IvO42OkrV1o2dUJ7TdDcsaZIHsHg9H/3tMFHzAtfGcprpIcYAA==
 
-"@formily/shared@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/shared/-/shared-2.0.19.tgz#602ce0738fe39fb0773accc3345329ba3b0cbbac"
-  integrity sha512-1zKNZLKoEEH31Y9+rBXdByHVsUModWyshkPj7fsZv0KkaObn/wV2WUCKLQW4c4Hn1y+yojPH//8SD2oOZ4wZXw==
+"@formily/shared@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/shared/-/shared-2.0.0-rc.17.tgz#c6e88df5652f376783130be908e428d3533093b5"
+  integrity sha512-+09L5mOP0MwOk5AOOiphNlZ1PPNYxPI/7pMthulyF3BIDSAJF9Odx3IGvGz+YthzD0fYpi3QX7ly2OuwAA3FhA==
   dependencies:
-    "@formily/path" "2.0.19"
+    "@formily/path" "2.0.0-rc.17"
     camel-case "^4.1.1"
     lower-case "^2.0.1"
     no-case "^3.0.4"
@@ -2864,12 +2865,12 @@
     pascal-case "^3.1.1"
     upper-case "^2.0.1"
 
-"@formily/validator@2.0.19":
-  version "2.0.19"
-  resolved "https://registry.yarnpkg.com/@formily/validator/-/validator-2.0.19.tgz#4d14191b6ab92b0298a59b42964cab008ef7f551"
-  integrity sha512-KS9g0WXKR77ET+3blKGxDL2w4e8gp0z5kkd5BDm7bIUmfNb67rTuSaacs+8MbOuckt09B7qU1nzOekXkskaRNw==
+"@formily/validator@2.0.0-rc.17":
+  version "2.0.0-rc.17"
+  resolved "https://registry.yarnpkg.com/@formily/validator/-/validator-2.0.0-rc.17.tgz#e11bf27a5f5b14bed92dcdc8e5dbf2430965018d"
+  integrity sha512-srjQrfY8ubKaFjldb75lcHhBVgXKNY6Q1R6BvFr2Xogslbkriv2ct752Bix0YC+cFZ4elFwWyiOknSaupnzZRg==
   dependencies:
-    "@formily/shared" "2.0.19"
+    "@formily/shared" "2.0.0-rc.17"
 
 "@hapi/address@^2.1.2":
   version "2.1.4"