Commit 302f9e28 authored by 叶飞's avatar 叶飞

feat: notice

parent 175a10ee
......@@ -23,8 +23,7 @@ class HttpRequest {
// }
// ------------------------------------------------------------------------------------
if (!config.ignoreSite && this.getSite()) {
// config.headers['civ-site'] = this.getSite();
config.headers['civ-site'] = "192_168_19_105_site_c8de50fc";
config.headers['civ-site'] = this.getSite();
}
this.removePending(config); //在一个ajax发送前执行一下取消操作
config.cancelToken = new cancelToken(c => {
......
......@@ -8,8 +8,6 @@ const API = {
GET_USER_INFO: '/CityInterface/rest/services.svc/getUserInfo',
GET_OA: '/CityInterface/rest/services/OA.svc/getLoginInfo',
GET_LOGS: '/CityInterface/rest/services/portal.svc/OMMonitor/SaveLoginInfo',
GET_INFORMATION:'/CityInterface/rest/services/CountyProduct.svc/SCADAOper/GetInformationInfo',
GET_MQTT_SITE_CODE:'/CityInterface/rest/services/CountyProduct.svc/SCADAOper/getMqttSitecode',
GET_CITY: 'https://pv.sohu.com/cityjson?ie=utf-8',
GET_ALL_GROUPS_INFO_FORUSER:
'CityInterface/rest/Services/Portal.svc/AuthorityManage/GetAllGroupsInfoForUser',
......@@ -35,12 +33,6 @@ export default vm => {
vm.writeLogs = (data = {}, config = {}) =>
vm.post(API.GET_LOGS, data, config).then(res => Promise.resolve(res));
vm.getInformationInfo = (data = {}) =>
vm.get(API.GET_INFORMATION, data).then(res => Promise.resolve(res));
vm.getMqttSiteCode = (data = {}) =>
vm.get(API.GET_MQTT_SITE_CODE, data).then(res => Promise.resolve(res));
vm.getCity = () => vm.get(API.GET_CITY).then(res => Promise.resolve(res));
vm.generateTokenQuick = (data = {}) =>
......
import http from './apiConfig';
import BaseService from './baseService';
import NotificationService from './notificationService';
BaseService(http);
NotificationService(http);
export default http
import { params } from 'kit_utils';
import qs from 'qs';
const API = {
GET_INFORMATION:
'/CityInterface/rest/services/CountyProduct.svc/SCADAOper/GetInformationInfo',
GET_MQTT_SITE_CODE:
'/CityInterface/rest/services/CountyProduct.svc/SCADAOper/getMqttSitecode',
POST_INFORMATION_STATUS:
'/CityInterface/rest/services/CountyProduct.svc/SCADAOper/PostInformationStatus',
};
export default vm => {
vm.getInformationInfo = (data = {}) =>
vm.get(API.GET_INFORMATION, data).then(res => Promise.resolve(res));
vm.getMqttSiteCode = (data = {}) =>
vm.get(API.GET_MQTT_SITE_CODE, data).then(res => Promise.resolve(res));
vm.postInformationStatus = (params = {}) =>{
let paramsStr=qs.stringify(params, { arrayFormat: 'brackets' });
return vm.post(API.POST_INFORMATION_STATUS+"?"+paramsStr).then(res => Promise.resolve(res));
}
};
......@@ -9,19 +9,25 @@ import styles from './index.less';
import Notifier, {NEW_MESSAGE} from '../Notifier';
class NoticeIconView extends Component {
state = {
constructor(props){
super(props);
this.state = {
count: 0,
noticeData: [],
};
this.notifier = new Notifier(this.props.global.userInfo);
}
async componentDidMount() {
let notifier = new Notifier(this.props.global.userInfo);
notifier.subscribe("NEW_MESSAGE",this.onNewMessage.bind(this));
notifier.start();
this.notifier.subscribe(NEW_MESSAGE,this.onNewMessage.bind(this));
this.notifier.start();
}
componentWillUnmount(){
this.notifier && this.notifier.stop();
}
onNewMessage = messages => {
debugger;
this.setState({
count:messages.totalCount,
noticeData:messages.messages
......@@ -84,13 +90,12 @@ class NoticeIconView extends Component {
return (
<NoticeIcon
className={styles.action}
count={this.state.count > 99 ? 99 : this.state.count}
count={this.state.count}
onItemClick={item => {
this.changeReadState(item);
}}
loading={fetchingNotices}
clearText="清空"
viewMoreText="查看更多"
confirmRead={this.notifier.confirmRead.bind(this.notifier)}
onClear={this.handleNoticeClear}
onPopupVisibleChange={onNoticeVisibleChange}
onViewMore={() => message.info('Click on view more')}
......@@ -102,6 +107,7 @@ class NoticeIconView extends Component {
title="通知"
emptyText="你已查看所有通知"
showViewMore
confirmRead={this.notifier.confirmRead.bind(this.notifier)}
/>
</NoticeIcon>
);
......
import { Button, Result } from 'antd';
import React from 'react';
const NoFoundPage = (props) => {
return (
<Result
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
extra={
<Button type="primary" onClick={() => {props.history.push('/civbase')}}>
Back Home
</Button>
}
/>
)
};
export default NoFoundPage;
import React from 'react';
import {
Avatar,
List,
} from 'antd';
import { Avatar, List } 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 NoticeList = ({
data = [],
onClick,
onClear,
title,
onViewMore,
emptyText,
showClear = true,
clearText,
viewMoreText,
showViewMore = false,
}) => {
if (!data || data.length === 0) {
return (
const Empty = ({ emptyText }) => (
<div className={styles.notFound}>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
......@@ -29,9 +16,12 @@ const NoticeList = ({
/>
<div>{emptyText}</div>
</div>
);
}
);
const NoticeList = ({ data = [], onGoToWidget, emptyText, confirmRead }) => {
if (!data || data.length === 0) {
return <Empty emptyText={emptyText} />;
}
return (
<div>
<List
......@@ -41,65 +31,37 @@ const NoticeList = ({
const itemCls = classNames(styles.item, {
[styles.read]: item.read,
});
// eslint-disable-next-line no-nested-ternary
const leftIcon = item.avatar ? (
typeof item.avatar === 'string' ? (
<Avatar className={styles.avatar} src={item.avatar} />
) : (
<span className={styles.iconElement}>{item.avatar}</span>
)
) : null;
const content = item.InfoContent.replace('\n', '');
console.log(content);
return (
<List.Item
className={itemCls}
key={item.key || i}
onClick={() => onClick && onClick(item)}
>
<List.Item.Meta
className={styles.meta}
avatar={leftIcon}
title={
<div className={styles.title}>
{item.title}
<div
className={styles.extra}
dangerouslySetInnerHTML={{
__html: content,
}}
/>
</div>
}
description={
<div>
<div className={styles.description}>{item.description}</div>
<div className={styles.datetime}>{item.datetime}</div>
</div>
let messageTemplate = <></>;
switch (item.infoType) {
case 'scadaType':
messageTemplate = (
<Alarm message={item} confirmRead={confirmRead} />
);
break;
case 'caseType':
messageTemplate = (
<Case message={item} confirmRead={confirmRead} />
);
break;
case 'sysType':
messageTemplate = (
<Notice message={item} confirmRead={confirmRead} />
);
break;
default:
messageTemplate = (
<Unknown message={item} confirmRead={confirmRead} />
);
break;
}
/>
return (
<List.Item className={itemCls} key={item.id || i}>
{messageTemplate}
</List.Item>
);
}}
/>
<div className={styles.bottomBar}>
{showClear ? (
<div onClick={onClear}>
{clearText} {title}
</div>
) : null}
{showViewMore ? (
<div
onClick={e => {
if (onViewMore) {
onViewMore(e);
}
}}
>
{viewMoreText}
</div>
) : null}
</div>
<div className={styles.bottomBar}>下拉加载更多</div>
</div>
);
};
......
......@@ -7,63 +7,19 @@
display: none;
}
.item {
padding-right: 24px;
padding-left: 24px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s;
.meta {
width: 100%;
}
.avatar {
margin-top: 4px;
background: @component-background;
}
.iconElement {
font-size: 32px;
}
&.read {
opacity: 0.4;
&:hover {
background-color: @primary-1;
}
&:last-child {
border-bottom: 0;
}
&:hover {
background: @primary-1;
}
.title {
margin-bottom: 8px;
font-weight: normal;
}
.description {
font-size: 12px;
line-height: @line-height-base;
}
.datetime {
margin-top: 4px;
font-size: 12px;
line-height: @line-height-base;
}
.extra {
float: right;
margin-top: -1.5px;
margin-right: 0;
color: @text-color-secondary;
font-weight: normal;
}
}
.loadMore {
padding: 8px 0;
color: @primary-6;
text-align: center;
cursor: pointer;
&.loadedAll {
color: rgba(0, 0, 0, 0.25);
cursor: unset;
}
}
}
......
import React from 'react';
import styles from './index.less';
import commonStyles from '../common.less';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
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 }) => {
let alarmContent = message.infoContent;
const history = useHistory();
const goPath = (item) => {
confirmRead(false,[message.id]);
let path= item.webPath ? `/civweb4/${item.webPath}` : `/civweb4/product/scada/AlertMonitoring/AlertMonitoring`
history.push(path);
}
return (
<div className={classNames(styles.scada,commonStyles.messageContainer) } title="点击查看详情" onClick={()=> goPath(message)}>
<div className={commonStyles.title}>
<span>消息</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={() => {
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>{alarmContent.alarmType}</i>
{alarmContent.alarmDevice}
</p>
<p>{alarmContent.alarmContent}</p>
<p>
<font style={{ color: '#ff0000' }} title="点击查看详情">
{alarmContent.alarmValue.split(' ')[0]}
</font>
{alarmContent.alarmValue.split(' ')[1] +
' / ' +
alarmContent.alarmThreshold.split(' ').join('')}
</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 styles from './index.less';
import commonStyles from '../common.less';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
//"caseType":"待办工单","flowName":"维修处理流程","nodeName":"审核关单","content":"请迅速到现场处理","time":"2020-11-03 09:11:12"
export class CaseContent {
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 }) => {
let caseContent = message.infoContent;
const history = useHistory();
const goPath = (item) => {
confirmRead(false,[message.id]);
let path= item.webPath ? `/civweb4/${item.webPath}` : `/civweb4/product/maintenance/CaseManage/CaseDoingBox/StardCaseDoingBoxView|isDelay=1`
history.push(path);
}
return (
<div className={classNames(styles.case,commonStyles.messageContainer)} title="点击查看详情" onClick={()=> goPath(message)}>
<div className={commonStyles.title}>
<span>消息</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={() => {
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;
}
\ No newline at end of file
import React from 'react';
import styles from './index.less';
import commonStyles from '../common.less';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
//'平台公告','{"noticeTitle":"光谷智慧园停水","noticeType":"停水公告","noticeContent":"从今天中午20点开始停水,停三个小时!","time":"2020-11-03 09:11:12"
export class NoticeContent {
constructor({
noticeTitle,
noticeType,
noticeContent,
time
} = content) {
this.noticeTitle = noticeTitle;
this.noticeType = noticeType;
this.noticeContent = noticeContent;
this.time = time;
}
}
const Notice = ({ message, confirmRead }) => {
let noticeContent = message.infoContent;
const history = useHistory();
const goPath = (item) => {
confirmRead(false,[message.id]);
let path= item.webPath ? `/civweb4/${item.webPath}` : `/civweb4/product/notification/HistoryNotice/HistoryNotice`
history.push(path);
}
return (
<div className={classNames(styles.notice,commonStyles.messageContainer)} title="点击查看详情" onClick={()=> goPath(message)}>
<div className={commonStyles.title}>
<span>公告</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={() => {
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>{noticeContent.noticeType}</i>
{noticeContent.noticeTitle}
</p>
<p>{noticeContent.noticeContent}</p>
<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;
}
\ No newline at end of file
import React from 'react';
import styles from './index.less';
import commonStyles from '../common.less';
import classNames from 'classnames';
const Unknown = ({ message, confirmRead }) => {
let alarmContent = message.infoContent;
return (
<div className={classNames(styles.unknown,commonStyles.messageContainer) } title="点击查看详情">
<div className={commonStyles.title}>
<span>消息</span>
<img
className={commonStyles.confirm}
title="点击标为已读"
onClick={() => {
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>{message.infoContent}</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: 5px;
}
.messageTime {
float: right;
margin-right: 30px;
}
}
}
\ No newline at end of file
import React from 'react';
import {
Badge,
Tabs,
} from 'antd';
import { Badge, Tabs } from 'antd';
import classNames from 'classnames';
import { connect } from 'react-redux';
import useMergeValue from 'use-merge-value';
......@@ -13,41 +10,56 @@ import Icon from '@ant-design/icons';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
import NoticeList from './NoticeList';
import http from '../../api';
const { TabPane } = Tabs;
const messageSvg = () => (
<svg version="1.1" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" space="preserve">
<path fill="#5F718C" d="M20.486,16.373l-1.721-2.246v-0.984v-0.352V9.924c0-1.919-0.664-3.698-1.871-5.007
<svg
version="1.1"
x="0px"
y="0px"
width="24px"
height="24px"
viewBox="0 0 24 24"
enable-background="new 0 0 24 24"
space="preserve"
>
<path
fill="#5F718C"
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="#5F718C" 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
c0,0.334,0.123,0.654,0.336,0.875l1.642,2.164l0.013,1.037L4.899,17.996z"
/>
<path
fill="#5F718C"
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"/>
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} style={{transform: 'scale(0.88)'}}/>;
);
const BellOutlined = props => <Icon component={messageSvg} {...props} />;
const NoticeIcon = props => {
const getNotificationBox = () => {
const {
children,
loading,
confirmRead,
onClear,
onTabChange,
onItemClick,
onViewMore,
clearText,
viewMoreText,
} = props;
if (!children) {
return null;
}
const panes = [];
React.Children.forEach(children, child => {
if (!child) {
return;
......@@ -66,8 +78,6 @@ const NoticeIcon = props => {
panes.push(
<NoticeList
{...child.props}
clearText={clearText}
viewMoreText={viewMoreText}
data={list}
key={child}
onClear={() => onClear && onClear(title, tabKey)}
......@@ -82,8 +92,8 @@ const NoticeIcon = props => {
return (
<>
<div className={styles.header}>
<span>通知</span>
<span>全部标记已读</span>
<span className={styles.title}>通知</span>
<span onClick={()=>{confirmRead(true)}}>全部标记已读</span>
</div>
{panes}
</>
......@@ -103,6 +113,8 @@ const NoticeIcon = props => {
<span className={classNames(noticeButtonClass, { opened: visible })}>
<Badge
count={props.count}
overflowCount={99}
offset={[-8, 8]}
style={{ boxShadow: 'none' }}
className={styles.badge}
>
......
......@@ -39,10 +39,17 @@
padding: 8px;
display: flex;
justify-content: space-between;
.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;
}
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -3,7 +3,7 @@ import React from 'react';
import { Helmet } from 'react-helmet';
import { connect } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import { BrowserRouter as Router, Switch } from 'react-router-dom';
import { BrowserRouter as Router, Switch,Redirect } from 'react-router-dom';
import { dyRoutes } from '../../routes/config';
......@@ -25,7 +25,11 @@ function App(props) {
/>
</Helmet>
<Router basename="civbase">
<Switch>{renderRoutes(dyRoutes(props.menu || []).routes)}</Switch>
<Switch>
{renderRoutes(dyRoutes(props.menu || []).routes)}
{/* <Redirect to="/notFound" /> */}
</Switch>
</Router>
</>
);
......
......@@ -4,6 +4,7 @@ import BootPage from '../pages/bootpage';
import Login from '../pages/user/login';
export const dyRoutes = routes => {
debugger;
const dyRoutes = routes;
return {
routes: [
......
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