Przeglądaj źródła

fix: merge next

wzyyy 3 lat temu
rodzic
commit
5310bc990a

Plik diff jest za duży
+ 1 - 1
public/icons/iconfont.js


+ 6 - 6
src/components/ProTableCard/CardItems/Scene/index.tsx

@@ -53,23 +53,23 @@ const notifyRender = (data: ActionsType | undefined) => {
       if (data?.options?.provider === 'dingTalkRobotWebHook') {
         return `通过群机器人消息发送${data?.options?.templateName || data?.notify?.templateId}`;
       }
-      return `向${data?.options?.notifierName || data?.notify?.notifierId}通过钉钉发送${
+      return `通过钉钉向${data?.options?.notifierName || data?.notify?.notifierId}发送${
         data?.options?.templateName || data?.notify?.templateId
       }`;
     case 'weixin':
-      return `向${data?.options?.sendTo || ''}${data?.options?.orgName || ''}${
+      return `通过微信向${data?.options?.sendTo || ''}${data?.options?.orgName || ''}${
         data?.options?.tagName || ''
-      }通过微信发送${data?.options?.templateName || data?.notify?.templateId}`;
+      }发送${data?.options?.templateName || data?.notify?.templateId}`;
     case 'email':
-      return `向${data?.options?.sendTo || ''}通过邮件发送${
+      return `通过邮件向${data?.options?.sendTo || ''}发送${
         data?.options?.templateName || data?.notify?.templateId
       }`;
     case 'voice':
-      return `向${data?.options?.sendTo || ''}通过语音发送 ${
+      return `通过语音向${data?.options?.sendTo || ''}发送 ${
         data?.options?.templateName || data?.notify?.templateId
       }`;
     case 'sms':
-      return `向${data?.options?.sendTo || ''}通过短信发送${
+      return `通过短信向${data?.options?.sendTo || ''}发送${
         data?.options?.templateName || data?.notify?.templateId
       }`;
     case 'webhook':

+ 19 - 13
src/components/TitleComponent/index.less

@@ -1,21 +1,27 @@
 @import '~antd/es/style/themes/default.less';
 
 .title {
-  position: relative;
+  display: flex;
+  align-items: center;
   width: 100%;
   margin-bottom: 10px;
-  padding-left: 10px;
-  color: rgba(0, 0, 0, 0.8);
-  font-weight: 600;
-  line-height: 1;
 
-  .title-before {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 4px;
-    height: calc(100% - 2px);
-    background-color: @primary-color;
-    border-radius: 0 3px 3px 0;
+  .title-content {
+    position: relative;
+    padding-left: 10px;
+    color: rgba(0, 0, 0, 0.8);
+    font-weight: 600;
+    line-height: 1;
+
+    &::before {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 4px;
+      height: 100%;
+      background-color: @primary-color;
+      border-radius: 0 3px 3px 0;
+      content: ' ';
+    }
   }
 }

+ 7 - 2
src/components/TitleComponent/index.tsx

@@ -4,12 +4,17 @@ import './index.less';
 interface TitleComponentProps {
   data: ReactNode | string;
   style?: CSSProperties;
+  after?: ReactNode | string;
 }
 const TitleComponent = (props: TitleComponentProps) => {
   return (
     <div className="title" style={props.style}>
-      <div className={'title-before'}></div>
-      <span>{props.data}</span>
+      <div className={'title-content'}>
+        {/*<div className={'title-before'}></div>*/}
+        {/*<span></span>*/}
+        {props.data}
+      </div>
+      {props.after}
     </div>
   );
 };

+ 13 - 13
src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.less

@@ -108,16 +108,16 @@
   }
 }
 
-.trigger-options-content {
-  .trigger-options-name {
-    .ellipsisFn(1, 30%);
-  }
-
-  .trigger-options-type {
-    .ellipsisFn(1, 10%);
-  }
-
-  .trigger-options-when {
-    .ellipsisFn(1, 15%);
-  }
-}
+//.trigger-options-content {
+//  .trigger-options-name {
+//    .ellipsisFn(1, 30%);
+//  }
+//
+//  .trigger-options-type {
+//    .ellipsisFn(1, 10%);
+//  }
+//
+//  .trigger-options-when {
+//    .ellipsisFn(1, 15%);
+//  }
+//}

+ 9 - 7
src/pages/rule-engine/Scene/Save/action/ListItem/FilterCondition.tsx

@@ -6,6 +6,7 @@ import classNames from 'classnames';
 import { observer } from '@formily/react';
 import { Popconfirm, Space } from 'antd';
 import './index.less';
+import { AIcon } from '@/components';
 
 interface FilterProps {
   branchesName: number;
@@ -79,7 +80,6 @@ export default observer((props: FilterProps) => {
     (columnValue?: string) => {
       if (columnValue) {
         const labelOptions = columnOptionsMap.get(columnValue);
-        console.log('filter-convertLabelValue', labelOptions);
         if (labelOptions) {
           const _termTypeOptions: any[] =
             labelOptions?.termTypes?.map((tItem: any) => ({ title: tItem.name, key: tItem.id })) ||
@@ -106,13 +106,12 @@ export default observer((props: FilterProps) => {
           return {
             ...item,
             key: item.id,
-            fullName: item.name,
             title: name,
             disabled: true,
             children: handleTreeData(item.children),
           };
         }
-        return { ...item, key: item.id, fullName: item.name, title: name };
+        return { ...item, key: item.id, title: name };
       });
     }
     return [];
@@ -205,6 +204,7 @@ export default observer((props: FilterProps) => {
               termType: _termTypeValue,
             });
           }}
+          icon={<AIcon type={'icon-zhihangdongzuoxie-1'} />}
         />
         <DropdownButton
           options={ttOptions}
@@ -267,6 +267,7 @@ export default observer((props: FilterProps) => {
                 props.onLabelChange?.(labelCache.current);
                 valueEventChange(_myValue);
               }}
+              icon={<AIcon type={'icon-canshu'} style={{ fontSize: 16 }} />}
             />
             <ParamsDropdown
               options={valueOptions}
@@ -300,6 +301,7 @@ export default observer((props: FilterProps) => {
                 props.onLabelChange?.(labelCache.current);
                 valueEventChange(_myValue);
               }}
+              icon={<AIcon type={'icon-canshu'} style={{ fontSize: 16 }} />}
             />
           </>
         ) : (
@@ -331,6 +333,7 @@ export default observer((props: FilterProps) => {
               props.onLabelChange?.(labelCache.current);
               valueEventChange(v);
             }}
+            icon={<AIcon type={'icon-canshu'} style={{ fontSize: 16 }} />}
           />
         )}
       </div>
@@ -352,10 +355,9 @@ export default observer((props: FilterProps) => {
           />
         </div>
       ) : (
-        <div className="terms-filter-add" onClick={props.onAdd}>
-          <div className="terms-filter-content">
-            <PlusOutlined style={{ fontSize: 12, paddingRight: 4 }} />
-            <span>条件</span>
+        <div className="terms-add" onClick={props.onAdd}>
+          <div className="terms-content">
+            <PlusOutlined style={{ fontSize: 12 }} />
           </div>
         </div>
       )}

+ 6 - 2
src/pages/rule-engine/Scene/Save/action/ListItem/FilterGroup.tsx

@@ -174,7 +174,11 @@ export default observer((props: TermsProps) => {
             }}
           </Observer>
           <Popconfirm title={'确认删除?'} onConfirm={props.onDelete}>
-            <div className={classNames('terms-params-delete', { show: deleteVisible })}>
+            <div
+              className={classNames('terms-params-delete filter-terms-params-delete', {
+                show: deleteVisible,
+              })}
+            >
               <CloseOutlined />
             </div>
           </Popconfirm>
@@ -195,7 +199,7 @@ export default observer((props: TermsProps) => {
             />
           </div>
         ) : (
-          <div className="terms-add" onClick={props.onAddGroup}>
+          <div className="terms-group-add" onClick={props.onAddGroup}>
             <div className="terms-content">
               <PlusOutlined style={{ fontSize: 12, paddingRight: 4 }} />
               <span>分组</span>

+ 143 - 124
src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx

@@ -12,6 +12,8 @@ import { TermsType } from '@/pages/rule-engine/Scene/typings';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
 import { queryBuiltInParams } from '@/pages/rule-engine/Scene/Save/action/service';
 import { randomString } from '@/utils/util';
+import classNames from 'classnames';
+import { AIcon } from '@/components';
 
 export enum ParallelEnum {
   'parallel' = 'parallel',
@@ -85,13 +87,12 @@ export default (props: ItemProps) => {
           return {
             ...item,
             key: item.id,
-            fullName: item.name,
             title: name,
             disabled: true,
             children: handleTreeData(item.children),
           };
         }
-        return { ...item, key: item.id, fullName: item.name, title: name };
+        return { ...item, key: item.id, title: name };
       });
     }
     return [];
@@ -129,13 +130,13 @@ export default (props: ItemProps) => {
         }
         return (
           <div>
-            向<span className={'notify-text-highlight'}>{options?.orgName || ''}</span>
-            <span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             通过
             <span className={'notify-img-highlight'}>
               <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
               钉钉
             </span>
+            向<span className={'notify-text-highlight'}>{options?.orgName || ''}</span>
+            <span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             发送
             <span className={'notify-text-highlight'}>
               {options?.templateName || data?.notify?.templateId}
@@ -145,14 +146,14 @@ export default (props: ItemProps) => {
       case 'weixin':
         return (
           <div>
-            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
-            <span className={'notify-text-highlight'}>{options?.orgName || ''}</span>
-            <span className={'notify-text-highlight'}>{options?.tagName || ''}</span>
             通过
             <span className={'notify-img-highlight'}>
               <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
               微信
             </span>
+            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
+            <span className={'notify-text-highlight'}>{options?.orgName || ''}</span>
+            <span className={'notify-text-highlight'}>{options?.tagName || ''}</span>
             发送
             <span className={'notify-text-highlight'}>
               {options?.templateName || data?.notify?.templateId}
@@ -162,12 +163,12 @@ export default (props: ItemProps) => {
       case 'email':
         return (
           <div>
-            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             通过
             <span className={'notify-img-highlight'}>
               <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
               邮件
             </span>
+            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             发送
             <span className={'notify-text-highlight'}>
               {options?.templateName || data?.notify?.templateId}
@@ -177,12 +178,12 @@ export default (props: ItemProps) => {
       case 'voice':
         return (
           <div>
-            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             通过
             <span className={'notify-img-highlight'}>
               <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
               语音
             </span>
+            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             发送
             <span className={'notify-text-highlight'}>
               {options?.templateName || data?.notify?.templateId}
@@ -192,12 +193,12 @@ export default (props: ItemProps) => {
       case 'sms':
         return (
           <div>
-            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             通过
             <span className={'notify-img-highlight'}>
               <img width={18} src={itemNotifyIconMap.get(data?.notify?.notifyType)} />
               短信
             </span>
+            向<span className={'notify-text-highlight'}>{options?.sendTo || ''}</span>
             发送
             <span className={'notify-text-highlight'}>
               {options?.templateName || data?.notify?.templateId}
@@ -222,11 +223,19 @@ export default (props: ItemProps) => {
   };
 
   const deviceRender = (data: ActionsType | undefined) => {
+    const typeIconMap = {
+      READ_PROPERTY: 'icon-zhihangdongzuodu',
+      INVOKE_FUNCTION: 'icon-zhihangdongzuoxie-1',
+      WRITE_PROPERTY: 'icon-zhihangdongzuoxie',
+    };
     switch (data?.device?.selector) {
       case 'fixed':
         return (
           <div>
-            {`${data?.options?.type} ${data?.options?.name} ${data?.options?.properties} ${
+            <AIcon type={typeIconMap[data!.device!.message!.messageType]} />
+            <span style={{ paddingRight: 4 }}>{data?.options?.type}</span>
+            <AIcon type={'icon-mubiao'} style={{ paddingRight: 2 }} />
+            {`${data?.options?.name} ${data?.options?.properties} ${
               data?.options?.propertiesValue ? `为 ${data?.options?.propertiesValue}` : ''
             }`}
           </div>
@@ -234,6 +243,7 @@ export default (props: ItemProps) => {
       case 'tag':
         return (
           <div>
+            <AIcon type={typeIconMap[data!.device!.message!.messageType]} />
             {data?.options?.type}
             {data.options?.taglist.map((item: any) => (
               <span>
@@ -249,6 +259,7 @@ export default (props: ItemProps) => {
       case 'relation':
         return (
           <div>
+            <AIcon type={typeIconMap[data!.device!.message!.messageType]} />
             {data?.options?.type}与<span>{data?.options?.name}</span>具有相同
             {data?.options?.relationName}的{data?.options?.productName}设备的
             {data?.options?.properties}
@@ -369,96 +380,120 @@ export default (props: ItemProps) => {
           </div>
         </Popconfirm>
       </div>
-      <div className={'actions-item-filter-warp'}>
-        {props.parallel ? null : thenTerms && thenTerms.length ? (
-          thenTerms.map((termsItem, index) => (
-            <FilterGroup
-              action={props.name}
-              key={termsItem.key}
-              branchGroup={props.branchGroup}
-              branchesName={props.branchesName}
-              name={index}
-              data={termsItem}
-              columns={optionsColumns}
-              isLast={index === thenTerms.length - 1}
-              paramsOptions={paramsOptions}
-              label={props.options?.terms?.[index]}
-              actionColumns={props.options?.otherColumns}
-              onColumnsChange={(columns) => {
-                const filterColumns = new Set(flattenDeep(columns)); // 平铺去重
-                let newColumns = [...filterColumns.values()];
-                if (optionsRef.current?.otherColumns) {
-                  newColumns = [...optionsRef.current.otherColumns, ...newColumns];
-                }
-                optionsRef.current['columns'] = newColumns;
-                optionsRef.current['termsColumns'] = columns;
-                props.onUpdate(cacheValueRef.current, optionsRef.current);
-              }}
-              onAddGroup={() => {
-                const newThenTerms = [...thenTerms];
-                newThenTerms.push({
-                  type: 'and',
-                  key: randomString(),
-                  terms: [
-                    {
-                      column: undefined,
-                      value: undefined,
-                      termType: undefined,
-                      type: 'and',
-                      key: randomString(),
-                    },
-                  ],
-                });
-                const _data = cacheValueRef.current;
-                set(_data, 'terms', newThenTerms);
-                props.onUpdate(_data, optionsRef.current);
-              }}
-              onValueChange={(termsData) => {
-                const _data = cacheValueRef.current;
-                set(_data, ['terms', index], termsData);
-                // cacheValueRef.current = _data;
-                props.onUpdate(_data, {
-                  ...optionsRef.current,
-                });
-              }}
-              onLabelChange={(lb) => {
-                const newLabel: any[] = props.options?.terms || [];
-                newLabel.splice(index, 1, lb);
-                optionsRef.current['terms'] = newLabel;
-                props.onUpdate(cacheValueRef.current, optionsRef.current);
-              }}
-              onDelete={() => {
-                const _data = thenTerms.filter((a) => a.key !== termsItem.key);
-                if (optionsRef.current?.termsColumns) {
-                  optionsRef.current.termsColumns[index] = [];
-                  const filterColumns = new Set(flattenDeep(optionsRef.current.termsColumns)); // 平铺去重
+      <div
+        className={classNames('actions-item-filter-warp', { 'filter-border': !!thenTerms?.length })}
+      >
+        {!!thenTerms?.length && (
+          <div className={'actions-item-filter-warp-tip'}>满足此条件后才会执行后续条件</div>
+        )}
+        <div className={classNames('actions-item-filter-overflow')}>
+          {props.parallel ? null : thenTerms && thenTerms.length ? (
+            thenTerms.map((termsItem, index) => (
+              <FilterGroup
+                action={props.name}
+                key={termsItem.key}
+                branchGroup={props.branchGroup}
+                branchesName={props.branchesName}
+                name={index}
+                data={termsItem}
+                columns={optionsColumns}
+                isLast={index === thenTerms.length - 1}
+                paramsOptions={paramsOptions}
+                label={props.options?.terms?.[index]}
+                actionColumns={props.options?.otherColumns}
+                onColumnsChange={(columns) => {
+                  const filterColumns = new Set(flattenDeep(columns)); // 平铺去重
                   let newColumns = [...filterColumns.values()];
                   if (optionsRef.current?.otherColumns) {
                     newColumns = [...optionsRef.current.otherColumns, ...newColumns];
                   }
                   optionsRef.current['columns'] = newColumns;
-                }
-                props.onUpdate(
-                  {
-                    ...cacheValueRef.current,
-                    terms: _data,
-                  },
-                  optionsRef.current,
-                );
-              }}
-            />
-          ))
-        ) : (
-          <div
-            className="filter-add-button"
-            onClick={() => {
-              getParams();
-              let _data = cacheValueRef.current;
-              optionsRef.current['terms'] = [];
-              if (!_data.terms) {
-                _data = {
-                  ..._data,
-                  terms: [
+                  optionsRef.current['termsColumns'] = columns;
+                  props.onUpdate(cacheValueRef.current, optionsRef.current);
+                }}
+                onAddGroup={() => {
+                  const newThenTerms = [...thenTerms];
+                  newThenTerms.push({
+                    type: 'and',
+                    key: randomString(),
+                    terms: [
+                      {
+                        column: undefined,
+                        value: undefined,
+                        termType: undefined,
+                        type: 'and',
+                        key: randomString(),
+                      },
+                    ],
+                  });
+                  const _data = cacheValueRef.current;
+                  set(_data, 'terms', newThenTerms);
+                  props.onUpdate(_data, optionsRef.current);
+                }}
+                onValueChange={(termsData) => {
+                  const _data = cacheValueRef.current;
+                  set(_data, ['terms', index], termsData);
+                  // cacheValueRef.current = _data;
+                  props.onUpdate(_data, {
+                    ...optionsRef.current,
+                  });
+                }}
+                onLabelChange={(lb) => {
+                  const newLabel: any[] = props.options?.terms || [];
+                  newLabel.splice(index, 1, lb);
+                  optionsRef.current['terms'] = newLabel;
+                  props.onUpdate(cacheValueRef.current, optionsRef.current);
+                }}
+                onDelete={() => {
+                  const _data = thenTerms.filter((a) => a.key !== termsItem.key);
+                  if (optionsRef.current?.termsColumns) {
+                    optionsRef.current.termsColumns[index] = [];
+                    const filterColumns = new Set(flattenDeep(optionsRef.current.termsColumns)); // 平铺去重
+                    let newColumns = [...filterColumns.values()];
+                    if (optionsRef.current?.otherColumns) {
+                      newColumns = [...optionsRef.current.otherColumns, ...newColumns];
+                    }
+                    optionsRef.current['columns'] = newColumns;
+                  }
+                  props.onUpdate(
+                    {
+                      ...cacheValueRef.current,
+                      terms: _data,
+                    },
+                    optionsRef.current,
+                  );
+                }}
+              />
+            ))
+          ) : (
+            <div
+              className="filter-add-button"
+              onClick={() => {
+                getParams();
+                let _data = cacheValueRef.current;
+                optionsRef.current['terms'] = [];
+                if (!_data.terms) {
+                  _data = {
+                    ..._data,
+                    terms: [
+                      {
+                        type: 'and',
+                        key: randomString(),
+                        terms: [
+                          {
+                            column: undefined,
+                            value: undefined,
+                            termType: undefined,
+                            type: 'and',
+                            key: randomString(),
+                          },
+                        ],
+                      },
+                    ],
+                  };
+                  props.onUpdate(_data, optionsRef.current);
+                } else {
+                  _data.terms = [
                     {
                       type: 'and',
                       key: randomString(),
@@ -472,32 +507,15 @@ export default (props: ItemProps) => {
                         },
                       ],
                     },
-                  ],
-                };
-                props.onUpdate(_data, optionsRef.current);
-              } else {
-                _data.terms = [
-                  {
-                    type: 'and',
-                    key: randomString(),
-                    terms: [
-                      {
-                        column: undefined,
-                        value: undefined,
-                        termType: undefined,
-                        type: 'and',
-                        key: randomString(),
-                      },
-                    ],
-                  },
-                ];
-                props.onUpdate(_data, optionsRef.current);
-              }
-            }}
-          >
-            <PlusOutlined style={{ paddingRight: 16 }} /> 添加过滤条件
-          </div>
-        )}
+                  ];
+                  props.onUpdate(_data, optionsRef.current);
+                }
+              }}
+            >
+              <PlusOutlined style={{ paddingRight: 4 }} /> 添加过滤条件
+            </div>
+          )}
+        </div>
       </div>
       {visible && (
         <Modal
@@ -509,7 +527,6 @@ export default (props: ItemProps) => {
             setVisible(false);
           }}
           save={(data: ActionsType, options) => {
-            // setOp(options);
             optionsRef.current = options;
             props.onUpdate(data, options);
             setVisible(false);
@@ -537,10 +554,12 @@ export default (props: ItemProps) => {
           setActionType('');
         }}
         save={(data: ActionsType, options) => {
-          // setOp(options);
           optionsRef.current = options;
           props.onUpdate(data, options);
           setActionType('');
+          setTimeout(() => {
+            getParams();
+          }, 10);
         }}
         parallel={props.parallel}
       />

+ 23 - 8
src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx

@@ -1,5 +1,4 @@
 import { useEffect, useState } from 'react';
-import { AddButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
 import Modal from '../Modal/add';
 import './index.less';
 import type { ActionsType } from '@/pages/rule-engine/Scene/typings';
@@ -7,6 +6,9 @@ import Item from './Item';
 import type { ParallelType } from './Item';
 import { Observer } from '@formily/react';
 import { pick } from 'lodash';
+import { Button } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+import classNames from 'classnames';
 
 interface ListProps {
   branchesName: number;
@@ -55,14 +57,27 @@ export default (props: ListProps) => {
           ));
         }}
       </Observer>
+      <div className={classNames('actions-add-list', { border: actions.length })}>
+        <Button
+          icon={<PlusOutlined />}
+          onClick={() => {
+            setVisible(true);
+          }}
+          type="primary"
+          ghost
+          style={{ width: '100%' }}
+        >
+          添加执行动作
+        </Button>
+      </div>
 
-      <AddButton
-        onClick={() => {
-          setVisible(true);
-        }}
-      >
-        点击配置执行动作
-      </AddButton>
+      {/*<AddButton*/}
+      {/*  onClick={() => {*/}
+      {/*    setVisible(true);*/}
+      {/*  }}*/}
+      {/*>*/}
+      {/*  点击配置执行动作*/}
+      {/*</AddButton>*/}
       {visible && (
         <Modal
           // type={props.type}

+ 98 - 21
src/pages/rule-engine/Scene/Save/action/ListItem/index.less

@@ -1,3 +1,26 @@
+.deleteBtn() {
+  position: absolute;
+  top: -10px;
+  right: -10px;
+  display: none;
+  width: 20px;
+  height: 20px;
+  color: #999;
+  line-height: 20px;
+  text-align: center;
+  background-color: #f1f1f1;
+  border-radius: 50%;
+  cursor: pointer;
+
+  &.show {
+    display: block;
+  }
+
+  &:hover {
+    background-color: #f3f3f3;
+  }
+}
+
 .action-list-content {
   padding: 8px;
 
@@ -12,12 +35,18 @@
   }
 
   .filter-add-button {
-    display: inline-block;
-    margin-bottom: 16px;
-    margin-left: 8px;
-    color: #2f54eb;
+    width: 100%;
+    color: rgba(0, 0, 0, 0.3);
+    text-align: center;
     cursor: pointer;
   }
+
+  .actions-add-list {
+    &.border {
+      padding-top: 16px;
+      border-top: 1px solid @primary-color;
+    }
+  }
 }
 
 .actions-item {
@@ -28,17 +57,19 @@
   border-radius: 2px;
 
   .item-options-warp {
-    display: flex;
+    display: inline-flex;
     height: 48px;
+    border: 1px solid #e0e0e0;
+    border-radius: 6px;
 
     .item-options-type {
       display: flex;
       align-items: center;
       justify-content: center;
       width: 48px;
-      height: 48px;
-      margin-right: 8px;
-      background-color: #fafafa;
+      background-color: #f0f0f0;
+      border-radius: 6px 0 0 6px;
+      cursor: pointer;
     }
 
     .item-options-content {
@@ -46,6 +77,7 @@
       align-items: center;
       padding: 0 8px;
       background: #fafafa;
+      border-radius: 0 6px 6px 0;
       cursor: pointer;
 
       div {
@@ -98,6 +130,19 @@
   }
 }
 
+.terms-params-delete {
+  .deleteBtn();
+
+  &.danger {
+    color: #e50012;
+    background-color: rgba(229, 0, 18, 0.1);
+  }
+
+  &.filter-terms-params-delete {
+    transform: translateY(6px);
+  }
+}
+
 .filter-condition-warp {
   display: flex;
   align-items: center;
@@ -105,8 +150,7 @@
   .filter-condition-content {
     position: relative;
     display: flex;
-    padding: 4px;
-    border: 1px solid rgba(#000, 0.1);
+    gap: 2px;
     border-radius: 2px;
     transition: border 0.3s;
 
@@ -136,7 +180,6 @@
 
   .terms-filter-add {
     // display: inline-block;
-    width: 66px;
     margin-left: 16px;
     padding: 4px 8px;
     color: #333;
@@ -145,17 +188,53 @@
     border-radius: 30px;
     cursor: pointer;
   }
+
+  .terms-add {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 24px;
+    height: 24px;
+    margin-left: 16px;
+    color: rgba(0, 0, 0, 0.3);
+    border: 1px dashed rgba(0, 0, 0, 0.3);
+    border-radius: 50%;
+    cursor: pointer;
+  }
 }
 
 .actions-item-filter-warp {
-  display: flex;
+  position: relative;
   margin-bottom: 24px;
-  padding-top: 10px;
-  overflow-x: auto;
-  overflow-y: visible;
+  padding: 2px 0;
   border: 1px dashed #999;
-  border-radius: 2px;
-  row-gap: 16px;
+  border-radius: 30px;
+
+  &.filter-border {
+    padding: 2px 16px;
+    border-radius: 2px;
+  }
+
+  .actions-item-filter-overflow {
+    display: flex;
+    padding-top: 4px;
+    overflow-x: auto;
+    overflow-y: visible;
+    row-gap: 16px;
+  }
+
+  .actions-item-filter-warp-tip {
+    position: absolute;
+    top: 0;
+    left: 16px;
+    z-index: 2;
+    color: rgba(0, 0, 0, 0.55);
+    font-weight: 800;
+    font-size: 14px;
+    line-height: 1;
+    background-color: #fff;
+    transform: translateY(-50%);
+  }
 
   .terms-params {
     // display: inline-block;
@@ -174,10 +253,8 @@
     .terms-params-content {
       position: relative;
       display: flex;
-      // flex-wrap: wrap;
-      padding: 8px;
-      padding-bottom: 0;
-      background-color: #fff;
+      background-color: #fafafa;
+      border: unset;
       row-gap: 16px;
 
       .terms-params-item {

+ 3 - 1
src/pages/rule-engine/Scene/Save/components/Buttons/Dropdown.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
 import { Dropdown, Empty, Tree } from 'antd';
 import classNames from 'classnames';
 import styles from './index.less';
@@ -22,6 +22,7 @@ interface DropdownButtonProps {
   type: 'param' | 'termType' | 'value' | 'type';
   fieldNames?: any;
   showLabelKey?: string;
+  icon?: ReactNode;
 }
 
 const TypeStyle = {
@@ -149,6 +150,7 @@ const DropdownButton = (props: DropdownButtonProps) => {
           }
         }}
       >
+        {props.icon}
         {label ? label : props.placeholder}
       </div>
     </Dropdown>

+ 3 - 1
src/pages/rule-engine/Scene/Save/components/Buttons/ParamsDropdown.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useState } from 'react';
+import { ReactNode, useCallback, useEffect, useState } from 'react';
 import { Input, InputNumber, Menu, Tree } from 'antd';
 import classNames from 'classnames';
 import styles from './index.less';
@@ -21,6 +21,7 @@ export interface ParamsDropdownProps {
   name?: number;
   valueType: string;
   showLabelKey?: string;
+  icon?: ReactNode;
 }
 
 interface MenusProps {
@@ -299,6 +300,7 @@ export default (props: ParamsDropdownProps) => {
           setOpen(true);
         }}
       >
+        {props.icon}
         {label || props.placeholder}
       </div>
     </ParamsSelect>

+ 7 - 3
src/pages/rule-engine/Scene/Save/components/Buttons/index.less

@@ -1,11 +1,13 @@
 .rule-button-warp {
   display: inline-block;
-  padding: 8px;
+  padding: 14px 16px;
   background-color: #fafafa;
+  border: 1px solid #f0f0f0;
+  border-radius: 2px;
   cursor: pointer;
 
   .rule-button {
-    // display: inline-block;
+    display: inline-block;
     padding: 6px 20px;
     font-size: 14px;
     line-height: 22px;
@@ -16,6 +18,7 @@
 
   .add-button {
     color: #bdbdbd;
+
     &:hover,
     &:active {
       border-color: #d0d0d0;
@@ -24,7 +27,8 @@
 }
 
 .dropdown-button {
-  display: inline-block;
+  display: flex;
+  align-items: center;
   padding: 6px 8px;
   border: 1px solid #d9d9d9;
   border-radius: 8px;

+ 12 - 1
src/pages/rule-engine/Scene/Save/components/ShakeLimit/index.less

@@ -1,7 +1,18 @@
 .shakeLimit {
   display: flex;
-  gap: 16px;
+  gap: 4px;
   align-items: center;
   font-weight: 400;
   font-size: 14px;
+  :global(.ant-input-number-handler-wrap) {
+    display: none;
+  }
+
+  :global(.ant-radio-button-wrapper) {
+    padding: 0 16px;
+  }
+
+  input {
+    padding: 0 4px;
+  }
 }

+ 13 - 1
src/pages/rule-engine/Scene/Save/components/ShakeLimit/index.tsx

@@ -61,10 +61,19 @@ export default (props: ShakeLimitProps) => {
         unCheckedChildren="关闭防抖"
         checked={enabled}
         onChange={enabledChange}
+        style={{ marginRight: 12 }}
       />
       {enabled ? (
         <>
-          <InputNumber min={0} max={100} precision={0} value={time} onChange={timeChange} />
+          <InputNumber
+            min={0}
+            max={100}
+            precision={0}
+            value={time}
+            onChange={timeChange}
+            style={{ width: 32 }}
+            size={'small'}
+          />
           <span>秒内发送</span>
           <InputNumber
             min={0}
@@ -72,12 +81,15 @@ export default (props: ShakeLimitProps) => {
             precision={0}
             value={threshold}
             onChange={thresholdChange}
+            style={{ width: 32 }}
+            size={'small'}
           />
           <span>次及以上时,处理</span>
           <Radio.Group
             options={alarmFirstOptions}
             optionType="button"
             value={alarmFirst}
+            size={'small'}
             onChange={(e) => alarmFirstChange(e.target.value)}
           />
         </>

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

@@ -11,7 +11,7 @@ import { numberToString } from '../components/TimingTrigger/whenOption';
 import { timeUnitEnum } from '../components/TimingTrigger';
 import { Store } from 'jetlinks-store';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
-import { isEqual } from 'lodash';
+import { isArray, isEqual } from 'lodash';
 
 interface AddProps {
   options?: any;
@@ -20,6 +20,42 @@ interface AddProps {
   onSave?: (data: TriggerDevice, options: any) => void;
 }
 
+type continuousValueFn = (data: (string | number)[], type: string) => (number | string)[];
+
+const continuousValue: continuousValueFn = (data, type) => {
+  let start = 0;
+  const newArray: (number | string)[] = [];
+  const isWeek = type === 'week';
+  if (isArray(data)) {
+    data.forEach((item, index) => {
+      const _item = Number(item);
+      const nextValue = data[index + 1];
+      const previousValue = data[index - 1];
+      const nextItemValue = _item + 1;
+      const previousItemValue = _item - 1;
+      if (nextItemValue === nextValue && previousItemValue !== previousValue) {
+        start = _item;
+      } else if (previousItemValue === previousValue && nextItemValue !== nextValue) {
+        // 表示前start和item连续,并且item与nextValue不连续
+        if (_item - start >= 2) {
+          // 至少三位连续
+          newArray.push(
+            isWeek
+              ? `${numberToString[start]} - ${numberToString[_item]}`
+              : `${start} - ${_item}号`,
+          );
+        } else {
+          newArray.push(isWeek ? numberToString[start] : `${start}号`);
+          newArray.push(isWeek ? numberToString[_item] : `${_item}号`);
+        }
+      } else if (previousItemValue !== previousValue && nextItemValue !== nextValue) {
+        newArray.push(isWeek ? numberToString[_item] : `${_item}号`);
+      }
+    });
+  }
+  return newArray;
+};
+
 export interface DeviceModelProps extends Partial<TriggerDevice> {
   steps: { key: string; title: string }[];
   stepNumber: number;
@@ -127,21 +163,48 @@ export default observer((props: AddProps) => {
   const handleOptions = (data: TriggerDeviceOptions) => {
     // console.log(data);
 
+    const typeIconMap = {
+      writeProperty: 'icon-bianji1',
+      invokeFunction: 'icon-widgets',
+      reportEvent: 'icon-shijian',
+      readProperty: 'icon-Group',
+    };
+
     const _options: any = {
       name: '', // 名称
+      extraName: '', // 拓展参数
       onlyName: false,
       type: '', // 触发类型
+      typeIcon: typeIconMap[TriggerDeviceModel.options.action],
       productName: '',
+      selectorIcon: '',
       time: undefined,
       when: undefined,
       extraTime: undefined,
       action: TriggerDeviceModel.options.action,
     };
     if (TriggerDeviceModel.selector === 'fixed') {
-      _options.name = TriggerDeviceModel.selectorValues?.map((item) => item.name).join(',');
+      let isLimit = false;
+      let indexOf = 0;
+      const nameStr = TriggerDeviceModel.selectorValues!.reduce((_prev, next, index) => {
+        if (_prev.length <= 30) {
+          indexOf = index;
+          return index === 0 ? next.name : _prev + '、' + next.name;
+        } else {
+          isLimit = true;
+        }
+        return _prev;
+      }, '');
+      // _options.name = TriggerDeviceModel.selectorValues?.map((item) => item.name).join('、');
+      _options.name = nameStr;
+      if (isLimit && TriggerDeviceModel.selectorValues!.length > indexOf) {
+        _options.extraName = `等${TriggerDeviceModel.selectorValues!.length}台设备`;
+      }
+      _options.selectorIcon = 'icon-shebei1';
     } else if (TriggerDeviceModel.selector === 'org') {
       _options.name = TriggerDeviceModel.selectorValues?.[0].name + '的';
       _options.productName = TriggerDeviceModel.productDetail.name; // 产品名称
+      _options.selectorIcon = 'icon-zuzhi';
     } else {
       _options.name = '所有的' + TriggerDeviceModel.productDetail.name;
     }
@@ -151,18 +214,16 @@ export default observer((props: AddProps) => {
       if (_timer.trigger === 'cron') {
         _options.time = _timer.cron;
       } else {
-        _options.when =
-          _timer.when!.length === 0
-            ? '每天'
-            : `每${_timer
-                .when!.map((item) => {
-                  if (_timer!.trigger === 'week') {
-                    return numberToString[item];
-                  } else {
-                    return item + '号';
-                  }
-                })
-                .join(',')}`;
+        // console.log('continuousValue', continuousValue(_timer.when! || [], _timer!.trigger))
+        let whenStr = '每天';
+        if (_timer.when!.length) {
+          whenStr = _timer!.trigger === 'week' ? '每周' : '每月';
+          const whenStrArr = continuousValue(_timer.when! || [], _timer!.trigger);
+          whenStrArr.length = 3;
+          whenStr += whenStrArr.join('、');
+          whenStr += `等${_timer.when!.length}天`;
+        }
+        _options.when = whenStr;
         if (_timer.once) {
           _options.time = _timer.once.time + ' 执行1次';
         } else if (_timer.period) {
@@ -175,16 +236,19 @@ export default observer((props: AddProps) => {
     if (data.operator === 'online') {
       _options.type = '上线';
       _options.action = '';
+      _options.typeIcon = 'icon-a-Group4713';
     }
 
     if (data.operator === 'offline') {
       _options.type = '离线';
       _options.action = '';
+      _options.typeIcon = 'icon-a-Group4892';
     }
 
     if (data.operator === 'reportProperty') {
       _options.type = '属性上报';
       _options.action = '';
+      _options.typeIcon = 'icon-file-upload-outline';
     }
     return _options;
   };

+ 1 - 9
src/pages/rule-engine/Scene/Save/device/index.less

@@ -110,14 +110,6 @@
 
 .trigger-options-content {
   .trigger-options-name {
-    .ellipsisFn(1, 30%);
-  }
-
-  .trigger-options-type {
-    .ellipsisFn(1, 10%);
-  }
-
-  .trigger-options-when {
-    .ellipsisFn(1, 15%);
+    .ellipsisFn(1, 310px);
   }
 }

+ 8 - 2
src/pages/rule-engine/Scene/Save/device/index.tsx

@@ -13,7 +13,7 @@ import { TriggerDeviceModel } from './addModel';
 import { handleMetadata } from './product';
 import { set } from 'lodash';
 import { Form, FormInstance } from 'antd';
-import { TitleComponent } from '@/components';
+import { AIcon, TitleComponent } from '@/components';
 
 const defaultDeviceValue = {
   productId: '',
@@ -46,7 +46,13 @@ export default observer((props: Props) => {
   const handleLabel = (options: any): ReactChild | ReactChild[] => {
     if (!options || !Object.keys(options).length) return '点击配置设备触发';
 
-    const _label = [<span className="trigger-options-name">{options.name}</span>];
+    const _label = [
+      <div style={{ display: 'flex' }}>
+        {options.selectorIcon ? <AIcon type={options.selectorIcon} /> : null}
+        <span className="trigger-options-name">{options.name}</span>
+        {options.extraName ? <span>{options.extraName}</span> : null}
+      </div>,
+    ];
     if (!options.onlyName) {
       if (options.productName) {
         _label.push(<span className="trigger-options-type">{options.productName}</span>);

+ 26 - 25
src/pages/rule-engine/Scene/Save/terms/index.less

@@ -1,7 +1,16 @@
 @import '~antd/es/style/themes/default.less';
 
-.add-button-color {
-  color: @primary-color;
+.add-button() {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  color: rgba(0, 0, 0, 0.3);
+  background-color: #fff;
+  border: 1px dashed rgba(0, 0, 0, 0.3);
+  border-radius: 50%;
+  cursor: pointer;
 }
 
 .deleteBtn() {
@@ -28,6 +37,7 @@
 }
 
 .actions-terms {
+  width: 66.66%;
   .actions-terms-warp {
     display: flex;
     margin-bottom: 24px;
@@ -37,6 +47,10 @@
       margin-bottom: 0;
     }
 
+    .when-add-button {
+      .add-button();
+    }
+
     .actions-terms-title {
       width: 40px;
       padding-top: 16px;
@@ -82,15 +96,6 @@
       }
     }
   }
-
-  .terms-params-delete {
-    .deleteBtn();
-
-    &.danger {
-      color: #e50012;
-      background-color: rgba(229, 0, 18, 0.1);
-    }
-  }
 }
 
 .terms-params {
@@ -113,7 +118,9 @@
     // flex-wrap: wrap;
     padding: 8px;
     padding-bottom: 0;
-    background-color: #fafafa;
+    border: 1px dashed #e0e0e0;
+    //background-color: #fafafa;
+    border-radius: 6px;
     row-gap: 16px;
     .terms-params-item {
       display: flex;
@@ -121,7 +128,7 @@
     }
   }
 
-  .terms-add {
+  .terms-group-add {
     // display: inline-block;
     width: 66px;
     margin-left: 16px;
@@ -163,11 +170,11 @@
     position: relative;
     // display: inline-block;
     display: flex;
-    padding: 4px;
-    border: 1px solid rgba(0, 0, 0, 0.1);
-    border-radius: 2px;
-    transition: border 0.3s;
-
+    //padding: 4px;
+    //border: 1px solid rgba(0, 0, 0, 0.1);
+    //border-radius: 2px;
+    //transition: border 0.3s;
+    gap: 2px;
     // > div {
     //   flex-shrink: 0;
     // }
@@ -179,13 +186,7 @@
 
   .term-add {
     // display: inline-block;
-    width: 66px;
     margin-left: 16px;
-    padding: 4px 8px;
-    color: #333;
-    background: #e8e8e8;
-    border: 1px solid #f0f0f0;
-    border-radius: 30px;
-    cursor: pointer;
+    .add-button();
   }
 }

+ 11 - 16
src/pages/rule-engine/Scene/Save/terms/index.tsx

@@ -10,7 +10,7 @@ import type { TriggerType } from '@/pages/rule-engine/Scene/typings';
 import Actions from '@/pages/rule-engine/Scene/Save/action';
 import { cloneDeep, set } from 'lodash';
 import classNames from 'classnames';
-import { PlusCircleOutlined } from '@ant-design/icons';
+import { PlusOutlined } from '@ant-design/icons';
 import { randomString } from '@/utils/util';
 
 interface TermsModelProps {
@@ -114,16 +114,15 @@ export default observer((props: Props) => {
     <div className="actions-terms">
       <TitleComponent
         style={{ fontSize: 14 }}
-        data={
-          <span>
-            触发条件{' '}
-            <Switch
-              checked={open}
-              onChange={openChange}
-              checkedChildren={'开'}
-              unCheckedChildren={'关'}
-            />
-          </span>
+        data={'触发条件'}
+        after={
+          <Switch
+            checked={open}
+            onChange={openChange}
+            checkedChildren={'开'}
+            unCheckedChildren={'关'}
+            style={{ marginLeft: 4 }}
+          />
         }
       />
       {open ? (
@@ -168,11 +167,7 @@ export default observer((props: Props) => {
                     否则
                   </div>
                   <div className={classNames('actions-terms-options no-when')}>
-                    <PlusCircleOutlined
-                      className={'add-button-color'}
-                      style={{ fontSize: 32 }}
-                      onClick={addBranches}
-                    />
+                    <PlusOutlined className={'when-add-button'} onClick={addBranches} />
                   </div>
                 </div>
               );

+ 7 - 2
src/pages/rule-engine/Scene/Save/terms/paramsItem.tsx

@@ -11,6 +11,7 @@ import { observer } from '@formily/react';
 import './index.less';
 import { Popconfirm, Space } from 'antd';
 import { isArray, isObject } from 'lodash';
+import { AIcon } from '@/components';
 
 interface ParamsItemProps {
   data: TermsType;
@@ -242,6 +243,7 @@ const ParamsItem = observer((props: ParamsItemProps) => {
               termType: _termTypeValue,
             });
           }}
+          icon={<AIcon type={'icon-zhihangdongzuoxie-1'} />}
         />
         <DropdownButton
           options={ttOptions}
@@ -291,6 +293,7 @@ const ParamsItem = observer((props: ParamsItemProps) => {
                 props.onLabelChange?.([...labelCache.current]);
                 valueEventChange(_myValue);
               }}
+              icon={<AIcon type={'icon-canshu'} />}
             />
             <ParamsDropdown
               options={valueOptions}
@@ -312,6 +315,7 @@ const ParamsItem = observer((props: ParamsItemProps) => {
                 props.onLabelChange?.([...labelCache.current]);
                 valueEventChange(_myValue);
               }}
+              icon={<AIcon type={'icon-canshu'} />}
             />
           </>
         ) : (
@@ -333,6 +337,7 @@ const ParamsItem = observer((props: ParamsItemProps) => {
               props.onLabelChange?.([...labelCache.current]);
               valueEventChange(v);
             }}
+            icon={<AIcon type={'icon-canshu'} />}
           />
         )}
         <Popconfirm title={'确认删除?'} onConfirm={props.onDelete}>
@@ -361,8 +366,8 @@ const ParamsItem = observer((props: ParamsItemProps) => {
       ) : (
         <div className="term-add" onClick={props.onAdd}>
           <div className="terms-content">
-            <PlusOutlined style={{ fontSize: 12, paddingRight: 4 }} />
-            <span>条件</span>
+            <PlusOutlined style={{ fontSize: 12 }} />
+            {/*<span>条件</span>*/}
           </div>
         </div>
       )}

+ 1 - 1
src/pages/rule-engine/Scene/Save/terms/term.tsx

@@ -181,7 +181,7 @@ export default observer((props: TermsProps) => {
             />
           </div>
         ) : (
-          <div className="terms-add" onClick={addTerms}>
+          <div className="terms-group-add" onClick={addTerms}>
             <div className="terms-content">
               <PlusOutlined style={{ fontSize: 12, paddingRight: 4 }} />
               <span>分组</span>