| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- import { PageContainer } from '@ant-design/pro-layout';
- import {
- Button,
- Card,
- Form,
- Input,
- InputNumber,
- message,
- Radio,
- Space,
- Switch,
- Tooltip,
- } from 'antd';
- import { useIntl, useLocation } from 'umi';
- import { 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/device';
- import { service } from '../index';
- import './index.less';
- import { model } from '@formily/reactive';
- import type { FormModelType } from '@/pages/rule-engine/Scene/typings';
- type ShakeLimitType = {
- enabled: boolean;
- groupType?: string;
- time?: number;
- threshold?: number;
- alarmFirst?: boolean;
- };
- const DefaultShakeLimit = {
- enabled: false,
- alarmFirst: true,
- };
- export let FormModel = model<FormModelType>({});
- const CronRegEx = new RegExp(
- '(((^([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|^([0-9]|[0-5][0-9]) |^(\\* ))((([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|([0-9]|[0-5][0-9]) |(\\* ))((([0-9]|[01][0-9]|2[0-3])(\\,|\\-|\\/){1}([0-9]|[01][0-9]|2[0-3]) )|([0-9]|[01][0-9]|2[0-3]) |(\\* ))((([0-9]|[0-2][0-9]|3[01])(\\,|\\-|\\/){1}([0-9]|[0-2][0-9]|3[01]) )|(([0-9]|[0-2][0-9]|3[01]) )|(\\? )|(\\* )|(([1-9]|[0-2][0-9]|3[01])L )|([1-7]W )|(LW )|([1-7]\\#[1-4] ))((([1-9]|0[1-9]|1[0-2])(\\,|\\-|\\/){1}([1-9]|0[1-9]|1[0-2]) )|([1-9]|0[1-9]|1[0-2]) |(\\* ))(([1-7](\\,|\\-|\\/){1}[1-7])|([1-7])|(\\?)|(\\*)|(([1-7]L)|([1-7]\\#[1-4]))))|(((^([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|^([0-9]|[0-5][0-9]) |^(\\* ))((([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|([0-9]|[0-5][0-9]) |(\\* ))((([0-9]|[01][0-9]|2[0-3])(\\,|\\-|\\/){1}([0-9]|[01][0-9]|2[0-3]) )|([0-9]|[01][0-9]|2[0-3]) |(\\* ))((([0-9]|[0-2][0-9]|3[01])(\\,|\\-|\\/){1}([0-9]|[0-2][0-9]|3[01]) )|(([0-9]|[0-2][0-9]|3[01]) )|(\\? )|(\\* )|(([1-9]|[0-2][0-9]|3[01])L )|([1-7]W )|(LW )|([1-7]\\#[1-4] ))((([1-9]|0[1-9]|1[0-2])(\\,|\\-|\\/){1}([1-9]|0[1-9]|1[0-2]) )|([1-9]|0[1-9]|1[0-2]) |(\\* ))(([1-7](\\,|\\-|\\/){1}[1-7] )|([1-7] )|(\\? )|(\\* )|(([1-7]L )|([1-7]\\#[1-4]) ))((19[789][0-9]|20[0-9][0-9])\\-(19[789][0-9]|20[0-9][0-9])))',
- );
- export default () => {
- const location = useLocation();
- const [form] = Form.useForm();
- const intl = useIntl();
- const triggerRef = useRef<any>();
- const { getOtherPermission } = PermissionButton.usePermission('rule-engine/Scene');
- const [triggerType, setTriggerType] = useState('');
- // const [triggerValue, setTriggerValue] = useState<any>();
- const [loading, setLoading] = useState(false);
- const [parallel, setParallel] = useState(true); // 是否并行
- const [shakeLimit, setShakeLimit] = useState<ShakeLimitType>(DefaultShakeLimit);
- const [requestParams, setRequestParams] = useState<any>(undefined);
- const [actionsData, setActionsData] = useState<any[]>([]);
- const [isEdit, setIsEdit] = useState(false);
- const getDetail = async (id: string) => {
- const resp = await service.detail(id);
- if (resp.status === 200 && resp.result) {
- setIsEdit(true);
- const _data: any = resp.result;
- FormModel = _data;
- form.setFieldsValue(_data);
- setParallel(_data.parallel);
- setShakeLimit(_data.shakeLimit || DefaultShakeLimit);
- if (_data.trigger?.device?.selectorValues) {
- setRequestParams({ trigger: _data.trigger });
- }
- if (_data.actions) {
- setActionsData(_data.actions);
- }
- }
- };
- useEffect(() => {
- const params = new URLSearchParams(location.search);
- const id = params.get('id');
- if (id) {
- getDetail(id);
- }
- }, [location]);
- const saveData = async () => {
- const formData = await form.validateFields();
- let triggerData = undefined;
- // 获取触发条件数据
- if (triggerRef.current) {
- triggerData = await triggerRef.current.getTriggerForm();
- console.log(JSON.stringify(triggerData), 'trigger');
- if (!triggerData) {
- return;
- }
- formData.terms = triggerData.trigger;
- }
- console.log(formData);
- if (formData) {
- setLoading(true);
- const resp = formData.id ? await service.updateScene(formData) : await service.save(formData);
- setLoading(false);
- if (resp.status === 200) {
- message.success('操作成功');
- history.back();
- } else {
- message.error(resp.message);
- }
- }
- };
- const AntiShake = (
- <Space>
- <TitleComponent data={'触发条件'} style={{ margin: 0 }} />
- <Switch
- checked={shakeLimit.enabled}
- checkedChildren="开启防抖"
- unCheckedChildren="关闭防抖"
- onChange={(e) => {
- setShakeLimit({
- ...shakeLimit,
- enabled: e,
- });
- form.setFieldsValue({ shakeLimit });
- }}
- />
- {shakeLimit.enabled && (
- <>
- <InputNumber
- value={shakeLimit.time}
- onChange={(e: number) => {
- setShakeLimit({
- ...shakeLimit,
- time: e,
- });
- form.setFieldsValue({ shakeLimit });
- }}
- />
- <span> 秒内发生 </span>
- <InputNumber
- value={shakeLimit.threshold}
- onChange={(e: number) => {
- setShakeLimit({
- ...shakeLimit,
- threshold: e,
- });
- form.setFieldsValue({ shakeLimit });
- }}
- />
- <span>次及以上时,处理</span>
- <Radio.Group
- value={shakeLimit.alarmFirst}
- options={[
- { label: '第一次', value: true },
- { label: '最后一次', value: false },
- ]}
- optionType="button"
- onChange={(e) => {
- console.log(e);
- setShakeLimit({
- ...shakeLimit,
- alarmFirst: e.target.value,
- });
- form.setFieldsValue({ shakeLimit });
- }}
- ></Radio.Group>
- </>
- )}
- </Space>
- );
- return (
- <PageContainer>
- <Card>
- <Form
- form={form}
- colon={false}
- layout={'vertical'}
- preserve={false}
- className={'scene-save'}
- onValuesChange={(changeValue, allValues) => {
- if (changeValue.trigger) {
- setRequestParams({ trigger: allValues.trigger });
- }
- if (allValues.actions) {
- 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个字符',
- }),
- },
- ]}
- required
- >
- <Input placeholder={'请输入名称'} />
- </Form.Item>
- <Form.Item label={<TitleComponent data={'触发方式'} style={{ margin: 0 }} />} required>
- <Form.Item
- name={['trigger', 'type']}
- rules={[{ required: true, message: '请选择触发方式' }]}
- >
- <TriggerWay onSelect={setTriggerType} disabled={isEdit} />
- </Form.Item>
- {triggerType === TriggerWayType.timing && (
- <Form.Item
- name={['trigger', 'timer']}
- rules={[
- {
- validator: async (_: any, value: any) => {
- if (value) {
- if (value.trigger === 'cron') {
- if (!value.cron) {
- return Promise.reject(new Error('请输入cron表达式'));
- } else if (value.cron.length > 64) {
- return Promise.reject(new Error('最多可输入64个字符'));
- } else if (!CronRegEx.test(value.cron)) {
- return Promise.reject(new Error('请输入正确的cron表达式'));
- }
- } else {
- if (!value.when.length) {
- return Promise.reject(new Error('请选择时间'));
- }
- if (value.period) {
- if (!value.period.from || !value.period.to) {
- return Promise.reject(new Error('请选择时间周期'));
- }
- if (!value.period.every) {
- return Promise.reject(new Error('请输入周期频率'));
- }
- } else if (value.once) {
- if (!value.once.time) {
- return Promise.reject(new Error('请选择时间周期'));
- }
- }
- }
- }
- return Promise.resolve();
- },
- },
- ]}
- initialValue={{
- trigger: 'week',
- mod: 'period',
- when: [],
- period: {
- unit: 'seconds',
- },
- }}
- >
- <TimingTrigger className={'trigger-type-content'} />
- </Form.Item>
- )}
- {triggerType === TriggerWayType.device && (
- <Form.Item name={['trigger', 'device']}>
- <TriggerDevice className={'trigger-type-content'} />
- </Form.Item>
- )}
- </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>
- <span style={{ color: 'red', margin: '0 4px' }}>*</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"
- rules={[
- {
- validator: async (_: any, value: any) => {
- if (!value) {
- return Promise.reject(new Error('请添加执行动作'));
- }
- return Promise.resolve();
- },
- },
- ]}
- >
- {(fields, { add, remove }, { errors }) => (
- <>
- <div className={'scene-actions'}>
- {fields.map(({ key, name, ...restField }) => (
- <ActionItems
- key={key}
- form={form}
- restField={restField}
- name={name}
- triggerType={triggerType}
- onRemove={() => remove(name)}
- actionItemData={actionsData.length && actionsData[name]}
- />
- ))}
- <Form.Item noStyle>
- <Button type="dashed" onClick={() => add()} block 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={'shakeLimit'} initialValue={DefaultShakeLimit}>
- <Input />
- </Form.Item>
- ) : null}
- <Form.Item hidden name={'id'}>
- <Input />
- </Form.Item>
- </Form>
- <PermissionButton
- isPermission={getOtherPermission(['add', 'update'])}
- onClick={saveData}
- type={'primary'}
- loading={loading}
- >
- 保存
- </PermissionButton>
- {/*<Button*/}
- {/* onClick={() => {*/}
- {/* setTriggerValue({*/}
- {/* trigger: [*/}
- {/* {*/}
- {/* terms: [*/}
- {/* {*/}
- {/* column: '_now',*/}
- {/* termType: 'eq',*/}
- {/* source: 'manual',*/}
- {/* value: '2022-04-21 14:26:04',*/}
- {/* },*/}
- {/* ],*/}
- {/* },*/}
- {/* {*/}
- {/* terms: [*/}
- {/* {*/}
- {/* column: 'properties.test-zhibioa.recent',*/}
- {/* termType: 'lte',*/}
- {/* source: 'metrics',*/}
- {/* value: '123',*/}
- {/* },*/}
- {/* ],*/}
- {/* },*/}
- {/* ],*/}
- {/* });*/}
- {/* }}*/}
- {/*>*/}
- {/* 设置*/}
- {/*</Button>*/}
- </Card>
- </PageContainer>
- );
- };
|