فهرست منبع

feat(alarm): product alarm

Lind 4 سال پیش
والد
کامیت
66c806c9d6

+ 335 - 0
src/components/AlarmEditor/Action/index.ts

@@ -0,0 +1,335 @@
+import type { Field } from '@formily/core';
+import { FormPath, onFieldReact } from '@formily/core';
+import { service } from '@/pages/device/Product';
+import encodeQuery from '@/utils/encodeQuery';
+import type { Response } from '@/utils/typings';
+import type { DeviceInstance } from '@/pages/device/Instance/typings';
+import type { DeviceMetadata, FunctionMetadata } from '@/pages/device/Product/typings';
+
+const Action = {
+  schema: {
+    actions: {
+      type: 'array',
+      'x-component': 'ArrayCollapse',
+      'x-decorator': 'FormItem',
+      'x-component-props': {
+        ghost: true,
+      },
+      items: {
+        type: 'object',
+        'x-component': 'ArrayCollapse.CollapsePanel',
+        'x-component-props': {
+          header: '执行动作',
+        },
+        properties: {
+          index: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.Index',
+          },
+          executor: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            title: '动作类型',
+            required: true,
+            'x-component': 'Select',
+            enum: [
+              { label: '消息通知', value: 'notifier' },
+              { label: '设备输出', value: 'device-message-sender' },
+            ],
+          },
+          configuration: {
+            type: 'object',
+            properties: {
+              deviceMessage: {
+                type: 'void',
+                properties: {
+                  '{id:deviceId,productId,name:deviceName}': {
+                    type: 'object',
+                    'x-decorator': 'FormItem',
+                    title: '选择设备',
+                    required: true,
+                    'x-component': 'FSelectDevice',
+                  },
+                  message: {
+                    type: 'object',
+                    properties: {
+                      messageType: {
+                        type: 'string',
+                        'x-decorator': 'FormItem',
+                        title: '执行动作',
+                        required: true,
+                        'x-component': 'Select',
+                        enum: [
+                          { label: '设置属性', value: 'WRITE_PROPERTY' },
+                          { label: '调用功能', value: 'INVOKE_FUNCTION' },
+                        ],
+                      },
+                      properties: {
+                        type: 'object',
+                        'x-visible': false,
+                        properties: {
+                          key: {
+                            title: '属性列表',
+                            type: 'string',
+                            'x-decorator': 'FormItem',
+                            'x-component': 'Select',
+                            required: true,
+                          },
+                          value: {
+                            title: '属性值',
+                            type: 'string',
+                            'x-decorator': 'FormItem',
+                            'x-component': 'Input',
+                            required: true,
+                          },
+                        },
+                        'x-reactions': {
+                          dependencies: ['.messageType'],
+                          fulfill: {
+                            state: {
+                              visible: '{{$deps[0]==="WRITE_PROPERTY"}}',
+                            },
+                          },
+                        },
+                      },
+                      functionId: {
+                        title: '功能列表',
+                        type: 'string',
+                        'x-decorator': 'FormItem',
+                        'x-component': 'Select',
+                        'x-visible': false,
+                        required: true,
+                        'x-reactions': {
+                          dependencies: ['.messageType'],
+                          fulfill: {
+                            state: {
+                              visible: '{{$deps[0]==="INVOKE_FUNCTION"}}',
+                            },
+                          },
+                        },
+                      },
+
+                      inputs: {
+                        type: 'array',
+                        'x-component': 'ArrayItems',
+                        'x-decorator': 'FormItem',
+                        'x-visible': false,
+                        'x-reactions': {
+                          dependencies: ['.messageType'],
+                          fulfill: {
+                            state: {
+                              visible: '{{$deps[0]==="INVOKE_FUNCTION"}}',
+                            },
+                          },
+                        },
+                        items: {
+                          type: 'object',
+                          properties: {
+                            space: {
+                              type: 'void',
+                              'x-component': 'Space',
+                              'x-component-props': {
+                                align: 'center',
+                              },
+                              properties: {
+                                name: {
+                                  title: '参数名',
+                                  'x-decorator': 'FormItem',
+                                  'x-component': 'Input',
+                                  'x-disabled': true,
+                                  type: 'string',
+                                },
+                                value: {
+                                  title: '参数值',
+                                  'x-decorator': 'FormItem',
+                                  'x-component': 'Input',
+                                  type: 'string',
+                                },
+                              },
+                            },
+                          },
+                        },
+                      },
+                    },
+                  },
+                },
+                'x-visible': false,
+                'x-reactions': {
+                  dependencies: ['..executor'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]==="device-message-sender"}}',
+                    },
+                  },
+                },
+              },
+              notifier: {
+                type: 'void',
+                'x-visible': false,
+                'x-reactions': {
+                  dependencies: ['..executor'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]==="notifier"}}',
+                    },
+                  },
+                },
+                properties: {
+                  notifyType: {
+                    title: '通知类型',
+                    type: 'string',
+                    'x-decorator': 'FormItem',
+                    required: true,
+                    'x-component': 'Select',
+                    'x-reactions': ['{{useAsyncDataSource(loadNotifierType)}}'],
+                  },
+                  notifierId: {
+                    title: '通知配置',
+                    type: 'string',
+                    'x-decorator': 'FormItem',
+                    required: true,
+                    'x-component': 'Select',
+                  },
+                  templateId: {
+                    title: '通知模版',
+                    type: 'string',
+                    'x-decorator': 'FormItem',
+                    required: true,
+                    'x-component': 'Select',
+                    enum: [],
+                  },
+                },
+              },
+            },
+          },
+          remove: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.Remove',
+          },
+          moveUp: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.MoveUp',
+          },
+          moveDown: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.MoveDown',
+          },
+        },
+      },
+      properties: {
+        addition: {
+          type: 'void',
+          title: '添加动作',
+          'x-component': 'ArrayCollapse.Addition',
+        },
+      },
+    },
+  } as any,
+  effects: () => {
+    let targetDeviceFunction: Partial<FunctionMetadata>[] = [];
+    onFieldReact('actions.*.configuration.notifyType', async (field, f) => {
+      const type = (field as Field).value;
+      if (!type) return;
+      const data = await service.notifier.config(
+        encodeQuery({
+          paging: false,
+          terms: { type },
+        }),
+      );
+      const configPath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `actions.${parseInt(index)}.configuration.notifierId`,
+      );
+      f.setFieldState(configPath, (state) => {
+        state.dataSource = data.result?.map((i: { name: string; provider: string }) => ({
+          label: i.name,
+          value: i.provider,
+        }));
+      });
+    });
+    onFieldReact('actions.*.configuration.notifierId', async (field, f) => {
+      const provider = (field as Field).value;
+      const templatePath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `actions.${parseInt(index)}.configuration.templateId`,
+      );
+      const type = field.query('.notifyType').get('value');
+      if (!provider) return;
+      const data = await service.notifier.template(
+        encodeQuery({
+          paging: false,
+          terms: { type, provider },
+        }),
+      );
+      f.setFieldState(templatePath, (state) => {
+        state.dataSource = data.result?.map((i: { name: any; id: any }) => ({
+          label: i.name,
+          value: i.id,
+        }));
+      });
+    });
+    onFieldReact(
+      'actions.*.configuration.{id:deviceId,productId,name:deviceName}',
+      async (field, f) => {
+        const device = (field as Field).value;
+        console.log(device);
+        if (!device || Object.keys(device).length === 0) return;
+        const detail: Response<DeviceInstance> = await service.deviceDetail(device.id);
+
+        const metadata = JSON.parse((detail.result as DeviceInstance).metadata) as DeviceMetadata;
+        const propertyPath = FormPath.transform(
+          field.path,
+          /\d+/,
+          (index) => `actions.${index}.configuration.message.properties.key`,
+        );
+        const functionPath = FormPath.transform(
+          field.path,
+          /\d+/,
+          (index) => `actions.${index}.configuration.message.functionId`,
+        );
+        console.log(propertyPath, 'path', metadata);
+        f.setFieldState(propertyPath, (state) => {
+          state.dataSource = metadata.properties.map((i) => ({ label: i.name, value: i.id }));
+        });
+        f.setFieldState(functionPath, (state) => {
+          targetDeviceFunction = metadata.functions;
+          state.dataSource = metadata.functions.map((i) => ({ label: i.name, value: i.id }));
+        });
+      },
+    );
+    onFieldReact('actions.*.configuration.message.functionId', async (field, f) => {
+      const func = (field as Field).value;
+      const functionMetadata = targetDeviceFunction.find((i) => i.id === func);
+
+      if (!functionMetadata) return;
+      // console.log(func, functionMetadata, '配置信息', targetDeviceFunction)
+      const inputsPath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `actions.${index}.configuration.message.inputs`,
+      );
+      const t = functionMetadata?.inputs?.map((i: any) => ({ ...i, key: i.id, label: i.name }));
+      f.setFieldState(inputsPath, (state) => {
+        state.visible = true;
+        state.value = t;
+      });
+      const valuePath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `actions.${index}.configuration.message.inputs.*.value`,
+      );
+
+      t?.map((v: any, index: number) => {
+        const value = valuePath.replace('*', index);
+        f.setFieldState(value, (state) => {
+          state.decoratorProps = {
+            tooltip: `参数类型:${v.valueType.type}`,
+          };
+        });
+      });
+    });
+  },
+};
+export default Action;

+ 347 - 0
src/components/AlarmEditor/Trigger/index.ts

@@ -0,0 +1,347 @@
+import type { Field } from '@formily/core';
+import { FormPath, onFieldReact } from '@formily/core';
+import DB from '@/db';
+
+const Trigger = {
+  schema: {
+    triggers: {
+      type: 'array',
+      // title: '触发条件',
+      'x-component': 'ArrayCollapse',
+      'x-decorator': 'FormItem',
+      'x-component-props': {
+        ghost: true,
+      },
+      items: {
+        type: 'object',
+        'x-component': 'ArrayCollapse.CollapsePanel',
+        'x-component-props': {
+          header: '告警规则',
+          layout: 'vertical',
+        },
+        properties: {
+          index: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.Index',
+          },
+          layout: {
+            type: 'void',
+            'x-component': 'FormLayout',
+            'x-component-props': {
+              layout: 'vertical',
+            },
+            properties: {
+              trigger: {
+                type: 'string',
+                'x-decorator': 'FormItem',
+                title: '触发方式',
+                required: true,
+                'x-component': 'Select',
+                enum: [
+                  { label: '定时触发', value: 'timer' },
+                  { label: '设备触发', value: 'device' },
+                ],
+              },
+              cron: {
+                type: 'string',
+                'x-decorator': 'FormItem',
+                title: 'Cron表达式',
+                required: true,
+                'x-visible': false,
+                'x-component': 'Input',
+                'x-reactions': {
+                  dependencies: ['.trigger'],
+                  fulfill: {
+                    state: {
+                      visible: "{{$deps[0]==='timer'}}",
+                      value: undefined,
+                    },
+                  },
+                },
+              },
+              type: {
+                type: 'string',
+                'x-decorator': 'FormItem',
+                title: '类型',
+                required: true,
+                'x-component': 'Select',
+                'x-reactions': {
+                  dependencies: ['.trigger'],
+                  when: "{{$deps[0]!=='timer'}}",
+                  fulfill: {
+                    state: {
+                      dataSource: [
+                        { label: '上线', value: 'online' },
+                        { label: '离线', value: 'offline' },
+                        { label: '属性', value: 'properties' },
+                        { label: '事件', value: 'events' },
+                      ],
+                    },
+                  },
+                  otherwise: {
+                    state: {
+                      dataSource: [
+                        { label: '属性', value: 'properties' },
+                        { label: '功能', value: 'functions' },
+                      ],
+                    },
+                  },
+                },
+              },
+              modelId: {
+                // key 改为modelID
+                type: 'string',
+                'x-decorator': 'FormItem',
+                title: '事件/属性',
+                required: true,
+                'x-component': 'Select',
+                'x-visible': false,
+                'x-reactions': {
+                  dependencies: ['.trigger', '.type'],
+                  when: "{{$deps[0]==='timer'&&($deps[1]==='properties'||$deps[1]==='events'||$deps[1]==='functions')}}",
+                  fulfill: {
+                    state: {
+                      visible: true,
+                    },
+                  },
+                  otherwise: {
+                    state: {
+                      visible: false,
+                    },
+                  },
+                },
+              },
+              parameters: {
+                // key改为parameters
+                type: 'array',
+                'x-component': 'ArrayItems',
+                'x-decorator': 'FormItem',
+                'x-visible': false,
+                items: {
+                  type: 'object',
+                  properties: {
+                    space: {
+                      type: 'void',
+                      'x-component': 'Space',
+                      'x-component-props': {
+                        align: 'center',
+                      },
+                      properties: {
+                        key: {
+                          title: '参数名',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Input',
+                          'x-disabled': true,
+                          type: 'string',
+                        },
+                        value: {
+                          title: '参数值',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Input',
+                          type: 'string',
+                        },
+                      },
+                    },
+                  },
+                },
+              },
+              filter: {
+                title: '过滤条件',
+                type: 'array',
+                'x-component': 'ArrayItems',
+                'x-decorator': 'FormItem',
+                items: {
+                  type: 'object',
+                  properties: {
+                    space: {
+                      type: 'void',
+                      'x-component': 'Space',
+                      properties: {
+                        key: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'FAutoComplete',
+                          'x-component-props': {
+                            placeholder: 'key',
+                            style: {
+                              width: '200px',
+                            },
+                          },
+                        },
+                        operator: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Select',
+                          'x-component-props': {
+                            placeholder: '条件',
+                            style: {
+                              width: '110px',
+                            },
+                          },
+                          enum: [
+                            { label: '等于(=)', value: 'eq' },
+                            { label: '不等于(!=)', value: 'not' },
+                            { label: '大于(>)', value: 'gt' },
+                            { label: '小于(<)', value: 'lt' },
+                            { label: '大于等于(>=)', value: 'gte' },
+                            { label: '小于等于(<=)', value: 'lte' },
+                            { label: '模糊(%)', value: 'like' },
+                          ],
+                        },
+                        value: {
+                          type: 'string',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Input',
+                          'x-component-props': {
+                            placeholder: 'value',
+                          },
+                        },
+                        remove: {
+                          type: 'void',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'ArrayItems.Remove',
+                        },
+                      },
+                    },
+                  },
+                },
+                properties: {
+                  add: {
+                    type: 'void',
+                    title: '添加条目',
+                    'x-component': 'ArrayItems.Addition',
+                  },
+                },
+              },
+            },
+          },
+          remove: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.Remove',
+          },
+          moveUp: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.MoveUp',
+          },
+          moveDown: {
+            type: 'void',
+            'x-component': 'ArrayCollapse.MoveDown',
+          },
+        },
+      },
+      properties: {
+        addition: {
+          type: 'void',
+          title: '添加规则',
+          'x-component': 'ArrayCollapse.Addition',
+        },
+      },
+    },
+  } as any,
+  effects: () =>
+    onFieldReact('triggers.*.modelId' || 'triggers.*.type', async (field, f) => {
+      const triggerPath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `triggers.${parseInt(index)}.trigger`,
+      );
+      const typePath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `triggers.${parseInt(index)}.type`,
+      );
+      const keyPath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `triggers.${parseInt(index)}.filter.*.key`,
+      );
+      const functionConfigPath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `triggers.${parseInt(index)}.parameters`,
+      );
+      const trigger = (field.query(triggerPath).take() as Field)?.value as 'device' | 'timer';
+      const functionValuePath = FormPath.transform(
+        field.path,
+        /\d+/,
+        (index) => `triggers.${parseInt(index)}.parameters.*.value`,
+      );
+      const type = (field.query(typePath).take() as Field)?.value;
+      const item = (field as Field)?.value;
+      if (!type || type === 'online' || type === 'offline') return;
+      const data = await DB.getDB().table(type).toArray();
+      const temp = data.map((d) => ({ label: `${d.name}(${d.id})`, value: d.id }));
+
+      f.setFieldState(field.query('.modelId'), (state) => {
+        state.dataSource = temp;
+      });
+      if (trigger === 'timer' && type && item) {
+        const list = await DB.getDB().table(type).where('id').equals(item).last();
+        if (type === 'functions') {
+          const t = list.inputs?.map((i: any) => ({ ...i, key: i.id, label: i.name }));
+          f.setFieldState(functionConfigPath, (state) => {
+            state.visible = true;
+            state.value = t;
+          });
+
+          t?.map((v: any, index: number) => {
+            const value = functionValuePath.replace('*', index);
+            f.setFieldState(value, (state) => {
+              state.decoratorProps = {
+                tooltip: `参数类型:${v.valueType.type}`,
+              };
+            });
+          });
+        }
+        f.setFieldState(keyPath, (state) => {
+          // if (list.output?.length > 0) {
+          const output = list.output;
+          switch (output?.type) {
+            case 'object':
+              const propertyList = output.properties;
+              state.dataSource = (
+                propertyList.length > 0
+                  ? propertyList.map((i: { id: any; name: any }) => ({
+                      value: i.id,
+                      label: `${i.name}(${i.id})`,
+                    }))
+                  : []
+              ).concat({
+                label: 'this',
+                value: 'this',
+              });
+              break;
+            case 'enum':
+              const elements = output.elements;
+              state.dataSource = (
+                elements.length > 0
+                  ? elements.map((i: { text: any; value: any }) => ({
+                      value: i.value,
+                      label: `${i.text}(${i.value})`,
+                    }))
+                  : []
+              ).concat({
+                label: 'this',
+                value: 'this',
+              });
+              break;
+            default:
+              state.dataSource = [
+                {
+                  label: 'this',
+                  value: 'this',
+                },
+              ];
+              break;
+          }
+          // }
+        });
+      } else if (trigger === 'device') {
+        f.setFieldState(keyPath, (state) => {
+          state.dataSource = temp;
+        });
+      }
+    }),
+};
+
+export default Trigger;

+ 22 - 4
src/components/FSelectDevice/index.tsx

@@ -1,5 +1,6 @@
 import { Input, Modal } from 'antd';
 import { EditOutlined } from '@ant-design/icons';
+import type { Key } from 'react';
 import { useRef, useState } from 'react';
 import { connect } from '@formily/react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
@@ -10,8 +11,8 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import Service from '@/pages/device/Instance/service';
 
 interface Props {
-  value: string;
-  onChange: (id: string) => void;
+  value: Partial<DeviceInstance>;
+  onChange: (data: Partial<DeviceInstance>) => void;
 }
 
 export const service = new Service('device/instance');
@@ -66,11 +67,20 @@ const FSelectDevice = connect((props: Props) => {
     },
   ];
 
+  const [data, setData] = useState<Partial<DeviceInstance>>(props.value);
+  const rowSelection = {
+    onChange: (selectedRowKeys: Key[], selectedRows: DeviceInstance[]) => {
+      setData(selectedRows[0]);
+    },
+    selectedRowKeys: [data?.id] as Key[],
+  };
+  console.log(props?.value, 'de-name');
   return (
     <>
       <Input
         size="small"
-        value={props.value}
+        disabled
+        value={props.value?.name}
         addonAfter={<EditOutlined onClick={() => setVisible(true)} />}
       />
       {visible && (
@@ -81,12 +91,20 @@ const FSelectDevice = connect((props: Props) => {
           onCancel={() => setVisible(false)}
           onOk={() => {
             setVisible(false);
-            props.onChange('test');
+            console.log(data, 'dd');
+            props.onChange(data);
           }}
         >
           <ProTable<DeviceInstance>
+            tableAlertRender={false}
             rowSelection={{
               type: 'radio',
+              ...rowSelection,
+            }}
+            toolBarRender={false}
+            rowKey="id"
+            pagination={{
+              pageSize: 10,
             }}
             columns={columns}
             actionRef={actionRef}

+ 132 - 484
src/pages/device/Product/Detail/Alarm/Edit/index.tsx

@@ -1,6 +1,6 @@
-import { Drawer } from 'antd';
+import { Button, Drawer, message } from 'antd';
 import type { Field } from '@formily/core';
-import { createForm, FormPath, onFieldReact } from '@formily/core';
+import { createForm } from '@formily/core';
 import {
   ArrayCollapse,
   ArrayItems,
@@ -14,22 +14,24 @@ import {
   Radio,
   Select,
   Space,
+  Switch,
 } from '@formily/antd';
 import { createSchemaField } from '@formily/react';
 import type { ISchema } from '@formily/json-schema';
 import { useIntl } from '@@/plugin-locale/localeExports';
 
 import './index.less';
-import DB from '@/db';
 import FAutoComplete from '@/components/FAutoComplete';
 import { action } from '@formily/reactive';
-import { service } from '@/pages/device/Product';
-import encodeQuery from '@/utils/encodeQuery';
+import { productModel, service } from '@/pages/device/Product';
 import FSelectDevice from '@/components/FSelectDevice';
+import Trigger from '@/components/AlarmEditor/Trigger';
+import Action from '@/components/AlarmEditor/Action';
 
 interface Props {
   visible: boolean;
   close: () => void;
+  data: any;
 }
 
 const EditAlarm = (props: Props) => {
@@ -37,153 +39,10 @@ const EditAlarm = (props: Props) => {
 
   const intl = useIntl();
   const form = createForm({
-    initialValues: {},
+    initialValues: props.data?.alarmRule,
     effects() {
-      onFieldReact('triggers.*.item' || 'triggers.*.type', async (field, f) => {
-        const triggerPath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `triggers.${parseInt(index)}.trigger`,
-        );
-        const typePath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `triggers.${parseInt(index)}.type`,
-        );
-        const keyPath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `triggers.${parseInt(index)}.filter.*.key`,
-        );
-        const functionConfigPath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `triggers.${parseInt(index)}.functionConfig`,
-        );
-        const trigger = (field.query(triggerPath).take() as Field)?.value as 'device' | 'timer';
-        const functionValuePath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `triggers.${parseInt(index)}.functionConfig.*.value`,
-        );
-        const type = (field.query(typePath).take() as Field)?.value;
-        const item = (field as Field)?.value;
-        if (!type || type === 'online' || type === 'offline') return;
-        const data = await DB.getDB().table(type).toArray();
-        const temp = data.map((d) => ({ label: `${d.name}(${d.id})`, value: d.id }));
-
-        f.setFieldState(field.query('.item'), (state) => {
-          state.dataSource = temp;
-        });
-        if (trigger === 'timer' && type && item) {
-          const list = await DB.getDB().table(type).where('id').equals(item).last();
-          if (type === 'functions') {
-            const t = list.inputs.map((i: any) => ({ ...i, key: i.id, label: i.name }));
-            f.setFieldState(functionConfigPath, (state) => {
-              state.visible = true;
-              state.value = t;
-            });
-            t.map((v: any, index: number) => {
-              const value = functionValuePath.replace('*', index);
-              f.setFieldState(value, (state) => {
-                state.decoratorProps = {
-                  tooltip: `参数类型:${v.valueType.type}`,
-                };
-              });
-            });
-          }
-          f.setFieldState(keyPath, (state) => {
-            if (list.inputs.length > 0) {
-              const output = list.output;
-              switch (output.type) {
-                case 'object':
-                  const propertyList = output.properties;
-                  state.dataSource = (
-                    propertyList.length > 0
-                      ? propertyList.map((i: { id: any; name: any }) => ({
-                          value: i.id,
-                          label: `${i.name}(${i.id})`,
-                        }))
-                      : []
-                  ).concat({
-                    label: 'this',
-                    value: 'this',
-                  });
-                  break;
-                case 'enum':
-                  const elements = output.elements;
-                  state.dataSource = (
-                    elements.length > 0
-                      ? elements.map((i: { text: any; value: any }) => ({
-                          value: i.value,
-                          label: `${i.text}(${i.value})`,
-                        }))
-                      : []
-                  ).concat({
-                    label: 'this',
-                    value: 'this',
-                  });
-                  break;
-                default:
-                  state.dataSource = [
-                    {
-                      label: 'this',
-                      value: 'this',
-                    },
-                  ];
-                  break;
-              }
-            }
-          });
-        } else if (trigger === 'device') {
-          f.setFieldState(keyPath, (state) => {
-            state.dataSource = temp;
-          });
-        }
-      });
-      onFieldReact('actions.*.notifier.type', async (field, f) => {
-        const type = (field as Field).value;
-        if (!type) return;
-        const data = await service.notifier.config(
-          encodeQuery({
-            paging: false,
-            terms: { type },
-          }),
-        );
-        const configPath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `actions.${parseInt(index)}.notifier.config`,
-        );
-        f.setFieldState(configPath, (state) => {
-          state.dataSource = data.result?.map((i: { name: string; provider: string }) => ({
-            label: i.name,
-            value: i.provider,
-          }));
-        });
-      });
-      onFieldReact('actions.*.notifier.config', async (field, f) => {
-        const provider = (field as Field).value;
-        const templatePath = FormPath.transform(
-          field.path,
-          /\d+/,
-          (index) => `actions.${parseInt(index)}.notifier.template`,
-        );
-        const type = field.query('.type').get('value');
-        if (!provider) return;
-        const data = await service.notifier.template(
-          encodeQuery({
-            paging: false,
-            terms: { type, provider },
-          }),
-        );
-        f.setFieldState(templatePath, (state) => {
-          state.dataSource = data.result?.map((i: { name: any; id: any }) => ({
-            label: i.name,
-            value: i.id,
-          }));
-        });
-      });
+      Trigger.effects();
+      Action.effects();
     },
   });
 
@@ -202,13 +61,14 @@ const EditAlarm = (props: Props) => {
       FormTab,
       FAutoComplete,
       FSelectDevice,
+      Switch,
     },
   });
 
   const formTab = FormTab.createFormTab!();
 
   const loadNotifierType = async (field: Field) => {
-    const ac = field.query('..action').get('value');
+    const ac = field.query('...executor').get('value');
     if (!ac) return [];
     return service.notifier.types();
   };
@@ -243,238 +103,7 @@ const EditAlarm = (props: Props) => {
             'x-component-props': {
               tab: '触发条件',
             },
-            properties: {
-              triggers: {
-                type: 'array',
-                // title: '触发条件',
-                'x-component': 'ArrayCollapse',
-                'x-decorator': 'FormItem',
-                'x-component-props': {
-                  ghost: true,
-                },
-                items: {
-                  type: 'object',
-                  'x-component': 'ArrayCollapse.CollapsePanel',
-                  'x-component-props': {
-                    header: '告警规则',
-                    layout: 'vertical',
-                  },
-                  properties: {
-                    index: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.Index',
-                    },
-                    layout: {
-                      type: 'void',
-                      'x-component': 'FormLayout',
-                      'x-component-props': {
-                        layout: 'vertical',
-                      },
-                      properties: {
-                        trigger: {
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          title: '触发方式',
-                          required: true,
-                          'x-component': 'Select',
-                          enum: [
-                            { label: '定时触发', value: 'timer' },
-                            { label: '设备触发', value: 'device' },
-                          ],
-                        },
-                        cron: {
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          title: 'Cron表达式',
-                          required: true,
-                          'x-visible': false,
-                          'x-component': 'Input',
-                          'x-reactions': {
-                            dependencies: ['.trigger'],
-                            fulfill: {
-                              state: {
-                                visible: "{{$deps[0]==='timer'}}",
-                              },
-                            },
-                          },
-                        },
-                        type: {
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          title: '类型',
-                          required: true,
-                          'x-component': 'Select',
-                          'x-reactions': {
-                            dependencies: ['.trigger'],
-                            when: "{{$deps[0]!=='timer'}}",
-                            fulfill: {
-                              state: {
-                                dataSource: [
-                                  { label: '上线', value: 'online' },
-                                  { label: '离线', value: 'offline' },
-                                  { label: '属性', value: 'properties' },
-                                  { label: '事件', value: 'events' },
-                                ],
-                              },
-                            },
-                            otherwise: {
-                              state: {
-                                dataSource: [
-                                  { label: '属性', value: 'properties' },
-                                  { label: '功能', value: 'functions' },
-                                ],
-                              },
-                            },
-                          },
-                        },
-                        item: {
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          title: '事件/属性',
-                          required: true,
-                          'x-component': 'Select',
-                          'x-visible': false,
-                          'x-reactions': {
-                            dependencies: ['.trigger', '.type'],
-                            when: "{{$deps[0]==='timer'&&($deps[1]==='properties'||$deps[1]==='events'||$deps[1]==='functions')}}",
-                            fulfill: {
-                              state: {
-                                visible: true,
-                              },
-                            },
-                            otherwise: {
-                              state: {
-                                visible: false,
-                              },
-                            },
-                          },
-                        },
-                        functionConfig: {
-                          type: 'array',
-                          'x-component': 'ArrayItems',
-                          'x-decorator': 'FormItem',
-                          'x-visible': false,
-                          items: {
-                            type: 'object',
-                            properties: {
-                              space: {
-                                type: 'void',
-                                'x-component': 'Space',
-                                'x-component-props': {
-                                  align: 'center',
-                                },
-                                properties: {
-                                  key: {
-                                    title: '参数名',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'Input',
-                                    'x-disabled': true,
-                                    type: 'string',
-                                  },
-                                  value: {
-                                    title: '参数值',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'Input',
-                                    type: 'string',
-                                  },
-                                },
-                              },
-                            },
-                          },
-                        },
-                        filter: {
-                          title: '过滤条件',
-                          type: 'array',
-                          'x-component': 'ArrayItems',
-                          'x-decorator': 'FormItem',
-                          items: {
-                            type: 'object',
-                            properties: {
-                              space: {
-                                type: 'void',
-                                'x-component': 'Space',
-                                properties: {
-                                  key: {
-                                    type: 'string',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'FAutoComplete',
-                                    'x-component-props': {
-                                      placeholder: 'key',
-                                      style: {
-                                        width: '200px',
-                                      },
-                                    },
-                                  },
-                                  operator: {
-                                    type: 'string',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'Select',
-                                    'x-component-props': {
-                                      placeholder: '条件',
-                                      style: {
-                                        width: '110px',
-                                      },
-                                    },
-                                    enum: [
-                                      { label: '等于(=)', value: 'eq' },
-                                      { label: '不等于(!=)', value: 'not' },
-                                      { label: '大于(>)', value: 'gt' },
-                                      { label: '小于(<)', value: 'lt' },
-                                      { label: '大于等于(>=)', value: 'gte' },
-                                      { label: '小于等于(<=)', value: 'lte' },
-                                      { label: '模糊(%)', value: 'like' },
-                                    ],
-                                  },
-                                  value: {
-                                    type: 'string',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'Input',
-                                    'x-component-props': {
-                                      placeholder: 'value',
-                                    },
-                                  },
-                                  remove: {
-                                    type: 'void',
-                                    'x-decorator': 'FormItem',
-                                    'x-component': 'ArrayItems.Remove',
-                                  },
-                                },
-                              },
-                            },
-                          },
-                          properties: {
-                            add: {
-                              type: 'void',
-                              title: '添加条目',
-                              'x-component': 'ArrayItems.Addition',
-                            },
-                          },
-                        },
-                      },
-                    },
-                    remove: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.Remove',
-                    },
-                    moveUp: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.MoveUp',
-                    },
-                    moveDown: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.MoveDown',
-                    },
-                  },
-                },
-                properties: {
-                  addition: {
-                    type: 'void',
-                    title: '添加规则',
-                    'x-component': 'ArrayCollapse.Addition',
-                  },
-                },
-              },
-            },
+            properties: Trigger.schema,
           },
           tab2: {
             type: 'void',
@@ -483,7 +112,7 @@ const EditAlarm = (props: Props) => {
               tab: '转换配置',
             },
             properties: {
-              convert: {
+              properties: {
                 // title: '转换',
                 type: 'array',
                 'x-component': 'ArrayItems',
@@ -552,123 +181,142 @@ const EditAlarm = (props: Props) => {
             'x-component-props': {
               tab: '执行动作',
             },
+            properties: Action.schema,
+          },
+        },
+      },
+      name: {
+        title: intl.formatMessage({
+          id: 'pages.table.name',
+          defaultMessage: '名称',
+        }),
+        required: true,
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-index': 1,
+      },
+      shakeLimit: {
+        'x-index': 2,
+        type: 'object',
+        properties: {
+          enabled: {
+            type: 'boolean',
+            'x-component': 'Switch',
+            'x-decorator': 'FormItem',
+            'x-component-props': {
+              checkedChildren: '防抖',
+              unCheckedChildren: '防抖',
+            },
+          },
+          config: {
+            type: 'void',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              asterisk: true,
+              feedbackLayout: 'none',
+            },
+            'x-visible': false,
+            'x-component': 'Space',
+            'x-reactions': {
+              dependencies: ['.enabled'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]}}',
+                },
+              },
+            },
             properties: {
-              actions: {
-                // title: '执行动作',
-                type: 'array',
-                'x-component': 'ArrayCollapse',
+              time: {
+                type: 'number',
+                'x-component': 'NumberPicker',
                 'x-decorator': 'FormItem',
-                'x-component-props': {
-                  ghost: true,
+                'x-decorator-props': {
+                  addonAfter: '秒内发生',
                 },
-                items: {
-                  type: 'object',
-                  'x-component': 'ArrayCollapse.CollapsePanel',
-                  'x-component-props': {
-                    header: '执行动作',
-                  },
-                  properties: {
-                    index: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.Index',
-                    },
-                    action: {
-                      type: 'string',
-                      'x-decorator': 'FormItem',
-                      title: '动作类型',
-                      required: true,
-                      'x-component': 'Select',
-                      enum: [
-                        { label: '消息通知', value: 'notifier' },
-                        { label: '设备输出', value: 'device-message-sender' },
-                      ],
-                    },
-                    notifier: {
-                      type: 'object',
-                      'x-visible': false,
-                      properties: {
-                        type: {
-                          title: '通知类型',
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          required: true,
-                          'x-component': 'Select',
-                          'x-reactions': ['{{useAsyncDataSource(loadNotifierType)}}'],
-                        },
-                        config: {
-                          title: '通知配置',
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          required: true,
-                          'x-component': 'Select',
-                        },
-                        template: {
-                          title: '通知模版',
-                          type: 'string',
-                          'x-decorator': 'FormItem',
-                          required: true,
-                          'x-component': 'Select',
-                          enum: [],
-                        },
-                      },
-                      'x-reactions': {
-                        dependencies: ['..action'],
-                        fulfill: {
-                          state: {
-                            visible: "{{$deps[0]==='notifier'}}",
-                          },
-                        },
-                      },
-                    },
+                required: true,
+              },
+              threshold: {
+                type: 'number',
+                'x-component': 'NumberPicker',
+                'x-decorator': 'FormItem',
 
-                    device: {
-                      type: 'string',
-                      'x-decorator': 'FormItem',
-                      title: '选择设备',
-                      required: true,
-                      'x-component': 'FSelectDevice',
-                    },
-                    remove: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.Remove',
-                    },
-                    moveUp: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.MoveUp',
-                    },
-                    moveDown: {
-                      type: 'void',
-                      'x-component': 'ArrayCollapse.MoveDown',
-                    },
-                  },
+                'x-decorator-props': {
+                  addonAfter: '次及以上时,处理',
                 },
-                properties: {
-                  addition: {
-                    type: 'void',
-                    title: '添加动作',
-                    'x-component': 'ArrayCollapse.Addition',
+              },
+              alarmFirst: {
+                type: 'boolean',
+                'x-component': 'Radio.Group',
+                'x-decorator': 'FormItem',
+                'x-decorator-props': {
+                  style: {
+                    width: '200px',
                   },
                 },
+                'x-component-props': {
+                  buttonStyle: 'solid',
+                  optionType: 'button',
+                  size: 'small',
+                },
+                enum: [
+                  {
+                    label: '第一次',
+                    value: true,
+                  },
+                  {
+                    label: '最后一次',
+                    value: false,
+                  },
+                ],
               },
             },
           },
         },
       },
-      name: {
-        title: intl.formatMessage({
-          id: 'pages.table.name',
-          defaultMessage: '名称',
-        }),
-        required: true,
+      description: {
+        title: '说明',
         'x-decorator': 'FormItem',
-        'x-component': 'Input',
-        'x-index': 1,
+        'x-component': 'Input.TextArea',
+        'x-component-props': {
+          rows: 5,
+        },
       },
     },
   };
 
+  const handleSubmit = async () => {
+    const alarmRule = (await form.submit()) as any;
+    const id = productModel.current?.id;
+    if (id) {
+      const data = {
+        name: alarmRule?.name,
+        target: 'product',
+        targetId: id,
+        alarmRule: {
+          ...alarmRule,
+          productId: productModel.current?.id,
+          productName: productModel.current?.name,
+        },
+      };
+      await service.saveAlarm(id, data);
+      message.success('保存成功');
+      props.close();
+    } else {
+      message.error('刷新页面重试');
+    }
+  };
   return (
-    <Drawer title="编辑告警" visible={visible} onClose={() => close()} width="40vw">
+    <Drawer
+      title="编辑告警"
+      visible={visible}
+      onClose={() => close()}
+      width="40vw"
+      extra={
+        <Button type="primary" onClick={handleSubmit}>
+          保存数据
+        </Button>
+      }
+    >
       <Form form={form} layout="vertical" size="small">
         <SchemaField schema={schema} scope={{ formTab, useAsyncDataSource, loadNotifierType }} />
       </Form>

+ 22 - 4
src/pages/device/Product/Detail/Alarm/Setting/index.tsx

@@ -1,7 +1,7 @@
 import type { ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import type { AlarmSetting } from '@/pages/device/Product/typings';
-import { Button, message, Space, Tooltip } from 'antd';
+import { Button, Space, Tooltip } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { service } from '@/pages/device/Product';
 import { useParams } from 'umi';
@@ -19,6 +19,7 @@ const Setting = () => {
   const intl = useIntl();
   const param = useParams<{ id: string }>();
   const [edit, setEdit] = useState<boolean>(true);
+  const [data, setData] = useState<Record<string, unknown>>();
   const columns: ProColumns<AlarmSetting>[] = [
     {
       dataIndex: 'index',
@@ -66,7 +67,8 @@ const Setting = () => {
         <a
           key="editable"
           onClick={async () => {
-            message.success(record.id);
+            setEdit(true);
+            setData(record);
           }}
         >
           <Tooltip
@@ -111,7 +113,15 @@ const Setting = () => {
           </Space>
         )}
         toolBarRender={() => [
-          <Button onClick={() => setEdit(true)} key="button" icon={<PlusOutlined />} type="primary">
+          <Button
+            onClick={() => {
+              setEdit(true);
+              setData({});
+            }}
+            key="button"
+            icon={<PlusOutlined />}
+            type="primary"
+          >
             {intl.formatMessage({
               id: 'pages.data.option.add',
               defaultMessage: '新增',
@@ -124,13 +134,21 @@ const Setting = () => {
           return {
             result: { data: response.result },
             success: true,
+            status: 200,
           } as any;
         }}
         columns={columns}
         rowKey="id"
         search={false}
       />
-      <Edit visible={edit} close={() => setEdit(false)} />
+      <Edit
+        data={data}
+        visible={edit}
+        close={() => {
+          setEdit(false);
+          setData({});
+        }}
+      />
     </>
   );
 };