Преглед изворни кода

feat: 完善触发条件下拉

xieyonghong пре 3 година
родитељ
комит
1bdaa23775

+ 60 - 0
src/components/ProTableCard/CardItems/product.tsx

@@ -38,6 +38,64 @@ export const handlePermissionsMap = (permissions?: string[]) => {
     : '';
 };
 
+export const ExtraSceneProductCard = (props: ProductCardProps) => {
+  const intl = useIntl();
+  const [imgUrl, setImgUrl] = useState<string>(props.photoUrl || defaultImage);
+
+  return (
+    <TableCard
+      showMask={false}
+      status={props.state}
+      showTool={props.showTool}
+      actions={props.actions}
+      statusText={intl.formatMessage({
+        id: `pages.device.product.status.${props.state ? 'enabled' : 'disabled'}`,
+        defaultMessage: '正常',
+      })}
+      statusNames={{
+        0: StatusColorEnum.error,
+        1: StatusColorEnum.success,
+      }}
+      className={props.className}
+      onClick={props.onClick}
+    >
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img
+            width={88}
+            height={88}
+            src={imgUrl}
+            alt={''}
+            onError={() => {
+              setImgUrl(defaultImage);
+            }}
+          />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
+          </div>
+          <div className={'card-item-content'}>
+            <div>
+              <label>设备类型</label>
+              <Ellipsis title={props?.deviceType?.text} />
+            </div>
+            <div>
+              <label>接入方式</label>
+              <Ellipsis title={props.accessName || '未接入'} />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
+    </TableCard>
+  );
+};
+
 export const ExtraProductCard = (props: ProductCardProps) => {
   const intl = useIntl();
   const [imgUrl, setImgUrl] = useState<string>(props.photoUrl || defaultImage);
@@ -110,6 +168,8 @@ export default (props: ProductCardProps) => {
   const intl = useIntl();
   return (
     <TableCard
+      showMask={false}
+      showTool={props.showTool}
       detail={props.detail}
       actions={props.actions}
       status={props.state}

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

@@ -21,9 +21,13 @@
 
 .actions {
   .actions-title {
+    display: flex;
+    gap: 16px;
+    align-items: center;
     margin: 16px 0;
     font-weight: 800;
     font-size: 14px;
+    line-height: 32px;
   }
 
   .actions-warp {

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

@@ -1,17 +1,45 @@
 import { Collapse } from 'antd';
 import { List } from './ListItem';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
+import ShakeLimit from '../components/ShakeLimit';
 import './index.less';
 import { Observer } from '@formily/react';
+import { get } from 'lodash';
+import type { ShakeLimitType } from '../../typings';
 
 const { Panel } = Collapse;
 
-export default () => {
+interface ActionsProps {
+  name?: (string | number)[];
+  openShakeLimit?: boolean;
+}
+
+export default (props: ActionsProps) => {
   return (
     <div className="actions">
-      <div className="actions-title">执行</div>
+      <div className="actions-title">
+        <span>执行</span>
+        {props.openShakeLimit ? (
+          <Observer>
+            {() => {
+              const data: ShakeLimitType = get(FormModel, [...props.name!, 'shakeLimit']);
+              return (
+                <ShakeLimit
+                  enabled={data.enabled}
+                  time={data.time}
+                  threshold={data.threshold}
+                  alarmFirst={data.alarmFirst}
+                  onChange={(type, value) => {
+                    data[type] = value;
+                  }}
+                />
+              );
+            }}
+          </Observer>
+        ) : null}
+      </div>
       <div className="actions-warp">
-        <Collapse defaultActiveKey={['1', '2']}>
+        <Collapse defaultActiveKey={['1']}>
           <Panel
             header={
               <span>

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

@@ -27,7 +27,7 @@ const TypeStyle = {
   type: styles.type,
 };
 
-export default (props: DropdownButtonProps) => {
+const DropdownButton = (props: DropdownButtonProps) => {
   const [myValue, setMyValue] = useState(props.value);
   const [label, setLabel] = useState('');
   const [loading, setLoading] = useState(false);
@@ -96,3 +96,5 @@ export default (props: DropdownButtonProps) => {
     </Dropdown>
   );
 };
+
+export default DropdownButton;

+ 50 - 0
src/pages/rule-engine/Scene/Save/components/Buttons/ParamsDropdown.tsx

@@ -0,0 +1,50 @@
+import { useEffect, useMemo, useState } from 'react';
+import { Dropdown, Tabs } from 'antd';
+import classNames from 'classnames';
+import styles from './index.less';
+
+interface ParamsDropdownProps {
+  value?: any;
+  source?: string;
+  placeholder?: string;
+  onChange?: (value?: string) => void;
+  isMetric?: boolean;
+  metricOptions?: any[];
+  type?: string;
+  options?: any[];
+}
+
+export default (props: ParamsDropdownProps) => {
+  const [label] = useState('');
+  //   const [myValue, setMyValue] = useState(undefined);
+  const [, setSource] = useState('');
+  const [activeKey, setActiveKey] = useState('value_1');
+
+  const tabsChange = (key: string) => {
+    setActiveKey(key);
+    if (key.includes('value')) {
+      setSource('value');
+    } else {
+      setSource('metric');
+    }
+  };
+
+  useEffect(() => {}, [props.value]);
+
+  const DropdownRender = useMemo(() => {
+    return (
+      <Tabs activeKey={activeKey} onChange={tabsChange}>
+        <Tabs.TabPane key="value_1"></Tabs.TabPane>
+        {props.isMetric && <Tabs.TabPane key="metric"></Tabs.TabPane>}
+      </Tabs>
+    );
+  }, [props.isMetric]);
+
+  return (
+    <Dropdown dropdownRender={() => DropdownRender} trigger={['click']}>
+      <div className={classNames(styles['dropdown-button'], styles.value)}>
+        {label || props.placeholder}
+      </div>
+    </Dropdown>
+  );
+};

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

@@ -1,3 +1,4 @@
 import './index.less';
 export { default as AddButton } from './AddButton';
 export { default as DropdownButton } from './Dropdown';
+export { default as ParamsDropdown } from './ParamsDropdown';

+ 7 - 0
src/pages/rule-engine/Scene/Save/components/ShakeLimit/index.less

@@ -0,0 +1,7 @@
+.shakeLimit {
+  display: flex;
+  gap: 16px;
+  align-items: center;
+  font-weight: 400;
+  font-size: 14px;
+}

+ 87 - 0
src/pages/rule-engine/Scene/Save/components/ShakeLimit/index.tsx

@@ -0,0 +1,87 @@
+import { useEffect, useState } from 'react';
+import { Switch, InputNumber, Radio } from 'antd';
+import type { ShakeLimitType } from '@/pages/rule-engine/Scene/typings';
+import Styles from './index.less';
+
+const alarmFirstOptions = [
+  { label: '第一次', value: true },
+  { label: '最后一次', value: false },
+];
+
+interface ShakeLimitProps extends Partial<ShakeLimitType> {
+  onChange?: (type: string, value: any) => void;
+}
+
+export default (props: ShakeLimitProps) => {
+  const [enabled, setEnabled] = useState<boolean | undefined>(!!props.enabled);
+  const [time, setTime] = useState<number | undefined | null>(props.time || 1);
+  const [threshold, setThreshold] = useState<number | undefined | null>(props.threshold || 1);
+  const [alarmFirst, setAlarmFirst] = useState<boolean | undefined>(!!props.alarmFirst);
+
+  useEffect(() => {
+    setEnabled(props.enabled);
+  }, [props.enabled]);
+
+  useEffect(() => {
+    setTime(props.time);
+  }, [props.time]);
+
+  useEffect(() => {
+    setThreshold(props.threshold);
+  }, [props.threshold]);
+
+  useEffect(() => {
+    setAlarmFirst(props.alarmFirst);
+  }, [props.alarmFirst]);
+
+  const enabledChange = (value: boolean) => {
+    setEnabled(value);
+    props.onChange?.('enabled', value);
+  };
+
+  const timeChange = (value: number | null) => {
+    setTime(value);
+    props.onChange?.('time', value);
+  };
+
+  const thresholdChange = (value: number | null) => {
+    setThreshold(value);
+    props.onChange?.('threshold', value);
+  };
+
+  const alarmFirstChange = (value: boolean) => {
+    setAlarmFirst(value);
+    props.onChange?.('alarmFirst', value);
+  };
+
+  return (
+    <div className={Styles.shakeLimit}>
+      <Switch
+        checkedChildren="开启防抖"
+        unCheckedChildren="关闭防抖"
+        checked={enabled}
+        onChange={enabledChange}
+      />
+      {enabled ? (
+        <>
+          <InputNumber min={0} max={100} precision={0} value={time} onChange={timeChange} />
+          <span>秒内发送</span>
+          <InputNumber
+            min={0}
+            max={100}
+            precision={0}
+            value={threshold}
+            onChange={thresholdChange}
+          />
+          <span>次及以上时,处理</span>
+          <Radio.Group
+            options={alarmFirstOptions}
+            optionType="button"
+            value={alarmFirst}
+            onChange={(e) => alarmFirstChange(e.target.value)}
+          />
+        </>
+      ) : null}
+    </div>
+  );
+};

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

@@ -1,87 +1,9 @@
 import Terms from '@/pages/rule-engine/Scene/Save/terms';
-import ParamsSelect, { ItemProps } from '@/pages/rule-engine/Scene/Save/components/ParamsSelect';
-import { useState } from 'react';
-import { DataNode } from 'antd/es/tree';
-import { Tree } from 'antd';
-import MTimePicker from '../components/ParamsSelect/components/MTimePicker';
 
 export default () => {
-  const [value, setValue] = useState<any>(null);
-  const treeData: DataNode[] = [
-    {
-      title: 'parent 1',
-      key: '0-0',
-      children: [
-        {
-          title: 'parent 1-0',
-          key: '0-0-0',
-          children: [
-            {
-              title: 'leaf',
-              key: '0-0-0-0',
-            },
-            {
-              title: 'leaf',
-              key: '0-0-0-1',
-            },
-          ],
-        },
-        {
-          title: 'parent 1-1',
-          key: '0-0-1',
-          children: [{ title: 'sss', key: '0-0-1-0' }],
-        },
-      ],
-    },
-  ];
-  const [showValue, setShowValue] = useState<any>('');
-  const itemList: ItemProps[] = [
-    {
-      label: `手动输入`,
-      key: 'manual',
-      content: (
-        <MTimePicker
-          value={value}
-          onChange={(time: any, timeString: string) => {
-            setShowValue(timeString);
-            setValue(time);
-          }}
-        />
-      ),
-    },
-    {
-      label: `内置参数`,
-      key: 'built-in',
-      content: (
-        <Tree
-          treeData={treeData}
-          height={300}
-          defaultExpandAll
-          onSelect={(selectedKeys) => {
-            setValue(selectedKeys[0]);
-            setShowValue(selectedKeys[0]);
-          }}
-        />
-      ),
-    },
-  ];
-
   return (
     <div>
-      <div>
-        <ParamsSelect
-          style={{ width: 250 }}
-          inputProps={{
-            placeholder: '请选择',
-          }}
-          tabKey={'manual'}
-          itemList={itemList}
-          value={showValue}
-          onChange={(val: any) => {
-            setValue(val.timeString);
-          }}
-        />
-      </div>
+      <div></div>
       <Terms />
     </div>
   );

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

@@ -29,8 +29,7 @@ export const FormModel = observable<FormModelType>({
       ],
       key: 'branckes_1',
       shakeLimit: {
-        enabled: false,
-        groupType: 'device',
+        enabled: true,
         time: 1,
         threshold: 1,
         alarmFirst: false,
@@ -41,8 +40,7 @@ export const FormModel = observable<FormModelType>({
       when: [],
       key: 'branckes_2',
       shakeLimit: {
-        enabled: false,
-        groupType: 'device',
+        enabled: true,
         time: 1,
         threshold: 1,
         alarmFirst: false,

+ 6 - 4
src/pages/rule-engine/Scene/Save/terms/branchItem.tsx

@@ -4,6 +4,7 @@ import { FormModel } from '@/pages/rule-engine/Scene/Save';
 import { PlusCircleOutlined, DeleteOutlined } from '@ant-design/icons';
 import type { ActionBranchesProps } from '@/pages/rule-engine/Scene/typings';
 import Term from './term';
+import Actions from '@/pages/rule-engine/Scene/Save/action';
 import classNames from 'classnames';
 
 interface BranchesItemProps {
@@ -36,10 +37,9 @@ export default observer((props: BranchesItemProps) => {
     if (index > 0) {
       FormModel.branches?.push({
         when: [],
-        key: 'branch_' + FormModel.branches.length + 1,
+        key: `branch_${new Date().getTime()}`,
         shakeLimit: {
-          enabled: false,
-          groupType: 'device',
+          enabled: true,
           time: 1,
           threshold: 1,
           alarmFirst: false,
@@ -97,7 +97,9 @@ export default observer((props: BranchesItemProps) => {
             }
           </Observer>
         </div>
-        <div className="actions-branchs"></div>
+        <div className="actions-branchs">
+          <Actions openShakeLimit={true} name={['branches', props.name]} />
+        </div>
       </div>
     </div>
   );

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

@@ -40,6 +40,7 @@
     .actions-terms-options {
       position: relative;
       display: flex;
+      flex-direction: column;
       flex-grow: 1;
       width: 0;
 

+ 13 - 6
src/pages/rule-engine/Scene/Save/terms/paramsItem.tsx

@@ -1,6 +1,6 @@
 import { useState } from 'react';
 import type { TermsType } from '@/pages/rule-engine/Scene/typings';
-import { DropdownButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
+import { DropdownButton, ParamsDropdown } from '@/pages/rule-engine/Scene/Save/components/Buttons';
 import { CloseOutlined, PlusOutlined } from '@ant-design/icons';
 import classNames from 'classnames';
 import { observer } from '@formily/react';
@@ -31,7 +31,7 @@ export default observer((props: ParamsItemProps) => {
   };
 
   const addItem = () => {
-    const key = 'params_' + new Date().getTime();
+    const key = `params_${new Date().getTime()}`;
     const data = get(FormModel.branches, [...props.pName, 'terms']);
     data?.push({
       type: 'and',
@@ -53,13 +53,20 @@ export default observer((props: ParamsItemProps) => {
           options={paramOptions}
           type="param"
           placeholder="请选择参数"
-        ></DropdownButton>
+          value={props.data.column}
+        />
         <DropdownButton
           options={termTypeOptions}
           type="termType"
           placeholder="操作符"
-        ></DropdownButton>
-        <DropdownButton options={valueOptions} type="value" placeholder="参数值"></DropdownButton>
+          value={props.data.termType}
+        />
+        <ParamsDropdown
+          options={valueOptions}
+          type="value"
+          placeholder="参数值"
+          value={props.data.value}
+        />
         <div className={classNames('button-delete', { show: deleteVisible })} onClick={deleteItem}>
           <CloseOutlined />
         </div>
@@ -74,7 +81,7 @@ export default observer((props: ParamsItemProps) => {
             isTree={false}
             type="type"
             value="and"
-          ></DropdownButton>
+          />
         </div>
       ) : (
         <div className="term-add" onClick={addItem}>

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

@@ -29,7 +29,7 @@ export default observer((props: TermsProps) => {
 
   const addTerms = () => {
     const data = get(FormModel.branches, [...props.pName]);
-    const key = 'terms_' + new Date().getTime();
+    const key = `terms_${new Date().getTime()}`;
     const defaultValue = {
       type: 'and',
       terms: [
@@ -84,7 +84,7 @@ export default observer((props: TermsProps) => {
               isTree={false}
               type="type"
               value={props.data.type}
-            ></DropdownButton>
+            />
           </div>
         ) : (
           <div className="terms-add" onClick={addTerms}>

+ 0 - 0
src/pages/rule-engine/Scene/Save/terms/valueButton.tsx


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

@@ -112,7 +112,7 @@ export interface TriggerDevice {
 
 export interface ShakeLimitType {
   enabled: boolean;
-  groupType: string;
+  groupType?: string; // 执行动作没有该参数
   time: number;
   threshold: number;
   alarmFirst: boolean;
@@ -134,11 +134,11 @@ export type TriggerType = {
   /**
    * 防抖配置
    */
-  shakeLimit?: any;
+  shakeLimit?: ShakeLimitType;
   /**
    * 拓展信息
    */
-  options?: ShakeLimitType;
+  options?: Record<string, any>;
   /**
    * 设备触发配置
    */
@@ -189,7 +189,6 @@ export interface NotifyVariablesType {
   value?: Record<string, any>;
   upperKey?: string;
   relation?: PlatformRelation | Relationship;
-  options?: any;
 }
 
 export interface NotifyProps {
@@ -256,6 +255,7 @@ export interface ActionsType {
   terms?: TermsType[];
   /** map中的key,用于删除 */
   key?: string;
+  options?: Record<string, any>;
 }
 
 export interface FormModelType {
@@ -280,6 +280,6 @@ export interface FormModelType {
   /**
    * 拓展信息,用于前端存储一些渲染数据
    */
-  options?: any;
+  options?: Record<string, any>;
   description?: string;
 }