| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- import React, { useState } from 'react';
- import { StatusColorEnum } from '@/components/BadgeStatus';
- import { Ellipsis, TableCard } from '@/components';
- import '@/style/common.less';
- import '../../index.less';
- import styles from './index.less';
- import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
- import { CheckOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
- import classNames from 'classnames';
- import { ActionsType, BranchesThen } from '@/pages/rule-engine/Scene/typings';
- const imageMap = new Map();
- imageMap.set('timer', require('/public/images/scene/scene-timer.png'));
- imageMap.set('manual', require('/public/images/scene/scene-hand.png'));
- imageMap.set('device', require('/public/images/scene/scene-device.png'));
- const iconMap = new Map();
- iconMap.set('timer', require('/public/images/scene/trigger-type-icon/timing.png'));
- iconMap.set('manual', require('/public/images/scene/trigger-type-icon/manual.png'));
- iconMap.set('device', require('/public/images/scene/trigger-type-icon/device.png'));
- // @ts-ignore
- export interface SceneCardProps extends SceneItem {
- detail?: React.ReactNode;
- tools?: React.ReactNode[];
- avatarSize?: number;
- className?: string;
- onUnBind?: (e: any) => void;
- showBindBtn?: boolean;
- cardType?: 'bind' | 'unbind';
- showTool?: boolean;
- onClick?: () => void;
- }
- enum TriggerWayType {
- manual = '手动触发',
- timer = '定时触发',
- device = '设备触发',
- }
- enum UnitEnum {
- seconds = '秒',
- minutes = '分',
- hours = '小时',
- }
- const selectorRender = (obj: any) => {
- switch (obj?.selector) {
- case 'all':
- return (
- <span>
- 所有的<span className={styles['trigger-device']}>{obj?.productId}</span>
- </span>
- );
- case 'fixed':
- return (
- <span>
- 设备
- <span className={styles['trigger-device']}>
- {(obj?.selectorValues || '').map((item: any) => item?.name).join(',')}
- </span>
- </span>
- );
- case 'org':
- return (
- <span>
- 部门
- <span className={styles['trigger-device']}>
- {(obj?.selectorValues || '').map((item: any) => item?.name).join(',')}
- </span>
- </span>
- );
- default:
- return '';
- }
- };
- const timerRender = (timer: any) => {
- if (timer?.trigger && timer?.mod) {
- const trigger = timer?.trigger;
- const mod = timer?.mod;
- const str: string = trigger === 'week' ? '星期' : trigger === 'month' ? '月' : timer?.cron;
- if (mod === 'once') {
- return `,每${str}${timer.when.join('/')},${timer?.once.time}执行一次`;
- } else {
- return `每${str}${timer.when.join('/')},${timer?.period?.from}-${timer?.period.to},每${
- timer?.period.every
- }${UnitEnum[timer?.period?.unit]}执行一次`;
- }
- }
- return '';
- };
- const operatorRender = (operation: any) => {
- switch (operation?.operator) {
- case 'online':
- return '上线';
- case 'offline':
- return '离线';
- case 'reportEvent':
- return `上报事件${operation?.options?.eventName}`;
- case 'reportProperty':
- return `上报属性${(operation?.options?.propertiesName || []).join(',')}`;
- case 'readProperty':
- return `读取属性${(operation?.options?.propertiesName || []).join(',')}`;
- case 'writeProperty':
- return `修改属性${(operation?.options?.propertiesName || []).join(',')}`;
- case 'invokeFunction':
- return `调用功能${operation?.options?.functionName}`;
- default:
- return '';
- }
- };
- const notifyRender = (data: ActionsType | undefined) => {
- switch (data?.notify?.notifyType) {
- case 'dingTalk':
- return (
- <div>
- 向<span>{data?.options?.notifierName || data?.notify?.notifierId}</span>
- 通过<span className={styles['notify-img-highlight']}>钉钉</span>发送
- <span className={styles['notify-text-highlight']}>
- {data?.options?.templateName || data?.notify?.templateId}
- </span>
- </div>
- );
- case 'weixin':
- return (
- <div>
- 向<span className={styles['notify-text-highlight']}>{data?.options?.sendTo || ''}</span>
- <span className={styles['notify-text-highlight']}>{data?.options?.orgName || ''}</span>
- <span className={styles['notify-text-highlight']}>{data?.options?.tagName || ''}</span>
- 通过<span className={styles['notify-img-highlight']}>微信</span>发送
- <span className={styles['notify-text-highlight']}>
- {data?.options?.templateName || data?.notify?.templateId}
- </span>
- </div>
- );
- case 'email':
- return (
- <div>
- 向<span className={styles['notify-text-highlight']}>{data?.options?.sendTo || ''}</span>
- 通过<span className={styles['notify-img-highlight']}>邮件</span>发送
- <span className={styles['notify-text-highlight']}>
- {data?.options?.templateName || data?.notify?.templateId}
- </span>
- </div>
- );
- case 'voice':
- return (
- <div>
- 向
- <span className={styles['notify-text-highlight']}>
- {data?.options?.calledNumber || ''}
- </span>
- 通过<span className={styles['notify-img-highlight']}>语音</span>发送
- <span className={styles['notify-text-highlight']}>
- {data?.options?.templateName || data?.notify?.templateId}
- </span>
- </div>
- );
- case 'sms':
- return (
- <div>
- 向<span className={'notify-text-highlight'}>{data?.options?.sendTo || ''}</span>
- 通过<span className={'notify-img-highlight'}>短信</span>发送
- <span className={'notify-text-highlight'}>
- {data?.options?.templateName || data?.notify?.templateId}
- </span>
- </div>
- );
- case 'webhook':
- return (
- <div>
- 通过<span className={'notify-img-highlight'}>webhook</span>发送
- <span>{data?.options?.templateName || data?.notify?.templateId}</span>
- </div>
- );
- default:
- return null;
- }
- };
- const deviceRender = (data: ActionsType | undefined) => {
- console.log(data);
- switch (data?.device?.selector) {
- case 'relation':
- return (
- <div>
- {data?.options?.type || ''}
- 与【触发设备】具有相同【关系名称】的【产品名称】设备的【属性/功能】
- </div>
- );
- case 'tag': //`【读取/设置/执行】【标签名称】为【标签值】【并且/或者】【标签名称】为【标签值】的【产品名称】【属性/功能】`
- return (
- <div>
- {data?.options?.type || ''}
- <span className={styles['notify-text-highlight']}>{data?.options?.deviceName || ''}</span>
- 为
- <span className={styles['notify-text-highlight']}>
- {data?.options?.productName || ''}
- </span>
- <span className={styles['notify-text-highlight']}>
- {data?.options?.propertyName || data?.options?.functionName || ''}
- </span>
- </div>
- );
- default:
- return (
- <div>
- {data?.options?.type || ''}
- <span className={styles['notify-text-highlight']}>{data?.options?.deviceName || ''}</span>
- <span className={styles['notify-text-highlight']}>
- {data?.options?.propertyName || data?.options?.functionName || ''}
- </span>
- </div>
- );
- }
- };
- const actionRender = (action: ActionsType, index: number) => {
- switch (action?.executor) {
- case 'notify':
- return (
- <div
- className={styles['card-item-content-action-item-right-item']}
- key={action?.key || index}
- >
- <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
- {notifyRender(action)}
- </div>
- </div>
- );
- case 'delay':
- return (
- <div
- className={styles['card-item-content-action-item-right-item']}
- key={action?.key || index}
- >
- <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
- <span style={{ fontWeight: 'bold' }}>
- {action?.delay?.time}
- {UnitEnum[action?.delay?.unit || '']}
- </span>
- 后执行后续动作
- </div>
- </div>
- );
- case 'device':
- return (
- <div
- className={styles['card-item-content-action-item-right-item']}
- key={action?.key || index}
- >
- <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
- {deviceRender(action)}
- </div>
- </div>
- );
- case 'alarm':
- return (
- <div
- className={styles['card-item-content-action-item-right-item']}
- key={action?.key || index}
- >
- <div className={classNames(styles['trigger-contents'], 'ellipsis')}>
- 满足条件后将触发关联此场景的告警
- </div>
- </div>
- );
- default:
- return null;
- }
- };
- const conditionsRender = (when: any[], index: number) => {
- if (when.length) {
- return when[index];
- }
- return '';
- };
- const branchesActionRender = (actions: any[]) => {
- if (actions && actions?.length) {
- const list: any[] = [];
- actions.map((item, index) => {
- list.push(actionRender(item, index));
- });
- return list.map((item, index) => (
- <div className={styles['right-item-right-item-contents-item']}>
- <div style={{ minWidth: 40 }}>动作{index + 1}</div>
- {item}
- </div>
- ));
- }
- return '';
- };
- const ContentRender = (data: SceneCardProps) => {
- const [visible, setVisible] = useState<boolean>(false);
- const type = data.triggerType;
- if (
- type === 'device' &&
- (data.branches || [])?.length &&
- Object.keys(data?.trigger?.device || {}).length
- ) {
- const obj = data.trigger.device;
- const operation: any = data.trigger.device?.operation;
- return (
- <div className={styles['card-item-content-box']}>
- <div className={styles['card-item-content-trigger']}>
- {selectorRender(obj)}
- {operation ? (
- <span>
- {timerRender(operation?.timer)}
- {operatorRender(operation)}
- </span>
- ) : (
- ''
- )}
- </div>
- <div className={styles['card-item-content-action']}>
- {(visible ? data.branches || [] : (data?.branches || []).slice(0, 2)).map(
- (item: any, index) => {
- return (
- <div className={styles['card-item-content-action-item']} key={item?.key || index}>
- <div className={styles['card-item-content-action-item-left']}>
- {index === 0 ? '当' : '否则'}
- </div>
- <div className={styles['card-item-content-action-item-right']}>
- <div className={styles['card-item-content-action-item-right-item']}>
- <div className={styles['right-item-left']}>
- <div className={classNames(styles['trigger-conditions'], 'ellipsis')}>
- {conditionsRender(data.options?.terms || [], index)}
- </div>
- {item.shakeLimit?.enabled && (
- <div className={classNames(styles['trigger-shake'], 'ellipsis')}>
- ({item.shakeLimit?.time}秒内发生{item.shakeLimit?.threshold}
- 次以上时执行一次)
- </div>
- )}
- </div>
- <div className={styles['right-item-right']}>
- {(item?.then || []).map((i: BranchesThen, _index: number) => (
- <div key={i?.key || _index} className={styles['right-item-right-item']}>
- <div className={styles['trigger-ways']}>
- {i.parallel ? '并行执行' : '串行执行'}
- </div>
- <div className={classNames(styles['right-item-right-item-contents'])}>
- {branchesActionRender(Array.isArray(i?.actions) ? i?.actions : [])}
- </div>
- </div>
- ))}
- </div>
- </div>
- </div>
- </div>
- );
- },
- )}
- {(data?.branches || []).length > 1 && (
- <div
- className={styles['trigger-actions-more']}
- onClick={(e) => {
- e.stopPropagation();
- setVisible(!visible);
- }}
- >
- 展开更多{!visible ? <DownOutlined /> : <UpOutlined />}
- </div>
- )}
- </div>
- </div>
- );
- } else if (type !== 'device' && data.actions?.length) {
- return (
- <div className={styles['card-item-content-box']}>
- <div className={styles['card-item-content-action']}>
- {data.actions?.length && (
- <div className={styles['card-item-content-action-item']}>
- <div className={styles['card-item-content-action-item-left']}>执行</div>
- <div className={styles['card-item-content-action-item-right']}>
- {(data?.actions || []).map((item: any, index) => {
- return actionRender(item, index);
- })}
- </div>
- </div>
- )}
- </div>
- </div>
- );
- } else {
- return <div className={styles['card-item-content-box-empty']}>未配置规则</div>;
- }
- };
- export const ExtraSceneCard = (props: SceneCardProps) => {
- return (
- <TableCard
- status={props.state.value}
- statusText={props.state.text}
- statusNames={{
- started: StatusColorEnum.success,
- disable: StatusColorEnum.error,
- notActive: StatusColorEnum.warning,
- }}
- showTool={props.showTool}
- showMask={false}
- actions={props.tools}
- onClick={props.onClick}
- className={props.className}
- contentClassName={styles['content-class']}
- >
- <div className={'pro-table-card-item context-access'}>
- <div className={'card-item-avatar'}>
- <img width={88} height={88} src={imageMap.get(props.triggerType)} alt={''} />
- </div>
- <div className={'card-item-body'}>
- <div className={'card-item-header'}>
- <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
- <Ellipsis
- title={props.name}
- style={{ fontSize: 18, opacity: 0.85, color: '#000', fontWeight: 'bold' }}
- />
- </div>
- <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
- <Ellipsis
- title={props.description}
- style={{ color: 'rgba(0, 0, 0, 0.65)', margin: '3px 0 0 10px' }}
- />
- </div>
- </div>
- <ContentRender {...props} />
- </div>
- </div>
- <div className={styles['card-item-trigger-type']}>
- <div className={styles['card-item-trigger-type-text']}>
- <img height={16} src={iconMap.get(props.triggerType)} style={{ marginRight: 8 }} />
- {TriggerWayType[props.triggerType]}
- </div>
- </div>
- <div className={'checked-icon'}>
- <div>
- <CheckOutlined />
- </div>
- </div>
- </TableCard>
- );
- };
- export default (props: SceneCardProps) => {
- return (
- <TableCard
- showMask={false}
- detail={props.detail}
- showTool={props.showTool}
- actions={props.tools}
- status={props.state.value}
- statusText={props.state.text}
- statusNames={{
- started: StatusColorEnum.success,
- disable: StatusColorEnum.error,
- notActive: StatusColorEnum.warning,
- }}
- contentClassName={styles['content-class']}
- >
- <div className={'pro-table-card-item'} onClick={props.onClick}>
- <div className={'card-item-avatar'}>
- <img width={88} height={88} src={imageMap.get(props.triggerType)} alt={''} />
- </div>
- <div className={'card-item-body'}>
- <div className={'card-item-header'}>
- <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
- <Ellipsis
- title={props.name}
- style={{ fontSize: 18, opacity: 0.85, color: '#000', fontWeight: 'bold' }}
- />
- </div>
- <div className={'card-item-header-item'} style={{ maxWidth: '50%' }}>
- <Ellipsis
- title={props.description}
- style={{ color: 'rgba(0, 0, 0, 0.65)', margin: '3px 0 0 10px' }}
- />
- </div>
- </div>
- <ContentRender {...props} />
- </div>
- </div>
- <div className={styles['card-item-trigger-type']}>
- <div className={styles['card-item-trigger-type-text']}>
- <img height={16} src={iconMap.get(props.triggerType)} style={{ marginRight: 8 }} />
- {TriggerWayType[props.triggerType]}
- </div>
- </div>
- </TableCard>
- );
- };
|