xieyonghong 3 лет назад
Родитель
Сommit
acbe047043
24 измененных файлов с 1406 добавлено и 113 удалено
  1. 46 0
      src/components/ProTableCard/CardItems/noticeConfig.tsx
  2. 46 10
      src/components/ProTableCard/CardItems/noticeTemplate.tsx
  3. 17 3
      src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx
  4. 9 1
      src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx
  5. 36 6
      src/pages/rule-engine/Scene/Save/action/Modal/add.tsx
  6. 102 3
      src/pages/rule-engine/Scene/Save/action/notify/NotifyConfig.tsx
  7. 92 3
      src/pages/rule-engine/Scene/Save/action/notify/NotifyTemplate.tsx
  8. 47 14
      src/pages/rule-engine/Scene/Save/action/notify/NotifyWay.tsx
  9. 172 0
      src/pages/rule-engine/Scene/Save/action/notify/VariableDefinitions.tsx
  10. 13 21
      src/pages/rule-engine/Scene/Save/action/notify/components/notifyType/index.less
  11. 13 7
      src/pages/rule-engine/Scene/Save/action/notify/components/notifyType/index.tsx
  12. 140 0
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/buildIn.tsx
  13. 69 0
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/inputFile.tsx
  14. 67 0
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/org.tsx
  15. 50 0
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/tag.tsx
  16. 315 0
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/user.tsx
  17. 129 34
      src/pages/rule-engine/Scene/Save/action/notify/index.tsx
  18. 12 0
      src/pages/rule-engine/Scene/Save/action/service.ts
  19. 1 0
      src/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType.tsx
  20. 1 2
      src/pages/rule-engine/Scene/Save/components/TriggerWay/index.less
  21. 3 0
      src/pages/rule-engine/Scene/Save/components/TriggerWay/index.tsx
  22. 13 0
      src/pages/rule-engine/Scene/Save/index.tsx
  23. 6 2
      src/pages/rule-engine/Scene/Save/save.tsx
  24. 7 7
      src/pages/rule-engine/Scene/index.tsx

+ 46 - 0
src/components/ProTableCard/CardItems/noticeConfig.tsx

@@ -3,13 +3,59 @@ import { Ellipsis, TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
 import { imgMap, typeList } from './noticeTemplate';
+import { CheckOutlined } from '@ant-design/icons';
 
 export interface NoticeCardProps extends ConfigItem {
   detail?: React.ReactNode;
   actions?: React.ReactNode[];
   avatarSize?: number;
+  className?: string;
+  onUnBind?: (e: any) => void;
+  showBindBtn?: boolean;
+  cardType?: 'bind' | 'unbind';
+  showTool?: boolean;
+  onClick?: () => void;
 }
 
+export const ExtraNoticeConfigCard = (props: NoticeCardProps) => {
+  return (
+    <TableCard
+      showTool={props.showTool}
+      showMask={false}
+      showStatus={false}
+      actions={props.actions}
+      onClick={props.onClick}
+      className={props.className}
+    >
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={imgMap[props.type][props.provider]} alt={props.type} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
+          </div>
+          <div className={'card-item-content'}>
+            <div>
+              <label>通知方式</label>
+              <Ellipsis title={typeList[props.type][props.provider] || '暂无'} />
+            </div>
+            <div>
+              <label>说明</label>
+              <Ellipsis title={props.description} />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
+    </TableCard>
+  );
+};
+
 export default (props: NoticeCardProps) => {
   return (
     <TableCard detail={props.detail} actions={props.actions} showStatus={false} showMask={false}>

+ 46 - 10
src/components/ProTableCard/CardItems/noticeTemplate.tsx

@@ -2,11 +2,18 @@ import React from 'react';
 import { Ellipsis, TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
+import { CheckOutlined } from '@ant-design/icons';
 
 export interface NoticeCardProps extends TemplateItem {
   detail?: React.ReactNode;
   actions?: React.ReactNode[];
   avatarSize?: number;
+  className?: string;
+  onUnBind?: (e: any) => void;
+  showBindBtn?: boolean;
+  cardType?: 'bind' | 'unbind';
+  showTool?: boolean;
+  onClick?: () => void;
 }
 
 export const imgMap = {
@@ -55,6 +62,45 @@ export const typeList = {
   },
 };
 
+export const ExtraNoticeTemplateCard = (props: NoticeCardProps) => {
+  return (
+    <TableCard
+      showTool={props.showTool}
+      showMask={false}
+      showStatus={false}
+      actions={props.actions}
+      onClick={props.onClick}
+      className={props.className}
+    >
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={imgMap[props.type][props.provider]} alt={props.type} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
+          </div>
+          <div className={'card-item-content'}>
+            <div>
+              <label>通知方式</label>
+              <Ellipsis title={typeList[props.type][props.provider] || '暂无'} />
+            </div>
+            <div>
+              <label>说明</label>
+              <Ellipsis title={props.description} />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
+    </TableCard>
+  );
+};
+
 export default (props: NoticeCardProps) => {
   return (
     <TableCard actions={props.actions} showStatus={false} detail={props.detail} showMask={false}>
@@ -64,11 +110,6 @@ export default (props: NoticeCardProps) => {
         </div>
         <div className={'card-item-body'}>
           <div className={'card-item-header'}>
-            {/*<span className={'card-item-header-name ellipsis'}>*/}
-            {/*  <Tooltip placement="topLeft" title={props.name}>*/}
-            {/*    {props.name}*/}
-            {/*  </Tooltip>*/}
-            {/*</span>*/}
             <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
           </div>
           <div className={'card-item-content'}>
@@ -80,11 +121,6 @@ export default (props: NoticeCardProps) => {
             <div>
               <label>说明</label>
               <Ellipsis tooltip={{ placement: 'topLeft' }} title={props.description} />
-              {/*<div className={'ellipsis'}>*/}
-              {/*  <Tooltip placement="topLeft" title={props.description}>*/}
-              {/*    {props.description}*/}
-              {/*  </Tooltip>*/}
-              {/*</div>*/}
             </div>
           </div>
         </div>

+ 17 - 3
src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx

@@ -17,7 +17,7 @@ interface ItemProps {
 }
 
 export default (props: ItemProps) => {
-  const [visible] = useState<boolean>(false);
+  const [visible, setVisible] = useState<boolean>(false);
   return (
     <div className="actions-item-warp">
       <div className="actions-item">
@@ -25,7 +25,13 @@ export default (props: ItemProps) => {
           <div className="type">
             <img src="" />
           </div>
-          <div></div>
+          <div
+            onClick={() => {
+              setVisible(true);
+            }}
+          >
+            {'item'}
+          </div>
         </div>
         <div className="item-number">{props.name + 1}</div>
         <div
@@ -47,7 +53,15 @@ export default (props: ItemProps) => {
           <div>添加过滤条件</div>
         )
       ) : null}
-      {visible && <Modal />}
+      {visible && (
+        <Modal
+          name={props.name}
+          data={props.data}
+          close={() => {
+            setVisible(false);
+          }}
+        />
+      )}
     </div>
   );
 };

+ 9 - 1
src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx

@@ -40,7 +40,15 @@ export default (props: ListProps) => {
       >
         点击配置执行动作
       </AddButton>
-      {visible && <Modal />}
+      {visible && (
+        <Modal
+          name={props.actions.length + 1}
+          data={{}}
+          close={() => {
+            setVisible(false);
+          }}
+        />
+      )}
     </div>
   );
 };

+ 36 - 6
src/pages/rule-engine/Scene/Save/action/Modal/add.tsx

@@ -1,15 +1,43 @@
 import { Modal, Form } from 'antd';
-import ActionsType from '@/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType';
-import { useState } from 'react';
+import ActionsTypeComponent from '@/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType';
+import { useEffect, useState } from 'react';
 import Notify from '../notify';
-export default () => {
+import type { ActionsType } from '@/pages/rule-engine/Scene/typings';
+
+interface Props {
+  close: () => void;
+  data: Partial<ActionsType>;
+  name: number;
+}
+export default (props: Props) => {
   const [form] = Form.useForm();
   const [actionType, setActionType] = useState<string>('');
 
+  useEffect(() => {
+    if (props.data?.executor) {
+      form.setFieldsValue({
+        type: props.data.executor,
+      });
+    }
+  }, [props.data]);
+
   const actionTypeComponent = (type: string) => {
     switch (type) {
       case 'notify':
-        return <Notify />;
+        return (
+          <Notify
+            value={props.data?.notify || {}}
+            save={(data: any) => {
+              console.log(data); // value
+              setActionType('');
+              props.close();
+            }}
+            name={props.name}
+            cancel={() => {
+              setActionType('');
+            }}
+          />
+        );
       default:
         return null;
     }
@@ -20,7 +48,9 @@ export default () => {
       title="类型"
       open
       width={800}
-      onCancel={() => {}}
+      onCancel={() => {
+        props.close();
+      }}
       onOk={async () => {
         const values = await form.validateFields();
         setActionType(values.type);
@@ -33,7 +63,7 @@ export default () => {
           required
           rules={[{ required: true, message: '请选择类型' }]}
         >
-          <ActionsType />
+          <ActionsTypeComponent />
         </Form.Item>
       </Form>
       {actionTypeComponent(actionType)}

+ 102 - 3
src/pages/rule-engine/Scene/Save/action/notify/NotifyConfig.tsx

@@ -1,3 +1,102 @@
-export default () => {
-  return <div>通知配置</div>;
-};
+import { useRef, useState } from 'react';
+import SearchComponent from '@/components/SearchComponent';
+import { ProTableCard } from '@/components';
+import { ActionType, ProColumns } from '@jetlinks/pro-table';
+import { queryMessageConfigPaging } from '../service';
+import { ExtraNoticeConfigCard } from '@/components/ProTableCard/CardItems/noticeConfig';
+import { observer } from '@formily/react';
+import { NotifyModel } from './index';
+
+export default observer(() => {
+  const actionRef = useRef<ActionType>();
+  const [searchParam, setSearchParam] = useState({});
+
+  const columns: ProColumns<ConfigItem>[] = [
+    {
+      dataIndex: 'id',
+      title: 'ID',
+    },
+    {
+      dataIndex: 'name',
+      title: '名称',
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+    },
+  ];
+
+  return (
+    <div>
+      <SearchComponent<ConfigItem>
+        field={columns}
+        enableSave={false}
+        model={'simple'}
+        onSearch={async (data) => {
+          actionRef.current?.reset?.();
+          setSearchParam(data);
+        }}
+        target="scene-notify-config"
+      />
+      <div
+        style={{
+          height: 'calc(100vh - 440px)',
+          overflowY: 'auto',
+        }}
+      >
+        <ProTableCard<ConfigItem>
+          actionRef={actionRef}
+          columns={columns}
+          rowKey="id"
+          search={false}
+          gridColumn={2}
+          columnEmptyText={''}
+          cardRender={(record) => (
+            <ExtraNoticeConfigCard
+              showBindBtn={false}
+              showTool={false}
+              cardType={'bind'}
+              {...record}
+            />
+          )}
+          rowSelection={{
+            selectedRowKeys: [NotifyModel.notify?.notifierId || ''],
+            onChange: (selectedRowKeys) => {
+              if (selectedRowKeys.length) {
+                NotifyModel.notify.notifierId = String(selectedRowKeys[selectedRowKeys.length - 1]);
+              }
+            },
+          }}
+          request={(params) =>
+            queryMessageConfigPaging({
+              ...params,
+              terms: params?.terms
+                ? [
+                    ...params?.terms,
+                    {
+                      terms: [
+                        {
+                          column: 'type',
+                          termType: 'eq',
+                          value: NotifyModel.notify?.notifyType || '',
+                        },
+                      ],
+                    },
+                  ]
+                : [
+                    {
+                      column: 'type',
+                      termType: 'eq',
+                      value: NotifyModel.notify?.notifyType || '',
+                    },
+                  ],
+              sorts: [{ name: 'createTime', order: 'desc' }],
+            })
+          }
+          params={searchParam}
+          height={'none'}
+        />
+      </div>
+    </div>
+  );
+});

+ 92 - 3
src/pages/rule-engine/Scene/Save/action/notify/NotifyTemplate.tsx

@@ -1,3 +1,92 @@
-export default () => {
-  return <div>通知模板</div>;
-};
+import { useRef, useState } from 'react';
+import SearchComponent from '@/components/SearchComponent';
+import { ProTableCard } from '@/components';
+import { ActionType, ProColumns } from '@jetlinks/pro-table';
+import { queryMessageTemplatePaging } from '../service';
+import { ExtraNoticeTemplateCard } from '@/components/ProTableCard/CardItems/noticeTemplate';
+import { observer } from '@formily/react';
+import { NotifyModel } from './index';
+
+export default observer(() => {
+  const actionRef = useRef<ActionType>();
+  const [searchParam, setSearchParam] = useState({});
+
+  const columns: ProColumns<TemplateItem>[] = [
+    {
+      dataIndex: 'id',
+      title: 'ID',
+    },
+    {
+      dataIndex: 'name',
+      title: '名称',
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+    },
+  ];
+
+  return (
+    <div>
+      <SearchComponent<TemplateItem>
+        field={columns}
+        enableSave={false}
+        model={'simple'}
+        onSearch={async (data) => {
+          actionRef.current?.reset?.();
+          setSearchParam(data);
+        }}
+        target="scene-notify-template"
+      />
+      <div
+        style={{
+          height: 'calc(100vh - 440px)',
+          overflowY: 'auto',
+        }}
+      >
+        <ProTableCard<TemplateItem>
+          actionRef={actionRef}
+          columns={columns}
+          rowKey="id"
+          search={false}
+          gridColumn={2}
+          columnEmptyText={''}
+          cardRender={(record) => (
+            <ExtraNoticeTemplateCard
+              showBindBtn={false}
+              showTool={false}
+              cardType={'bind'}
+              {...record}
+            />
+          )}
+          rowSelection={{
+            selectedRowKeys: [NotifyModel.notify?.templateId || ''],
+            onChange: (selectedRowKeys) => {
+              if (selectedRowKeys.length) {
+                NotifyModel.notify.templateId = String(selectedRowKeys[selectedRowKeys.length - 1]);
+              }
+            },
+          }}
+          request={async (params) => {
+            const resp = await queryMessageTemplatePaging(NotifyModel.notify?.notifierId || '', {
+              ...params,
+              sorts: [{ name: 'createTime', order: 'desc' }],
+            });
+            return {
+              code: resp.message,
+              result: {
+                data: resp.result ? resp.result : [],
+                pageIndex: 0,
+                pageSize: resp.result.length,
+                total: resp.result.length,
+              },
+              status: resp.status,
+            };
+          }}
+          params={searchParam}
+          height={'none'}
+        />
+      </div>
+    </div>
+  );
+});

+ 47 - 14
src/pages/rule-engine/Scene/Save/action/notify/NotifyWay.tsx

@@ -1,30 +1,63 @@
-import { Form } from 'antd';
+import { Form, Spin } from 'antd';
 import NotifyType from './components/notifyType';
-import { useEffect, useState } from 'react';
+import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
 import { queryMessageType } from '@/pages/rule-engine/Scene/Save/action/service';
 
-export default () => {
+interface NotifyWayProps {
+  value: string | undefined;
+}
+export default forwardRef((props: NotifyWayProps, ref) => {
   const [form] = Form.useForm();
   const [list, setList] = useState<any[]>([]);
+  const [loading, setLoading] = useState<boolean>(false);
 
   useEffect(() => {
+    setLoading(true);
     queryMessageType().then((resp) => {
       if (resp.status === 200) {
         setList(resp.result);
       }
+      setLoading(false);
     });
   }, []);
 
+  useEffect(() => {
+    if (props.value) {
+      form.setFieldsValue({
+        notifyType: props.value || '',
+      });
+    }
+  }, [props.value]);
+
+  const getValue = () => {
+    return new Promise(async (resolve) => {
+      const formData = await form.validateFields().catch(() => {
+        resolve(false);
+      });
+      if (formData?.notifyType) {
+        resolve(formData?.notifyType);
+      } else {
+        resolve(false);
+      }
+    });
+  };
+
+  useImperativeHandle(ref, () => ({
+    save: getValue,
+  }));
+
   return (
-    <Form form={form} layout={'vertical'}>
-      <Form.Item
-        name="notifyType"
-        label="应用"
-        required
-        rules={[{ required: true, message: '请选择类型' }]}
-      >
-        <NotifyType options={list} />
-      </Form.Item>
-    </Form>
+    <Spin spinning={loading}>
+      <Form form={form} layout={'vertical'}>
+        <Form.Item
+          name="notifyType"
+          label="应用"
+          required
+          rules={[{ required: true, message: '请选择类型' }]}
+        >
+          <NotifyType options={list} />
+        </Form.Item>
+      </Form>
+    </Spin>
   );
-};
+});

+ 172 - 0
src/pages/rule-engine/Scene/Save/action/notify/VariableDefinitions.tsx

@@ -0,0 +1,172 @@
+import { NotifyModel } from './index';
+import { Empty } from '@/components';
+import { Form, Input } from 'antd';
+import User from './components/variableItem/user';
+import Org from './components/variableItem/org';
+import Tag from './components/variableItem/tag';
+import BuildIn from './components/variableItem/buildIn';
+import InputFile from './components/variableItem/inputFile';
+import { forwardRef, useCallback, useImperativeHandle } from 'react';
+
+interface Props {
+  name: number;
+}
+
+export default forwardRef((props: Props, ref) => {
+  const [form] = Form.useForm();
+  const typeComponents = (item: any) => {
+    const type = item.expands?.businessType || item.type;
+    switch (type) {
+      case 'user':
+        return <User />;
+      case 'org':
+        return <Org />;
+      case 'tag':
+        return <Tag />;
+      case 'file':
+        return <InputFile />;
+      case 'link':
+        return <Input placeholder={'请输入' + item.name} />;
+      default:
+        return <BuildIn data={item} name={props.name} />;
+    }
+  };
+
+  const getRules = useCallback(
+    (item: any, type: string): any[] => {
+      const rules: any[] = [];
+      if (item?.required) {
+        if (type === 'user') {
+          rules.push({
+            validator: async (_: any, value: any) => {
+              if (!value) {
+                return Promise.reject(new Error('请选择' + item.name));
+              } else {
+                if (value.source === 'fixed' && !value.value) {
+                  return Promise.reject(new Error('请输入' + item.name));
+                } else if (value.source === 'relation' && !value.value && !value.relation) {
+                  return Promise.reject(new Error('请选择' + item.name));
+                }
+              }
+              return Promise.resolve();
+            },
+          });
+        } else {
+          rules.push({
+            validator: async (_: any, value: any) => {
+              if (type === 'file' || type === 'link') {
+                if (!value) {
+                  return Promise.reject(new Error('请输入' + item.name));
+                }
+              } else {
+                if (!value || !value.value) {
+                  if (['date', 'org'].includes(type)) {
+                    return Promise.reject(new Error('请选择' + item.name));
+                  } else {
+                    return Promise.reject(new Error('请输入' + item.name));
+                  }
+                }
+              }
+              return Promise.resolve();
+            },
+          });
+        }
+      }
+
+      if (type === 'link') {
+        rules.push({ max: 64, message: '最多64个字符' });
+      }
+
+      if (type === 'user') {
+        if (NotifyModel.notify?.notifyType === 'email') {
+          rules.push({
+            validator: async (_: any, value: any) => {
+              if (value.value) {
+                const reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
+                if (!reg.test(value.value)) {
+                  return Promise.reject(new Error('请输入正确的邮箱地址'));
+                }
+              }
+              return Promise.resolve();
+            },
+          });
+        }
+
+        if (
+          NotifyModel.notify?.notifyType &&
+          ['sms', 'voice'].includes(NotifyModel.notify?.notifyType)
+        ) {
+          rules.push({
+            validator: async (_: any, value: any) => {
+              if (value.value) {
+                const reg = /^[1][3-9]\d{9}$/;
+                if (!reg.test(value.value)) {
+                  return Promise.reject(new Error('请输入正确的手机号码'));
+                }
+              }
+              return Promise.resolve();
+            },
+          });
+        }
+      }
+
+      return rules;
+    },
+    [NotifyModel.notify?.notifyType],
+  );
+
+  const saveBtn = () => {
+    return new Promise(async (resolve) => {
+      const formData = await form.validateFields().catch(() => {
+        resolve(false);
+      });
+      console.log(formData);
+      if (formData) {
+        resolve(formData);
+      } else {
+        resolve(false);
+      }
+    });
+  };
+
+  useImperativeHandle(ref, () => ({
+    save: saveBtn,
+  }));
+
+  return NotifyModel.variable.length ? (
+    <div>
+      <Form form={form} layout={'vertical'}>
+        {(NotifyModel?.variable || []).map((item) => {
+          const type = item.expands?.businessType || item.type;
+          let initialValue = undefined;
+          const rules = getRules(item, type);
+          if (type === 'user') {
+            initialValue = {
+              source: 'relation',
+              value: undefined,
+            };
+          } else if (['date', 'number', 'string'].includes(type)) {
+            initialValue = {
+              source: 'fixed',
+              value: undefined,
+            };
+          }
+          return (
+            <Form.Item
+              key={item.id}
+              name={item.id}
+              label={item.name}
+              initialValue={initialValue}
+              required={!!item.required}
+              rules={rules}
+            >
+              {typeComponents(item)}
+            </Form.Item>
+          );
+        })}
+      </Form>
+    </div>
+  ) : (
+    <Empty />
+  );
+});

+ 13 - 21
src/pages/rule-engine/Scene/Save/action/notify/components/notifyType/index.less

@@ -1,38 +1,30 @@
 @import '~antd/es/style/themes/default.less';
 
-.trigger-way-warp {
+.notify-type-warp {
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
   width: 100%;
 
-  .trigger-way-item {
+  .notify-type-item {
     display: flex;
-    justify-content: space-between;
-    width: 237px;
-    //width: 100%;
-    padding: 16px;
+    flex-direction: column;
+    align-items: center;
+    width: 172px;
     border: 1px solid #e0e4e8;
     border-radius: 2px;
     cursor: pointer;
-    opacity: 0.6;
     transition: all 0.3s;
 
-    .way-item-title {
-      p {
-        margin-bottom: 8px;
-        font-weight: bold;
-        font-size: 16px;
-      }
-
-      span {
-        color: rgba(#000, 0.35);
-        font-size: 12px;
-      }
+    .notify-type-item-title {
+      margin-bottom: 8px;
+      font-weight: 500;
+      font-size: 14px;
     }
 
-    .way-item-image {
-      margin: 0 !important;
+    .notify-type-item-image {
+      width: 106px;
+      margin: 16px 33px;
     }
 
     &:hover {
@@ -47,7 +39,7 @@
   }
 
   &.disabled {
-    .trigger-way-item {
+    .notify-type-item {
       cursor: not-allowed;
 
       &:hover {

+ 13 - 7
src/pages/rule-engine/Scene/Save/action/notify/components/notifyType/index.tsx

@@ -11,6 +11,14 @@ interface NotifyTypeProps {
   options: any[];
 }
 
+const iconMap = new Map();
+iconMap.set('dingTalk', require('/public/images/notice/dingtalk.png'));
+iconMap.set('weixin', require('/public/images/notice/wechat.png'));
+iconMap.set('email', require('/public/images/notice/email.png'));
+iconMap.set('voice', require('/public/images/notice/voice.png'));
+iconMap.set('sms', require('/public/images/notice/sms.png'));
+iconMap.set('webhook', require('/public/images/notice/webhook.png'));
+
 export default (props: NotifyTypeProps) => {
   const [type, setType] = useState(props.value || '');
 
@@ -29,23 +37,21 @@ export default (props: NotifyTypeProps) => {
   };
 
   return (
-    <div className={classNames('trigger-way-warp', props.className, { disabled: props.disabled })}>
+    <div className={classNames('notify-type-warp', props.className, { disabled: props.disabled })}>
       {(props?.options || []).map((item) => (
         <div
           key={item.id}
-          className={classNames('trigger-way-item', {
+          className={classNames('notify-type-item', {
             active: type === item.id,
           })}
           onClick={() => {
             onSelect(item.id);
           }}
         >
-          <div className={'way-item-title'}>
-            <p>{item.name}</p>
-          </div>
-          <div className={'way-item-image'}>
-            <img width={48} src={item.image} />
+          <div className={'notify-type-item-image'}>
+            <img width={'100%'} src={iconMap.get(item.id)} />
           </div>
+          <div className={'notify-type-item-title'}>{item.name}</div>
         </div>
       ))}
     </div>

+ 140 - 0
src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/buildIn.tsx

@@ -0,0 +1,140 @@
+import { DatePicker, Input, InputNumber, Select, TreeSelect } from 'antd';
+import { useCallback, useEffect, useState } from 'react';
+import { queryBuiltInParams } from '@/pages/rule-engine/Scene/Save/action/service';
+import { ItemGroup } from '@/pages/rule-engine/Scene/Save/components';
+import { BuiltInParamsHandleTreeData } from '@/pages/rule-engine/Scene/Save/components/BuiltInParams';
+import moment from 'moment';
+import { FormModel } from '@/pages/rule-engine/Scene/Save';
+
+type ChangeType = {
+  source?: string;
+  value?: string;
+  upperKey?: string;
+};
+
+interface BuiltInProps {
+  value?: ChangeType;
+  data?: any;
+  onChange?: (value: ChangeType) => void;
+  name: number;
+}
+
+export default (props: BuiltInProps) => {
+  const [source, setSource] = useState(props.value?.source);
+  const [value, setValue] = useState(props.value?.value);
+  const [upperKey, setUpperKey] = useState(props.value?.upperKey);
+
+  const [builtInList, setBuiltInList] = useState<any[]>([]);
+
+  const onChange = (_source: string = 'fixed', _value?: any, _upperKey?: string) => {
+    const obj: ChangeType = {
+      source: _source,
+    };
+    if (_value) {
+      obj.value = _value;
+    }
+    if (_upperKey) {
+      obj.upperKey = _upperKey;
+    }
+    if (props.onChange) {
+      props.onChange(obj);
+    }
+  };
+
+  const sourceChangeEvent = async () => {
+    const params = props.name - 1 >= 0 ? { action: props.name - 1 } : undefined;
+    queryBuiltInParams(FormModel, params).then((res: any) => {
+      if (res.status === 200) {
+        const _data = BuiltInParamsHandleTreeData(res.result);
+        setBuiltInList(_data);
+      }
+    });
+  };
+
+  useEffect(() => {
+    if (source === 'upper') {
+      sourceChangeEvent();
+    }
+  }, [source]);
+
+  useEffect(() => {
+    setSource(props.value?.source);
+    setValue(props.value?.value);
+    setUpperKey(props.value?.upperKey);
+  }, [props.value]);
+
+  const itemOnChange = useCallback(
+    (_value: any) => {
+      onChange(source, _value);
+    },
+    [source],
+  );
+
+  const sourceComponents = () => {
+    if (source === 'upper') {
+      return (
+        <TreeSelect
+          value={upperKey}
+          treeData={builtInList}
+          onChange={(key) => {
+            onChange(source, undefined, key);
+          }}
+          fieldNames={{ label: 'name', value: 'id' }}
+          placeholder={'请选择参数'}
+        />
+      );
+    } else {
+      const type = props.data?.type;
+      if (type === 'date') {
+        // @ts-ignore
+        return (
+          <DatePicker
+            value={value ? moment(value, 'YYYY-MM-DD HH:mm:ss') : undefined}
+            style={{ width: '100%' }}
+            format={'YYYY-MM-DD HH:mm:ss'}
+            onChange={(_: any, dateString) => {
+              itemOnChange(dateString);
+            }}
+          />
+        );
+      } else if (type === 'number') {
+        return (
+          <InputNumber
+            value={value}
+            placeholder={`请输入${props.data.name}`}
+            style={{ width: '100%' }}
+            onChange={itemOnChange}
+          />
+        );
+      } else {
+        return (
+          <Input
+            value={props.value?.value}
+            placeholder={`请输入${props.data.name}`}
+            onChange={(e) => {
+              onChange(source, e.target.value);
+            }}
+          />
+        );
+      }
+    }
+  };
+
+  return (
+    <ItemGroup compact>
+      <Select
+        value={source}
+        options={[
+          { label: '手动输入', value: 'fixed' },
+          { label: '内置参数', value: 'upper' },
+        ]}
+        style={{ width: 120 }}
+        onChange={(key) => {
+          setSource(key);
+          onChange(key, undefined, undefined);
+        }}
+      />
+      {sourceComponents()}
+    </ItemGroup>
+  );
+};

+ 69 - 0
src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/inputFile.tsx

@@ -0,0 +1,69 @@
+import { useEffect, useState } from 'react';
+import { Button, Input, Upload } from 'antd';
+import { UploadChangeParam } from 'antd/lib/upload/interface';
+import SystemConst from '@/utils/const';
+import Token from '@/utils/token';
+import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
+
+interface InputUploadProps {
+  value?: string;
+  onChange?: (value?: string) => void;
+  id?: string;
+}
+
+export default (props: InputUploadProps) => {
+  const { onChange } = props;
+
+  const [url, setUrl] = useState(props.value || undefined);
+  const [loading, setLoading] = useState<boolean>(false);
+
+  useEffect(() => {
+    setUrl(url);
+  }, [props.value]);
+
+  const handleChange = (info: UploadChangeParam) => {
+    if (info.file.status === 'uploading') {
+      setLoading(true);
+    }
+    if (info.file.status === 'done') {
+      info.file.url = info.file.response?.result;
+      setLoading(false);
+      if (onChange) {
+        const result = info.file.response?.result;
+        setUrl(result);
+        onChange(result);
+      }
+    }
+  };
+
+  const UploadNode = (
+    <Upload
+      action={`/${SystemConst.API_BASE}/file/static`}
+      headers={{
+        'X-Access-Token': Token.get(),
+      }}
+      showUploadList={false}
+      onChange={handleChange}
+      disabled={loading}
+    >
+      <Button type={'link'} style={{ height: 30 }}>
+        {loading ? <LoadingOutlined /> : <PlusOutlined />}
+        上传附件
+      </Button>
+    </Upload>
+  );
+
+  return (
+    <Input
+      value={url}
+      id={props.id}
+      onChange={(e) => {
+        if (onChange) {
+          onChange(e.target.value);
+        }
+      }}
+      addonAfter={UploadNode}
+      placeholder={'请上传文件'}
+    />
+  );
+};

+ 67 - 0
src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/org.tsx

@@ -0,0 +1,67 @@
+import { TreeSelect } from 'antd';
+import { useEffect, useState } from 'react';
+import {
+  queryDingTalkDepartments,
+  queryWechatDepartments,
+} from '@/pages/rule-engine/Scene/Save/action/service';
+import { NotifyModel } from '../../index';
+
+type ChangeType = {
+  source?: string;
+  value?: string[];
+};
+
+interface OrgProps {
+  value?: ChangeType;
+  onChange?: (value: ChangeType) => void;
+}
+
+export default (props: OrgProps) => {
+  const [keys, setKeys] = useState<any[]>(props.value?.value || []);
+  const [departmentTree, setDepartmentTree] = useState([]);
+
+  const getDepartment = async (id: string) => {
+    if (NotifyModel.notify?.notifyType === 'dingTalk') {
+      const resp = await queryDingTalkDepartments(id);
+      if (resp.status === 200) {
+        setDepartmentTree(resp.result);
+      }
+    } else {
+      const resp = await queryWechatDepartments(id);
+      if (resp.status === 200) {
+        setDepartmentTree(resp.result);
+      }
+    }
+  };
+
+  useEffect(() => {
+    if (NotifyModel.notify?.notifierId) {
+      getDepartment(NotifyModel.notify.notifierId);
+    }
+  }, []);
+
+  useEffect(() => {
+    setKeys(props.value?.value || []);
+  }, [props.value]);
+
+  return (
+    <TreeSelect
+      value={keys}
+      listHeight={200}
+      treeData={departmentTree}
+      fieldNames={{
+        label: 'name',
+        value: 'id',
+      }}
+      onChange={(key) => {
+        if (props.onChange) {
+          props.onChange({
+            source: 'fixed',
+            value: key,
+          });
+        }
+      }}
+      placeholder={'请选择组织'}
+    />
+  );
+};

+ 50 - 0
src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/tag.tsx

@@ -0,0 +1,50 @@
+import { Select } from 'antd';
+import { useEffect, useState } from 'react';
+import { queryTag } from '@/pages/rule-engine/Scene/Save/action/service';
+import { NotifyModel } from '../../index';
+
+interface TagSelectProps {
+  value?: string;
+  onChange?: (value: string) => void;
+}
+
+export default (props: TagSelectProps) => {
+  const [value, setValue] = useState<string | undefined>(props.value);
+  const [options, setOptions] = useState([]);
+
+  useEffect(() => {
+    if (NotifyModel.notify?.notifierId) {
+      queryTag(NotifyModel.notify.notifierId).then((res) => {
+        if (res.status === 200) {
+          setOptions(res.result);
+        } else {
+          setOptions([]);
+        }
+      });
+    } else {
+      setOptions([]);
+    }
+  }, [NotifyModel.notify.notifierId]);
+
+  useEffect(() => {
+    setValue(props.value);
+  }, [props.value]);
+
+  return (
+    <Select
+      value={value}
+      placeholder={'请选择标签'}
+      options={options}
+      fieldNames={{
+        label: 'name',
+        value: 'id',
+      }}
+      style={{ width: '100%' }}
+      onChange={(key) => {
+        if (props.onChange) {
+          props.onChange(key);
+        }
+      }}
+    />
+  );
+};

+ 315 - 0
src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/user.tsx

@@ -0,0 +1,315 @@
+// 收信人
+import { useEffect, useState } from 'react';
+import { ItemGroup } from '@/pages/rule-engine/Scene/Save/components';
+import { Input, Select, TreeSelect } from 'antd';
+import {
+  queryDingTalkUsers,
+  queryPlatformUsers,
+  queryRelationUsers,
+  queryWechatUsers,
+} from '@/pages/rule-engine/Scene/Save/action/service';
+import { useLocation } from '@/hooks';
+import { observer } from '@formily/react';
+import { NotifyModel } from '../../index';
+
+type ChangeType = {
+  source?: string;
+  value?: string;
+  relation?: any;
+};
+
+interface UserProps {
+  value?: ChangeType;
+  onChange?: (value: ChangeType) => void;
+  type?: string;
+}
+
+export default observer((props: UserProps) => {
+  const [source, setSource] = useState<string | undefined>(props.value?.source || '');
+  const [value, setValue] = useState<string | undefined>(undefined);
+  const [relationList, setRelationList] = useState<any[]>([]);
+  const [treeData, setTreeData] = useState<any[]>([
+    { name: '平台用户', id: 'p1', selectable: false, children: [] },
+  ]);
+
+  const location = useLocation();
+
+  useEffect(() => {
+    setSource(props.value?.source);
+    if (props.value?.source === 'relation') {
+      const relation = props.value?.relation;
+      if (relation) {
+        if (relation.objectId) {
+          // 平台用户
+          setValue(relation.objectId);
+        } else {
+          // 关系用户
+          setValue(relation.related?.relation);
+        }
+      }
+    } else {
+      setValue(props.value?.value);
+    }
+  }, [props.value]);
+
+  const getPlatformUser = async () => {
+    const newTree = [...treeData];
+    const platformResp = await queryPlatformUsers();
+
+    if (platformResp.status === 200) {
+      newTree[0].children = platformResp.result;
+    }
+
+    setTreeData(newTree);
+  };
+
+  const getRelationUsers = async (notifyType: string, configId: string) => {
+    if (notifyType === 'dingTalk') {
+      const resp = await queryDingTalkUsers(configId);
+      if (resp.status === 200) {
+        setRelationList(resp.result);
+      }
+    } else {
+      const resp = await queryWechatUsers(configId);
+      if (resp.status === 200) {
+        setRelationList(resp.result);
+      }
+    }
+  };
+
+  useEffect(() => {
+    if (
+      source === 'fixed' &&
+      ['dingTalk', 'weixin'].includes(NotifyModel.notify?.notifyType || '')
+    ) {
+      // 钉钉,微信用户
+      getRelationUsers(NotifyModel.notify?.notifyType || '', NotifyModel.notify?.notifierId || '');
+    } else {
+      getPlatformUser();
+    }
+  }, [source, NotifyModel.notify.notifyType]);
+
+  const options = [
+    { label: '平台用户', value: 'relation' },
+    {
+      label: NotifyModel.notify.notifyType === 'dingTalk' ? '钉钉用户' : '微信用户',
+      value: 'fixed',
+    },
+  ];
+
+  const otherOptions = [
+    { label: '平台用户', value: 'relation' },
+    {
+      label:
+        NotifyModel.notify.notifyType && NotifyModel.notify.notifyType === 'email'
+          ? '固定邮箱'
+          : '固定号码',
+      value: 'fixed',
+    },
+  ];
+
+  const onchange = (_source: string = 'fixed', _value?: string, isRelation?: boolean) => {
+    const obj: any = {
+      source: _source,
+    };
+    if (_source === 'relation') {
+      if (isRelation) {
+        obj.relation = {
+          objectType: 'device',
+          objectSource: {
+            source: 'upper',
+            upperKey: 'deviceId',
+          },
+          related: {
+            objectType: 'user',
+            relation: _value,
+          },
+        };
+      } else {
+        obj.relation = {
+          objectType: 'user',
+          objectId: _value,
+        };
+      }
+    } else {
+      obj.value = _value;
+    }
+
+    if (props.onChange) {
+      props.onChange(obj);
+    }
+  };
+
+  useEffect(() => {
+    if (props.type && source === 'relation') {
+      const newTree = [...treeData];
+      if (props.type === 'device') {
+        queryRelationUsers().then((relationResp) => {
+          if (relationResp.status === 200) {
+            newTree.push({
+              name: '关系用户',
+              id: 'p2',
+              selectable: false,
+              children: relationResp.result,
+            });
+            setTreeData(newTree);
+          }
+        });
+      } else {
+        if (newTree.length > 1) {
+          newTree.splice(1, 1);
+          setTreeData(newTree);
+        }
+      }
+
+      if (!location.query?.id) {
+        onchange(props.value?.source, '');
+      }
+    }
+  }, [props.type, source]);
+
+  const filterOption = (input: string, option: any) => {
+    return option.name ? option.name.toLowerCase().includes(input.toLowerCase()) : false;
+  };
+
+  const createTreeNode = (data: any): React.ReactNode => {
+    return data.map((item: any) => {
+      if (item.children) {
+        return (
+          <TreeSelect.TreeNode value={item.id} title={item.name} selectable={false}>
+            {createTreeNode(item.children)}
+          </TreeSelect.TreeNode>
+        );
+      } else {
+        return (
+          <TreeSelect.TreeNode
+            name={item.name}
+            value={item.id}
+            title={
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>{item.name}</span>
+                <span style={{ color: '#cfcfcf' }}>{item.username}</span>
+              </div>
+            }
+          />
+        );
+      }
+    });
+  };
+
+  const userSelect =
+    source === 'relation' ? (
+      <TreeSelect
+        showSearch
+        allowClear
+        value={value}
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
+          setValue(key);
+          onchange(source, key, node.isRelation);
+        }}
+        filterTreeNode={filterOption}
+      >
+        {createTreeNode(treeData)}
+      </TreeSelect>
+    ) : (
+      <Select
+        showSearch
+        allowClear
+        value={value}
+        options={relationList}
+        listHeight={200}
+        onChange={(key) => {
+          setValue(key);
+          onchange(source, key);
+        }}
+        fieldNames={{ label: 'name', value: 'id' }}
+        placeholder={'请选择收信人'}
+        filterOption={(input: string, option: any) => {
+          return option.name ? option.name.toLowerCase().includes(input.toLowerCase()) : false;
+        }}
+      />
+    );
+
+  const emailSelect =
+    source === 'relation' ? (
+      <TreeSelect
+        showSearch
+        allowClear
+        value={value}
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
+          setValue(key);
+          onchange(source, key, node.isRelation);
+        }}
+        filterTreeNode={filterOption}
+      >
+        {createTreeNode(treeData)}
+      </TreeSelect>
+    ) : (
+      <Input
+        value={value}
+        placeholder={'请输入固定邮箱'}
+        onChange={(e) => {
+          onchange(source, e.target.value);
+        }}
+      />
+    );
+
+  const voiceSelect =
+    source === 'relation' ? (
+      <TreeSelect
+        showSearch
+        allowClear
+        value={value}
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
+          setValue(key);
+          onchange(source, key, node.isRelation);
+        }}
+        filterTreeNode={filterOption}
+      >
+        {createTreeNode(treeData)}
+      </TreeSelect>
+    ) : (
+      <Input
+        value={value}
+        placeholder={'请输入固定号码'}
+        onChange={(e) => {
+          onchange(source, e.target.value);
+        }}
+      />
+    );
+
+  return (
+    <ItemGroup compact>
+      <Select
+        value={source}
+        options={
+          NotifyModel.notify.notifyType &&
+          ['dingTalk', 'weixin'].includes(NotifyModel.notify.notifyType)
+            ? options
+            : otherOptions
+        }
+        style={{ width: 120 }}
+        onChange={(key) => {
+          setSource(key);
+          onchange(key, undefined);
+        }}
+      />
+      {NotifyModel.notify.notifyType &&
+      ['dingTalk', 'weixin'].includes(NotifyModel.notify.notifyType)
+        ? userSelect
+        : null}
+      {NotifyModel.notify.notifyType && ['email'].includes(NotifyModel.notify.notifyType)
+        ? emailSelect
+        : null}
+      {NotifyModel.notify.notifyType && ['sms', 'voice'].includes(NotifyModel.notify.notifyType)
+        ? voiceSelect
+        : null}
+    </ItemGroup>
+  );
+});

+ 129 - 34
src/pages/rule-engine/Scene/Save/action/notify/index.tsx

@@ -1,76 +1,171 @@
 import { Modal, Button, Steps } from 'antd';
-import React from 'react';
+import { useEffect, useRef } from 'react';
 import { observer } from '@formily/react';
 import { model } from '@formily/reactive';
 import NotifyWay from './NotifyWay';
 import NotifyConfig from './NotifyConfig';
 import NotifyTemplate from './NotifyTemplate';
+import VariableDefinitions from './VariableDefinitions';
 import './index.less';
-const initSteps = [
-  {
-    key: 'way',
-    title: '通知方式',
-    content: <NotifyWay />,
-  },
-  {
-    key: 'config',
-    title: '通知配置',
-    content: <NotifyConfig />,
-  },
-  {
-    key: 'template',
-    title: '通知模板',
-    content: <NotifyTemplate />,
-  },
-];
+import { onlyMessage } from '@/utils/util';
+import { queryMessageTemplateDetail } from '@/pages/rule-engine/Scene/Save/action/service';
+import { NotifyProps } from '@/pages/rule-engine/Scene/typings';
+
+interface Props {
+  value: Partial<NotifyProps>;
+  save: (notify: Partial<NotifyProps>) => void;
+  cancel: () => void;
+  name: number;
+}
 
-const NotifyModel = model<{
-  steps: { key: string; title: string; content: React.ReactNode }[];
+export const NotifyModel = model<{
+  steps: { key: string; title: string }[];
   current: number;
+  notify: Partial<NotifyProps>;
+  variable: any[];
 }>({
-  steps: initSteps,
+  steps: [
+    {
+      key: 'way',
+      title: '通知方式',
+    },
+    {
+      key: 'config',
+      title: '通知配置',
+    },
+    {
+      key: 'template',
+      title: '通知模板',
+    },
+    {
+      key: 'variable',
+      title: '模板变量',
+    },
+  ],
   current: 0,
+  notify: {
+    notifyType: '',
+    notifierId: '',
+    templateId: '',
+  },
+  variable: [],
 });
 
-export default observer(() => {
-  const next = () => {
-    NotifyModel.current += 1;
+export default observer((props: Props) => {
+  const WayRef = useRef<{ save: any }>();
+  const VariableRef = useRef<{ save: any }>();
+
+  useEffect(() => {
+    NotifyModel.notify = props.value;
+  }, [props.value]);
+
+  const renderComponent = (type: string) => {
+    switch (type) {
+      case 'way':
+        return <NotifyWay ref={WayRef} value={NotifyModel.notify?.notifyType} />;
+      case 'config':
+        return <NotifyConfig />;
+      case 'template':
+        return <NotifyTemplate />;
+      case 'variable':
+        return <VariableDefinitions name={props.name} ref={VariableRef} />;
+      default:
+        return null;
+    }
   };
 
   const prev = () => {
     NotifyModel.current -= 1;
   };
 
+  const next = async () => {
+    if (NotifyModel.current === 0) {
+      const val = await WayRef.current?.save();
+      if (val) {
+        NotifyModel.notify.notifyType = val;
+        NotifyModel.current += 1;
+      }
+    } else if (NotifyModel.current === 1) {
+      if (NotifyModel.notify?.notifierId) {
+        NotifyModel.current += 1;
+      } else {
+        onlyMessage('请选择通知配置', 'error');
+      }
+    } else if (NotifyModel.current === 2) {
+      if (NotifyModel.notify?.templateId) {
+        const resp = await queryMessageTemplateDetail(NotifyModel.notify.templateId);
+        if (resp.status === 200) {
+          NotifyModel.variable = resp.result?.variableDefinitions || [];
+          NotifyModel.current += 1;
+        }
+      } else {
+        onlyMessage('请选择通知模板', 'error');
+      }
+    } else if (NotifyModel.current === 3) {
+      const resp = await VariableRef.current?.save();
+      if (resp) {
+        NotifyModel.notify.variables = resp;
+        props.save(NotifyModel.notify);
+        NotifyModel.current = 0;
+      }
+    }
+  };
+
   return (
     <Modal
       title={'执行动作'}
       open
-      width={1000}
+      width={800}
+      onCancel={() => {
+        props.cancel();
+        NotifyModel.current = 0;
+      }}
       footer={
         <div className="steps-action">
-          {NotifyModel.current === 0 && <Button onClick={() => {}}>取消</Button>}
+          {NotifyModel.current === 0 && (
+            <Button
+              onClick={() => {
+                props.cancel();
+                NotifyModel.current = 0;
+              }}
+            >
+              取消
+            </Button>
+          )}
+          {NotifyModel.current > 0 && (
+            <Button style={{ margin: '0 8px' }} onClick={() => prev()}>
+              上一步
+            </Button>
+          )}
           {NotifyModel.current < NotifyModel.steps.length - 1 && (
-            <Button type="primary" onClick={() => next()}>
+            <Button
+              type="primary"
+              onClick={() => {
+                next();
+              }}
+            >
               下一步
             </Button>
           )}
           {NotifyModel.current === NotifyModel.steps.length - 1 && (
-            <Button type="primary" onClick={() => {}}>
+            <Button
+              type="primary"
+              onClick={() => {
+                next();
+              }}
+            >
               确定
             </Button>
           )}
-          {NotifyModel.current > 0 && (
-            <Button style={{ margin: '0 8px' }} onClick={() => prev()}>
-              上一步
-            </Button>
-          )}
         </div>
       }
     >
       <div className="steps-steps">
         <Steps current={NotifyModel.current} items={NotifyModel.steps} />
       </div>
-      <div className="steps-content">{NotifyModel.steps[NotifyModel.current]?.content}</div>
+      <div className="steps-content">
+        {renderComponent(NotifyModel.steps[NotifyModel.current]?.key)}
+      </div>
     </Modal>
   );
 });

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

@@ -11,6 +11,12 @@ export const queryMessageConfig = (data: any) =>
     data,
   });
 
+export const queryMessageConfigPaging = (data: any) =>
+  request(`${SystemConst.API_BASE}/notifier/config/_query`, {
+    method: 'POST',
+    data,
+  });
+
 // 通知模板
 export const queryMessageTemplate = (data: any) =>
   request(`${SystemConst.API_BASE}/notifier/template/_query/no-paging?paging=false`, {
@@ -18,6 +24,12 @@ export const queryMessageTemplate = (data: any) =>
     data,
   });
 
+export const queryMessageTemplatePaging = (id: string, data: any) =>
+  request(`${SystemConst.API_BASE}/notifier/template/${id}/_query`, {
+    method: 'POST',
+    data,
+  });
+
 export const queryMessageTemplateDetail = (id: string) =>
   request(`${SystemConst.API_BASE}/notifier/template/${id}/detail`);
 

+ 1 - 0
src/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType.tsx

@@ -74,6 +74,7 @@ export default (props: ActionsTypeProps) => {
           className={classNames('trigger-way-item', {
             active: type === item.value,
           })}
+          style={{ width: 237 }}
           onClick={() => {
             onSelect(item.value);
           }}

+ 1 - 2
src/pages/rule-engine/Scene/Save/components/TriggerWay/index.less

@@ -9,8 +9,7 @@
   .trigger-way-item {
     display: flex;
     justify-content: space-between;
-    width: 237px;
-    //width: 100%;
+    // width: 100%;
     padding: 16px;
     border: 1px solid #e0e4e8;
     border-radius: 2px;

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

@@ -42,6 +42,7 @@ export default (props: TriggerWayProps) => {
         className={classNames('trigger-way-item', {
           active: type === TriggerWayType.device,
         })}
+        style={{ width: 204 }}
         onClick={() => {
           onSelect(TriggerWayType.device);
         }}
@@ -58,6 +59,7 @@ export default (props: TriggerWayProps) => {
         className={classNames('trigger-way-item', {
           active: type === TriggerWayType.manual,
         })}
+        style={{ width: 204 }}
         onClick={() => {
           onSelect(TriggerWayType.manual);
         }}
@@ -74,6 +76,7 @@ export default (props: TriggerWayProps) => {
         className={classNames('trigger-way-item', {
           active: type === TriggerWayType.timing,
         })}
+        style={{ width: 204 }}
         onClick={() => {
           onSelect(TriggerWayType.timing);
         }}

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

@@ -7,6 +7,8 @@ import Timer from '../Save/timer/index';
 import { TitleComponent } from '@/components';
 import { observable } from '@formily/reactive';
 import type { FormModelType } from '@/pages/rule-engine/Scene/typings';
+import { useEffect } from 'react';
+import { service } from '@/pages/rule-engine/Scene';
 
 export const FormModel = observable<FormModelType>({
   actions: [],
@@ -25,6 +27,17 @@ export const FormModel = observable<FormModelType>({
 export default () => {
   const location = useLocation();
   const triggerType = location?.query?.triggerType || '';
+  const id = location?.query?.id || '';
+
+  useEffect(() => {
+    if (id) {
+      service.detail(id).then((resp) => {
+        if (resp.status === 200) {
+          Object.assign(FormModel, resp.result);
+        }
+      });
+    }
+  }, [id]);
 
   const triggerRender = (type: string) => {
     switch (type) {

+ 6 - 2
src/pages/rule-engine/Scene/Save/save.tsx

@@ -25,7 +25,7 @@ export default (props: Props) => {
     <Modal
       title={props.data?.id ? '编辑' : '新增'}
       maskClosable={false}
-      visible
+      open
       onCancel={() => {
         props.close();
       }}
@@ -33,7 +33,11 @@ export default (props: Props) => {
         const values = await form.validateFields();
         props.close();
         const url = getMenuPathByCode('rule-engine/Scene/Save');
-        history.push(`${url}?triggerType=${values.trigger?.type}`);
+        if (props.data?.id) {
+          history.push(`${url}?triggerType=${values.trigger?.type}&id=${props.data?.id}`);
+        } else {
+          history.push(`${url}?triggerType=${values.trigger?.type}`);
+        }
       }}
       width={700}
     >

+ 7 - 7
src/pages/rule-engine/Scene/index.tsx

@@ -204,7 +204,9 @@ const Scene = () => {
           }}
           onClick={() => {
             const url = getMenuPathByCode('rule-engine/Scene/Save');
-            history.push(`${url}?id=${record.id}`, { view: true });
+            history.push(`${url}?id=${record.id}&triggerType=${record.triggerType}`, {
+              view: true,
+            });
           }}
         >
           <EyeOutlined />
@@ -342,8 +344,6 @@ const Scene = () => {
             type="primary"
             isPermission={permission.add}
             onClick={() => {
-              // const url = getMenuPathByCode('rule-engine/Scene/Save');
-              // history.push(url);
               setCurrent({});
               setVisible(true);
             }}
@@ -367,10 +367,10 @@ const Scene = () => {
                   title: '查看',
                 }}
                 onClick={() => {
-                  // const url = getMenuPathByCode('rule-engine/Scene/Save');
-                  // history.push(`${url}?id=${record.id}`, { view: true });
-                  // setCurrent({})
-                  // setVisible(true)
+                  const url = getMenuPathByCode('rule-engine/Scene/Save');
+                  history.push(`${url}?id=${record.id}&triggerType=${record.triggerType}`, {
+                    view: true,
+                  });
                 }}
               >
                 <EyeOutlined />