import React, { Component, lazy, Suspense, useMemo, useState } from 'react'; import { Button, Form, Input, Modal, notification, Pagination } from 'antd'; import { connect } from 'react-redux'; import { FormattedMessage } from '@wisdom-utils/components'; import { history } from '@wisdom-utils/runtime'; import { findPathByWidget, isJSON } from '@wisdom-utils/components/lib/AppLayout/helpers'; import classNames from 'classnames'; import { appService } from '@/api'; import { getVideoUrl } from '@/utils/utils'; import Notifier from '../../layouts/AppLayout/notifier/notice'; import NoticeIcon from '../../layouts/AppLayout/notifier'; import { ERR_OK, MESSAGE_TYPE, NEW_MESSAGE } from '../../layouts/AppLayout/notifier/constants'; import service from '../../api/service/notification'; import { actionCreators } from '../../containers/App/store'; import isProd from '../../utils/env'; import { getMessageTypeIcon } from '../../layouts/AppLayout/notifier/utils'; // import NoticeIcon from '../NoticeIcon'; import styles from './index.less'; const VideoSliderModal = lazy(() => import('@wisdom-components/videoslidermodal')); const { TextArea } = Input; const jessibucaObj = { operateBtns: { screenshot: false, }, loadingText: '视频加载中', decoder: '/civbase/JessibucaVideo/decoder.js', }; const PlatformModal = ({ platformVisible, handleClosePlatform, handlerMointer, messages = [], type }) => { const [pageIndex, setPageIndex] = useState(1); const message = useMemo(() => messages[pageIndex - 1], [messages, pageIndex]); const isAlarm = message.infoClasses === MESSAGE_TYPE.ALARM_TYPE; const isRecover = type === 'recover'; const defaultIcon = isAlarm && !isRecover ? 'https://panda-water.com/web4/assets/images/message/报警图标.svg' : getMessageTypeIcon(message); const icon = message.webIcon && !isRecover ? `${window.location.origin}/${message.webIcon}` : defaultIcon; return ( <Modal title={ isAlarm ? ( isRecover ? ( '报警恢复' ) : ( <FormattedMessage id="component.noticeIcon.modal.alarm.title" /> ) ) : ( <FormattedMessage id="component.noticeIcon.model.system.title" /> ) } maskClosable={false} mask={false} maskStyle={{ pointerEvents: 'none' }} visible={platformVisible} zIndex={5000} wrapClassName={styles.platformModalWrap} className={classNames( styles.platformModal, !isAlarm && styles.platformModalDefault, isRecover && styles.platformModalRecover, )} footer={ <Pagination simple total={messages.length} pageSize={1} pageIndex={pageIndex} showSizeChanger={false} onChange={(page, pageSize) => setPageIndex(page)} /> } onCancel={() => handleClosePlatform()} centered > <div className={styles.alarmContent}> {/* eslint-disable-next-line jsx-a11y/alt-text */} <img src={icon} alt="" /> <div className={styles.content}> <div className={styles['content-top']}> <div className={styles['content-top-title']}> <a onClick={event => handlerMointer(event, message, true)} title={message && message.infoContent && message.infoContent.title} > {message && message.infoContent && `${message.infoContent.title} ${message.infoContent.title}`} </a> </div> <span title="点击标为已读" onClick={event => handlerMointer(event, message, false)} /> </div> {isAlarm ? ( <> <div className={styles['content-mid']}> <b className={classNames(isRecover && styles['content-mid-recover'])}> {message && message.infoContent && message.infoContent.alarmType} </b> {`|${message && message.infoContent && message.infoContent.alarmContent}`} </div> <div className={styles['content-bottom']}> <p> 报警值: <b className={classNames(isRecover && styles['content-bottom-recover'])}> {message && message.infoContent && message.infoContent.alarmValue} </b> {!isRecover && ( <> {' / '} 预设值: {message && message.infoContent && message.infoContent.alarmThreshold} </> )} </p> </div> </> ) : ( <div className={styles['content-bottom']}> <p>{message && message.infoContent && message.infoContent.content}</p> </div> )} <p className={styles['message-time']}>{message && message.time}</p> </div> </div> </Modal> ); }; /* eslint-disable */ class NoticeIconView extends Component { constructor(props) { super(props); this.state = { count: 0, noticeData: [], platformVisible: false, platformRecoverVisible: false, videoVisible: false, // eslint-disable-next-line react/no-unused-state noticeVisible: false, // eslint-disable-next-line react/no-unused-state renderVideo: null, // eslint-disable-next-line react/no-unused-state initVisible: false, sysTopVisible: false, sysMessage: {}, alarmMessage: {}, videoMessage: {}, popupVisible: false, }; // this.renderPlatform try { window.share && window.share.event && window.share.event.removeAllListeners('reloadNotice'); } catch (error) { } this.notifier = new Notifier( this.props.global.userInfo, this.renderVideo, this.renderPlatform, this.renderSysPlatform, this.props, ); } get platformMessages() { return this.state.noticeData.filter(item => item.infoLevel === '4' && !item?.infoContent?.alarmState); } get platformRecoverMessages() { return this.state.noticeData.filter(item => item.infoLevel === '4' && item?.infoContent?.alarmState === 1 ); } async componentDidMount() { this.notifier.subscribe(NEW_MESSAGE, this.onNewMessage.bind(this)); this.notifier.start(); window.share && window.share.event && window.share.event.on('reloadNotice', () => { try { // eslint-disable-next-line no-unused-expressions this.notifier && this.notifier.stop(); } catch (error) { // eslint-disable-next-line no-empty } finally { this.notifier = new Notifier( this.props.global.userInfo, this.renderVideo, this.renderPlatform, this.renderSysPlatform, this.props, ); this.notifier.subscribe(NEW_MESSAGE, this.onNewMessage.bind(this)); this.notifier.start(); } }); } componentWillUnmount() { try { // eslint-disable-next-line no-unused-expressions this.notifier && this.notifier.stop(); } catch (error) { // eslint-disable-next-line no-empty } finally { window.share.event.removeAllListeners('reloadNotice'); } } onPopupVisibleChange = value => { if(value && this.notifier?.loadHisMessages) { this.notifier.loadHisMessages(this.notifier.currentPageIndex || 1, this.notifier.currentPageSize || 10).then(()=>{ this.setState({ popupVisible: value, platformVisible: this.platformMessages.length > 0 ? true : this.state.platformVisible, platformRecoverVisible: this.platformRecoverMessages.length > 0 ? true : this.state.platformRecoverVisible, sysTopVisible: false, videoVisible: false, }); }); } else { this.setState({ popupVisible: value, platformVisible: this.platformMessages.length > 0 ? true : this.state.platformVisible, platformRecoverVisible: this.platformRecoverMessages.length > 0 ? true : this.state.platformRecoverVisible, sysTopVisible: false, videoVisible: false, }); } }; onNewMessage = messages => { this.setState({ count: messages.totalCount, noticeData: messages.messages, }); }; modalVisible = () => { const data = this.state.noticeData.filter(item => item.infoLevel === '4'); return data.length > 0; }; handleCloseVideo = () => { this.setState({ videoVisible: false }); this.notifier.destoryVideo(); }; handleClosePlatform = event => { this.setState({ platformVisible: false, platformRecoverVisible: false, }); }; // 显示报警弹窗 renderPlatform = message => { const { infoContent = {} } = message; const { alarmState = 0 } = infoContent; if (!alarmState && !this.state.platformVisible) { this.setState({ platformVisible: true, platformRecoverVisible: false }) } else if (alarmState && !this.state.platformRecoverVisible) { this.setState({ platformVisible: false, platformRecoverVisible: true, }) } }; renderVideo = async message => { try { this.setState({ videoVisible: true, }); const { props } = this; if(!message.infoContent?.deviceCode&&!message.infoContent?.DeviceCode)return; const {data} = await appService.GetVideoPlayListByDeviceCode({ UserID: 1, DeviceCode: message.infoContent?.deviceCode || message.infoContent?.DeviceCode, _site:props.global.userInfo.site || '', }) const dataList = [] if(Array.isArray(data)){ data.forEach((list) => { dataList.push({ id: list.vmS_DeviceID, name: list.channelName, protocol: list.protocol, username: list.username, password: list.password, dataRate: 'Sub', pandavmsHost: getVideoUrl(), gateway: true, address: list.address, channel: list.channelID * 1, }); }); } this.setState({ videoMessage: { list:dataList, title:message.infoContent?.messContent || '' }, }); } catch (error) { } }; // 消息弹窗Footer renderNotifierFooter = () => { const toNotifications = e => { e.stopPropagation(); history.push && history.push('/civbase/system/notifications') } return ( <div className={styles.notificationFoter}> <Button type='link' onClick={toNotifications}>查看通知消息</Button> </div> ) } handlerOptions = value => { // eslint-disable-next-line no-undef service .postAddOptions({ UserName: this.props.global.userInfo.fullName, Type: '其他问题', System: '熊猫智联', Content: value.content, Phone: value.phone, UserID: this.props.global.userInfo.OID, Picture: '', }) .then(res => { if (res.statusCode === ERR_OK) { this.setState({ sysTopVisible: false, }); } }); }; renderSysPlatform = message => { try { const noticeContent = this.props.global.mqtt_mess.MessageLevel === '2.0' && isJSON(message.infoContent) ? JSON.parse(message.infoContent) : message.infoContent; this.setState({ sysTopVisible: true, platformVisible: false, platformRecoverVisible: false, videoVisible: false, sysMessage: { message, noticeContent, }, }); } catch (error) {} }; handlerMointer = (event, item, detail) => { event.stopPropagation(); this.notifier.confirmRead(false, [item.id]); if (detail) { const widgetID = 'widget_city_综合运营_管网监控_实时监控_报警监控'; const webPath = 'product/scada/AlertMonitoring/AlertMonitoring'; const widget = findPathByWidget( 'productex/water/IOTMonitor/RealTimeAlarm/RealTimeAlarm', this.props.global.widgets, '', 'url', ); window.share.event.emit('listenerMointer', { widgetId: widgetID, label: '实时报警', url: widget.url || webPath, }); } this.setState({ platformVisible: false, platformRecoverVisible: false, }); }; handlerSysDetail = message => { this.setState({ sysTopVisible: true, }); this.renderSysPlatform(message); }; handlerUnknowDetail = (message, product, type) => { // 需要有跳转路径 if (!message.webPath) return notification.info({ message: '提示', duration: 3, description: '未配置跳转路径' }); const parsePath = url => { const [path, param = ''] = url.split('|'); let params = param.split('&').reduce((pre, item) => { if(!item) return pre; const [key, value] = item.split('='); pre[key] = value; return pre; }, {}); return [path, param, params]; } const [path,,params] = parsePath(message.webPath); // 需要有widget参数 if (!params.widget) return notification.info({ message: '提示', duration: 3, description: '未配置跳转路径' }); // 根据widget匹配菜单 const menu = (this.props.flatMenu || []).find(item => item.params && item.params.widget === params.widget); // const menu = (this.props.flatMenu || []).find(item => item.path.indexOf(path) > -1); let targetMenuPath = menu ? menu.path.replace(/^\//, '') : ''; if (targetMenuPath == '') { notification.info({ message: '提示', duration: 3, description: '你没有跳转菜单权限,请联系管理员添加权限' }); return; } params._source = '消息通知'; params._target = message.webPath; if (product) { if (/civweb4/.test(targetMenuPath)) return true; } sessionStorage.setItem('routerParams', JSON.stringify(params)); window.history.pushState(params, '', `/civbase/${targetMenuPath}${type ? '' : `?v=${Date.now()}`}`); } render() { return ( <> <NoticeIcon className={styles.action} count={this.state.count} confirmRead={this.notifier.confirmRead} config={this.props.global} bell={this.props.bell} renderFooter={this.renderNotifierFooter} popupVisible={this.state.popupVisible} onPopupVisibleChange={this.onPopupVisibleChange} > <NoticeIcon.Tab list={this.state.noticeData} title={<FormattedMessage id='component.noticeIcon.title'/>} emptyText={<FormattedMessage id='component.noticeIcon.allClear'/>} confirmRead={this.notifier.confirmRead} handlerSysDetail={this.handlerSysDetail} handlerUnknowDetail={this.handlerUnknowDetail.bind(this)} loadMore={this.notifier.loadMore} hasMore={this.notifier.hasMore} /> </NoticeIcon> { this.state.platformVisible && this.platformMessages.length > 0 && ( <PlatformModal platformVisible={this.state.platformVisible} handleClosePlatform={this.handleClosePlatform} messages={this.platformMessages} handlerMointer={this.handlerMointer} /> ) } { this.state.platformRecoverVisible && this.platformRecoverMessages.length > 0 && ( <PlatformModal platformVisible={this.state.platformRecoverVisible} handleClosePlatform={this.handleClosePlatform} messages={this.platformRecoverMessages} handlerMointer={this.handlerMointer} type={'recover'} /> ) } {this.state.videoVisible && this.state.videoMessage?.list?.length && ( <Suspense fallback={<></>}> <VideoSliderModal modalInfo={{ title: this.state.videoMessage.title, open: this.state.videoVisible, visible:this.state.videoVisible, className:'noticeVideoModal', onCancel: () => { this.handleCloseVideo() }, }} videoInfos={this.state.videoMessage.list} JessibucaObj={jessibucaObj} /> </Suspense> )} {this.state.sysTopVisible && this.state.sysMessage && !this.props.global.isNewYear && ( <Modal title={<FormattedMessage id='component.noticeIcon.model.system.title'/>} visible zIndex={8000} className={styles.noticeModal} onCancel={() => this.setState({ sysTopVisible: false })} footer={null} centered > <div className={styles.alarmContent}> <div className={styles.title}> {this.state.sysMessage.noticeContent.noticeTitle} </div> <div className={styles.content} dangerouslySetInnerHTML={{ __html: this.state.sysMessage.noticeContent.noticeContent, }} /> <div className={styles.time}> {this.state.sysMessage.message.time} </div> <Form formLayout="vertical" onFinish={this.handlerOptions}> <div>1、您还有其他建议和反馈吗?</div> <Form.Item name="content" rules={[{ required: true, message: '请填写反馈内容' }]} > <Input /> </Form.Item> <div> 2、怎么联系您(您填写了建议和反馈,也请帮忙填写下您的联系方式,以便我们有不清楚的问题时,能与您联系沟通) </div> <Form.Item name="phone"> <Input /> </Form.Item> <Form.Item> <Button htmlType="submit" type="primary"> 提交 </Button> </Form.Item> </Form> </div> </Modal> )} {this.state.sysTopVisible && this.state.sysMessage && this.props.global.isNewYear && ( <Modal visible zIndex={9000} footer={null} centered className={styles.newYearMessage} width={876} > <div className={styles['topPopup-body']}> <div className={styles['topPopup-title']}> <img src="https://panda-water.com/web4/assets/images/message/%E5%85%B3%E9%97%AD%E6%8C%89%E9%92%AE.png" onClick={() => this.setState({ sysTopVisible: false })}/> <div className={styles['newYearTitle']}> <img src="https://panda-water.com/web4/assets/images/message/%E7%83%9F%E8%8A%B1%E5%B7%A6.png"/> <span title={this.state.sysMessage.noticeContent.noticeTitle}>{this.state.sysMessage.noticeContent.noticeTitle}</span> <img src="https://panda-water.com/web4/assets/images/message/%E7%83%9F%E8%8A%B1%E5%8F%B3.png"/> </div> </div> <div className={styles['topPopup-content']}> <div className={styles['content']}> <div className={styles['content-mid']} dangerouslySetInnerHTML={{ __html: this.state.sysMessage.noticeContent.noticeContent, }}></div> <div className={styles['content-bottom']}> <span className={styles['message-time']}>{this.state.sysMessage.message.time}</span> </div> </div> </div> <div className={styles['feedbackBox']}> <Form layout="vertical" onFinish={this.handlerOptions}> <div className={styles['question']}>您还有其他建议和反馈吗?</div> <Form.Item name="content" rules={[{ required: true, message: '请填写反馈内容' }]} > <TextArea placeholder="请输入您的建议和反馈" rows={3}/> </Form.Item> <div className={styles['question']}> 请填写您的联系方式,以便我们及时与您沟通! </div> <div className={styles['horiztion']}> <Form.Item name="phone"> <Input autoComplete='off' placeholder="请输入您的邮箱或者电话号码"/> </Form.Item> <Form.Item> <Button htmlType="submit" type="primary"> </Button> </Form.Item> </div> </Form> </div> </div> </Modal> )} </> ); } } const mapStateToProps = state => ({ global: state.getIn(['global', 'globalConfig']), flatMenu: state.getIn(['global', 'flatMenu']), }); const mapDispatchToProps = dispatch => ({ updateConfig(config) { dispatch(actionCreators.getConfig(config)); }, }); export default connect( mapStateToProps, mapDispatchToProps, )(NoticeIconView);