|
|
@@ -1,446 +1,65 @@
|
|
|
-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 { useLocation } from '@/hooks';
|
|
|
-
|
|
|
-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 () => {
|
|
|
- const location = useLocation();
|
|
|
+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';
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ close: () => void;
|
|
|
+ data: Partial<SceneItem>;
|
|
|
+}
|
|
|
+
|
|
|
+export default (props: Props) => {
|
|
|
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(() => {
|
|
|
- FormModel = {};
|
|
|
- }, []);
|
|
|
-
|
|
|
- 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);
|
|
|
- }}
|
|
|
- />
|
|
|
- <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);
|
|
|
- };
|
|
|
+ form.setFieldsValue({
|
|
|
+ ...props.data,
|
|
|
+ });
|
|
|
+ }, [props.data]);
|
|
|
|
|
|
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>
|
|
|
+ <Modal
|
|
|
+ title={props.data?.id ? '编辑' : '新增'}
|
|
|
+ maskClosable={false}
|
|
|
+ visible
|
|
|
+ onCancel={() => {
|
|
|
+ props.close();
|
|
|
+ }}
|
|
|
+ onOk={async () => {
|
|
|
+ const values = await form.validateFields();
|
|
|
+ console.log(values);
|
|
|
+ }}
|
|
|
+ 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>
|
|
|
);
|
|
|
};
|