Просмотр исходного кода

feat(执行动作): 新增执行动作页面

xieyonghong 3 лет назад
Родитель
Сommit
51473927e4

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

@@ -1,18 +1,19 @@
-import { Button, InputNumber, Select } from 'antd';
+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;
 }
 
-export default (props: ActionProps) => {
-  const [type1, setType1] = useState('message');
-  const [configValue, setConfigValue] = useState<string | undefined>(undefined);
-  const [templateValue, setTemplateValue] = useState<string | undefined>(undefined);
+const ActionItem = (props: ActionProps) => {
+  const { name } = props;
+  const [type1, setType1] = useState('');
   const [templateData, setTemplateData] = useState<any>(undefined);
 
   const { data: messageType, run: queryMessageTypes } = useRequest(queryMessageType, {
@@ -40,48 +41,47 @@ export default (props: ActionProps) => {
 
   const MessageNodes = (
     <>
-      <Select
-        options={messageType}
-        fieldNames={{ value: 'id', label: 'name' }}
-        placeholder={'请选择通知方式'}
-        style={{ width: 140 }}
-        onSelect={async (key: string) => {
-          setConfigValue(undefined);
-          setTemplateValue(undefined);
-          setTemplateData(undefined);
-          await queryMessageConfigs({
-            terms: [{ column: 'type$IN', value: key }],
-          });
-        }}
-      />
-      <Select
-        value={configValue}
-        options={messageConfig}
-        loading={messageConfigLoading}
-        fieldNames={{ value: 'id', label: 'name' }}
-        onSelect={async (key: string) => {
-          setConfigValue(key);
-          setTemplateValue(undefined);
-          setTemplateData(undefined);
-          await queryMessageTemplates({
-            terms: [{ column: 'configId', value: key }],
-          });
-        }}
-        style={{ width: 160 }}
-        placeholder={'请选择通知配置'}
-      />
-      <Select
-        value={templateValue}
-        options={messageTemplate}
-        loading={messageTemplateLoading}
-        fieldNames={{ value: 'id', label: 'name' }}
-        style={{ width: 160 }}
-        placeholder={'请选择通知模板'}
-        onSelect={async (key: string, nodeData: any) => {
-          setTemplateData(nodeData);
-          setTemplateValue(key);
-        }}
-      />
+      <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>
     </>
   );
 
@@ -133,25 +133,30 @@ export default (props: ActionProps) => {
         执行动作 {props.title} <Button onClick={props.onRemove}>删除</Button>
       </div>
       <div style={{ display: 'flex', gap: 12 }}>
-        <Select
-          options={[
-            { label: '消息通知', value: 'message' },
-            { label: '设备输出', value: 'device' },
-            { label: '延迟执行', value: 'delay' },
-          ]}
-          value={type1}
-          onSelect={(key: string) => {
-            setType1(key);
-          }}
-          style={{ width: 100 }}
-        />
+        <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>
-      {type1 === 'message' && <MessageContent {...props} template={templateData} />}
     </div>
   );
 };
+
+export default ActionItem;

+ 10 - 26
src/pages/rule-engine/Scene/Save/action/messageContent.tsx

@@ -1,9 +1,8 @@
-import { Col, Form, Row, Select } from 'antd';
+import { Col, Form, Row, Select, TimePicker, Input } from 'antd';
 import { ItemGroup } from '@/pages/rule-engine/Scene/Save/components';
-import { ProFormText, ProFormSelect, ProFormDatePicker } from '@ant-design/pro-form';
 
 interface MessageContentProps {
-  type?: string;
+  name: number;
   template?: any;
 }
 
@@ -13,31 +12,13 @@ export default (props: MessageContentProps) => {
   const inputNodeByType = (data: any) => {
     switch (data.type) {
       case 'enum':
-        return (
-          <ProFormSelect
-            name={['variables', data.id]}
-            placeholder={`请选择${name}`}
-            style={{ width: '100%' }}
-          />
-        );
+        return <Select placeholder={`请选择${data.name}`} style={{ width: '100%' }} />;
       case 'timmer':
-        return (
-          <ProFormDatePicker
-            name={['variables', data.id]}
-            placeholder={'请选择时间'}
-            style={{ width: '100%' }}
-          />
-        );
+        return <TimePicker style={{ width: '100%' }} />;
       case 'number':
-        return (
-          <ProFormText
-            name={['variables', data.id]}
-            placeholder={`请输入${name}`}
-            style={{ width: '100%' }}
-          />
-        );
+        return <Input placeholder={`请输入${data.name}`} style={{ width: '100%' }} />;
       default:
-        return <ProFormText name={['variables', data.id]} placeholder={`请输入${name}`} />;
+        return <Input placeholder={`请输入${data.name}`} />;
     }
   };
 
@@ -51,7 +32,10 @@ export default (props: MessageContentProps) => {
                 // const rules = !item.required ? [{ required: true, message: '请输入'+ item.name }] : undefined
                 return (
                   <Col span={12} key={item.id}>
-                    <Form.Item label={item.name} required={!item.required}>
+                    <Form.Item
+                      name={[props.name, 'notify', 'variables', item.id]}
+                      label={item.name}
+                    >
                       <ItemGroup>
                         <Select
                           defaultValue={'1'}

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

@@ -17,3 +17,12 @@ export const queryMessageTemplate = (data: any) =>
     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' });

+ 30 - 63
src/pages/rule-engine/Scene/Save/index.tsx

@@ -1,18 +1,15 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { Card, Input, Radio } from 'antd';
-import { useIntl, useLocation } from 'umi';
-import { useEffect, useState } from 'react';
-import { TriggerWay, TimingTrigger } from './components';
-import Actions from './action';
+import { Button, Card, Form } from 'antd';
+import { useLocation } from 'umi';
+import { useEffect } from 'react';
 import { PermissionButton } from '@/components';
-import ProForm from '@ant-design/pro-form';
+import ActionItems from './action/action';
+import { PlusOutlined } from '@ant-design/icons';
 
 export default () => {
-  const intl = useIntl();
   const location = useLocation();
-  const [form] = ProForm.useForm();
+  const [form] = Form.useForm();
 
-  const [actionType, setActionType] = useState(1);
   const { getOtherPermission } = PermissionButton.usePermission('rule-engine/Scene');
 
   const getDetail = async () => {
@@ -35,62 +32,32 @@ export default () => {
   return (
     <PageContainer>
       <Card>
-        <ProForm form={form} layout={'vertical'}>
-          <ProForm.Item
-            name="name"
-            label={intl.formatMessage({
-              id: 'pages.table.name',
-              defaultMessage: '名称',
-            })}
-            required={true}
-            rules={[
-              { required: true, message: '请输入名称' },
-              {
-                max: 64,
-                message: '最多可输入64个字符',
-              },
-            ]}
-          >
-            <Input placeholder={'请输入名称'} />
-          </ProForm.Item>
-          <ProForm.Item label={'触发方式'} required>
-            <TriggerWay />
-            <TimingTrigger />
-          </ProForm.Item>
-          <ProForm.Item
-            label={
+        <Form form={form} autoComplete="off">
+          <Form.List name="users">
+            {(fields, { add, remove }) => (
               <>
-                <span>执行动作</span>
-                <Radio.Group
-                  value={actionType}
-                  optionType="button"
-                  buttonStyle="solid"
-                  size={'small'}
-                  style={{ marginLeft: 12 }}
-                  onChange={(e) => {
-                    setActionType(e.target.value);
-                  }}
-                  options={[
-                    { label: '串行', value: 1 },
-                    { label: '并行', value: 2 },
-                  ]}
-                />
+                {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>
               </>
-            }
-            tooltip={
-              <div style={{ width: 200 }}>
-                <div>并行:满足任意条件时会触发执行动作</div>
-                <div>穿行:满足所有执行条件才会触发执行动作</div>
-              </div>
-            }
-            required
-          >
-            <Actions />
-          </ProForm.Item>
-          <ProForm.Item name={'describe'} label={'说明'}>
-            <Input.TextArea rows={4} maxLength={200} showCount placeholder={'请输入说明'} />
-          </ProForm.Item>
-        </ProForm>
+            )}
+          </Form.List>
+          <Form.Item>
+            <Button type="primary" htmlType="submit">
+              Submit
+            </Button>
+          </Form.Item>
+        </Form>
         <PermissionButton isPermission={getOtherPermission(['add', 'update'])} onClick={saveData}>
           保存
         </PermissionButton>

+ 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>
+  );
+};

+ 4 - 1
src/utils/menu/index.ts

@@ -31,7 +31,10 @@ const extraRouteObj = {
     ],
   },
   'rule-engine/Scene': {
-    children: [{ code: 'Save', name: '详情' }],
+    children: [
+      { code: 'Save', name: '详情' },
+      { code: 'Save2', name: '测试详情' },
+    ],
   },
 };
 

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

@@ -62,6 +62,7 @@ export enum MENUS_CODE {
   'rule-engine/Scene' = 'rule-engine/Scene',
   'rule-engine/Alarm/Config' = 'rule-engine/Alarm/Config',
   'rule-engine/Scene/Save' = 'rule-engine/Scene/Save',
+  'rule-engine/Scene/Save2' = 'rule-engine/Scene/Save2',
   'simulator/Device' = 'simulator/Device',
   'system/DataSource' = 'system/DataSource',
   'system/Department/Assets' = 'system/Department/Assets',