Wzyyy98 пре 3 година
родитељ
комит
59c449d1d6
26 измењених фајлова са 1130 додато и 101 уклоњено
  1. 5 1
      .github/workflows/ci.yml
  2. 1 2
      .github/workflows/deploy.yml
  3. 3 1
      .github/workflows/docker.yml
  4. 1 1
      package.json
  5. 4 4
      src/components/ProTableCard/CardItems/scene.tsx
  6. 9 2
      src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx
  7. 18 6
      src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx
  8. 11 0
      src/pages/rule-engine/Scene/Save/action/ListItem/index.less
  9. 2 7
      src/pages/rule-engine/Scene/Save/action/ListItem/index.tsx
  10. 21 0
      src/pages/rule-engine/Scene/Save/action/Modal/add.tsx
  11. 8 0
      src/pages/rule-engine/Scene/Save/action/index.less
  12. 5 2
      src/pages/rule-engine/Scene/Save/action/index.tsx
  13. 7 2
      src/pages/rule-engine/Scene/Save/components/Buttons/AddButton.tsx
  14. 3 0
      src/pages/rule-engine/Scene/Save/components/Buttons/index.less
  15. 1 1
      src/pages/rule-engine/Scene/Save/components/Buttons/index.tsx
  16. 71 0
      src/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType.tsx
  17. 3 0
      src/pages/rule-engine/Scene/Save/device/index.tsx
  18. 68 0
      src/pages/rule-engine/Scene/Save/index.less
  19. 435 59
      src/pages/rule-engine/Scene/Save/index.tsx
  20. 8 0
      src/pages/rule-engine/Scene/Save/manual/index.tsx
  21. 70 0
      src/pages/rule-engine/Scene/Save/save.tsx
  22. 28 0
      src/pages/rule-engine/Scene/Save/timer/TimerTrigger/index.tsx
  23. 32 0
      src/pages/rule-engine/Scene/Save/timer/index.tsx
  24. 10 10
      src/pages/rule-engine/Scene/index.tsx
  25. 1 1
      src/utils/menu/index.ts
  26. 305 2
      yarn.lock

+ 5 - 1
.github/workflows/ci.yml

@@ -1,6 +1,10 @@
 name: Node CI
 
-on: [ push, pull_request ]
+on: 
+    push: 
+        branches: ["master", "2.0"]
+    pull_request: 
+        branches: ["master", "2.0"]
 
 jobs:
   build:

+ 1 - 2
.github/workflows/deploy.yml

@@ -1,8 +1,7 @@
 name: Deploy CI
 on:
   push:
-    branches:
-      - master
+    branches: ["master", "2.0"]
 
 jobs:
   build-and-deploy:

+ 3 - 1
.github/workflows/docker.yml

@@ -1,5 +1,7 @@
 name: build images
-on: [push]
+on:
+  push:
+    branches: ["master", "2.0"]
 jobs:
   build-and-deploy:
     runs-on: ubuntu-latest

+ 1 - 1
package.json

@@ -72,7 +72,7 @@
     "@liveqing/liveplayer": "^2.6.4",
     "@umijs/route-utils": "^1.0.36",
     "ahooks": "^2.10.9",
-    "antd": "4.19.5",
+    "antd": "^4.19.5",
     "braft-editor": "^2.3.9",
     "classnames": "^2.3.1",
     "dexie": "^3.0.3",

+ 4 - 4
src/components/ProTableCard/CardItems/scene.tsx

@@ -45,10 +45,10 @@ export default (props: DeviceCardProps) => {
               <label>触发方式</label>
               <Ellipsis title={TriggerWayType[props.triggerType]} />
             </div>
-            <div>
-              <label>说明</label>
-              <Ellipsis title={props.description} />
-            </div>
+            {/*<div>*/}
+            {/*  <label>说明</label>*/}
+            {/*  <Ellipsis title={props.description} />*/}
+            {/*</div>*/}
           </div>
         </div>
       </div>

+ 9 - 2
src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx

@@ -1,10 +1,17 @@
+import { AddButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
+export type ItemType = 'serial' | 'parallel';
 interface ItemProps {
   name: number;
   resetField: any;
   remove: (index: number | number[]) => void;
-  // type: 'serial' | 'parallel'
+  type: ItemType;
 }
 
 export default (props: ItemProps) => {
-  return <div className="">{props.name}</div>;
+  return (
+    <div className="actions-item">
+      {props.name}
+      <AddButton>点击配置执行动作</AddButton>
+    </div>
+  );
 };

+ 18 - 6
src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx

@@ -1,25 +1,37 @@
 import { Button, Form } from 'antd';
 import { PlusOutlined } from '@ant-design/icons';
-import Item from './Item';
+import { Item } from './index';
+import type { ItemType } from './Item';
+import './index.less';
 
-export default () => {
+interface PropsType {
+  type: ItemType;
+}
+
+export default (props: PropsType) => {
   const [form] = Form.useForm();
 
   return (
     <div className="action-list-content">
       <Form form={form} colon={false} layout={'vertical'} preserve={false} name="actions">
-        <Form.List name="actions">
+        <Form.List name="actions" initialValue={[{}]}>
           {(fields, { add, remove }) => (
-            <>
+            <div className="actions-list_form">
               {fields.map(({ key, name, ...resetField }) => (
-                <Item key={key} name={name} resetField={resetField} remove={remove} />
+                <Item
+                  key={key}
+                  name={name}
+                  resetField={resetField}
+                  remove={remove}
+                  type={props.type}
+                />
               ))}
               <div className="action-list-add">
                 <Button type="primary" onClick={add} ghost icon={<PlusOutlined />}>
                   新增
                 </Button>
               </div>
-            </>
+            </div>
           )}
         </Form.List>
       </Form>

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

@@ -0,0 +1,11 @@
+.action-list-content {
+  .actions-list_form {
+    .action-list-add {
+      display: flex;
+      justify-content: flex-end;
+      margin-top: 16px;
+      padding-top: 16px;
+      border-top: 1px solid @primary-color;
+    }
+  }
+}

+ 2 - 7
src/pages/rule-engine/Scene/Save/action/ListItem/index.tsx

@@ -1,7 +1,2 @@
-import List from './List';
-import Item from './Item';
-
-export default {
-  List,
-  Item,
-};
+export { default as List } from './List';
+export { default as Item } from './Item';

+ 21 - 0
src/pages/rule-engine/Scene/Save/action/Modal/add.tsx

@@ -0,0 +1,21 @@
+import { Modal, Form } from 'antd';
+import ActionsType from '@/pages/rule-engine/Scene/Save/components/TriggerWay/actionsType';
+
+export default () => {
+  const [form] = Form.useForm();
+
+  return (
+    <Modal title="类型">
+      <Form form={form}>
+        <Form.Item
+          name="type"
+          label="类型"
+          required
+          rules={[{ required: true, message: '请选择类型' }]}
+        >
+          <ActionsType />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};

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

@@ -1,3 +1,5 @@
+@import '~antd/es/style/themes/default.less';
+
 .actions-items {
   padding: 26px 24px 12px 24px;
   background-color: #fafafa;
@@ -23,4 +25,10 @@
     font-weight: 800;
     font-size: 14px;
   }
+
+  .actions-warp {
+    .actions-list {
+      width: 100%;
+    }
+  }
 }

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

@@ -1,4 +1,5 @@
 import { Collapse } from 'antd';
+import { List } from './ListItem';
 
 const { Panel } = Collapse;
 
@@ -7,7 +8,7 @@ export default () => {
     <div className="actions">
       <div className="actions-title">执行</div>
       <div className="actions-warp">
-        <Collapse defaultActiveKey={[]}>
+        <Collapse defaultActiveKey={['1']}>
           <Panel
             header={
               <span>
@@ -16,7 +17,9 @@ export default () => {
             }
             key="1"
           >
-            <div className="actions-list"></div>
+            <div className="actions-list">
+              <List type="serial" />
+            </div>
           </Panel>
           <Panel
             header={

+ 7 - 2
src/pages/rule-engine/Scene/Save/components/Buttons/AddButton.tsx

@@ -1,12 +1,17 @@
+import type { CSSProperties } from 'react';
+
 interface ButtonProps {
   children?: React.ReactDOM | string;
   onClick?: () => void;
+  style?: CSSProperties;
 }
 
-export default (props: ButtonProps) => {
+const AddButton = (props: ButtonProps) => {
   return (
-    <div className="rule-button-warp">
+    <div className="rule-button-warp" style={props.style}>
       <div className="rule-button add-button">{props.children}</div>
     </div>
   );
 };
+
+export default AddButton;

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

@@ -1,8 +1,11 @@
 .rule-button-warp {
+  display: inline-block;
   padding: 8px;
   background-color: #fafafa;
+  cursor: pointer;
 
   .rule-button {
+    display: inline-block;
     padding: 6px 20px;
     font-size: 14px;
     line-height: 22px;

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

@@ -1,2 +1,2 @@
 import './index.less';
-export * as AddButton from './AddButton';
+export { default as AddButton } from './AddButton';

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

@@ -0,0 +1,71 @@
+import { useEffect, useState } from 'react';
+import classNames from 'classnames';
+import './index.less';
+
+interface ActionsTypeProps {
+  value?: string;
+  className?: string;
+  onChange?: (type: string) => void;
+  onSelect?: (type: string) => void;
+  disabled?: boolean;
+}
+
+export enum ActionsTypeEnum {
+  manual = 'manual',
+  timing = 'timer',
+  device = 'device',
+}
+
+const TypeList = [
+  {
+    label: '设备输出',
+    value: 'device',
+    image: '',
+    tip: '配置设备调用功能、读取属性、设置属性规则',
+  },
+  { label: '消息通知', value: 'notify', image: '', tip: '' },
+  { label: '延迟执行', value: 'delay', image: '', tip: '' },
+  { label: '触发告警', value: 'trigger', image: '', tip: '' },
+  { label: '解除告警', value: 'relieve', image: '', tip: '' },
+];
+
+export default (props: ActionsTypeProps) => {
+  const [type, setType] = useState(props.value || '');
+
+  useEffect(() => {
+    setType(props.value || '');
+  }, [props.value]);
+
+  const onSelect = (_type: string) => {
+    if (!props.disabled) {
+      setType(_type);
+
+      if (props.onChange) {
+        props.onChange(_type);
+      }
+    }
+  };
+
+  return (
+    <div className={classNames('trigger-way-warp', props.className, { disabled: props.disabled })}>
+      {TypeList.map((item) => (
+        <div
+          className={classNames('trigger-way-item', {
+            active: type === item.value,
+          })}
+          onClick={() => {
+            onSelect(item.value);
+          }}
+        >
+          <div className={'way-item-title'}>
+            <p>{item.label}</p>
+            <span>{item.tip}</span>
+          </div>
+          <div className={'way-item-image'}>
+            <img width={48} src={item.image} />
+          </div>
+        </div>
+      ))}
+    </div>
+  );
+};

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

@@ -0,0 +1,3 @@
+export default () => {
+  return <div>设备触发</div>;
+};

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

@@ -0,0 +1,68 @@
+@bgColor: #fafafa;
+
+.scene-save {
+  .trigger-type-content,
+  & .scene-actions {
+    .actions-item {
+      padding: 24px 24px 0 24px;
+      background-color: @bgColor;
+
+      &:not(:first-child) {
+        margin-top: 16px;
+      }
+
+      .actions-item-title {
+        margin-bottom: 16px;
+      }
+
+      .actions-item-footer {
+        display: flex;
+        justify-content: flex-end;
+        padding-top: 16px;
+        border-top: 1px solid #2f54eb;
+      }
+
+      .template-variable {
+        margin-bottom: 16px;
+        padding: 16px;
+        border: 1px solid #e6e6e6;
+      }
+    }
+  }
+
+  .ant-form-item-with-help .ant-form-item-explain {
+    height: 0 !important;
+  }
+
+  //.trigger-type-content {
+  //  > .ant-row {
+  //    margin-bottom: 24px;
+  //
+  //    &:last-child,
+  //    &:first-child {
+  //      margin-bottom: 0;
+  //    }
+  //  }
+  //}
+}
+
+.scene-content {
+  position: relative;
+
+  .scene-content-left {
+    width: 66.66%;
+  }
+
+  .scene-content-right {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 33.33%;
+    height: 100%;
+
+    > div {
+      height: 100%;
+      overflow-y: auto;
+    }
+  }
+}

+ 435 - 59
src/pages/rule-engine/Scene/Save/index.tsx

@@ -1,70 +1,446 @@
-import { Modal } from '@/components';
-import { Form, Input } from 'antd';
-import TriggerWay from '@/pages/rule-engine/Scene/Save/components/TriggerWay';
-import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
-import { useEffect } from 'react';
+import { PageContainer } from '@ant-design/pro-layout';
+import { Button, Card, Form, Input, InputNumber, Radio, Space, Switch, Tooltip } from 'antd';
+import { useIntl, useHistory } from 'umi';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { PermissionButton, TitleComponent } from '@/components';
+import ActionItems from './action/action';
+import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import { TimingTrigger, TriggerWay } from './components';
+import { TriggerWayType } from './components/TriggerWay';
+import TriggerTerm from '@/pages/rule-engine/Scene/TriggerTerm';
+import TriggerDevice from './trigger';
+import { service } from '../index';
+import './index.less';
+import { model } from '@formily/reactive';
+import type { FormModelType } from '@/pages/rule-engine/Scene/typings';
+import { onlyMessage } from '@/utils/util';
+import Explanation from './Explanation';
 import { getMenuPathByCode } from '@/utils/menu';
-import useHistory from '@/hooks/route/useHistory';
+import { useLocation } from '@/hooks';
 
-interface Props {
-  close: () => void;
-  data: Partial<SceneItem>;
-}
+type ShakeLimitType = {
+  enabled: boolean;
+  groupType?: string;
+  time?: number;
+  threshold?: number;
+  alarmFirst?: boolean;
+};
+
+const DefaultShakeLimit = {
+  enabled: false,
+  alarmFirst: false,
+};
+
+export let FormModel = model<FormModelType>({});
 
-export default (props: Props) => {
+export default () => {
+  const location = useLocation();
   const [form] = Form.useForm();
+  const intl = useIntl();
+  const triggerRef = useRef<any>();
   const history = useHistory();
 
+  const { getOtherPermission } = PermissionButton.usePermission('rule-engine/Scene');
+  const [triggerType, setTriggerType] = useState('device');
+
+  const [loading, setLoading] = useState(false);
+  const [parallel, setParallel] = useState(true); // 是否并行
+  const [shakeLimit, setShakeLimit] = useState<ShakeLimitType>(DefaultShakeLimit);
+  const [targetTypeDisabled, setTargetTypeDisabled] = useState(false);
+
+  const [requestParams, setRequestParams] = useState<any>(undefined);
+  const [triggerValue, setTriggerValue] = useState<any>([]);
+  const [triggerDatas, setTriggerDatas] = useState<any>({});
+  const [actionParams, setActionParams] = useState<any>(undefined);
+  const [actionDataCount, setActionDataCount] = useState(0);
+
+  const [actionsData, setActionsData] = useState<any[]>([]);
+  const [isEdit, setIsEdit] = useState(false);
+  const [view, setView] = useState<boolean>(false);
+
   useEffect(() => {
-    form.setFieldsValue({
-      ...props.data,
-    });
-  }, [props.data]);
+    FormModel = {};
+  }, []);
 
-  return (
-    <Modal
-      title={props.data?.id ? '编辑' : '新增'}
-      maskClosable={false}
-      visible
-      onCancel={() => {
-        props.close();
-      }}
-      onOk={async () => {
-        const values = await form.validateFields();
-        props.close();
-        const url = getMenuPathByCode('rule-engine/Scene/Detail');
-        history.push(`${url}?type=${values.trigger?.type}`);
-      }}
-      width={700}
-    >
-      <Form name="scene-save" layout={'vertical'} form={form} autoComplete="off">
-        <Form.Item
-          name={'name'}
-          label={'名称'}
-          rules={[
-            { required: true, message: '请输入名称' },
-            {
-              max: 64,
-              message: '最多输入64个字符',
-            },
-          ]}
-        >
-          <Input placeholder={'请输入名称'} />
-        </Form.Item>
-        <Form.Item
-          label={'触发方式'}
-          name={['trigger', 'type']}
-          rules={[{ required: true, message: '请选择触发方式' }]}
-          initialValue={'device'}
-        >
-          <TriggerWay
-            // onSelect={(val) => {
-            //   // console.log(val);
-            // }}
-            disabled={!!props.data?.id}
+  const getDetail = useCallback(
+    async (id: string) => {
+      const resp = await service.detail(id);
+      if (resp.status === 200 && resp.result) {
+        setIsEdit(true);
+        const _data: any = resp.result;
+        FormModel = _data;
+        console.log(_data);
+
+        form.setFieldsValue(_data);
+        // debugger;
+        setParallel(_data.parallel);
+
+        setTriggerValue({ trigger: _data.terms || [] });
+        setTriggerDatas(_data.trigger);
+        setActionParams({ trigger: _data.trigger });
+        if (_data.trigger?.shakeLimit) {
+          setShakeLimit(_data.trigger?.shakeLimit || DefaultShakeLimit);
+        }
+        if (_data.trigger?.device) {
+          setRequestParams({ trigger: _data.trigger });
+        }
+
+        if (_data.actions) {
+          setActionsData(_data.actions);
+        }
+      }
+    },
+    [triggerRef],
+  );
+
+  useEffect(() => {
+    const params = new URLSearchParams(location.search);
+    const id = params.get('id');
+    const targetType = params.get('targetType');
+    if (id) {
+      getDetail(id);
+    }
+
+    if (targetType) {
+      setTargetTypeDisabled(true);
+    }
+
+    if (location && location.state) {
+      setView(location.state.view);
+    }
+  }, [location]);
+
+  const saveData = useCallback(async () => {
+    const formData = await form.validateFields();
+    let triggerData = undefined;
+    // 获取触发条件数据
+    if (triggerRef.current && formData.trigger) {
+      triggerData = await triggerRef.current.getTriggerForm();
+      if (triggerData) {
+        formData.terms = triggerData.trigger;
+      }
+    }
+    console.log('save', formData);
+    if (formData) {
+      if (shakeLimit.enabled) {
+        formData.trigger = {
+          ...formData.trigger,
+          shakeLimit: shakeLimit,
+        };
+      }
+      setLoading(true);
+      const resp = formData.id ? await service.updateScene(formData) : await service.save(formData);
+
+      // 处理跳转新增
+      if ((window as any).onTabSaveSuccess) {
+        if (resp.result) {
+          (window as any).onTabSaveSuccess(resp);
+          setTimeout(() => window.close(), 300);
+        }
+      }
+
+      setLoading(false);
+      if (resp.status === 200) {
+        onlyMessage('操作成功');
+        const url = getMenuPathByCode('rule-engine/Scene');
+        history.push(url);
+      } else {
+        onlyMessage(resp.message);
+      }
+    }
+  }, [shakeLimit]);
+
+  const AntiShake = (
+    <Space>
+      <TitleComponent data={'触发条件'} style={{ margin: 0 }} />
+      <Switch
+        checked={shakeLimit.enabled}
+        checkedChildren="开启防抖"
+        unCheckedChildren="关闭防抖"
+        onChange={(e) => {
+          const newShake = {
+            ...shakeLimit,
+            enabled: e,
+          };
+          setShakeLimit(newShake);
+        }}
+        style={{ marginRight: 16 }}
+      />
+      {shakeLimit.enabled && (
+        <>
+          <InputNumber
+            value={shakeLimit.time}
+            min={1}
+            max={100}
+            onChange={(e: number) => {
+              const newShake = {
+                ...shakeLimit,
+                time: e,
+              };
+              setShakeLimit(newShake);
+            }}
           />
-        </Form.Item>
-      </Form>
-    </Modal>
+          <span style={{ padding: '0 16px' }}> 秒内发生 </span>
+          <InputNumber
+            value={shakeLimit.threshold}
+            min={1}
+            max={100}
+            onChange={(e: number) => {
+              const newShake = {
+                ...shakeLimit,
+                threshold: e,
+              };
+              setShakeLimit(newShake);
+            }}
+          />
+          <span style={{ padding: '0 16px' }}>次及以上时,处理</span>
+          <Radio.Group
+            value={shakeLimit.alarmFirst}
+            options={[
+              { label: '第一次', value: true },
+              { label: '最后一次', value: false },
+            ]}
+            optionType="button"
+            onChange={(e) => {
+              const newShake = {
+                ...shakeLimit,
+                alarmFirst: e.target.value,
+              };
+              setShakeLimit(newShake);
+            }}
+          ></Radio.Group>
+        </>
+      )}
+    </Space>
+  );
+
+  const hasKeyInObject = (keys: string[], obj: any) => {
+    return keys.some((key) => key in obj);
+  };
+
+  return (
+    <PageContainer>
+      <Card>
+        <div className={'scene-content'}>
+          <div className={'scene-content-left'}>
+            <Form
+              scrollToFirstError={{
+                behavior: 'smooth',
+                block: 'end',
+              }}
+              form={form}
+              colon={false}
+              name="basicForm"
+              layout={'vertical'}
+              preserve={false}
+              className={'scene-save'}
+              onValuesChange={(changeValue, allValues) => {
+                if (changeValue.trigger) {
+                  if (changeValue.trigger.device) {
+                    if (
+                      changeValue.trigger.device.productId ||
+                      changeValue.trigger.device.selectorValues ||
+                      (changeValue.trigger.device.operation &&
+                        hasKeyInObject(
+                          ['operator', 'eventId', 'functionId'],
+                          changeValue.trigger.device.operation,
+                        ))
+                    ) {
+                      setTriggerValue([]);
+                      setRequestParams({ trigger: allValues.trigger });
+                      setTriggerDatas(allValues.trigger);
+                    }
+
+                    if (
+                      hasKeyInObject(['productId'], changeValue.trigger.device) ||
+                      (changeValue.trigger.device.operation &&
+                        hasKeyInObject(
+                          ['operator', 'eventId', 'functionId'],
+                          changeValue.trigger.device.operation,
+                        ))
+                    ) {
+                      setActionParams({ trigger: allValues.trigger }); // 用于内置参数请求
+                    }
+                  } else if (['timer', 'manual'].includes(changeValue.trigger.type)) {
+                    setActionParams({ trigger: allValues.trigger }); // 用于内置参数请求
+                  }
+                }
+                if (allValues.actions) {
+                  // debugger;
+                  console.log(allValues.actions, 'onValuesChange');
+                  setActionsData(allValues.actions);
+                }
+                FormModel = { ...allValues };
+              }}
+            >
+              <Form.Item
+                name={'name'}
+                label={<TitleComponent data={'名称'} style={{ margin: 0 }} />}
+                rules={[
+                  { required: true, message: '请输入名称' },
+                  {
+                    max: 64,
+                    message: intl.formatMessage({
+                      id: 'pages.form.tip.max64',
+                      defaultMessage: '最多输入64个字符',
+                    }),
+                  },
+                ]}
+              >
+                <Input placeholder={'请输入名称'} />
+              </Form.Item>
+              <Form.Item
+                label={<TitleComponent data={'触发方式'} style={{ margin: 0 }} />}
+                required
+              >
+                <Form.Item
+                  name={['trigger', 'type']}
+                  rules={[{ required: true, message: '请选择触发方式' }]}
+                  initialValue={'device'}
+                >
+                  <TriggerWay onSelect={setTriggerType} disabled={isEdit || targetTypeDisabled} />
+                </Form.Item>
+                {triggerType === TriggerWayType.timing && (
+                  <TimingTrigger
+                    name={['trigger']}
+                    form={form}
+                    className={'trigger-type-content'}
+                  />
+                )}
+                {triggerType === TriggerWayType.device && (
+                  <TriggerDevice
+                    value={triggerDatas}
+                    className={'trigger-type-content'}
+                    form={form}
+                  />
+                )}
+              </Form.Item>
+              {triggerType === TriggerWayType.device &&
+              requestParams &&
+              requestParams.trigger?.device?.productId ? (
+                <Form.Item label={AntiShake}>
+                  <TriggerTerm ref={triggerRef} params={requestParams} value={triggerValue} />
+                </Form.Item>
+              ) : null}
+              <Form.Item hidden name={'parallel'} initialValue={true}>
+                <Input />
+              </Form.Item>
+              <Form.Item
+                label={
+                  <Space>
+                    <TitleComponent data={<span>执行动作</span>} style={{ margin: 0 }} />
+                    <Tooltip
+                      title={
+                        <div>
+                          <div>串行:按顺序依次执行动作</div>
+                          <div>并行:同时执行所有动作</div>
+                        </div>
+                      }
+                    >
+                      <QuestionCircleOutlined style={{ margin: '0 8px', fontSize: 14 }} />
+                    </Tooltip>
+                    <Radio.Group
+                      value={parallel}
+                      options={[
+                        { label: '串行', value: false },
+                        { label: '并行', value: true },
+                      ]}
+                      optionType="button"
+                      onChange={(e) => {
+                        setParallel(e.target.value);
+                        form.setFieldsValue({ parallel: e.target.value });
+                      }}
+                    ></Radio.Group>
+                  </Space>
+                }
+              >
+                <Form.List name="actions">
+                  {(fields, { add, remove, move }, { errors }) => (
+                    <>
+                      <div className={'scene-actions'} style={{ paddingBottom: 24 }}>
+                        {fields.map(({ key, name, ...restField }) => (
+                          <ActionItems
+                            key={key}
+                            form={form}
+                            restField={restField}
+                            name={name}
+                            trigger={actionParams}
+                            triggerType={triggerType}
+                            triggerRef={triggerRef.current}
+                            onRemove={() => {
+                              remove(name);
+                              setActionDataCount(actionDataCount - 1);
+                            }}
+                            onMove={(type) => {
+                              if (type === 'up') {
+                                move(name, name - 1);
+                              } else {
+                                move(name, name + 1);
+                              }
+                            }}
+                            actionItemData={actionsData[name]}
+                            isLast={!actionDataCount || actionDataCount - 1 === name}
+                            parallel={parallel}
+                            isEdit={isEdit}
+                          />
+                        ))}
+                        <Form.Item noStyle>
+                          <Button
+                            type="primary"
+                            ghost
+                            style={{
+                              width: '100%',
+                              marginTop: 16,
+                            }}
+                            onClick={() => {
+                              add();
+                              setActionDataCount(actionDataCount + 1);
+                            }}
+                            icon={<PlusOutlined />}
+                          >
+                            新增
+                          </Button>
+                        </Form.Item>
+                      </div>
+                      <Form.ErrorList errors={errors} />
+                    </>
+                  )}
+                </Form.List>
+              </Form.Item>
+              <Form.Item
+                label={<TitleComponent data={'说明'} style={{ margin: 0 }} />}
+                name={'description'}
+              >
+                <Input.TextArea showCount maxLength={200} placeholder={'请输入说明'} rows={4} />
+              </Form.Item>
+              {/*{triggerType === TriggerWayType.device &&*/}
+              {/*requestParams &&*/}
+              {/*requestParams.trigger?.device?.productId ? (*/}
+              {/*  <Form.Item hidden name={['trigger','shakeLimit']}>*/}
+              {/*    <Input />*/}
+              {/*  </Form.Item>*/}
+              {/*) : null}*/}
+              <Form.Item hidden name={'id'}>
+                <Input />
+              </Form.Item>
+              {!view && (
+                <PermissionButton
+                  isPermission={getOtherPermission(['add', 'update'])}
+                  onClick={saveData}
+                  type={'primary'}
+                  loading={loading}
+                  htmlType="submit"
+                >
+                  保存
+                </PermissionButton>
+              )}
+            </Form>
+          </div>
+          <div className={'scene-content-right'}>
+            <Explanation type={triggerType} />
+          </div>
+        </div>
+      </Card>
+    </PageContainer>
   );
 };

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

@@ -0,0 +1,8 @@
+import Actions from '../action';
+export default () => {
+  return (
+    <div>
+      <Actions />
+    </div>
+  );
+};

+ 70 - 0
src/pages/rule-engine/Scene/Save/save.tsx

@@ -0,0 +1,70 @@
+import { Modal } from '@/components';
+import { Form, Input } from 'antd';
+import TriggerWay from '@/pages/rule-engine/Scene/Save/components/TriggerWay';
+import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
+import { useEffect } from 'react';
+import { getMenuPathByCode } from '@/utils/menu';
+import useHistory from '@/hooks/route/useHistory';
+
+interface Props {
+  close: () => void;
+  data: Partial<SceneItem>;
+}
+
+export default (props: Props) => {
+  const [form] = Form.useForm();
+  const history = useHistory();
+
+  useEffect(() => {
+    form.setFieldsValue({
+      ...props.data,
+    });
+  }, [props.data]);
+
+  return (
+    <Modal
+      title={props.data?.id ? '编辑' : '新增'}
+      maskClosable={false}
+      visible
+      onCancel={() => {
+        props.close();
+      }}
+      onOk={async () => {
+        const values = await form.validateFields();
+        props.close();
+        const url = getMenuPathByCode('rule-engine/Scene/Save');
+        history.push(`${url}?triggerType=${values.trigger?.type}`);
+      }}
+      width={700}
+    >
+      <Form name="scene-save" layout={'vertical'} form={form} autoComplete="off">
+        <Form.Item
+          name={'name'}
+          label={'名称'}
+          rules={[
+            { required: true, message: '请输入名称' },
+            {
+              max: 64,
+              message: '最多输入64个字符',
+            },
+          ]}
+        >
+          <Input placeholder={'请输入名称'} />
+        </Form.Item>
+        <Form.Item
+          label={'触发方式'}
+          name={['trigger', 'type']}
+          rules={[{ required: true, message: '请选择触发方式' }]}
+          initialValue={'device'}
+        >
+          <TriggerWay
+            // onSelect={(val) => {
+            //   // console.log(val);
+            // }}
+            disabled={!!props.data?.id}
+          />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};

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

@@ -0,0 +1,28 @@
+import { SceneItem } from '@/pages/rule-engine/Scene/typings';
+import { Modal } from 'antd';
+
+interface Props {
+  close: () => void;
+  data: Partial<SceneItem>;
+  save: (data: any) => void;
+}
+
+export default (props: Props) => {
+  return (
+    <Modal
+      title={'定时触发'}
+      maskClosable={false}
+      visible
+      onCancel={() => {
+        props.close();
+      }}
+      onOk={async () => {
+        // const values = await form.validateFields();
+        // props.close();
+      }}
+      width={700}
+    >
+      123
+    </Modal>
+  );
+};

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

@@ -0,0 +1,32 @@
+import { AddButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
+import { useState } from 'react';
+import TimerTrigger from './TimerTrigger';
+
+export default () => {
+  const [visible, setVisible] = useState<boolean>(false);
+
+  return (
+    <div style={{ marginLeft: 40 }}>
+      <div
+        style={{ width: 200 }}
+        onClick={() => {
+          setVisible(true);
+        }}
+      >
+        <AddButton>点击配置定时触发规则</AddButton>
+      </div>
+      <div>执行动作</div>
+      {visible && (
+        <TimerTrigger
+          data={{}}
+          save={(data: any) => {
+            console.log(data);
+          }}
+          close={() => {
+            setVisible(false);
+          }}
+        />
+      )}
+    </div>
+  );
+};

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

@@ -20,7 +20,7 @@ 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';
+import Save from './Save/save';
 
 export const service = new Service('scene');
 
@@ -36,7 +36,7 @@ const Scene = () => {
   const { permission } = PermissionButton.usePermission('rule-engine/Scene');
   const [searchParams, setSearchParams] = useState<any>({});
   const [visible, setVisible] = useState<boolean>(false);
-  const [current, setCurrent] = useState<Partial<SceneItem>>({});
+  const [current] = useState<Partial<SceneItem>>({});
   const history = useHistory();
 
   const deleteById = async (id: string) => {
@@ -98,8 +98,8 @@ const Scene = () => {
             : undefined
         }
         onClick={() => {
-          setVisible(true);
-          setCurrent(record);
+          const url = getMenuPathByCode('rule-engine/Scene/Save');
+          history.push(`${url}?id=${record.id}`);
         }}
       >
         <EditOutlined />
@@ -342,10 +342,10 @@ const Scene = () => {
             type="primary"
             isPermission={permission.add}
             onClick={() => {
-              // const url = getMenuPathByCode('rule-engine/Scene/Save');
-              // history.push(url);
-              setCurrent({});
-              setVisible(true);
+              const url = getMenuPathByCode('rule-engine/Scene/Save');
+              history.push(url);
+              // setCurrent({});
+              // setVisible(true);
             }}
           >
             {intl.formatMessage({
@@ -367,8 +367,8 @@ const Scene = () => {
                   title: '查看',
                 }}
                 onClick={() => {
-                  // const url = getMenuPathByCode('rule-engine/Scene/Save');
-                  // history.push(`${url}?id=${record.id}`, { view: true });
+                  const url = getMenuPathByCode('rule-engine/Scene/Save');
+                  history.push(`${url}?id=${record.id}`, { view: true });
                   // setCurrent({})
                   // setVisible(true)
                 }}

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

@@ -10,7 +10,7 @@ export const MENUS_BUTTONS_CACHE = 'MENUS_BUTTONS_CACHE';
 
 const DetailCode = 'detail';
 
-// 额外子级路由
+// 额外子级路由F
 const extraRouteObj = {
   notice: {
     children: [

+ 305 - 2
yarn.lock

@@ -2033,6 +2033,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.16.7":
+  version "7.20.1"
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9"
+  integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==
+  dependencies:
+    regenerator-runtime "^0.13.10"
+
 "@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@@ -2825,6 +2832,15 @@
   resolved "https://registry.yarnpkg.com/@qixian.cs/path-to-regexp/-/path-to-regexp-6.1.0.tgz#6b84ad01596332aba95fa29d2e70104698cd5c45"
   integrity sha512-2jIiLiVZB1jnY7IIRQKtoV8Gnr7XIhk4mC88ONGunZE3hYt5IHUG4BE/6+JiTBjjEWQLBeWnZB8hGpppkufiVw==
 
+"@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.2":
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/@rc-component/portal/-/portal-1.0.3.tgz#3aa2c229a7a20ac2412d864e8977e6377973416e"
+  integrity sha512-rG9j7OMiI9eLFLF6G0B4OcfLac9W8Z7Vjeizbjt/A6R+zzw7vhHbJ4GIkrDpUqXDvFdEEzdxfICpb8/noLwG+w==
+  dependencies:
+    "@babel/runtime" "^7.18.0"
+    classnames "^2.3.2"
+    rc-util "^5.24.4"
+
 "@react-dnd/asap@^5.0.1":
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
@@ -4984,7 +5000,7 @@ antd-mobile@^2.3.1:
     rmc-tabs "~1.2.0"
     rmc-tooltip "~1.0.0"
 
-antd@4.19.5, antd@^4.1.2, antd@^4.1.3:
+antd@^4.1.2, antd@^4.1.3:
   version "4.19.5"
   resolved "https://registry.yarnpkg.com/antd/-/antd-4.19.5.tgz#38d08f3e1391a7a69c2ca76f50968bb12ec2ac93"
   integrity sha512-C4H/VJqlVO5iMvHZyiV27R8SbPs4jsOKCGPhDXIHUry/RnUCbMmVeQaPRfUIxSI1NbqDflsuQfevPtz1svyIlg==
@@ -5033,6 +5049,55 @@ antd@4.19.5, antd@^4.1.2, antd@^4.1.3:
     rc-util "^5.19.3"
     scroll-into-view-if-needed "^2.2.25"
 
+antd@^4.19.5:
+  version "4.24.2"
+  resolved "https://registry.npmmirror.com/antd/-/antd-4.24.2.tgz#3c3d8f42846857e34eba86a1374ad63423a1d282"
+  integrity sha512-ItSltW72hkQCPE/xjl27n58STTGEhL85+Gj2I6TPnu5JYsAKHWfHu7hyGWXPXV015Oh48tqL1QUovxd/kCaUtg==
+  dependencies:
+    "@ant-design/colors" "^6.0.0"
+    "@ant-design/icons" "^4.7.0"
+    "@ant-design/react-slick" "~0.29.1"
+    "@babel/runtime" "^7.18.3"
+    "@ctrl/tinycolor" "^3.4.0"
+    classnames "^2.2.6"
+    copy-to-clipboard "^3.2.0"
+    lodash "^4.17.21"
+    moment "^2.29.2"
+    rc-cascader "~3.7.0"
+    rc-checkbox "~2.3.0"
+    rc-collapse "~3.4.2"
+    rc-dialog "~9.0.2"
+    rc-drawer "~6.0.0"
+    rc-dropdown "~4.0.0"
+    rc-field-form "~1.27.0"
+    rc-image "~5.12.0"
+    rc-input "~0.1.4"
+    rc-input-number "~7.3.9"
+    rc-mentions "~1.11.0"
+    rc-menu "~9.7.2"
+    rc-motion "^2.6.1"
+    rc-notification "~4.6.0"
+    rc-pagination "~3.2.0"
+    rc-picker "~2.7.0"
+    rc-progress "~3.4.1"
+    rc-rate "~2.9.0"
+    rc-resize-observer "^1.2.0"
+    rc-segmented "~2.1.0"
+    rc-select "~14.1.13"
+    rc-slider "~10.0.0"
+    rc-steps "~5.0.0-alpha.2"
+    rc-switch "~3.2.0"
+    rc-table "~7.26.0"
+    rc-tabs "~12.3.0"
+    rc-textarea "~0.4.5"
+    rc-tooltip "~5.2.0"
+    rc-tree "~5.7.0"
+    rc-tree-select "~5.5.0"
+    rc-trigger "^5.2.10"
+    rc-upload "~4.3.0"
+    rc-util "^5.22.5"
+    scroll-into-view-if-needed "^2.2.25"
+
 "antd@^4.20.0 ":
   version "4.22.3"
   resolved "https://registry.yarnpkg.com/antd/-/antd-4.22.3.tgz#c6d4ced17e9c21b55c6234595d962c5f9d6768db"
@@ -6279,6 +6344,11 @@ classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classna
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
   integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
 
+classnames@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.npmmirror.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
+  integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
+
 clean-regexp@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7"
@@ -14597,6 +14667,18 @@ rc-cascader@~3.6.0:
     rc-tree "~5.6.3"
     rc-util "^5.6.1"
 
+rc-cascader@~3.7.0:
+  version "3.7.0"
+  resolved "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.7.0.tgz#98134df578ce1cca22be8fb4319b04df4f3dca36"
+  integrity sha512-SFtGpwmYN7RaWEAGTS4Rkc62ZV/qmQGg/tajr/7mfIkleuu8ro9Hlk6J+aA0x1YS4zlaZBtTcSaXM01QMiEV/A==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+    array-tree-filter "^2.1.0"
+    classnames "^2.3.1"
+    rc-select "~14.1.0"
+    rc-tree "~5.7.0"
+    rc-util "^5.6.1"
+
 rc-checkbox@~2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-2.0.3.tgz#436a9d508948e224980f0535ea738b48177a8f25"
@@ -14647,6 +14729,17 @@ rc-collapse@~3.3.0:
     rc-util "^5.2.1"
     shallowequal "^1.1.0"
 
+rc-collapse@~3.4.2:
+  version "3.4.2"
+  resolved "https://registry.npmmirror.com/rc-collapse/-/rc-collapse-3.4.2.tgz#1310be7ad4cd0dcfc622c45f6c3b5ffdee403ad7"
+  integrity sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "2.x"
+    rc-motion "^2.3.4"
+    rc-util "^5.2.1"
+    shallowequal "^1.1.0"
+
 rc-dialog@~8.6.0:
   version "8.6.0"
   resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.6.0.tgz#3b228dac085de5eed8c6237f31162104687442e7"
@@ -14667,6 +14760,17 @@ rc-dialog@~8.9.0:
     rc-motion "^2.3.0"
     rc-util "^5.21.0"
 
+rc-dialog@~9.0.0, rc-dialog@~9.0.2:
+  version "9.0.2"
+  resolved "https://registry.npmmirror.com/rc-dialog/-/rc-dialog-9.0.2.tgz#aadfebdeba145f256c1fac9b9f509f893cdbb5b8"
+  integrity sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    "@rc-component/portal" "^1.0.0-8"
+    classnames "^2.2.6"
+    rc-motion "^2.3.0"
+    rc-util "^5.21.0"
+
 rc-drawer@~4.4.2:
   version "4.4.3"
   resolved "https://registry.npmmirror.com/rc-drawer/-/rc-drawer-4.4.3.tgz#2094937a844e55dc9644236a2d9fba79c344e321"
@@ -14686,6 +14790,17 @@ rc-drawer@~5.1.0-alpha.1:
     rc-motion "^2.6.1"
     rc-util "^5.21.2"
 
+rc-drawer@~6.0.0:
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/rc-drawer/-/rc-drawer-6.0.1.tgz#437040ac7ba305b5d964ba51e88f30797671e8f8"
+  integrity sha512-ibWXGf8I+KRPXE03X4s0/xXzQI37YWXUV+oPy+R29GKxkjr98UTMgwvoQDKlZTm5AiaRuVFqhTKm0kNHqJh+TQ==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    "@rc-component/portal" "^1.0.0-6"
+    classnames "^2.2.6"
+    rc-motion "^2.6.1"
+    rc-util "^5.21.2"
+
 rc-dropdown@^3.2.0, rc-dropdown@~3.4.0:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.4.1.tgz#909e8c666a9f994bd804147aaf7f8f5859dae0db"
@@ -14741,6 +14856,17 @@ rc-gesture@~0.0.18, rc-gesture@~0.0.22:
   dependencies:
     babel-runtime "6.x"
 
+rc-image@~5.12.0:
+  version "5.12.0"
+  resolved "https://registry.npmmirror.com/rc-image/-/rc-image-5.12.0.tgz#20bf4b7e8cfc63e05c47e8fb470a299ba83a7ca6"
+  integrity sha512-ubZIPfT81jmb0hLf/sIKbgi7kJT2+26RxWPshppDElhXoJZ9Xb0y+QRBcYGgCAPy76RIuaKT2RL5x8Owvhrcjg==
+  dependencies:
+    "@babel/runtime" "^7.11.2"
+    "@rc-component/portal" "^1.0.2"
+    classnames "^2.2.6"
+    rc-dialog "~9.0.0"
+    rc-util "^5.0.6"
+
 rc-image@~5.2.5:
   version "5.2.5"
   resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.2.5.tgz#44e6ffc842626827960e7ab72e1c0d6f3a8ce440"
@@ -14779,6 +14905,15 @@ rc-input-number@~7.3.5:
     classnames "^2.2.5"
     rc-util "^5.23.0"
 
+rc-input-number@~7.3.9:
+  version "7.3.11"
+  resolved "https://registry.npmmirror.com/rc-input-number/-/rc-input-number-7.3.11.tgz#c7089705a220e1a59ba974fabf89693e00dd2442"
+  integrity sha512-aMWPEjFeles6PQnMqP5eWpxzsvHm9rh1jQOWXExUEIxhX62Fyl/ptifLHOn17+waDG1T/YUb6flfJbvwRhHrbA==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.5"
+    rc-util "^5.23.0"
+
 rc-input@~0.0.1-alpha.5:
   version "0.0.1-alpha.7"
   resolved "https://registry.npmmirror.com/rc-input/-/rc-input-0.0.1-alpha.7.tgz#53e3f13871275c21d92b51f80b698f389ad45dd3"
@@ -14788,6 +14923,27 @@ rc-input@~0.0.1-alpha.5:
     classnames "^2.2.1"
     rc-util "^5.18.1"
 
+rc-input@~0.1.4:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/rc-input/-/rc-input-0.1.4.tgz#45cb4ba209ae6cc835a2acb8629d4f8f0cb347e0"
+  integrity sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA==
+  dependencies:
+    "@babel/runtime" "^7.11.1"
+    classnames "^2.2.1"
+    rc-util "^5.18.1"
+
+rc-mentions@~1.11.0:
+  version "1.11.0"
+  resolved "https://registry.npmmirror.com/rc-mentions/-/rc-mentions-1.11.0.tgz#ffbe046af52a0ec057393073d0b1ca4210c3325e"
+  integrity sha512-0C78O4wvG8UwsT7DtcwV8j7k4T+urrM0VuRT9gmSGbX187Ftl/JbCXL6WMGHSWBYI+LxInw1x4gw0Zi6qOcaig==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.6"
+    rc-menu "~9.7.2"
+    rc-textarea "^0.4.0"
+    rc-trigger "^5.0.4"
+    rc-util "^5.22.5"
+
 rc-mentions@~1.6.1:
   version "1.6.5"
   resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.5.tgz#d9516abd19a757c674df1c88a3459628fe95a149"
@@ -14851,6 +15007,19 @@ rc-menu@~9.6.0:
     rc-util "^5.12.0"
     shallowequal "^1.1.0"
 
+rc-menu@~9.7.2:
+  version "9.7.2"
+  resolved "https://registry.npmmirror.com/rc-menu/-/rc-menu-9.7.2.tgz#0b400e2d81377c8001795559139cb29871143be1"
+  integrity sha512-zyri6Qwr955SOdjzDn7/ylz7Zj8r89wGyoRw0lV9G8K6a3VBfSrP2XMYEc0JgeC62OTghUcRWad7KFqNXysxaA==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "2.x"
+    rc-motion "^2.4.3"
+    rc-overflow "^1.2.8"
+    rc-trigger "^5.1.2"
+    rc-util "^5.12.0"
+    shallowequal "^1.1.0"
+
 rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4:
   version "2.6.0"
   resolved "https://registry.npmmirror.com/rc-motion/-/rc-motion-2.6.0.tgz#c60c3e7f15257f55a8cd7794a539f0e2cc751399"
@@ -14860,7 +15029,7 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motio
     classnames "^2.2.1"
     rc-util "^5.21.0"
 
-rc-motion@^2.6.1:
+rc-motion@^2.6.1, rc-motion@^2.6.2:
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.2.tgz#3d31f97e41fb8e4f91a4a4189b6a98ac63342869"
   integrity sha512-4w1FaX3dtV749P8GwfS4fYnFG4Rb9pxvCYPc/b2fw1cmlHJWNNgOFIz7ysiD+eOrzJSvnLJWlNQQncpNMXwwpg==
@@ -14899,6 +15068,16 @@ rc-overflow@^1.0.0, rc-overflow@^1.2.0:
     rc-resize-observer "^1.0.0"
     rc-util "^5.19.2"
 
+rc-overflow@^1.2.8:
+  version "1.2.8"
+  resolved "https://registry.npmmirror.com/rc-overflow/-/rc-overflow-1.2.8.tgz#40f140fabc244118543e627cdd1ef750d9481a88"
+  integrity sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ==
+  dependencies:
+    "@babel/runtime" "^7.11.1"
+    classnames "^2.2.1"
+    rc-resize-observer "^1.0.0"
+    rc-util "^5.19.2"
+
 rc-pagination@~3.1.17:
   version "3.1.17"
   resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.17.tgz#91e690aa894806e344cea88ea4a16d244194a1bd"
@@ -14915,6 +15094,14 @@ rc-pagination@~3.1.9:
     "@babel/runtime" "^7.10.1"
     classnames "^2.2.1"
 
+rc-pagination@~3.2.0:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/rc-pagination/-/rc-pagination-3.2.0.tgz#4f2fdba9fdac0f48e5c9fb1141973818138af7e1"
+  integrity sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.1"
+
 rc-picker@~2.6.10:
   version "2.6.10"
   resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.6.10.tgz#8d0a473c079388bdb2d7358a2a54c7d5095893b4"
@@ -14943,6 +15130,20 @@ rc-picker@~2.6.4:
     rc-util "^5.4.0"
     shallowequal "^1.1.0"
 
+rc-picker@~2.7.0:
+  version "2.7.0"
+  resolved "https://registry.npmmirror.com/rc-picker/-/rc-picker-2.7.0.tgz#3c19881da27a0c5ee4c7e7504e21b552bd43a94c"
+  integrity sha512-oZH6FZ3j4iuBxHB4NvQ6ABRsS2If/Kpty1YFFsji7/aej6ruGmfM7WnJWQ88AoPfpJ++ya5z+nVEA8yCRYGKyw==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.1"
+    date-fns "2.x"
+    dayjs "1.x"
+    moment "^2.24.0"
+    rc-trigger "^5.0.4"
+    rc-util "^5.4.0"
+    shallowequal "^1.1.0"
+
 rc-progress@~3.2.1:
   version "3.2.4"
   resolved "https://registry.npmmirror.com/rc-progress/-/rc-progress-3.2.4.tgz#4036acdae2566438545bc4df2203248babaf7549"
@@ -14961,6 +15162,15 @@ rc-progress@~3.3.2:
     classnames "^2.2.6"
     rc-util "^5.16.1"
 
+rc-progress@~3.4.1:
+  version "3.4.1"
+  resolved "https://registry.npmmirror.com/rc-progress/-/rc-progress-3.4.1.tgz#a9ffe099e88a4fc03afb09d8603162bf0760d743"
+  integrity sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.6"
+    rc-util "^5.16.1"
+
 rc-rate@~2.9.0:
   version "2.9.1"
   resolved "https://registry.npmmirror.com/rc-rate/-/rc-rate-2.9.1.tgz#e43cb95c4eb90a2c1e0b16ec6614d8c43530a731"
@@ -15035,6 +15245,19 @@ rc-select@~14.1.0, rc-select@~14.1.1:
     rc-util "^5.16.1"
     rc-virtual-list "^3.2.0"
 
+rc-select@~14.1.13:
+  version "14.1.13"
+  resolved "https://registry.npmmirror.com/rc-select/-/rc-select-14.1.13.tgz#7eb53d00be82fb8e5050de3094e72edcf27ce6f6"
+  integrity sha512-WMEsC3gTwA1dbzWOdVIXDmWyidYNLq68AwvvUlRROw790uGUly0/vmqDozXrIr0QvN/A3CEULx12o+WtLCAefg==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "2.x"
+    rc-motion "^2.0.1"
+    rc-overflow "^1.0.0"
+    rc-trigger "^5.0.4"
+    rc-util "^5.16.1"
+    rc-virtual-list "^3.2.0"
+
 rc-slider@~10.0.0:
   version "10.0.1"
   resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9"
@@ -15078,6 +15301,15 @@ rc-steps@~4.1.0:
     classnames "^2.2.3"
     rc-util "^5.0.1"
 
+rc-steps@~5.0.0-alpha.2:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/rc-steps/-/rc-steps-5.0.0.tgz#2e2403f2dd69eb3966d65f461f7e3a8ee1ef69fe"
+  integrity sha512-9TgRvnVYirdhbV0C3syJFj9EhCRqoJAsxt4i1rED5o8/ZcSv5TLIYyo4H8MCjLPvbe2R+oBAm/IYBEtC+OS1Rw==
+  dependencies:
+    "@babel/runtime" "^7.16.7"
+    classnames "^2.2.3"
+    rc-util "^5.16.1"
+
 rc-swipeout@~2.0.0:
   version "2.0.11"
   resolved "https://registry.yarnpkg.com/rc-swipeout/-/rc-swipeout-2.0.11.tgz#dfad9c7b38a15ea0376e39cb3356e36fed7a4155"
@@ -15119,6 +15351,17 @@ rc-table@~7.25.3:
     rc-util "^5.22.5"
     shallowequal "^1.1.0"
 
+rc-table@~7.26.0:
+  version "7.26.0"
+  resolved "https://registry.npmmirror.com/rc-table/-/rc-table-7.26.0.tgz#9d517e7fa512e7571fdcc453eb1bf19edfac6fbc"
+  integrity sha512-0cD8e6S+DTGAt5nBZQIPFYEaIukn17sfa5uFL98faHlH/whZzD8ii3dbFL4wmUDEL4BLybhYop+QUfZJ4CPvNQ==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.5"
+    rc-resize-observer "^1.1.0"
+    rc-util "^5.22.5"
+    shallowequal "^1.1.0"
+
 rc-tabs@^11.7.1:
   version "11.12.0"
   resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.12.0.tgz#66bfa8be1e5d1fb7b115ee93155f28ff377b9800"
@@ -15155,6 +15398,19 @@ rc-tabs@~11.16.0:
     rc-resize-observer "^1.0.0"
     rc-util "^5.5.0"
 
+rc-tabs@~12.3.0:
+  version "12.3.0"
+  resolved "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-12.3.0.tgz#a346cafa7eafbe5faeeeb8c07b8ad95197f985dc"
+  integrity sha512-/mOHuq4h/mNC0QmB3iEOrYeiNMvciosSo/v7SMtvoc+jfk63AzZtQzwsC50t6kkL9ViEqyjDqj4bFfxKdQtozA==
+  dependencies:
+    "@babel/runtime" "^7.11.2"
+    classnames "2.x"
+    rc-dropdown "~4.0.0"
+    rc-menu "~9.7.2"
+    rc-motion "^2.6.2"
+    rc-resize-observer "^1.0.0"
+    rc-util "^5.16.0"
+
 rc-textarea@^0.3.0, rc-textarea@~0.3.0:
   version "0.3.7"
   resolved "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-0.3.7.tgz#987142891efdedb774883c07e2f51b318fde5a11"
@@ -15166,6 +15422,17 @@ rc-textarea@^0.3.0, rc-textarea@~0.3.0:
     rc-util "^5.7.0"
     shallowequal "^1.1.0"
 
+rc-textarea@^0.4.0, rc-textarea@~0.4.5:
+  version "0.4.6"
+  resolved "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-0.4.6.tgz#65a46c9bb45da65c2acb9b071551eb420f6568e4"
+  integrity sha512-HEKCu8nouXXayqYelQnhQm8fdH7v92pAQvfVCz+jhIPv2PHTyBxVrmoZJMn3B8cU+wdyuvRGkshngO3/TzBn4w==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "^2.2.1"
+    rc-resize-observer "^1.0.0"
+    rc-util "^5.24.4"
+    shallowequal "^1.1.0"
+
 rc-tooltip@^3.4.2:
   version "3.7.3"
   resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.3.tgz#280aec6afcaa44e8dff0480fbaff9e87fc00aecc"
@@ -15214,6 +15481,17 @@ rc-tree-select@~5.4.0:
     rc-tree "~5.6.1"
     rc-util "^5.16.1"
 
+rc-tree-select@~5.5.0:
+  version "5.5.4"
+  resolved "https://registry.npmmirror.com/rc-tree-select/-/rc-tree-select-5.5.4.tgz#5eef6845fd71085c52c042553abb3bfe7a0108f3"
+  integrity sha512-RdqOm3o+ybpdD3Jc+fYJ2hXlcODcMQIqSytjWzdTh7n2h5i7yIjSx4mprdbrF/buykU6kg1LURseWz9S2e6P2Q==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "2.x"
+    rc-select "~14.1.0"
+    rc-tree "~5.7.0"
+    rc-util "^5.16.1"
+
 rc-tree@~5.4.3:
   version "5.4.4"
   resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.4.4.tgz#2ea3663ad3c566aef79a46ba6a1e050d24323e01"
@@ -15236,6 +15514,17 @@ rc-tree@~5.6.1, rc-tree@~5.6.3, rc-tree@~5.6.5:
     rc-util "^5.16.1"
     rc-virtual-list "^3.4.8"
 
+rc-tree@~5.7.0:
+  version "5.7.0"
+  resolved "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.7.0.tgz#d0e316eeeac2ba4a1c36b2b2201d84884f1c76a1"
+  integrity sha512-F+Ewkv/UcutshnVBMISP+lPdHDlcsL+YH/MQDVWbk+QdkfID7vXiwrHMEZn31+2Rbbm21z/HPceGS8PXGMmnQg==
+  dependencies:
+    "@babel/runtime" "^7.10.1"
+    classnames "2.x"
+    rc-motion "^2.0.1"
+    rc-util "^5.16.1"
+    rc-virtual-list "^3.4.8"
+
 rc-trigger@^2.2.2:
   version "2.6.5"
   resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.6.5.tgz#140a857cf28bd0fa01b9aecb1e26a50a700e9885"
@@ -15318,6 +15607,15 @@ rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.1
     react-is "^16.12.0"
     shallowequal "^1.1.0"
 
+rc-util@^5.16.0, rc-util@^5.24.4:
+  version "5.24.4"
+  resolved "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz#a4126f01358c86f17c1bf380a1d83d6c9155ae65"
+  integrity sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==
+  dependencies:
+    "@babel/runtime" "^7.18.3"
+    react-is "^16.12.0"
+    shallowequal "^1.1.0"
+
 rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.2:
   version "3.4.7"
   resolved "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.4.7.tgz#ca0ba5ecddff686cd3833562d07c2678d1c9cb2e"
@@ -15995,6 +16293,11 @@ regenerator-runtime@^0.11.0:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
   integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
 
+regenerator-runtime@^0.13.10:
+  version "0.13.10"
+  resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee"
+  integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==
+
 regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
   version "0.13.9"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"