Commit 84d46bf1 authored by 邓晓峰's avatar 邓晓峰

feat: add notifier

parent 0596fdd5
Pipeline #47274 skipped with stages
...@@ -110,7 +110,7 @@ ...@@ -110,7 +110,7 @@
"@wisdom-map/Map": "^1.0.12-17", "@wisdom-map/Map": "^1.0.12-17",
"@wisdom-map/arcgismap": "^1.0.79-17", "@wisdom-map/arcgismap": "^1.0.79-17",
"@wisdom-map/util": "^1.0.27-0", "@wisdom-map/util": "^1.0.27-0",
"@wisdom-utils/components": "0.0.29", "@wisdom-utils/components": "0.0.31",
"@wisdom-utils/runtime": "0.0.15", "@wisdom-utils/runtime": "0.0.15",
"@wisdom-utils/utils": "0.0.77", "@wisdom-utils/utils": "0.0.77",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
......
...@@ -19,7 +19,6 @@ export default { ...@@ -19,7 +19,6 @@ export default {
const options = { const options = {
newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors` newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
changeUrl(cssUrl) { changeUrl(cssUrl) {
console.log("cssUrl", cssUrl);
return `/${pkg.name.toLowerCase()}/${cssUrl}`; // while router is not `hash` mode, it needs absolute path return `/${pkg.name.toLowerCase()}/${cssUrl}`; // while router is not `hash` mode, it needs absolute path
}, },
}; };
......
...@@ -5,9 +5,9 @@ import { connect } from 'react-redux'; ...@@ -5,9 +5,9 @@ import { connect } from 'react-redux';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { useHistory } from '@wisdom-utils/runtime'; import { useHistory } from '@wisdom-utils/runtime';
import { HeaderSearch } from '@wisdom-utils/components';
import { useIntl } from '@/locales/localeExports'; import { useIntl } from '@/locales/localeExports';
import { actionCreators } from '../../containers/App/store'; import { actionCreators } from '../../containers/App/store';
import { HeaderSearch } from '@wisdom-utils/components';
import Avatar from './AvatarDropdown'; import Avatar from './AvatarDropdown';
import styles from './index.less'; import styles from './index.less';
import NoticeIconView from './NoticeIconView'; import NoticeIconView from './NoticeIconView';
......
...@@ -2,14 +2,21 @@ import React, { Component } from 'react'; ...@@ -2,14 +2,21 @@ import React, { Component } from 'react';
import { Button, Form, Input, Modal } from 'antd'; import { Button, Form, Input, Modal } from 'antd';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Notifier, NoticeIcon } from '@wisdom-utils/components';
import {
ERR_OK,
MESSAGE_TYPE,
NEW_MESSAGE,
} from '@wisdom-utils/components/lib/AppLayout/notifier/constants';
import {
findPathByWidget,
isJSON,
} from '@wisdom-utils/components/lib/AppLayout/helpers';
import { FormattedMessage } from '@/locales/localeExports'; import { FormattedMessage } from '@/locales/localeExports';
import service from '../../api/service/notification'; import service from '../../api/service/notification';
import { actionCreators } from '../../containers/App/store'; import { actionCreators } from '../../containers/App/store';
import isProd from '../../utils/env'; import isProd from '../../utils/env';
import { findPathByWidget, isJSON } from '../../utils/utils'; // import NoticeIcon from '../NoticeIcon';
import NoticeIcon from '../NoticeIcon';
import Notifier from '../Notifier';
import { ERR_OK, MESSAGE_TYPE, NEW_MESSAGE } from '../Notifier/constants';
import styles from './index.less'; import styles from './index.less';
const { TextArea } = Input; const { TextArea } = Input;
......
...@@ -4,10 +4,10 @@ import { message } from 'antd'; ...@@ -4,10 +4,10 @@ import { message } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import { HeaderSearch } from '@wisdom-utils/components';
import { useIntl } from '@/locales/localeExports'; import { useIntl } from '@/locales/localeExports';
import { actionCreators } from '../../containers/App/store'; import { actionCreators } from '../../containers/App/store';
import { HeaderSearch } from '@wisdom-utils/components';
import Avatar from './AvatarDropdown'; import Avatar from './AvatarDropdown';
import styles from './index.less'; import styles from './index.less';
import NoticeIconView from './NoticeIconView'; import NoticeIconView from './NoticeIconView';
......
import React from 'react';
import {
List,
Spin,
} from 'antd';
import classNames from 'classnames';
import styles from './NoticeList.less';
import Alarm from './Templates/Alarm';
import Case from './Templates/Case';
import Notice from './Templates/Notice';
import Unknown from './Templates/Unknown';
const Empty = ({ emptyText }) => (
<div className={styles.notFound}>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
alt="not found"
/>
<div>{emptyText}</div>
</div>
);
class NoticeList extends React.Component {
constructor(props) {
super(props);
this.emptyText = props.emptyText;
this.confirmRead = props.confirmRead;
this.handlerSysDetail = props.handlerSysDetail;
this.loadMore = props.loadMore;
this.hasMore = props.hasMore;
this.container = React.createRef();
this.state = {
isLoading: false,
};
this.mounted = false;
this.handleScrollCallback = this.throttle(
this.handleScroll.bind(this),
30,
).bind(this);
}
componentDidMount() {
this.mounted = true;
if (this.container.current) {
this.container.current.addEventListener(
'scroll',
this.handleScrollCallback,
);
}
}
componentWillUnmount() {
this.mounted = false;
}
throttle(fn, wait) {
/* eslint-disable */
let pre = Date.now();
return function() {
const context = this;
// eslint-disable-next-line prefer-rest-params
const args = arguments;
const now = Date.now();
if (now - pre >= wait) {
fn.apply(context, args);
pre = Date.now();
}
};
}
handleScroll(e) {
e.stopPropagation();
if (!this.mounted) return;
if (!this.container.current) return;
const { current } = this.container;
if (
current.scrollHeight - current.scrollTop - current.offsetHeight <=
100
) {
this.handleLoadMore();
}
}
handleLoadMore() {
if (this.state.isLoading) return;
if (!this.hasMore()) return;
this.setState(
{
isLoading: true,
},
() => {
if (!this.loadMore) return;
this.loadMore().then(data => {
if (!this.mounted) return;
this.setState({
isLoading: false,
});
});
},
);
}
render() {
if (!this.props.data || this.props.data.length === 0) {
return <Empty emptyText={this.emptyText} />;
}
return (
<div className={styles.container} ref={this.container}>
<List
className={styles.list}
dataSource={this.props.data}
renderItem={(item, i) => {
const itemCls = classNames(styles.item, {
[styles.read]: item.read,
});
let messageTemplate = <></>;
switch (item.infoType) {
case 'scadaType':
messageTemplate = (
<Alarm message={item} confirmRead={this.confirmRead} config={this.props.config} />
);
break;
case 'caseType':
messageTemplate = (
<Case message={item} confirmRead={this.confirmRead} />
);
break;
case 'sysType':
messageTemplate = (
<Notice message={item} confirmRead={this.confirmRead} config={this.props.config} handlerSysDetail={this.handlerSysDetail}/>
);
break;
default:
messageTemplate = (
<Unknown message={item} confirmRead={this.confirmRead} />
);
break;
}
return (
<List.Item className={itemCls} key={item.id || i}>
{messageTemplate}
</List.Item>
);
}}
/>
<div className={styles.bottomBar}>
{this.state.isLoading ? (
<>
<Spin /> 加载中...
</>
) : this.hasMore() ? (
<span>下拉加载更多</span>
) : (
<span style={{fontSize: '12px', color: 'rgba(0, 0, 0, 0.6)'}}>已经没有更多消息了</span>
)}
</div>
</div>
);
}
}
export default NoticeList;
@import '~antd/es/style/themes/default.less';
.container {
max-height: 400px;
overflow: auto;
&::-webkit-scrollbar {
width: 5px;
height: 5px;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 10px;
background: rgba(0, 0, 0, 0.1);
-webkit-box-shadow: inset 0 0 6px rgba(177, 177, 177, 0.5);
box-shadow: inset 0 0 6px rgba(177, 177, 177, 0.5);
}
&::-webkit-scrollbar-track {
/*滚动条里面轨道*/
-webkit-box-shadow: inset 0 0 6px rgba(193, 193, 193, 0.3);
box-shadow: inset 0 0 6px rgba(193, 193, 193, 0.3);
border-radius: 10px;
}
.list {
.item {
overflow: hidden;
padding: 0px;
cursor: pointer;
.meta {
width: 100%;
}
&:hover {
background-color: @primary-1;
}
&:last-child {
border-bottom: 0;
}
}
}
.bottomBar {
height: 46px;
color: @text-color;
line-height: 46px;
text-align: center;
transition: all 0.3s;
}
}
.notFound {
padding: 73px 0 88px;
color: @text-color-secondary;
text-align: center;
border-radius: 0 !important;
img {
display: inline-block;
height: 76px;
margin-bottom: 16px;
}
}
.notFound {
padding: 73px 0 88px;
color: @text-color-secondary;
text-align: center;
border-radius: 0 !important;
img {
display: inline-block;
height: 76px;
margin-bottom: 16px;
}
}
import React from 'react';
import _ from 'lodash';
import classNames from 'classnames';
import { findPathByWidget, isJSON } from '../../../../utils/utils';
import commonStyles from '../common.less';
import styles from './index.less';
/* eslint-disable */
export class AlarmContent {
constructor({
alarmType,
deviceCode,
alarmDevice,
alarmContent,
alarmValue,
alarmThreshold,
time,
} = content) {
this.alarmType = alarmType;
this.deviceCode = deviceCode;
this.alarmDevice = alarmDevice;
this.alarmContent = alarmContent;
this.alarmValue = alarmValue;
this.alarmThreshold = alarmThreshold;
this.time = time;
}
}
const Alarm = ({ message, confirmRead, config }) => {
const alarmContent = (config.mqtt_mess.MessageLevel === "2.0" && isJSON(message.infoContent)) ? JSON.parse(message.infoContent): message.infoContent;
const goPath = item => {
confirmRead(false, [message.id]);
const widgetID = 'widget_city_综合运营_管网监控_实时监控_报警监控';
const webPath = 'product/scada/AlertMonitoring/AlertMonitoring';
const widget = findPathByWidget("productex/water/IOTMonitor/RealTimeAlarm/RealTimeAlarm", config.widgets, "", "url");
window.share.event.emit('listenerMointer', {
widgetId: widgetID,
label: '实时报警',
url: widget.url || webPath,
});
};
const alarmValue = alarmContent && alarmContent.alarmValue && alarmContent.alarmValue.split(' ');
let alarmTitle = alarmContent && alarmContent.title && alarmContent.title.split(' ') || alarmContent.alarmDevice;
alarmTitle = Array.isArray(alarmTitle) ? alarmTitle[1]: alarmTitle;
return (
<div
className={classNames(styles.scada, commonStyles.messageContainer)}
title="点击查看详情"
onClick={() => goPath(message)}
>
<div className={commonStyles.title}>
<span>消息</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={e => {
e.stopPropagation();
confirmRead(false, [message.id]);
}}
alt=""
src="https://panda-water.cn/Web4/assets/images/message/%E5%8B%BE%E6%B5%85.png"
/>
</div>
<div className={commonStyles.content}>
<p>
<i>{alarmContent.alarmType}</i>
{alarmTitle}
</p>
<p style={{color: '#888'}}>{alarmContent.alarmContent}</p>
<p style={{color: '#888'}} >
{
alarmContent && _.isString(alarmContent.content) && alarmContent.content.split(",")[1]
}
</p>
<p className={commonStyles.messageTime}>{message.time}</p>
</div>
</div>
);
};
export default Alarm;
.scada {
background: url(https://panda-water.cn/Web4/assets/images/message/%E6%B6%88%E6%81%AF.png)
16px 10px no-repeat;
}
import React from 'react';
import classNames from 'classnames';
import commonStyles from '../common.less';
import styles from './index.less';
// "caseType":"待办工单","flowName":"维修处理流程","nodeName":"审核关单","content":"请迅速到现场处理","time":"2020-11-03 09:11:12"
export class CaseContent {
// eslint-disable-next-line no-use-before-define
constructor({ caseType, flowName, nodeName, content, time } = content) {
this.caseType = caseType;
this.flowName = flowName;
this.nodeName = nodeName;
this.content = content;
this.time = time;
}
}
const Case = ({ message, confirmRead }) => {
const caseContent = message.infoContent;
const goPath = item => {
confirmRead(false, [message.id]);
const messageType = item.messType;
let label = '';
let widgetID = '';
switch (messageType) {
case '任务派发':
label = '任务派发';
widgetID = 'widget_city_综合运营_二供管理_维修管理_工单办理';
break;
case '任务驳回':
label = '任务驳回';
widgetID = 'widget_city_综合运营_二供管理_维修管理_工单办理';
// eslint-disable-next-line no-underscore-dangle,no-case-declarations
let _tab;
// eslint-disable-next-line prefer-const
_tab = '任务驳回';
break;
default:
label = '工单办理';
widgetID = 'widget_city_综合运营_二供管理_维修管理_工单办理';
break;
}
const webPath =
item.webPath ||
'product/maintenance/CaseManage/CaseDoingBox/StardCaseDoingBoxView|isDelay=1';
window.share.event.emit('listenerMointer', {
widgetId: widgetID,
label,
url: webPath,
});
};
return (
<div
className={classNames(styles.case, commonStyles.messageContainer)}
title="点击查看详情"
onClick={() => goPath(message)}
>
<div className={commonStyles.title}>
<span>消息</span>
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={e => {
e.stopPropagation();
confirmRead(false, [message.id]);
}}
src="https://panda-water.cn/Web4/assets/images/message/%E5%8B%BE%E6%B5%85.png"
/>
</div>
<div className={commonStyles.content}>
<p>
<i>{caseContent.caseType}</i>
{caseContent.flowName}
</p>
<p>
{caseContent.nodeName} : {caseContent.content}
</p>
<p className={commonStyles.messageTime}>{message.time}</p>
</div>
</div>
);
};
export default Case;
.case {
background: url(https://panda-water.cn/Web4/assets/images/message/%E6%B6%88%E6%81%AF.png)
16px 10px no-repeat;
}
import React from 'react';
import classNames from 'classnames';
import { isJSON } from '../../../../utils/utils';
import commonStyles from '../common.less';
import styles from './index.less';
export class NoticeContent {
// eslint-disable-next-line no-undef
constructor({ noticeTitle, noticeType, noticeContent, time } = content) {
this.noticeTitle = noticeTitle;
this.noticeType = noticeType;
this.noticeContent = noticeContent;
this.time = time;
}
}
const Notice = ({ message, confirmRead, config, handlerSysDetail }) => {
// eslint-disable-next-line no-debugger
const noticeContent =
config.mqtt_mess.MessageLevel === '2.0' && isJSON(message.infoContent)
? JSON.parse(message.infoContent)
: message.infoContent;
const goPath = item => {
// eslint-disable-next-line no-debugger
// eslint-disable-next-line no-debugger
confirmRead(false, [message.id]);
handlerSysDetail && handlerSysDetail(message);
};
return (
<div
className={classNames(styles.notice, commonStyles.messageContainer)}
title="点击查看详情"
onClick={() => goPath(message)}
>
<div className={commonStyles.title}>
<span>公告</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={e => {
e.stopPropagation();
confirmRead(false, [message.id]);
}}
src="https://panda-water.cn/Web4/assets/images/message/%E5%8B%BE%E6%B5%85.png"
alt=""
/>
</div>
<div className={commonStyles.content}>
<p>
<i>{noticeContent.noticeType}</i>
{noticeContent.noticeTitle}
</p>
{/* eslint-disable-next-line react/no-danger */}
<p dangerouslySetInnerHTML={{ __html: noticeContent.noticeContent }} />
<p className={commonStyles.messageTime}>{message.time}</p>
</div>
</div>
);
};
export default Notice;
.notice {
background: url(https://panda-water.cn/Web4/assets/images/message/%E6%B6%88%E6%81%AF.png)
16px 10px no-repeat;
}
import React from 'react';
import classNames from 'classnames';
import commonStyles from '../common.less';
import styles from './index.less';
const Unknown = ({ message, confirmRead }) => {
let content = '';
try {
const type = typeof message.infoContent;
switch (type) {
case 'object':
content = JSON.stringify(message.infoContent);
break;
case 'string':
content = message.infoContent;
break;
default:
break;
}
} catch (e) {
// i
}
return (
<div
className={classNames(styles.unknown, commonStyles.messageContainer)}
title="点击查看详情"
>
<div className={commonStyles.title}>
<span>消息</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={e => {
e.stopPropagation();
confirmRead(false, [message.id]);
}}
src="https://panda-water.cn/Web4/assets/images/message/%E5%8B%BE%E6%B5%85.png"
alt=""
/>
</div>
<div className={commonStyles.content}>
<p>{content}</p>
<p className={commonStyles.messageTime}>{message.time}</p>
</div>
</div>
);
};
export default Unknown;
.unknown {
background: url(https://panda-water.cn/Web4/assets/images/message/%E6%B6%88%E6%81%AF.png)
16px 10px no-repeat;
}
.messageContainer {
padding-left: 50px;
padding-right: 20px;
padding-top: 8px;
width: 100%;
.title {
display: flex;
justify-content: space-between;
span {
font-size: 16px;
color: #666;
font-weight: normal;
text-shadow: none;
letter-spacing: 0;
}
.confirm {
margin-right: 20px;
width: 20px;
height: 16px;
}
}
.content {
margin-top: 10px;
p {
i {
font-size: 14px;
text-shadow: none;
color: #000;
font-style: normal;
color: #1ba6f9;
margin-right: 10px;
}
margin-bottom: 3px;
font-size: 12px;
}
.messageTime {
float: right;
margin-right: 30px;
}
}
}
import React from 'react';
import { Badge } from 'antd';
import classNames from 'classnames';
import { connect } from 'react-redux';
import useMergeValue from 'use-merge-value';
import Icon from '@ant-design/icons';
import { HeaderDropdown } from '@wisdom-utils/components';
import styles from './index.less';
import NoticeList from './NoticeList';
const messageSvg = () => (
<svg
version="1.1"
x="0px"
y="0px"
width="24px"
height="24px"
viewBox="0 0 24 24"
enableBackground="new 0 0 24 24"
space="preserve"
>
<path
fill="hsla(221, 100%, 95%, 0.7)"
d="M20.486,16.373l-1.721-2.246v-0.984v-0.352V9.924c0-1.919-0.664-3.698-1.871-5.007
c-0.712-0.776-1.57-1.349-2.551-1.705c-0.091-0.514-0.35-0.983-0.737-1.335c-0.879-0.791-2.334-0.791-3.21,0
c-0.394,0.354-0.653,0.823-0.741,1.336C8.676,3.568,7.817,4.14,7.105,4.917C5.899,6.229,5.234,8.008,5.234,9.923l0.005,4.194
l-1.708,2.234c-0.241,0.256-0.372,0.584-0.372,0.932v1.092c0,0.75,0.615,1.357,1.372,1.357H19.47c0.757,0,1.371-0.607,1.371-1.357
v-1.092C20.841,16.936,20.71,16.607,20.486,16.373z M4.899,17.996v1.016l-0.001-2.061l1.628-2.154
c0.227-0.244,0.353-0.561,0.353-0.893v-4.05c0-1.516,0.509-2.914,1.436-3.935c0.459-0.506,1.001-0.895,1.608-1.166
c1.276-0.565,2.883-0.565,4.155,0c0.609,0.273,1.15,0.667,1.608,1.168c0.925,1.021,1.437,2.417,1.437,3.934v2.762v0.338v0.949
c0,0.334,0.123,0.654,0.336,0.875l1.642,2.164l0.013,1.037L4.899,17.996z"
/>
<path
fill="hsla(221, 100%, 95%, 0.7)"
d="M13.685,20.236c-0.101,0.238-0.248,0.453-0.444,0.631c-0.677,0.617-1.799,0.615-2.473,0.002
c-0.194-0.18-0.344-0.396-0.446-0.633H8.895c0.146,0.627,0.474,1.199,0.955,1.639c0.588,0.543,1.354,0.841,2.158,0.841
c0.801,0,1.566-0.298,2.154-0.837c0.481-0.439,0.808-1.012,0.954-1.641L13.685,20.236z"
/>
</svg>
);
const BellOutlined = props => <Icon component={messageSvg} {...props} />;
const NoticeIcon = props => {
const getNotificationBox = () => {
const { children, confirmRead } = props;
if (!children) {
return null;
}
const panes = [];
React.Children.forEach(children, child => {
if (!child) {
return;
}
const { list, title } = child.props;
panes.push(
<NoticeList
{...child.props}
data={list}
key={child}
title={title}
config={props.global}
/>,
);
});
const childProps = panes.length === 1 ? panes[0].props : { list: [] };
return (
<>
<div className={styles.header}>
<span className={styles.title}>通知</span>
<span
onClick={() =>
childProps.list.length > 0 ? confirmRead(true) : null
}
style={{
color: childProps.list.length === 0 && 'rgba(0, 0, 0, 0.5)',
}}
>
全部标记已读
</span>
</div>
{panes}
</>
);
};
const { className, bell } = props;
const [visible, setVisible] = useMergeValue(false, {
value: props.popupVisible,
onChange: props.onPopupVisibleChange,
});
const noticeButtonClass = classNames(className, styles.noticeButton);
const notificationBox = getNotificationBox();
const NoticeBellIcon = bell || (
<BellOutlined className={styles.icon} title="报警" />
);
const trigger = (
<span className={classNames(noticeButtonClass, { opened: visible })}>
<Badge
count={props.count}
overflowCount={99}
offset={[0, 8]}
style={{ boxShadow: 'none' }}
title="报警"
className={classNames(
styles.badge,
props.count > 0 ? styles.fountMessage : '',
)}
>
{NoticeBellIcon}
</Badge>
<span className={styles.title}>消息</span>
</span>
);
if (!notificationBox) {
return trigger;
}
return (
<>
<HeaderDropdown
placement="bottomRight"
overlay={notificationBox}
overlayClassName={styles.popover}
trigger={['click']}
visible={visible}
onVisibleChange={setVisible}
>
{trigger}
</HeaderDropdown>
</>
);
};
NoticeIcon.defaultProps = {
emptyImage:
'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
};
NoticeIcon.Tab = NoticeList;
const mapStateToProps = state => ({
global: state.getIn(['global', 'globalConfig']),
});
export default connect(
mapStateToProps,
null,
)(NoticeIcon);
@import '~antd/es/style/themes/default.less';
.popover {
position: absolute;
width: 336px;
z-index: 1100;
}
.noticeButton {
display: inline-block;
cursor: pointer;
transition: border-color 0.3s, background 0.3s, padding 0.1s cubic-bezier(0.215, 0.61, 0.355, 1);
.title {
color: hsla(221, 100%, 95%, 0.7);
}
&:hover {
.fountMessage {
svg {
path {
fill: #fff;
}
}
}
.title {
color: #fff;
}
}
}
.icon {
padding: 4px;
vertical-align: middle;
}
.badge {
font-size: 16px;
:global {
.@{ant-prefix}-badge-multiple-words {
padding: 0 4px;
}
}
&.fountMessage {
.icon {
animation: bellshake 2s ease-out infinite;
}
}
}
.tabs {
:global {
.@{ant-prefix}-tabs-nav-list {
margin: auto;
}
.@{ant-prefix}-tabs-nav-scroll {
text-align: center;
}
.@{ant-prefix}-tabs-bar {
margin-bottom: 0;
}
}
}
.header {
border-radius: 0 !important;
padding: 8px;
display: flex;
justify-content: space-between;
align-items: center;
.title {
font-size: 16px;
font-weight: bold;
margin-left: 5px;
color: rgb(102, 102, 102);
}
span {
&:last-child {
cursor: pointer;
color: @primary-color;
font-size: 12px;
}
}
}
@keyframes bellshake {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
3.75% {
-webkit-transform: rotate(25deg);
transform: rotate(25deg);
}
15% {
-webkit-transform: rotate(-25deg);
transform: rotate(-25deg);
}
22.5% {
-webkit-transform: rotate(15deg);
transform: rotate(15deg);
}
29% {
-webkit-transform: rotate(-10deg);
transform: rotate(-10deg);
}
35% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg);
}
43% {
-webkit-transform: rotate(-2deg);
transform: rotate(-2deg);
}
50%,
to {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
export const REQUEST_SERVICE = {
EIMTopic: '/EIM/info',
SaveWaTopic: '/saveWater/info',
SystemTopic: '/system/info',
WorkerOrderTopic: '/workerOrder/info',
ScadaTopic: '/scada/alarm',
UserTopic: '/Message/',
};
export const MESSAGE_TYPE = {
SCADA_TYPE: 'scadaType',
CASE_TYPE: 'caseType',
SYS_TYPE: 'sysType',
SAVE_WA_TYPE: 'saveWaType',
EIMT_TYPE: 'EIMType',
MESSAGE_ALERT: 'remindType',
UNKNOWN: 'unknown',
};
export const NEW_MESSAGE = 'NEW_MESSAGE';
export const MESSAGE_TEXT_TYPE = {
PROJECT_FLOW: '工单流程',
SYS_MESSAGE: '系统消息',
SYS_NOTICE: '系统通知',
SAWATER: '节水通知',
EMTT: '信息化',
MESSAGE_ALERT: '消息提醒',
SCADA_ALARM: 'SCADA报警',
};
export const USERNAME = 'mao2080';
export const PASSWORD = '123';
export const PLATFORM_LEVEL = '4';
export const VIDEO_LEVEL = '5';
export const SYS_LEVEL = '6';
export const ERR_OK = '0000';
export const DEFAULT_TCP_PORT = 443;
export const DEFAULT_TCP_IP = 'emqttd.panda-water.cn';
export const DEFAULT_MQTT_PATH = '/mqtt';
export const DEFAULT_KEEPLIVE = 60;
export const DEFAULT_TIMEOUT = 50;
export const DEFAULT_PARSE_LEVEL = '1.0';
import 'kit_logger';
import _ from 'lodash';
import MqttClient from 'mqtt-client';
import { noticeService } from '../../api';
import { postInformationStatus } from '../../api/service/notification';
import { isJSON } from '../../utils/utils';
import {
DEFAULT_KEEPLIVE,
DEFAULT_MQTT_PATH,
DEFAULT_PARSE_LEVEL,
DEFAULT_TCP_IP,
DEFAULT_TCP_PORT,
DEFAULT_TIMEOUT,
ERR_OK,
MESSAGE_TEXT_TYPE,
MESSAGE_TYPE,
NEW_MESSAGE,
PASSWORD,
PLATFORM_LEVEL,
REQUEST_SERVICE,
SYS_LEVEL,
USERNAME,
VIDEO_LEVEL,
} from './constants';
import createMessage from './message';
import { setGlobalState } from '@/actions';
/* eslint-disable */
// eslint-disable-next-line no-undef
const Logger = logger('mqtt');
class Notifier {
constructor(userInfo, renderVideo, renderPlatform, renderSysPlatform, props) {
this.userInfo = userInfo;
this.messageCache = {
totalCount: 0,
messages: [],
};
// 当前消息缓存
this._subscribers = {}; // 订阅器缓存
this._siteConfig = {
site_code: this.userInfo.site,
TcpIP: '',
TcpPort: DEFAULT_TCP_PORT,
TimeOut: '',
KeepAlive: '',
IsSSL: true,
mqtt_path: DEFAULT_MQTT_PATH,
mqtt_mess: {},
nginxStart: false
};
this.MQTTCount = 0;
this.MQTTClient = null;
this.MQTTOptions = {};
this.IsNeedReconnect = true;
this.currentPageIndex = 1;
this.currentPageSize = 10;
this.start = this.start.bind(this);
this.stop = this.stop.bind(this);
this.subscribe = this.subscribe.bind(this);
this.unsubscribe = this.unsubscribe.bind(this);
this.confirmRead = this.confirmRead.bind(this);
this.loadMore = this.loadMore.bind(this);
this.hasMore = this.hasMore.bind(this);
this.renderVideo = renderVideo;
this.renderPlatform = renderPlatform;
this.renderSysPlatform = renderSysPlatform;
this.props = props;
}
// 对外接口
async start() {
window.cc = this.messageCache;
this.getMqttSiteCode().then((res) => {
this.loadHisMessages(this.currentPageIndex, this.currentPageSize);
this.connectMQTTServer();
});
}
stop() {
this.disconnectMQTTServer();
}
subscribe(type, handler) {
if (!(type in this._subscribers)) {
this._subscribers[type] = [];
}
this._subscribers[type].push(handler);
}
unsubscribe(type, handler) {
if (!(type in this._subscribers)) {
logger.info('无效事件无法删除');
}
if (!handler) {
delete this._subscribers[type];
} else {
const idx = this._subscribers[type].findIndex(ele => ele === handler);
if (idx === -1) {
logger.info('无效事件无法删除');
return;
}
this._subscribers[type].splice(idx, 1);
if (this._subscribers[type].length === 0) {
delete this._subscribers[type];
}
}
}
publish(type, payload) {
if (!(type in this._subscribers) || this._subscribers[type].length === 0) {
return;
}
this._subscribers[type].forEach(handler => {
try {
handler(payload);
} catch (e) {
// eslint-disable-next-line no-undef
logger.warn(`订阅器委托错误${e.message}`);
}
});
}
confirmRead(isAll = false, hisIDs = []) {
if (
this.messageCache &&
this.messageCache.totalCount === 0 &&
this.messageCache.messages.length === 0
) {
return;
}
if (isAll) hisIDs = this.messageCache.messages.map(item => item.id);
const self = this;
// eslint-disable-next-line no-undef
postInformationStatus({
query: {
userID: this.userInfo.OID || window.globalConfig.userInfo.OID,
hisID: hisIDs.join(','),
isAll: isAll ? 1 : '',
}
})
.then(res => {
if (res.statusCode !== ERR_OK) {
Logger.info(res.errMsg);
return;
}
if (isAll) {
self.messageCache.totalCount = 0;
self.messageCache.messages = [];
self.currentPageIndex = 1;
} else {
hisIDs.forEach(id => {
const index = self.messageCache.messages.findIndex(
item => item.id === id,
);
if (index > -1) {
self.messageCache.messages.splice(index, 1);
// eslint-disable-next-line no-plusplus
self.messageCache.totalCount--;
}
});
}
self.publish(NEW_MESSAGE, self.messageCache);
})
.catch(err => {
// eslint-disable-next-line no-undef
logger.error(`postInformationStatus调用失败${err}`);
});
}
hasMore() {
if (!this.messageCache) return false;
if (!this.messageCache.totalCount) return false;
return this.messageCache.totalCount > this.messageCache.messages.length;
}
loadMore(callback) {
if (!this.hasMore()) return Promise.resolve([]);
this.currentPageIndex += 1;
return this.loadHisMessages(this.currentPageIndex, this.currentPageSize);
}
// mqtt
async connectMQTTServer() {
const hostname = this._siteConfig.TcpIP;
const port = this._siteConfig.TcpPort;
const clientId = `client-${this.generatedId()}`;
const timeout = DEFAULT_TIMEOUT;
const keepAlive = DEFAULT_KEEPLIVE;
const cleanSession = true;
const ssl = this._siteConfig.IsSSL;
const userName = USERNAME;
const password = PASSWORD;
const path = this._siteConfig.mqtt_path;
this.MQTTCount = 0;
if (hostname) {
this.MQTTClient = new MqttClient.Client(hostname, port, path, clientId);
this.MQTTOptions = {
invocationContext: {
host: hostname,
port,
path,
clientId,
},
timeout,
keepAliveInterval: keepAlive,
cleanSession,
useSSL: ssl,
userName,
password,
onSuccess: this.onMQTTConnect.bind(this),
onFailure(e) {
console.log(e);
},
};
this.MQTTClient.connect(this.MQTTOptions);
this.MQTTClient.onConnectionLost = this.onMQTTConnectionLost.bind(this);
this.MQTTClient.onMessageArrived = this.onMessageArrived.bind(this);
}
}
disconnectMQTTServer() {
if (this.MQTTClient) {
this.IsNeedReconnect = false;
this.MQTTClient.disconnect();
this.MQTTClient = null;
}
}
getSiteCode() {
return this._siteConfig.site_code;
}
getUserInfo() {
return this.userInfo;
}
getSiteConfig() {
return this._siteConfig;
}
onMQTTConnect() {
const site = this.getSiteCode();
// 信息化主题
this.MQTTClient.subscribe(site + REQUEST_SERVICE.EIMTopic);
// 节水主题
this.MQTTClient.subscribe(site + REQUEST_SERVICE.SaveWaTopic);
// 系统主题
this.MQTTClient.subscribe(site + REQUEST_SERVICE.SystemTopic);
// 工单主题
this.MQTTClient.subscribe(site + REQUEST_SERVICE.WorkerOrderTopic);
// 报警主题
this.MQTTClient.subscribe(site + REQUEST_SERVICE.ScadaTopic);
// 用户主题
this.MQTTClient.subscribe(
`${site}${REQUEST_SERVICE.UserTopic}${this.userInfo.OID}`,
);
}
onMQTTConnectionLost(responseObject) {
const self = this;
if (this.IsNeedReconnect) {
this.MQTTClient.connect(self.MQTTOptions);
this.MQTTtester = setInterval(function() {
if (self.MQTTClient.isConnected) {
clearInterval(self.MQTTtester);
} else {
self.MQTTClient.connect(self.MQTTOptions);
}
}, 1000);
}
}
onMessageArrived(buffer) {
try {
const parseMessage = JSON.parse(buffer.payloadString);
const userInfo = this.getUserInfo();
const infoType = this.getMessageType(parseMessage.infoType);
let state = null;
if (
_.isEmpty(parseMessage.tousers) ||
userInfo.OID == parseMessage.tousers ||
parseMessage.tousers.includes(`${userInfo.OID},`) ||
parseMessage.tousers.includes(`,${userInfo.OID}`)
) {
if (
document.visibilityState === 'visible' ||
document.visibilityState === 'hidden'
) {
this.renderWindowsInfo(buffer);
}
let messContent = parseMessage.content;
if(this._siteConfig.MessageLevel && this._siteConfig.MessageLevel === "2.0") {
messContent = this.messageThrome(infoType, JSON.parse(parseMessage.content))
}
const timeH =
(new Date() - new Date(parseMessage.createTime)) / 1000 / 60;
const timeMss =
Math.abs(timeH) > 1440
? parseMessage.createTime.split('.')[0]
: Math.abs(timeH) > 60
? `${(timeH / 60).toFixed(0)}小时前`
: Math.abs(timeH) > 1
? `${timeH.toFixed(0)}分钟前`
: '刚刚';
const messString = {
id: parseMessage.infoId,
infoContent: parseMessage.content,
infoLevel: parseMessage.level,
time: `${timeMss}`,
infoType,
dateTime: parseMessage.createTime,
webConfig: parseMessage.web_config,
webPath: parseMessage.web_path,
messType: parseMessage.MessType,
};
if (parseMessage.tousers === '') {
messString.messType = '公告';
let content = (_.isObject(messContent) ? messContent.content: messContent).replace(/\\n/g, ',');
content = this.replaceSpeak(content)
state = new window.SpeechSynthesisUtterance(
`您有新的公告:${content
.substring(0, content.lastIndexOf(','))
.replace(':', ',')} 时间:${timeMss}`,
);
} else {
let content = ( _.isObject(messContent) ? messContent.content: messContent).replace(/\\n/g, ',');
content = this.replaceSpeak(content)
state = new window.SpeechSynthesisUtterance(
`您有新的消息:${content
.substring(0, content.lastIndexOf(','))
.replace(':', ',')} 时间:${timeMss}`,
);
}
if (this.getParseVesion()) {
messString.infoContent = this.parseMessageToJSON(
infoType,
messString,
);
} else {
messString.infoContent = JSON.parse(
JSON.stringify(messString.infoContent || '{}'),
);
}
const message = createMessage(messString);
this.messageCache.totalCount += 1;
if(message.infoLevel === SYS_LEVEL) {
this.messageCache.messages.unshift(message)
} else {
this.messageCache.messages.push(message);
}
this.publish(NEW_MESSAGE, this.messageCache);
if (
messString.infoLevel === PLATFORM_LEVEL &&
messString.infoType === MESSAGE_TYPE.SCADA_TYPE
) {
this.parseScadaMessage(Object.assign(message, { messContent }));
this.renderPopPlatform(Object.assign(message, { messContent }));
} else if (messString.infoLevel === VIDEO_LEVEL) {
this.renderPopVideo(Object.assign(message, { messContent }));
state.lang = 'zh';
state.rate = 1;
window.speechSynthesis.speak(state);
} else if (message.infoLevel === PLATFORM_LEVEL) {
} else if(message.infoLevel === SYS_LEVEL) {
this.renderSysNoticePlatform(Object.assign(message, { messContent }))
} else {
state.lang = 'zh';
state.rate = 1;
window.speechSynthesis.speak(state);
}
}
} catch (e) {
Logger.error(`收到消息处理异常:${e.message}`);
}
}
replaceSpeak(msg) {
msg = msg.replaceAll(" ", "");
msg = msg.replace(/MPa/ig, "兆帕");
msg = msg.replace(/m³\/h/ig, "立方米每小时");
msg = msg.replace(/m³/ig, "立方米");
msg = msg.replace(/kWh/ig, "千瓦时");
msg = msg.replace(/kW/ig, "千瓦");
msg = msg.replace(/min/ig, "分钟");
msg = msg.replace(/m/ig, "米");
msg = msg.replace(/A/ig, "安");
msg = msg.replace(/V/ig, "伏");
msg = msg.replace(/h/ig, "小时");
return msg;
}
messageThrome(themeName, info) {
let messageInfo = "";
switch (themeName) {
case MESSAGE_TYPE.CASE_TYPE:
case "工单提醒":
messageInfo = `【${info.caseType}${info.flowName}\\n${info.nodeName}\\n承办意见:${info.content}`;
break;
case MESSAGE_TYPE.SCADA_TYPE:
case "通用报警":
messageInfo = `${info.title} \\n ${info.content.replace(", ", "\\n")}\\n${info.deviceCode}`;
break;
case MESSAGE_TYPE.SYS_TYPE:
case "系统通知":
messageInfo = `【${info.noticeType}${info.noticeTitle}\\n${info.noticeContent}`;
break;
default:
messageInfo = info;
break;
}
return messageInfo;
}
renderWindowsInfo(message) {
const self = this;
function notifyMessage(message) {
const parseMessage = JSON.parse(message.payloadString);
let content = '';
if(message.level !== SYS_LEVEL) {
if(self.getMessageLevel() === "2.0") {
if(message.level === "4") {
const messageContent = JSON.parse(parseMessage.content);
content += `${messageContent.alarmType} ${messageContent.alarmDevice} ${messageContent.alarmContent} ${messageContent.alarmValue} / ${messageContent.alarmThreshold}`
}
} else {
for (let i = 0; i < parseMessage.content.split('\\n').length; i++) {
content += `${parseMessage.content.split('\\n')[i]} `;
}
}
}
const messageBody = {
title: '',
content,
};
if (parseMessage.tousers === '') {
messageBody.title = '新公告:';
} else {
messageBody.title = '新通知:';
}
if(content !== "") {
if (!('Notification' in window)) {
message.warn('This browser does not support desktop notification');
} else if (Notification.permission === 'granted') {
const notification = new Notification(messageBody.title, {
body: `${messageBody.content}`,
icon: 'https://panda-water.com/web4/assets/images/icon/熊猫新1.png',
});
notification.onclick = () => {
notification.close();
};
} else if (Notification.permission !== 'denied') {
Notification.requestPermission(permission => {
if (permission === 'granted') {
const notification = new Notification(messageBody.title, {
body: `${messageBody.content}`,
icon:
'https://panda-water.com/web4/assets/images/icon/熊猫新1.png',
});
notification.onclick = () => {
notification.close();
};
}
});
}
}
}
notifyMessage(message);
}
renderPopPlatform(message) {
this.renderPlatformElement =
this.renderPlatform && this.renderPlatorm(message);
}
renderSysNoticePlatform(message) {
this.renderSysPlatform && this.renderSysPlatform(message)
}
renderPopVideo(message) {
this.renderVideoElement = this.renderVideo && this.renderVideo(message);
}
destoryPlatform() {
this.renderPlatformElement = null;
}
destoryVideo() {
this.renderVideoElement = null;
}
// 工具类
async loadHisMessages(pageIndex, pageSize) {
const self = this;
return noticeService.getInformationInfo({
userID: self.getUserInfo().OID,
pageIndex,
pageSize,
'request.preventCache': Date.now(),
}).then(res => {
if (res) {
const result = {
totalCount: res.totalRcdNum,
messages: (Array.isArray(res.getMe) ? res.getMe: []) .map(this.parseHisToMessage.bind(this)),
};
self.messageCache.totalCount = result.totalCount;
(result.messages || []).forEach(message => {
const index = self.messageCache.messages.findIndex(
item => item.id === message.id,
);
index === -1 && self.messageCache.messages.push(message);
});
if (
self.messageCache.totalCount > self.messageCache.messages.length &&
res.getMe.length === 0
) {
// 服务端返回总数还有,但是查不到数据了,前端修正服务端返回的总数
self.messageCache.totalCount = self.messageCache.messages.length;
}
self.publish(NEW_MESSAGE, self.messageCache);
console.log('render');
const selectData = result.messages.find(item => { return item.infoLevel == 6 });
if (selectData) {
if (this._siteConfig.MessageLevel && this._siteConfig.MessageLevel === "2.0") {
selectData.Info = self.messageThrome(selectData.infoType, JSON.parse(JSON.stringify(selectData.infoContent)));
}
self.renderSysNoticePlatform(selectData)
}
return Promise.resolve(result);
}
});
}
async getMqttSiteCode() {
const self = this;
return noticeService.getMqttSiteCode({ 'request.preventCache': Date.now() }).then(
res => {
if (res && res.say.statusCode === ERR_OK) {
let mqttConfig = {
mqtt_mess: {},
mqtt_path: self._siteConfig.mqtt_path,
nginxStart: self._siteConfig.NginxStart,
mqtt_IsSSL: true
};
if (Array.isArray(res.getMe) && res.getMe.length > 0) {
if (res.getMe[0]) {
const data = res.getMe[0];
mqttConfig.mqtt_IsSSL = self._siteConfig.IsSSL = data.IsSSL ? data.IsSSL : false;
mqttConfig.mqtt_mess.site_code = self._siteConfig.site_code = data.SiteCode || self._siteConfig.site_code;
mqttConfig.mqtt_mess.TcpIP = self._siteConfig.TcpIP = data.TcpIP;
mqttConfig.mqtt_mess.TcpPort = self._siteConfig.TcpPort = data.TcpPort ? parseInt(data.TcpPort) : 8083;
mqttConfig.mqtt_mess.MessageLevel = self._siteConfig.MessageLevel = data.MessageLevel ? data.MessageLevel : DEFAULT_PARSE_LEVEL;
if (data.NginxStart) {
mqttConfig.NginxStart = self._siteConfig.NginxStart = data.NginxStart;
mqttConfig.mqtt_mess.TcpIP = self._siteConfig.mqtt_mess.TcpIP = window.location.hostname;
mqttConfig.mqtt_mess.TcpPort = self._siteConfig.mqtt_mess.TcpPort = parseInt(window.location.port);
mqttConfig.mqtt_path = self._siteConfig.mqtt_path = '/ws/';
} else {
mqttConfig.nginxStart = data.NginxStart
}
}else {
mqttConfig.mqtt_mess.TcpIP = self._siteConfig.mqtt_mess.TcpIP = DEFAULT_TCP_IP;
mqttConfig.mqtt_mess.TcpPort = self._siteConfig.mqtt_mess.TcpPort = DEFAULT_TCP_PORT;
mqttConfig.mqtt_IsSSL = self._siteConfig.IsSSL = self._siteConfig.mqtt_mess.TcpIP + ":" + self._siteConfig.mqtt_mess.TcpPort;
}
mqttConfig.mqtt_iotIP = self._siteConfig.mqtt_iotIP = mqttConfig.mqtt_mess.TcpIP + ":" + mqttConfig.mqtt_mess.TcpPort;
// self.props.updateConfig && self.props.updateConfig(Object.assign({}, self.props.global, {
// ...mqttConfig
// }))
// 应用共享状态只在initMicro中更新了一次,异步修改了的globalConfig,子应用读取到的是旧的,需要更新一下应用共享状态
// setGlobalState({
// globalConfig: window.globalConfig
// });
}
} else {
Logger.info('获取mqtt服务器参数失败');
}
},
);
}
getMessageLevel() {
return this._siteConfig.MessageLevel
}
generatedId() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
.replace(/[xy]/g, function(c) {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
})
.toUpperCase();
}
getMessageType(messageType) {
let infoType = MESSAGE_TYPE.SCADA_TYPE;
switch (messageType) {
case MESSAGE_TEXT_TYPE.PROJECT_FLOW:
infoType = MESSAGE_TYPE.CASE_TYPE;
break;
case MESSAGE_TEXT_TYPE.SYS_MESSAGE:
case MESSAGE_TEXT_TYPE.SYS_NOTICE:
infoType = MESSAGE_TYPE.SYS_TYPE;
break;
case MESSAGE_TEXT_TYPE.SAWATER:
infoType = MESSAGE_TYPE.SAVE_WA_TYPE;
break;
case MESSAGE_TEXT_TYPE.EMTT:
infoType = MESSAGE_TYPE.EIMT_TYPE;
break;
case MESSAGE_TEXT_TYPE.MESSAGE_ALERT:
infoType = MESSAGE_TYPE.MESSAGE_ALERT;
break;
default:
break;
}
return infoType;
}
parseHisToMessage(hisMessage) {
const timeH = (new Date() - new Date(hisMessage.HisCreateTime)) / 1000 / 60;
const timeMss =
Math.abs(timeH) > 1440
? hisMessage.HisCreateTime.split('.')[0]
: Math.abs(timeH) > 60
? `${(timeH / 60).toFixed(0)}小时前`
: Math.abs(timeH) > 1
? `${timeH.toFixed(0)}分钟前`
: '刚刚';
const message = {
id: hisMessage.HisID,
infoContent: this._siteConfig.MessageLevel === DEFAULT_PARSE_LEVEL ? hisMessage.InfoContent: isJSON(hisMessage.InfoContent) ? JSON.parse(hisMessage.InfoContent): hisMessage.InfoContent,
time: `${timeMss}`,
infoType: this.getMessageType(hisMessage.InfoType),
dateTime: hisMessage.HisCreateTime,
infoLevel: hisMessage.InfoLevel,
webConfig: hisMessage.web_config,
webPath: hisMessage.web_path,
messType: hisMessage.MessType,
};
if (this.getParseVesion()) {
message.infoContent = this.parseMessageToJSON(message.infoType, message);
} else {
message.infoContent = JSON.parse(
JSON.stringify(message.infoContent || '{}'),
);
}
return createMessage(message);
}
getParseVesion() {
const siteConfig = this.getSiteConfig();
return !siteConfig.MessageLevel || siteConfig.MessageLevel !== '2.0';
}
parseMessageToJSON(messageType, messageContent) {
let messageBody = messageContent;
try {
switch (messageType) {
case MESSAGE_TYPE.SCADA_TYPE:
messageBody = this.parseMessageAlarm(messageContent);
break;
case MESSAGE_TYPE.CASE_TYPE:
messageBody = this.parseMessageCase(messageContent);
break;
case MESSAGE_TYPE.SYS_TYPE:
messageBody = this.parseMessageNotice(messageContent);
break;
case MESSAGE_TYPE.SAVE_WA_TYPE:
break;
case MESSAGE_TYPE.EIMT_TYPE:
break;
case MESSAGE_TYPE.UNKNOWN:
break;
default:
break;
}
} catch (e) {
logger.info(
`1.0消息通知解析消息内容出错:${e.message}`,
'消息对象:',
messageBody,
);
} finally {
}
return messageBody;
}
parseMessageCase(messString) {
const attr = messString.infoContent.split('\\n');
const caseContent = {
caseType: attr[0].split('】')[0].split('【')[1],
flowName: attr[0].split('】')[1],
nodeName: attr[1],
content: attr[2],
time: messString.dateTime,
};
return caseContent;
}
parseMessageAlarm(messString) {
const attr = messString.infoContent.split('\\n');
const alarmContent = {
alarmType: attr[0].split('】')[0].split('【')[1],
deviceCode: '',
alarmDevice: attr[0].split('】')[1],
alarmContent: attr[1],
alarmThreshold: attr[2].includes(' / ')
? attr[2].split('/')[1].trim(' ')
: '',
alarmValue: attr[2].includes(' / ')
? attr[2].split('/')[0].trim(' ')
: attr[2].includes(':')
? attr[2].split(':')[1]
: '',
time: messString.dateTime,
};
return alarmContent;
}
parseMessageNotice(messString) {
const attr = messString.infoContent.split('\\n');
const noticeContent = {
noticeType: attr[0].split('】')[0].split('【')[1],
noticeTitle: attr[0].split('】')[1],
noticeContent: attr[1],
time: messString.dateTime,
};
return noticeContent;
}
parseScadaMessage(message) {
const data = message.messContent.split('\\n');
const last = data[2];
const alarmType = data[0].split('】')[0].split('【')[1];
const sensor = data[1];
const station = data[0].split('】')[1];
let lastValue = last;
if (last.includes(' / ')) {
lastValue = '';
const alaVal = last.split(' / ');
let newVal = 0;
let setVal = 0;
let unit = '';
if (alaVal[0].includes(' ')) {
newVal = alaVal[0].split(' ')[0] * 1;
setVal = alaVal[1].split(' ')[0] * 1;
unit = alaVal[0].split(' ')[1];
} else {
newVal = alaVal[0] * 1;
setVal = alaVal[1] * 1;
}
lastValue = Math.abs(setVal - newVal).toFixed(2) + unit;
} else {
lastValue = `,报警实时值${lastValue}`;
}
let msg = `紧急报警:${station},${alarmType},${sensor}${lastValue},请注意!!!`;
for (let i = 0; i < 3; i++) {
msg += msg;
}
const state = new window.SpeechSynthesisUtterance(msg);
state.lang = 'zh';
state.rate = 1;
window.speechSynthesis.cancel();
window.speechSynthesis.speak(state);
}
}
export default Notifier;
/* eslint-disable */
class Message {
constructor({
id,
infoContent,
infoLevel,
time,
infoType,
dateTime,
webConfig,
webPath,
messType,
} = message) {
this.id = id;
this.infoContent = infoContent;
this.infoLevel = infoLevel;
this.time = time;
this.infoType = infoType; // 方案类型 - 工单 报警 公告 定时推送
this.dateTime = dateTime;
this.webConfig = webConfig;
this.webPath = webPath;
this.messType = messType; // 方案名称 - 大类型下细类型
}
}
function createMessage(message) {
return new Message(message);
}
export default createMessage;
...@@ -3,9 +3,9 @@ import { List, Tooltip, Select, Switch } from 'antd'; ...@@ -3,9 +3,9 @@ import { List, Tooltip, Select, Switch } from 'antd';
import defaultSettings from '../../../config/defaultSetting'; import defaultSettings from '../../../config/defaultSetting';
import { getFormatMessage } from './index'; import { getFormatMessage } from './index';
export const renderLayoutSettingItem = (item) => { export const renderLayoutSettingItem = item => {
const action = React.cloneElement(item.action, { const action = React.cloneElement(item.action, {
disabled: item.disabled disabled: item.disabled,
}); });
return ( return (
<Tooltip title={item.disabled ? item.disabledReason : ''} placement="left"> <Tooltip title={item.disabled ? item.disabledReason : ''} placement="left">
...@@ -13,13 +13,9 @@ export const renderLayoutSettingItem = (item) => { ...@@ -13,13 +13,9 @@ export const renderLayoutSettingItem = (item) => {
<span style={{ opacity: item.disabled ? 0.5 : 1 }}>{item.title}</span> <span style={{ opacity: item.disabled ? 0.5 : 1 }}>{item.title}</span>
</List.Item> </List.Item>
</Tooltip> </Tooltip>
) );
}; };
const LayoutSetting = ({ settings = {}, changeSetting }) => { const LayoutSetting = ({ settings = {}, changeSetting }) => {
const formatMessage = getFormatMessage(); const formatMessage = getFormatMessage();
const { contentWidth, splitMenus, fixedHeader, layout, fixSiderbar } = const { contentWidth, splitMenus, fixedHeader, layout, fixSiderbar } =
...@@ -39,7 +35,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => { ...@@ -39,7 +35,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => {
value={contentWidth || 'Fixed'} value={contentWidth || 'Fixed'}
size="small" size="small"
className="content-width" className="content-width"
onSelect={(value) => { onSelect={value => {
changeSetting('contentWidth', value); changeSetting('contentWidth', value);
}} }}
style={{ width: 80 }} style={{ width: 80 }}
...@@ -72,7 +68,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => { ...@@ -72,7 +68,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => {
disabled disabled
className="fixed-header" className="fixed-header"
checked={!!fixedHeader} checked={!!fixedHeader}
onChange={(checked) => { onChange={checked => {
changeSetting('fixedHeader', checked); changeSetting('fixedHeader', checked);
}} }}
/> />
...@@ -94,7 +90,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => { ...@@ -94,7 +90,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => {
disabled disabled
className="fix-siderbar" className="fix-siderbar"
checked={!!fixSiderbar} checked={!!fixSiderbar}
onChange={(checked) => changeSetting('fixSiderbar', checked)} onChange={checked => changeSetting('fixSiderbar', checked)}
/> />
), ),
}, },
...@@ -106,7 +102,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => { ...@@ -106,7 +102,7 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => {
size="small" size="small"
checked={!!splitMenus} checked={!!splitMenus}
className="split-menus" className="split-menus"
onChange={(checked) => { onChange={checked => {
changeSetting('splitMenus', checked); changeSetting('splitMenus', checked);
}} }}
/> />
...@@ -116,6 +112,6 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => { ...@@ -116,6 +112,6 @@ const LayoutSetting = ({ settings = {}, changeSetting }) => {
renderItem={renderLayoutSettingItem} renderItem={renderLayoutSettingItem}
/> />
); );
}; };
export default LayoutSetting; export default LayoutSetting;
\ No newline at end of file
...@@ -35,5 +35,5 @@ export const REQUEST_METHOD_PUT = 'put'; ...@@ -35,5 +35,5 @@ export const REQUEST_METHOD_PUT = 'put';
export const REQUEST_METHOD_DELETE = 'delete'; export const REQUEST_METHOD_DELETE = 'delete';
export const WEB_GIS_TYPE = { export const WEB_GIS_TYPE = {
ARCGIS: 'ArcgisMap', ARCGIS: 'ArcgisMap',
AMAP: 'AMap' AMAP: 'AMap',
} };
...@@ -35,7 +35,7 @@ import KeepAlive from 'react-activation'; ...@@ -35,7 +35,7 @@ import KeepAlive from 'react-activation';
import AMapLoader from '@amap/amap-jsapi-loader'; import AMapLoader from '@amap/amap-jsapi-loader';
import RightContent from '@/components/GlobalHeader/ExtendRightContent'; import RightContent from '@/components/GlobalHeader/ExtendRightContent';
// import Panel from '@/components/SliderPanel/MinPanel'; // import Panel from '@/components/SliderPanel/MinPanel';
import { MinPanel as Panel } from '@wisdom-utils/components' import { MinPanel as Panel } from '@wisdom-utils/components';
import { actionCreators } from '@/containers/App/store'; import { actionCreators } from '@/containers/App/store';
import { ArcgisMap, AMap, CesiumMap } from '../pages/map'; import { ArcgisMap, AMap, CesiumMap } from '../pages/map';
import { WEB_GIS_TYPE } from '../constants'; import { WEB_GIS_TYPE } from '../constants';
......
...@@ -19,7 +19,7 @@ import { ...@@ -19,7 +19,7 @@ import {
DEFAULT_TCP_IP, DEFAULT_TCP_IP,
DEFAULT_TCP_PORT, DEFAULT_TCP_PORT,
DEFAULT_PARSE_LEVEL, DEFAULT_PARSE_LEVEL,
} from '@/components/Notifier/constants'; } from '@wisdom-utils/components/lib/AppLayout/notifier/constants';
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const Logger = logger('login'); const Logger = logger('login');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment