瀏覽代碼

fix: 卡片

100011797 3 年之前
父節點
當前提交
f4593f313b
共有 41 個文件被更改,包括 940 次插入297 次删除
  1. 二進制
      public/images/scene/action-bind-icon.png
  2. 二進制
      public/images/scene/action-delay-icon.png
  3. 二進制
      public/images/scene/action-device-icon.png
  4. 二進制
      public/images/scene/action-notify-icon.png
  5. 0 0
      public/images/scene/action-unbind-icon.png
  6. 二進制
      public/images/scene/notify-item-img/dingtalk.png
  7. 二進制
      public/images/scene/notify-item-img/email.png
  8. 二進制
      public/images/scene/notify-item-img/sms.png
  9. 二進制
      public/images/scene/notify-item-img/voice.png
  10. 二進制
      public/images/scene/notify-item-img/webhook.png
  11. 二進制
      public/images/scene/notify-item-img/weixin.png
  12. 二進制
      public/images/scene/scene-device.png
  13. 二進制
      public/images/scene/scene-hand.png
  14. 二進制
      public/images/scene/scene-timer.png
  15. 二進制
      public/images/scene/trigger-type-icon/device.png
  16. 二進制
      public/images/scene/trigger-type-icon/manual.png
  17. 二進制
      public/images/scene/trigger-type-icon/timing.png
  18. 二進制
      public/images/scene/trigger-type/device.png
  19. 0 0
      public/images/scene/trigger-type/scene.png
  20. 109 0
      src/components/ProTableCard/CardItems/Scene/index.less
  21. 496 0
      src/components/ProTableCard/CardItems/Scene/index.tsx
  22. 0 104
      src/components/ProTableCard/CardItems/scene.tsx
  23. 17 5
      src/pages/rule-engine/Alarm/Configuration/Save/Scene/Save/index.tsx
  24. 20 12
      src/pages/rule-engine/Alarm/Configuration/Save/Scene/index.tsx
  25. 11 0
      src/pages/rule-engine/Alarm/Configuration/service.ts
  26. 144 5
      src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx
  27. 19 2
      src/pages/rule-engine/Scene/Save/action/ListItem/index.less
  28. 4 4
      src/pages/rule-engine/Scene/Save/action/index.tsx
  29. 5 1
      src/pages/rule-engine/Scene/Save/action/notify/NotifyConfig.tsx
  30. 5 1
      src/pages/rule-engine/Scene/Save/action/notify/NotifyTemplate.tsx
  31. 2 2
      src/pages/rule-engine/Scene/Save/action/notify/VariableDefinitions.tsx
  32. 5 1
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/org.tsx
  33. 5 1
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/tag.tsx
  34. 19 10
      src/pages/rule-engine/Scene/Save/action/notify/components/variableItem/user.tsx
  35. 4 1
      src/pages/rule-engine/Scene/Save/action/notify/index.tsx
  36. 1 1
      src/pages/rule-engine/Scene/Save/device/addModel.tsx
  37. 1 0
      src/pages/rule-engine/Scene/Save/index.tsx
  38. 26 12
      src/pages/rule-engine/Scene/Save/save.tsx
  39. 0 2
      src/pages/rule-engine/Scene/Save/timer/index.tsx
  40. 34 131
      src/pages/rule-engine/Scene/index.tsx
  41. 13 2
      src/pages/rule-engine/Scene/typings.d.ts

二進制
public/images/scene/action-bind-icon.png


二進制
public/images/scene/action-delay-icon.png


二進制
public/images/scene/action-device-icon.png


二進制
public/images/scene/action-notify-icon.png


public/images/scene/action-alarm-icon.png → public/images/scene/action-unbind-icon.png


二進制
public/images/scene/notify-item-img/dingtalk.png


二進制
public/images/scene/notify-item-img/email.png


二進制
public/images/scene/notify-item-img/sms.png


二進制
public/images/scene/notify-item-img/voice.png


二進制
public/images/scene/notify-item-img/webhook.png


二進制
public/images/scene/notify-item-img/weixin.png


二進制
public/images/scene/scene-device.png


二進制
public/images/scene/scene-hand.png


二進制
public/images/scene/scene-timer.png


二進制
public/images/scene/trigger-type-icon/device.png


二進制
public/images/scene/trigger-type-icon/manual.png


二進制
public/images/scene/trigger-type-icon/timing.png


二進制
public/images/scene/trigger-type/device.png


public/images/scene.png → public/images/scene/trigger-type/scene.png


+ 109 - 0
src/components/ProTableCard/CardItems/Scene/index.less

@@ -0,0 +1,109 @@
+.content-class {
+  padding-top: 40px !important;
+  &::before {
+    background-image: none !important;
+  }
+  .card-item-trigger-type {
+    position: absolute;
+    top: 0;
+    left: -14px;
+    height: 32px;
+    padding: 0 30px;
+    color: rgba(0, 0, 0, 0.65);
+    line-height: 32px;
+    background-color: rgba(0, 0, 0, 0.06);
+    transform: skewX(-45deg);
+    .card-item-trigger-type-text {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transform: skewX(45deg);
+    }
+  }
+  .card-item-content-box {
+    .card-item-content-trigger {
+      color: rgba(0, 0, 0, 0.85);
+      font-weight: bold;
+      font-size: 15px;
+      .trigger-device {
+        color: rgba(47, 84, 235);
+      }
+    }
+    .card-item-content-action-item {
+      display: flex;
+      margin-top: 5px;
+      .card-item-content-action-item-left {
+        width: 40px;
+        margin-right: 20px;
+        color: #6968be;
+        font-weight: bold;
+        font-size: 18px;
+      }
+      .card-item-content-action-item-right {
+        width: calc(100% - 58px);
+        padding: 5px;
+        border: 1px solid rgba(0, 0, 0, 0.08);
+        .card-item-content-action-item-right-item {
+          display: flex;
+          width: 100%;
+          .trigger-contents {
+            color: rgba(0, 0, 0, 0.85);
+            //width: calc(85% - 110px);
+          }
+          .right-item-left {
+            width: 15%;
+            .trigger-conditions {
+              color: #fab247;
+            }
+            .trigger-shake {
+              color: rgba(0, 0, 0, 0.55);
+            }
+          }
+          .right-item-right {
+            width: 85%;
+            padding-left: 20px;
+            .right-item-right-item {
+              display: flex;
+              width: 100%;
+              .trigger-ways {
+                width: 75px;
+                margin-right: 20px;
+                color: rgba(0, 0, 0, 0.85);
+                font-size: 16px;
+              }
+              .right-item-right-item-contents {
+                display: flex;
+                width: calc(100% - 95px);
+                overflow: hidden;
+                .right-item-right-item-contents-item {
+                  display: flex;
+                  color: rgba(0, 0, 0, 0.65);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .trigger-actions-more {
+      margin-top: 10px;
+      color: rgba(0, 0, 0, 0.45);
+      cursor: pointer;
+    }
+
+    .notify-text-highlight {
+      margin-left: 5px;
+      font-weight: bold;
+    }
+    .notify-img-highlight {
+      margin: 0 5px;
+      color: rgba(0, 0, 0, 0.8);
+      font-weight: bold;
+    }
+  }
+  .card-item-content-box-empty {
+    color: #e50012;
+    font-weight: bold;
+  }
+}

+ 496 - 0
src/components/ProTableCard/CardItems/Scene/index.tsx

@@ -0,0 +1,496 @@
+import React, { useState } from 'react';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import { Ellipsis, TableCard } from '@/components';
+import '@/style/common.less';
+import '../../index.less';
+import styles from './index.less';
+import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
+import { CheckOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
+import classNames from 'classnames';
+import { ActionsType, BranchesThen } from '@/pages/rule-engine/Scene/typings';
+
+const imageMap = new Map();
+imageMap.set('timer', require('/public/images/scene/scene-timer.png'));
+imageMap.set('manual', require('/public/images/scene/scene-hand.png'));
+imageMap.set('device', require('/public/images/scene/scene-device.png'));
+
+const iconMap = new Map();
+iconMap.set('timer', require('/public/images/scene/trigger-type-icon/timing.png'));
+iconMap.set('manual', require('/public/images/scene/trigger-type-icon/manual.png'));
+iconMap.set('device', require('/public/images/scene/trigger-type-icon/device.png'));
+
+// @ts-ignore
+export interface SceneCardProps extends SceneItem {
+  detail?: React.ReactNode;
+  tools?: React.ReactNode[];
+  avatarSize?: number;
+  className?: string;
+  onUnBind?: (e: any) => void;
+  showBindBtn?: boolean;
+  cardType?: 'bind' | 'unbind';
+  showTool?: boolean;
+  onClick?: () => void;
+}
+
+enum TriggerWayType {
+  manual = '手动触发',
+  timer = '定时触发',
+  device = '设备触发',
+}
+
+enum UnitEnum {
+  seconds = '秒',
+  minutes = '分',
+  hours = '小时',
+}
+
+const selectorRender = (obj: any) => {
+  switch (obj?.selector) {
+    case 'all':
+      return (
+        <span>
+          所有的<span className={styles['trigger-device']}>{obj?.productId}</span>
+        </span>
+      );
+    case 'fixed':
+      return (
+        <span>
+          设备
+          <span className={styles['trigger-device']}>
+            {(obj?.selectorValues || '').map((item: any) => item?.name).join(',')}
+          </span>
+        </span>
+      );
+    case 'org':
+      return (
+        <span>
+          部门
+          <span className={styles['trigger-device']}>
+            {(obj?.selectorValues || '').map((item: any) => item?.name).join(',')}
+          </span>
+        </span>
+      );
+    default:
+      return '';
+  }
+};
+
+const timerRender = (timer: any) => {
+  if (timer?.trigger && timer?.mod) {
+    const trigger = timer?.trigger;
+    const mod = timer?.mod;
+    const str: string = trigger === 'week' ? '星期' : trigger === 'month' ? '月' : timer?.cron;
+    if (mod === 'once') {
+      return `,每${str}${timer.when.join('/')},${timer?.once.time}执行一次`;
+    } else {
+      return `每${str}${timer.when.join('/')},${timer?.period?.from}-${timer?.period.to},每${
+        timer?.period.every
+      }${UnitEnum[timer?.period?.unit]}执行一次`;
+    }
+  }
+  return '';
+};
+
+const operatorRender = (operation: any) => {
+  switch (operation?.operator) {
+    case 'online':
+      return '上线';
+    case 'offline':
+      return '离线';
+    case 'reportEvent':
+      return `上报事件${operation?.options?.eventName}`;
+    case 'reportProperty':
+      return `上报属性${(operation?.options?.propertiesName || []).join(',')}`;
+    case 'readProperty':
+      return `读取属性${(operation?.options?.propertiesName || []).join(',')}`;
+    case 'writeProperty':
+      return `修改属性${(operation?.options?.propertiesName || []).join(',')}`;
+    case 'invokeFunction':
+      return `调用功能${operation?.options?.functionName}`;
+    default:
+      return '';
+  }
+};
+
+const notifyRender = (data: ActionsType | undefined) => {
+  switch (data?.notify?.notifyType) {
+    case 'dingTalk':
+      return (
+        <div>
+          向<span>{data?.options?.notifierName || data?.notify?.notifierId}</span>
+          通过<span className={styles['notify-img-highlight']}>钉钉</span>发送
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.templateName || data?.notify?.templateId}
+          </span>
+        </div>
+      );
+    case 'weixin':
+      return (
+        <div>
+          向<span className={styles['notify-text-highlight']}>{data?.options?.sendTo || ''}</span>
+          <span className={styles['notify-text-highlight']}>{data?.options?.orgName || ''}</span>
+          <span className={styles['notify-text-highlight']}>{data?.options?.tagName || ''}</span>
+          通过<span className={styles['notify-img-highlight']}>微信</span>发送
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.templateName || data?.notify?.templateId}
+          </span>
+        </div>
+      );
+    case 'email':
+      return (
+        <div>
+          向<span className={styles['notify-text-highlight']}>{data?.options?.sendTo || ''}</span>
+          通过<span className={styles['notify-img-highlight']}>邮件</span>发送
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.templateName || data?.notify?.templateId}
+          </span>
+        </div>
+      );
+    case 'voice':
+      return (
+        <div>
+          向
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.calledNumber || ''}
+          </span>
+          通过<span className={styles['notify-img-highlight']}>语音</span>发送
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.templateName || data?.notify?.templateId}
+          </span>
+        </div>
+      );
+    case 'sms':
+      return (
+        <div>
+          向<span className={'notify-text-highlight'}>{data?.options?.sendTo || ''}</span>
+          通过<span className={'notify-img-highlight'}>短信</span>发送
+          <span className={'notify-text-highlight'}>
+            {data?.options?.templateName || data?.notify?.templateId}
+          </span>
+        </div>
+      );
+    case 'webhook':
+      return (
+        <div>
+          通过<span className={'notify-img-highlight'}>webhook</span>发送
+          <span>{data?.options?.templateName || data?.notify?.templateId}</span>
+        </div>
+      );
+    default:
+      return null;
+  }
+};
+
+const deviceRender = (data: ActionsType | undefined) => {
+  console.log(data);
+  switch (data?.device?.selector) {
+    case 'relation':
+      return (
+        <div>
+          {data?.options?.type || ''}
+          与【触发设备】具有相同【关系名称】的【产品名称】设备的【属性/功能】
+        </div>
+      );
+    case 'tag': //`【读取/设置/执行】【标签名称】为【标签值】【并且/或者】【标签名称】为【标签值】的【产品名称】【属性/功能】`
+      return (
+        <div>
+          {data?.options?.type || ''}
+          <span className={styles['notify-text-highlight']}>{data?.options?.deviceName || ''}</span>
+          为
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.productName || ''}
+          </span>
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.propertyName || data?.options?.functionName || ''}
+          </span>
+        </div>
+      );
+    default:
+      return (
+        <div>
+          {data?.options?.type || ''}
+          <span className={styles['notify-text-highlight']}>{data?.options?.deviceName || ''}</span>
+          <span className={styles['notify-text-highlight']}>
+            {data?.options?.propertyName || data?.options?.functionName || ''}
+          </span>
+        </div>
+      );
+  }
+};
+
+const actionRender = (action: ActionsType, index: number) => {
+  switch (action?.executor) {
+    case 'notify':
+      return (
+        <div
+          className={styles['card-item-content-action-item-right-item']}
+          key={action?.key || index}
+        >
+          <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
+            {notifyRender(action)}
+          </div>
+        </div>
+      );
+    case 'delay':
+      return (
+        <div
+          className={styles['card-item-content-action-item-right-item']}
+          key={action?.key || index}
+        >
+          <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
+            <span style={{ fontWeight: 'bold' }}>
+              {action?.delay?.time}
+              {UnitEnum[action?.delay?.unit || '']}
+            </span>
+            后执行后续动作
+          </div>
+        </div>
+      );
+    case 'device':
+      return (
+        <div
+          className={styles['card-item-content-action-item-right-item']}
+          key={action?.key || index}
+        >
+          <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
+            {deviceRender(action)}
+          </div>
+        </div>
+      );
+    case 'alarm':
+      return (
+        <div
+          className={styles['card-item-content-action-item-right-item']}
+          key={action?.key || index}
+        >
+          <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
+            满足条件后将触发关联此场景的告警
+          </div>
+        </div>
+      );
+    default:
+      return null;
+  }
+};
+
+const conditionsRender = (when: any[], index: number) => {
+  if (when.length) {
+    return when[index];
+  }
+  return '';
+};
+
+const branchesActionRender = (actions: any[]) => {
+  if (actions && actions?.length) {
+    const list: any[] = [];
+    actions.map((item, index) => {
+      list.push(actionRender(item, index));
+    });
+    return list.map((item, index) => (
+      <div className={styles['right-item-right-item-contents-item']}>
+        <div style={{ minWidth: 40 }}>动作{index + 1}</div>
+        {item}
+      </div>
+    ));
+  }
+  return '';
+};
+const ContentRender = (data: SceneCardProps) => {
+  const [visible, setVisible] = useState<boolean>(false);
+  const type = data.triggerType;
+  if (
+    type === 'device' &&
+    (data.branches || [])?.length &&
+    Object.keys(data?.trigger?.device || {}).length
+  ) {
+    const obj = data.trigger.device;
+    const operation: any = data.trigger.device?.operation;
+    return (
+      <div className={styles['card-item-content-box']}>
+        <div className={styles['card-item-content-trigger']}>
+          {selectorRender(obj)}
+          {operation ? (
+            <span>
+              {timerRender(operation?.timer)}
+              {operatorRender(operation)}
+            </span>
+          ) : (
+            ''
+          )}
+        </div>
+        <div className={styles['card-item-content-action']}>
+          {(visible ? data.branches || [] : (data?.branches || []).slice(0, 2)).map(
+            (item: any, index) => {
+              return (
+                <div className={styles['card-item-content-action-item']} key={item?.key || index}>
+                  <div className={styles['card-item-content-action-item-left']}>
+                    {index === 0 ? '当' : '否则'}
+                  </div>
+                  <div className={styles['card-item-content-action-item-right']}>
+                    <div className={styles['card-item-content-action-item-right-item']}>
+                      <div className={styles['right-item-left']}>
+                        <div className={classNames(styles['trigger-conditions'], 'ellipsis')}>
+                          {conditionsRender(data.options?.terms || [], index)}
+                        </div>
+                        {item.shakeLimit?.enabled && (
+                          <div className={classNames(styles['trigger-shake'], 'ellipsis')}>
+                            ({item.shakeLimit?.time}秒内发生{item.shakeLimit?.threshold}
+                            次以上时执行一次)
+                          </div>
+                        )}
+                      </div>
+                      <div className={styles['right-item-right']}>
+                        {(item?.then || []).map((i: BranchesThen, _index: number) => (
+                          <div key={i?.key || _index} className={styles['right-item-right-item']}>
+                            <div className={styles['trigger-ways']}>
+                              {i.parallel ? '并行执行' : '串行执行'}
+                            </div>
+                            <div className={classNames(styles['right-item-right-item-contents'])}>
+                              {branchesActionRender(Array.isArray(i?.actions) ? i?.actions : [])}
+                            </div>
+                          </div>
+                        ))}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              );
+            },
+          )}
+          {(data?.branches || []).length > 1 && (
+            <div
+              className={styles['trigger-actions-more']}
+              onClick={(e) => {
+                e.stopPropagation();
+                setVisible(!visible);
+              }}
+            >
+              展开更多{!visible ? <DownOutlined /> : <UpOutlined />}
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  } else if (type !== 'device' && data.actions?.length) {
+    return (
+      <div className={styles['card-item-content-box']}>
+        <div className={styles['card-item-content-action']}>
+          {data.actions?.length && (
+            <div className={styles['card-item-content-action-item']}>
+              <div className={styles['card-item-content-action-item-left']}>执行</div>
+              <div className={styles['card-item-content-action-item-right']}>
+                {(data?.actions || []).map((item: any, index) => {
+                  return actionRender(item, index);
+                })}
+              </div>
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  } else {
+    return <div className={styles['card-item-content-box-empty']}>未配置规则</div>;
+  }
+};
+
+export const ExtraSceneCard = (props: SceneCardProps) => {
+  return (
+    <TableCard
+      status={props.state.value}
+      statusText={props.state.text}
+      statusNames={{
+        started: StatusColorEnum.success,
+        disable: StatusColorEnum.error,
+        notActive: StatusColorEnum.warning,
+      }}
+      showTool={props.showTool}
+      showMask={false}
+      actions={props.tools}
+      onClick={props.onClick}
+      className={props.className}
+      contentClassName={styles['content-class']}
+    >
+      <div className={'pro-table-card-item context-access'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={imageMap.get(props.triggerType)} alt={''} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
+              <Ellipsis
+                title={props.name}
+                style={{ fontSize: 18, opacity: 0.85, color: '#000', fontWeight: 'bold' }}
+              />
+            </div>
+            <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
+              <Ellipsis
+                title={props.description}
+                style={{ color: 'rgba(0, 0, 0, 0.65)', margin: '3px 0 0 10px' }}
+              />
+            </div>
+          </div>
+          <ContentRender {...props} />
+        </div>
+      </div>
+      <div className={styles['card-item-trigger-type']}>
+        <div className={styles['card-item-trigger-type-text']}>
+          <img height={16} src={iconMap.get(props.triggerType)} style={{ marginRight: 8 }} />
+          {TriggerWayType[props.triggerType]}
+        </div>
+      </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
+    </TableCard>
+  );
+};
+
+export default (props: SceneCardProps) => {
+  return (
+    <TableCard
+      showMask={false}
+      detail={props.detail}
+      showTool={props.showTool}
+      actions={props.tools}
+      status={props.state.value}
+      statusText={props.state.text}
+      statusNames={{
+        started: StatusColorEnum.success,
+        disable: StatusColorEnum.error,
+        notActive: StatusColorEnum.warning,
+      }}
+      contentClassName={styles['content-class']}
+    >
+      <div className={'pro-table-card-item'} onClick={props.onClick}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={imageMap.get(props.triggerType)} alt={''} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
+              <Ellipsis
+                title={props.name}
+                style={{ fontSize: 18, opacity: 0.85, color: '#000', fontWeight: 'bold' }}
+              />
+            </div>
+            <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
+              <Ellipsis
+                title={props.description}
+                style={{ color: 'rgba(0, 0, 0, 0.65)', margin: '3px 0 0 10px' }}
+              />
+            </div>
+          </div>
+          <ContentRender {...props} />
+        </div>
+      </div>
+      <div className={styles['card-item-trigger-type']}>
+        <div className={styles['card-item-trigger-type-text']}>
+          <img height={16} src={iconMap.get(props.triggerType)} style={{ marginRight: 8 }} />
+          {TriggerWayType[props.triggerType]}
+        </div>
+      </div>
+    </TableCard>
+  );
+};

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

@@ -1,104 +0,0 @@
-import React from 'react';
-import { StatusColorEnum } from '@/components/BadgeStatus';
-import { Ellipsis, TableCard } from '@/components';
-import '@/style/common.less';
-import '../index.less';
-import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
-import { CheckOutlined } from '@ant-design/icons';
-
-const defaultImage = require('/public/images/scene.png');
-
-// @ts-ignore
-export interface SceneCardProps extends SceneItem {
-  detail?: React.ReactNode;
-  actions?: React.ReactNode[];
-  avatarSize?: number;
-  className?: string;
-  onUnBind?: (e: any) => void;
-  showBindBtn?: boolean;
-  cardType?: 'bind' | 'unbind';
-  showTool?: boolean;
-  onClick?: () => void;
-}
-
-enum TriggerWayType {
-  manual = '手动触发',
-  timer = '定时触发',
-  device = '设备触发',
-}
-
-export const ExtraSceneCard = (props: SceneCardProps) => {
-  return (
-    <TableCard
-      status={props.state.value}
-      statusText={props.state.text}
-      statusNames={{
-        started: StatusColorEnum.success,
-        disable: StatusColorEnum.error,
-        notActive: StatusColorEnum.warning,
-      }}
-      showTool={props.showTool}
-      showMask={false}
-      actions={props.actions}
-      onClick={props.onClick}
-      className={props.className}
-    >
-      <div className={'pro-table-card-item context-access'}>
-        <div className={'card-item-avatar'}>
-          <img width={88} height={88} src={defaultImage} alt={''} />
-        </div>
-        <div className={'card-item-body'}>
-          <div className={'card-item-header'}>
-            <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
-          </div>
-          <div>
-            <div>
-              <label>触发方式</label>
-              <Ellipsis title={TriggerWayType[props.triggerType]} />
-            </div>
-          </div>
-        </div>
-      </div>
-      <div className={'checked-icon'}>
-        <div>
-          <CheckOutlined />
-        </div>
-      </div>
-    </TableCard>
-  );
-};
-
-export default (props: SceneCardProps) => {
-  return (
-    <TableCard
-      showMask={false}
-      detail={props.detail}
-      showTool={props.showTool}
-      actions={props.actions}
-      status={props.state.value}
-      statusText={props.state.text}
-      statusNames={{
-        started: StatusColorEnum.success,
-        disable: StatusColorEnum.error,
-        notActive: StatusColorEnum.warning,
-      }}
-    >
-      <div className={'pro-table-card-item'}>
-        <div className={'card-item-avatar'}>
-          <img width={88} height={88} src={defaultImage} alt={''} />
-        </div>
-        <div className={'card-item-body'}>
-          <div className={'card-item-header'}>
-            <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
-          </div>
-          <div>
-            <div>
-              <label>触发方式</label>
-              <Ellipsis title={TriggerWayType[props.triggerType]} />
-            </div>
-          </div>
-        </div>
-      </div>
-    </TableCard>
-  );
-};

+ 17 - 5
src/pages/rule-engine/Alarm/Configuration/Save/Scene/Save/index.tsx

@@ -1,16 +1,17 @@
 import { Modal, ProTableCard } from '@/components';
 import SearchComponent from '@/components/SearchComponent';
 import { SceneItem } from '@/pages/rule-engine/Scene/typings';
-import { ExtraSceneCard } from '@/components/ProTableCard/CardItems/scene';
+import { ExtraSceneCard } from '@/components/ProTableCard/CardItems/Scene';
 import { service as sceneService } from '@/pages/rule-engine/Scene';
 import { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { useRef, useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { onlyMessage } from '@/utils/util';
-
+import { service } from '@/pages/rule-engine/Alarm/Configuration';
 interface Props {
   close: () => void;
   id: string;
+  ok: () => void;
 }
 
 export default (props: Props) => {
@@ -81,7 +82,17 @@ export default (props: Props) => {
       }}
       onOk={async () => {
         if (selectKeys.length > 0) {
-          props.close();
+          const list = selectKeys.map((item) => {
+            return {
+              alarmId: id,
+              ruleId: item,
+            };
+          });
+          const resp = await service.bindScene([...list]);
+          if (resp.status === 200) {
+            onlyMessage('操作成功');
+            props.ok();
+          }
         } else {
           onlyMessage('请选择至少一条数据', 'error');
         }
@@ -110,7 +121,7 @@ export default (props: Props) => {
           rowKey="id"
           search={false}
           onlyCard={true}
-          gridColumn={2}
+          gridColumn={1}
           columnEmptyText={''}
           // @ts-ignore
           request={(params) => {
@@ -133,7 +144,8 @@ export default (props: Props) => {
                 {
                   terms: [
                     {
-                      column: 'sceneId',
+                      column: 'id',
+                      termType: 'alarm-bind-rule$not',
                       value: id,
                     },
                   ],

+ 20 - 12
src/pages/rule-engine/Alarm/Configuration/Save/Scene/index.tsx

@@ -1,15 +1,15 @@
 import { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import { PermissionButton, ProTableCard } from '@/components';
-import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
-import SceneCard from '@/components/ProTableCard/CardItems/scene';
+import { DisconnectOutlined, PlusOutlined } from '@ant-design/icons';
+import SceneCard from '@/components/ProTableCard/CardItems/Scene';
 import { useRef, useState } from 'react';
 import { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import useLocation from '@/hooks/route/useLocation';
-// import { service } from "@/pages/rule-engine/Alarm/Configuration";
 import { service as sceneService } from '@/pages/rule-engine/Scene';
 import Save from './Save';
-// import SearchComponent from "@/components/SearchComponent";
+import { service } from '@/pages/rule-engine/Alarm/Configuration';
+import { onlyMessage } from '@/utils/util';
 
 export default () => {
   const intl = useIntl();
@@ -118,7 +118,8 @@ export default () => {
               {
                 terms: [
                   {
-                    column: 'sceneId',
+                    column: 'id',
+                    termType: 'alarm-bind-rule',
                     value: id,
                   },
                 ],
@@ -134,6 +135,7 @@ export default () => {
           });
         }}
         rowKey="id"
+        gridColumn={1}
         search={false}
         onlyCard={true}
         headerTitle={[
@@ -157,7 +159,7 @@ export default () => {
             {...record}
             showBindBtn={false}
             cardType={'bind'}
-            actions={[
+            tools={[
               <PermissionButton
                 key={'unbind'}
                 type={'link'}
@@ -165,15 +167,17 @@ export default () => {
                 isPermission={permission.update}
                 popConfirm={{
                   title: '确认解绑?',
-                  onConfirm: () => {
-                    console.log(record.id);
+                  onConfirm: async () => {
+                    const resp = await service.unbindScene(id, [record.id]);
+                    if (resp.status === 200) {
+                      onlyMessage('操作成功!');
+                      actionRef.current?.reload();
+                    }
                   },
                 }}
-                tooltip={{
-                  title: <span>解绑</span>,
-                }}
               >
-                <DeleteOutlined />
+                <DisconnectOutlined />
+                解绑
               </PermissionButton>,
             ]}
           />
@@ -185,6 +189,10 @@ export default () => {
           close={() => {
             setVisible(false);
           }}
+          ok={() => {
+            setVisible(false);
+            actionRef.current?.reload();
+          }}
         />
       )}
     </>

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

@@ -50,6 +50,17 @@ class Service extends BaseService<ConfigItem> {
     request(`/${SystemConst.API_BASE}/alarm/config/default/level`, {
       method: 'GET',
     });
+
+  public bindScene = (data: any) =>
+    request(`/${SystemConst.API_BASE}/alarm/rule/bind`, {
+      method: 'PATCH',
+      data,
+    });
+  public unbindScene = (id: string, data: any) =>
+    request(`/${SystemConst.API_BASE}/alarm/rule/bind/${id}/_delete`, {
+      method: 'POST',
+      data,
+    });
 }
 
 export default Service;

+ 144 - 5
src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx

@@ -5,6 +5,7 @@ import { DeleteOutlined } from '@ant-design/icons';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
 import './index.less';
 import TriggerAlarm from '../TriggerAlarm';
+import { AddButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
 export enum ParallelEnum {
   'parallel' = 'parallel',
   'serial' = 'serial',
@@ -18,12 +19,128 @@ interface ItemProps {
 }
 
 const iconMap = new Map();
-iconMap.set('alarm', require('/public/images/scene/action-alarm-icon.png'));
+iconMap.set('trigger', require('/public/images/scene/action-bind-icon.png'));
+iconMap.set('notify', require('/public/images/scene/action-notify-icon.png'));
+iconMap.set('device', require('/public/images/scene/action-device-icon.png'));
+iconMap.set('relieve', require('/public/images/scene/action-unbind-icon.png'));
+iconMap.set('delay', require('/public/images/scene/action-delay-icon.png'));
+
+export const itemNotifyIconMap = new Map();
+itemNotifyIconMap.set('dingTalk', require('/public/images/scene/notify-item-img/dingtalk.png'));
+itemNotifyIconMap.set('weixin', require('/public/images/scene/notify-item-img/weixin.png'));
+itemNotifyIconMap.set('email', require('/public/images/scene/notify-item-img/email.png'));
+itemNotifyIconMap.set('voice', require('/public/images/scene/notify-item-img/voice.png'));
+itemNotifyIconMap.set('sms', require('/public/images/scene/notify-item-img/sms.png'));
+itemNotifyIconMap.set('webhook', require('/public/images/scene/notify-item-img/webhook.png'));
 
 export default (props: ItemProps) => {
   const [visible, setVisible] = useState<boolean>(false);
   const [triggerVisible, setTriggerVisible] = useState<boolean>(false);
 
+  const notifyRender = (data: ActionsType | undefined) => {
+    switch (data?.notify?.notifyType) {
+      case 'dingTalk':
+        return (
+          <div>
+            向<span>{data?.options?.notifierName || data?.notify?.notifierId}</span>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              钉钉
+            </span>
+            发送
+            <span className={'notify-text-highlight'}>
+              {data?.options?.templateName || data?.notify?.templateId}
+            </span>
+          </div>
+        );
+      case 'weixin':
+        return (
+          <div>
+            向<span className={'notify-text-highlight'}>{data?.options?.sendTo || ''}</span>
+            <span className={'notify-text-highlight'}>{data?.options?.orgName || ''}</span>
+            <span className={'notify-text-highlight'}>{data?.options?.tagName || ''}</span>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              微信
+            </span>
+            发送
+            <span className={'notify-text-highlight'}>
+              {data?.options?.templateName || data?.notify?.templateId}
+            </span>
+          </div>
+        );
+      case 'email':
+        return (
+          <div>
+            向<span className={'notify-text-highlight'}>{data?.options?.sendTo || ''}</span>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              邮件
+            </span>
+            发送
+            <span className={'notify-text-highlight'}>
+              {data?.options?.templateName || data?.notify?.templateId}
+            </span>
+          </div>
+        );
+      case 'voice':
+        return (
+          <div>
+            向<span className={'notify-text-highlight'}>{data?.options?.calledNumber || ''}</span>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              语音
+            </span>
+            发送
+            <span className={'notify-text-highlight'}>
+              {data?.options?.templateName || data?.notify?.templateId}
+            </span>
+          </div>
+        );
+      case 'sms':
+        return (
+          <div>
+            向<span className={'notify-text-highlight'}>{data?.options?.sendTo || ''}</span>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              短信
+            </span>
+            发送
+            <span className={'notify-text-highlight'}>
+              {data?.options?.templateName || data?.notify?.templateId}
+            </span>
+          </div>
+        );
+      case 'webhook':
+        return (
+          <div>
+            通过
+            <span className={'notify-img-highlight'}>
+              <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
+              webhook
+            </span>
+            发送
+            <span>{data?.options?.templateName || data?.notify?.templateId}</span>
+          </div>
+        );
+      default:
+        return (
+          <AddButton
+            onClick={() => {
+              setVisible(true);
+            }}
+          >
+            点击配置执行动作
+          </AddButton>
+        );
+    }
+  };
+
   const contentRender = () => {
     if (props?.data?.alarm?.mode === 'trigger') {
       return (
@@ -51,8 +168,18 @@ export default (props: ItemProps) => {
           </a>
         </div>
       );
+    } else if (props?.data?.executor === 'notify') {
+      return notifyRender(props?.data);
     }
-    return '';
+    return (
+      <AddButton
+        onClick={() => {
+          setVisible(true);
+        }}
+      >
+        点击配置执行动作
+      </AddButton>
+    );
   };
 
   return (
@@ -60,9 +187,21 @@ export default (props: ItemProps) => {
       <div className="actions-item">
         <div className="item-options-warp">
           <div className="item-options-type">
-            <img style={{ width: 48 }} src={iconMap.get(props?.data.executor)} />
+            <img
+              style={{ width: 48 }}
+              src={iconMap.get(
+                props?.data.executor === 'alarm' ? props?.data?.alarm?.mode : props?.data.executor,
+              )}
+            />
+          </div>
+          <div
+            className={'item-options-content'}
+            onClick={() => {
+              setVisible(true);
+            }}
+          >
+            {contentRender()}
           </div>
-          <div className={'item-options-content'}>{contentRender()}</div>
         </div>
         <div className="item-number">{props.name + 1}</div>
         <div
@@ -92,7 +231,7 @@ export default (props: ItemProps) => {
             setVisible(false);
           }}
           save={(data: ActionsType) => {
-            console.log(data);
+            FormModel.actions[props.name] = data;
             setVisible(false);
           }}
         />

+ 19 - 2
src/pages/rule-engine/Scene/Save/action/ListItem/index.less

@@ -28,14 +28,31 @@
       justify-content: center;
       width: 48px;
       margin-right: 8px;
-      background-color: #fafafa;
+      background: rgba(47, 84, 235, 0.08);
     }
 
     .item-options-content {
       display: flex;
       align-items: center;
       padding: 0 8px;
-      background-color: #fafafa;
+      background: rgba(47, 84, 235, 0.08);
+      cursor: pointer;
+      div {
+        padding: 6px 10px;
+        color: #333;
+        font-size: 14px;
+        line-height: 22px;
+        background-color: #fff;
+        border-radius: 22px;
+        .notify-text-highlight {
+          margin-left: 5px;
+          font-weight: bold;
+        }
+        .notify-img-highlight {
+          margin: 0 10px;
+          color: rgba(0, 0, 0, 0.8);
+        }
+      }
     }
   }
 

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

@@ -25,10 +25,10 @@ export default (props: ActionsProps) => {
               const data: ShakeLimitType = get(FormModel, [...props.name!, 'shakeLimit']);
               return (
                 <ShakeLimit
-                  enabled={data.enabled}
-                  time={data.time}
-                  threshold={data.threshold}
-                  alarmFirst={data.alarmFirst}
+                  enabled={data?.enabled}
+                  time={data?.time}
+                  threshold={data?.threshold}
+                  alarmFirst={data?.alarmFirst}
                   onChange={(type, value) => {
                     data[type] = value;
                   }}

+ 5 - 1
src/pages/rule-engine/Scene/Save/action/notify/NotifyConfig.tsx

@@ -64,9 +64,13 @@ export default observer(() => {
           rowSelection={{
             type: 'radio',
             selectedRowKeys: [NotifyModel.notify?.notifierId || ''],
-            onChange: (selectedRowKeys) => {
+            onChange: (selectedRowKeys, list) => {
               if (selectedRowKeys.length) {
                 NotifyModel.notify.notifierId = String(selectedRowKeys[selectedRowKeys.length - 1]);
+                NotifyModel.notify.options = {
+                  ...NotifyModel.notify.options,
+                  notifierName: list[list.length - 1]?.name,
+                };
               }
             },
           }}

+ 5 - 1
src/pages/rule-engine/Scene/Save/action/notify/NotifyTemplate.tsx

@@ -64,9 +64,13 @@ export default observer(() => {
           rowSelection={{
             type: 'radio',
             selectedRowKeys: [NotifyModel.notify?.templateId || ''],
-            onChange: (selectedRowKeys) => {
+            onChange: (selectedRowKeys, list) => {
               if (selectedRowKeys.length) {
                 NotifyModel.notify.templateId = String(selectedRowKeys[selectedRowKeys.length - 1]);
+                NotifyModel.notify.options = {
+                  ...NotifyModel.notify.options,
+                  templateName: list[list.length - 1]?.name,
+                };
               }
             },
           }}

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

@@ -120,9 +120,9 @@ export default forwardRef((props: Props, ref) => {
       const formData = await form.validateFields().catch(() => {
         resolve(false);
       });
-      console.log(formData);
       if (formData) {
-        resolve(formData);
+        console.log(formData);
+        // resolve(formData);
       } else {
         resolve(false);
       }

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

@@ -53,8 +53,12 @@ export default (props: OrgProps) => {
         label: 'name',
         value: 'id',
       }}
-      onChange={(key) => {
+      onChange={(key, label) => {
         if (props.onChange) {
+          NotifyModel.notify.options = {
+            ...NotifyModel.notify.options,
+            orgName: label,
+          };
           props.onChange({
             source: 'fixed',
             value: key,

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

@@ -40,8 +40,12 @@ export default (props: TagSelectProps) => {
         value: 'id',
       }}
       style={{ width: '100%' }}
-      onChange={(key) => {
+      onChange={(key, option: any) => {
         if (props.onChange) {
+          NotifyModel.notify.options = {
+            ...NotifyModel.notify.options,
+            tagName: option ? option?.label : '',
+          };
           props.onChange(key);
         }
       }}

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

@@ -108,7 +108,12 @@ export default observer((props: UserProps) => {
     },
   ];
 
-  const onchange = (_source: string = 'fixed', _value?: string, isRelation?: boolean) => {
+  const onchange = (
+    _source: string = 'fixed',
+    _value?: string,
+    isRelation?: boolean,
+    _name?: string,
+  ) => {
     const obj: any = {
       source: _source,
     };
@@ -136,6 +141,10 @@ export default observer((props: UserProps) => {
     }
 
     if (props.onChange) {
+      NotifyModel.notify.options = {
+        ...NotifyModel.notify.options,
+        sendTo: _name,
+      };
       props.onChange(obj);
     }
   };
@@ -163,7 +172,7 @@ export default observer((props: UserProps) => {
       }
 
       if (!location.query?.id) {
-        onchange(props.value?.source, '');
+        onchange(props.value?.source, '', false, '');
       }
     }
   }, [props.type, source]);
@@ -207,7 +216,7 @@ export default observer((props: UserProps) => {
         placeholder={'请选择收信人'}
         onSelect={(key: any, node: any) => {
           setValue(key);
-          onchange(source, key, node.isRelation);
+          onchange(source, key, node.isRelation, node.name);
         }}
         filterTreeNode={filterOption}
       >
@@ -220,9 +229,9 @@ export default observer((props: UserProps) => {
         value={value}
         options={relationList}
         listHeight={200}
-        onChange={(key) => {
+        onChange={(key, option) => {
           setValue(key);
-          onchange(source, key);
+          onchange(source, key, false, option?.label);
         }}
         fieldNames={{ label: 'name', value: 'id' }}
         placeholder={'请选择收信人'}
@@ -242,7 +251,7 @@ export default observer((props: UserProps) => {
         placeholder={'请选择收信人'}
         onSelect={(key: any, node: any) => {
           setValue(key);
-          onchange(source, key, node.isRelation);
+          onchange(source, key, node.isRelation, node.name);
         }}
         filterTreeNode={filterOption}
       >
@@ -253,7 +262,7 @@ export default observer((props: UserProps) => {
         value={value}
         placeholder={'请输入固定邮箱'}
         onChange={(e) => {
-          onchange(source, e.target.value);
+          onchange(source, e.target.value, false, e.target.value);
         }}
       />
     );
@@ -268,7 +277,7 @@ export default observer((props: UserProps) => {
         placeholder={'请选择收信人'}
         onSelect={(key: any, node: any) => {
           setValue(key);
-          onchange(source, key, node.isRelation);
+          onchange(source, key, node.isRelation, node.name);
         }}
         filterTreeNode={filterOption}
       >
@@ -279,7 +288,7 @@ export default observer((props: UserProps) => {
         value={value}
         placeholder={'请输入固定号码'}
         onChange={(e) => {
-          onchange(source, e.target.value);
+          onchange(source, e.target.value, false, e.target.value);
         }}
       />
     );
@@ -297,7 +306,7 @@ export default observer((props: UserProps) => {
         style={{ width: 120 }}
         onChange={(key) => {
           setSource(key);
-          onchange(key, undefined);
+          onchange(key, undefined, false, '');
         }}
       />
       {NotifyModel.notify.notifyType &&

+ 4 - 1
src/pages/rule-engine/Scene/Save/action/notify/index.tsx

@@ -10,6 +10,7 @@ import './index.less';
 import { onlyMessage } from '@/utils/util';
 import { queryMessageTemplateDetail } from '@/pages/rule-engine/Scene/Save/action/service';
 import { NotifyProps } from '@/pages/rule-engine/Scene/typings';
+import { FormModel } from '@/pages/rule-engine/Scene/Save';
 
 interface Props {
   value: Partial<NotifyProps>;
@@ -105,7 +106,9 @@ export default observer((props: Props) => {
       const resp = await VariableRef.current?.save();
       if (resp) {
         NotifyModel.notify.variables = resp;
-        props.save(NotifyModel.notify);
+        const { options, ...extra } = NotifyModel.notify;
+        FormModel.actions[props.name].options = options;
+        props.save(extra);
         NotifyModel.current = 0;
       }
     }

+ 1 - 1
src/pages/rule-engine/Scene/Save/device/addModel.tsx

@@ -76,7 +76,7 @@ export default observer((props: AddProps) => {
   };
 
   const handleOptions = (data: TriggerDeviceOptions) => {
-    console.log(data);
+    // console.log(data);
 
     const _options: any = {
       name: '', // 名称

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

@@ -65,6 +65,7 @@ export default () => {
       service.detail(id).then((resp) => {
         if (resp.status === 200) {
           Object.assign(FormModel, resp.result);
+          console.log(FormModel, '11111');
         }
       });
     }

+ 26 - 12
src/pages/rule-engine/Scene/Save/save.tsx

@@ -5,6 +5,7 @@ import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import { useEffect } from 'react';
 import { getMenuPathByCode } from '@/utils/menu';
 import useHistory from '@/hooks/route/useHistory';
+import { service } from '../index';
 
 interface Props {
   close: () => void;
@@ -31,12 +32,30 @@ export default (props: Props) => {
       }}
       onOk={async () => {
         const values = await form.validateFields();
-        props.close();
-        const url = getMenuPathByCode('rule-engine/Scene/Save');
-        if (props.data?.id) {
-          history.push(`${url}?triggerType=${values.trigger?.type}&id=${props.data?.id}`);
-        } else {
-          history.push(`${url}?triggerType=${values.trigger?.type}`);
+        // const obj = {...values}
+        // if(values.trigger?.type === 'device') {
+        //   obj.trigger = {
+        //     ...obj.trigger,
+        //     device: obj.trigger?.device || {}
+        //   }
+        // }
+        // if(values.trigger?.type === 'timer') {
+        //   obj.trigger = {
+        //     ...obj.trigger,
+        //     timer: obj.trigger?.timer || {}
+        //   }
+        // }
+        const resp = props.data?.id
+          ? await service.modify(props.data?.id, { ...values })
+          : await service.save(values);
+        if (resp.status === 200) {
+          props.close();
+          const url = getMenuPathByCode('rule-engine/Scene/Save');
+          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}
@@ -61,12 +80,7 @@ export default (props: Props) => {
           rules={[{ required: true, message: '请选择触发方式' }]}
           initialValue={'device'}
         >
-          <TriggerWay
-            // onSelect={(val) => {
-            //   // console.log(val);
-            // }}
-            disabled={!!props.data?.id}
-          />
+          <TriggerWay disabled={!!props.data?.id} />
         </Form.Item>
       </Form>
     </Modal>

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

@@ -30,8 +30,6 @@ export default observer(() => {
       <div style={{ marginBottom: 16 }}>
         <Observer>
           {() => {
-            console.log(FormModel.options);
-
             const label = handleLabel(FormModel.options);
             return (
               <AddButton

+ 34 - 131
src/pages/rule-engine/Scene/index.tsx

@@ -5,31 +5,23 @@ import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import {
   DeleteOutlined,
   EditOutlined,
-  EyeOutlined,
   LikeOutlined,
   PlayCircleOutlined,
   PlusOutlined,
   StopOutlined,
 } from '@ant-design/icons';
-import { BadgeStatus, PermissionButton, ProTableCard } from '@/components';
+import { PermissionButton, ProTableCard } from '@/components';
 import SearchComponent from '@/components/SearchComponent';
-import SceneCard from '@/components/ProTableCard/CardItems/scene';
+import SceneCard from '@/components/ProTableCard/CardItems/Scene';
 import Service from './service';
 import { useIntl } from 'umi';
-import { getMenuPathByCode } from '@/utils/menu';
-import { StatusColorEnum } from '@/components/BadgeStatus';
 import { onlyMessage } from '@/utils/util';
 import useHistory from '@/hooks/route/useHistory';
 import Save from './Save/save';
+import { getMenuPathByCode } from '@/utils/menu';
 
 export const service = new Service('scene');
 
-export enum TriggerWayType {
-  manual = '手动触发',
-  timer = '定时触发',
-  device = '设备触发',
-}
-
 const Scene = () => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
@@ -51,8 +43,24 @@ const Scene = () => {
     }
   };
 
-  const Tools = (record: any, type: 'card' | 'table'): React.ReactNode[] => {
-    const item = [
+  const Tools = (record: SceneItem): React.ReactNode[] => {
+    return [
+      <PermissionButton
+        key={'update'}
+        type={'link'}
+        style={{ padding: 0 }}
+        isPermission={permission.update}
+        onClick={() => {
+          setVisible(true);
+          setCurrent(record);
+        }}
+      >
+        <EditOutlined />
+        {intl.formatMessage({
+          id: 'pages.data.option.edit',
+          defaultMessage: '编辑',
+        })}
+      </PermissionButton>,
       record.triggerType === 'manual' && (
         <PermissionButton
           key="trigger"
@@ -79,37 +87,10 @@ const Scene = () => {
           }}
         >
           <LikeOutlined />
-          {type === 'table' ? '' : '手动触发'}
+          {'手动触发'}
         </PermissionButton>
       ),
       <PermissionButton
-        key={'update'}
-        type={'link'}
-        style={{ padding: 0 }}
-        isPermission={permission.update}
-        tooltip={
-          type === 'table'
-            ? {
-                title: intl.formatMessage({
-                  id: 'pages.data.option.edit',
-                  defaultMessage: '编辑',
-                }),
-              }
-            : undefined
-        }
-        onClick={() => {
-          setVisible(true);
-          setCurrent(record);
-        }}
-      >
-        <EditOutlined />
-        {type !== 'table' &&
-          intl.formatMessage({
-            id: 'pages.data.option.edit',
-            defaultMessage: '编辑',
-          })}
-      </PermissionButton>,
-      <PermissionButton
         key={'started'}
         type={'link'}
         style={{ padding: 0 }}
@@ -147,25 +128,12 @@ const Scene = () => {
             }
           },
         }}
-        tooltip={
-          type === 'table'
-            ? {
-                title: intl.formatMessage({
-                  id: `pages.data.option.${
-                    record.state.value === 'started' ? 'disabled' : 'enabled'
-                  }`,
-                  defaultMessage: '启用',
-                }),
-              }
-            : undefined
-        }
       >
         {record.state.value === 'started' ? <StopOutlined /> : <PlayCircleOutlined />}
-        {type !== 'table' &&
-          intl.formatMessage({
-            id: `pages.data.option.${record.state.value === 'started' ? 'disabled' : 'enabled'}`,
-            defaultMessage: record.state.value === 'started' ? '禁用' : '启用',
-          })}
+        {intl.formatMessage({
+          id: `pages.data.option.${record.state.value === 'started' ? 'disabled' : 'enabled'}`,
+          defaultMessage: record.state.value === 'started' ? '禁用' : '启用',
+        })}
       </PermissionButton>,
       <PermissionButton
         key={'delete'}
@@ -192,30 +160,6 @@ const Scene = () => {
         <DeleteOutlined />
       </PermissionButton>,
     ];
-    if (type === 'table') {
-      return [
-        <PermissionButton
-          key={'update'}
-          type={'link'}
-          style={{ padding: 0 }}
-          isPermission={permission.view}
-          tooltip={{
-            title: '查看',
-          }}
-          onClick={() => {
-            const url = getMenuPathByCode('rule-engine/Scene/Save');
-            history.push(`${url}?id=${record.id}&triggerType=${record.triggerType}`, {
-              view: true,
-            });
-          }}
-        >
-          <EyeOutlined />
-        </PermissionButton>,
-        ...item,
-      ];
-    } else {
-      return item;
-    }
   };
 
   const columns: ProColumns<SceneItem>[] = [
@@ -235,7 +179,6 @@ const Scene = () => {
         id: 'pages.ruleEngine.scene.triggers',
         defaultMessage: '触发方式',
       }),
-      // width: 120,
       valueType: 'select',
       valueEnum: {
         manual: {
@@ -251,7 +194,6 @@ const Scene = () => {
           status: 'device',
         },
       },
-      renderText: (record) => TriggerWayType[record],
     },
     {
       dataIndex: 'description',
@@ -259,7 +201,6 @@ const Scene = () => {
         id: 'pages.system.description',
         defaultMessage: '说明',
       }),
-      hideInSearch: true,
     },
     {
       dataIndex: 'state',
@@ -267,22 +208,7 @@ const Scene = () => {
         id: 'pages.searchTable.titleStatus',
         defaultMessage: '状态',
       }),
-      // width: '90px',
       valueType: 'select',
-      renderText: (record) =>
-        record ? (
-          <BadgeStatus
-            status={record.value}
-            text={record.text}
-            statusNames={{
-              started: StatusColorEnum.processing,
-              disable: StatusColorEnum.error,
-              notActive: StatusColorEnum.warning,
-            }}
-          />
-        ) : (
-          ''
-        ),
       valueEnum: {
         started: {
           text: '正常',
@@ -294,17 +220,6 @@ const Scene = () => {
         },
       },
     },
-    {
-      title: intl.formatMessage({
-        id: 'pages.data.option',
-        defaultMessage: '操作',
-      }),
-      valueType: 'option',
-      align: 'left',
-      width: 200,
-      fixed: 'right',
-      render: (text, record) => Tools(record, 'table'),
-    },
   ];
 
   return (
@@ -323,6 +238,8 @@ const Scene = () => {
         scroll={{ x: 1366 }}
         params={searchParams}
         columnEmptyText={''}
+        gridColumn={1}
+        onlyCard={true}
         options={{ fullScreen: true }}
         request={(params) =>
           service.query({
@@ -357,26 +274,12 @@ const Scene = () => {
         cardRender={(record) => (
           <SceneCard
             {...record}
-            detail={
-              <PermissionButton
-                key={'update'}
-                type={'link'}
-                style={{ padding: 0, fontSize: 24, color: '#fff' }}
-                isPermission={permission.view}
-                tooltip={{
-                  title: '查看',
-                }}
-                onClick={() => {
-                  const url = getMenuPathByCode('rule-engine/Scene/Save');
-                  history.push(`${url}?id=${record.id}&triggerType=${record.triggerType}`, {
-                    view: true,
-                  });
-                }}
-              >
-                <EyeOutlined />
-              </PermissionButton>
-            }
-            // tools={Tools(record, 'card')}
+            onClick={() => {
+              console.log(123);
+              const url = getMenuPathByCode('rule-engine/Scene/Save');
+              history.push(`${url}?triggerType=${record.trigger?.type}&id=${record?.id}`);
+            }}
+            tools={Tools(record)}
           />
         )}
       />

+ 13 - 2
src/pages/rule-engine/Scene/typings.d.ts

@@ -6,7 +6,7 @@ type Action = {
 };
 
 type Trigger = {
-  trigger: string;
+  type: string;
   device: Record<string, unknown>;
 };
 
@@ -118,14 +118,24 @@ export interface ShakeLimitType {
   alarmFirst: boolean;
 }
 
+export interface BranchesType {
+  enabled: boolean;
+  groupType?: string; // 执行动作没有该参数
+  time: number;
+  threshold: number;
+  alarmFirst: boolean;
+}
+
 export interface SceneItem {
   parallel: boolean;
   state: State;
   actions: Action[];
-  triggers: Trigger[];
+  trigger: Trigger;
   id: string;
   name: string;
   description: string;
+  branches: BranchesType[];
+  options: any;
   triggerType: string;
 }
 
@@ -196,6 +206,7 @@ export interface NotifyProps {
   notifierId: string;
   templateId: string;
   variables: Record<string, NotifyVariablesType>;
+  options?: Record<string, any>;
 }
 
 export type SelectorValuesType =