|
|
@@ -1,20 +1,16 @@
|
|
|
import { PageContainer } from '@ant-design/pro-layout';
|
|
|
import { EChartsOption } from 'echarts';
|
|
|
-import { useEffect, useState } from 'react';
|
|
|
-import { Statistic, StatisticCard } from '@ant-design/pro-card';
|
|
|
-import { Card, Select } from 'antd';
|
|
|
+import { useEffect, useRef, useState } from 'react';
|
|
|
+import { Card, Col } from 'antd';
|
|
|
import './index.less';
|
|
|
-import Header from '@/components/DashBoard/header';
|
|
|
-import Echarts from '@/components/DashBoard/echarts';
|
|
|
import Service from './service';
|
|
|
import { observer } from '@formily/react';
|
|
|
import { model } from '@formily/reactive';
|
|
|
-
|
|
|
-const imgStyle = {
|
|
|
- display: 'block',
|
|
|
- width: 42,
|
|
|
- height: 42,
|
|
|
-};
|
|
|
+import DashBoard, { DashBoardTopCard } from '@/components/DashBoard';
|
|
|
+import { FireOutlined } from '@ant-design/icons';
|
|
|
+import styles from './index.less';
|
|
|
+import moment from 'moment';
|
|
|
+import Echarts from '@/components/DashBoard/echarts';
|
|
|
|
|
|
const service = new Service();
|
|
|
export const state = model<{
|
|
|
@@ -24,6 +20,8 @@ export const state = model<{
|
|
|
enabledConfig: number;
|
|
|
disabledConfig: number;
|
|
|
alarmList: any[];
|
|
|
+ ranking: { targetId: string; targetName: string; count: number }[];
|
|
|
+ fifteenOptions: any;
|
|
|
}>({
|
|
|
today: 0,
|
|
|
thisMonth: 0,
|
|
|
@@ -31,12 +29,18 @@ export const state = model<{
|
|
|
enabledConfig: 0,
|
|
|
disabledConfig: 0,
|
|
|
alarmList: [],
|
|
|
+ ranking: [],
|
|
|
+ fifteenOptions: {},
|
|
|
});
|
|
|
|
|
|
type DashboardItem = {
|
|
|
group: string;
|
|
|
data: Record<string, any>;
|
|
|
};
|
|
|
+
|
|
|
+type RefType = {
|
|
|
+ getValues: Function;
|
|
|
+};
|
|
|
const Dashboard = observer(() => {
|
|
|
const [options, setOptions] = useState<EChartsOption>({});
|
|
|
|
|
|
@@ -48,12 +52,12 @@ const Dashboard = observer(() => {
|
|
|
dimension: 'agg',
|
|
|
group: 'today',
|
|
|
params: {
|
|
|
- time: '1h',
|
|
|
+ time: '1d',
|
|
|
targetType: 'device',
|
|
|
format: 'HH:mm:ss',
|
|
|
- from: 'now-1d',
|
|
|
+ from: moment(new Date(new Date().setHours(0, 0, 0, 0))).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
to: 'now',
|
|
|
- limit: 24,
|
|
|
+ // limit: 24,
|
|
|
},
|
|
|
};
|
|
|
// 当月告警
|
|
|
@@ -71,45 +75,63 @@ const Dashboard = observer(() => {
|
|
|
from: 'now-1M',
|
|
|
},
|
|
|
};
|
|
|
- // 告警趋势
|
|
|
- const chartData = {
|
|
|
- dashboard: 'alarm',
|
|
|
- object: 'record',
|
|
|
- measurement: 'trend',
|
|
|
- dimension: 'agg',
|
|
|
- group: 'alarmTrend',
|
|
|
- params: {
|
|
|
- time: '1M',
|
|
|
- targetType: 'device', // product、device、org、other
|
|
|
- from: 'now-1y', // now-1d、now-1w、now-1M、now-1y
|
|
|
- format: 'M月',
|
|
|
- to: 'now',
|
|
|
- limit: 12,
|
|
|
- },
|
|
|
- };
|
|
|
|
|
|
- // 告警排名
|
|
|
- const order = {
|
|
|
+ const fifteen = {
|
|
|
dashboard: 'alarm',
|
|
|
object: 'record',
|
|
|
- measurement: 'rank',
|
|
|
+ measurement: 'trend',
|
|
|
dimension: 'agg',
|
|
|
- group: 'alarmRank',
|
|
|
+ group: '15day',
|
|
|
params: {
|
|
|
- time: '1h',
|
|
|
- targetType: 'device',
|
|
|
- from: 'now-1w',
|
|
|
+ time: '1d',
|
|
|
+ format: 'yyyy-MM-dd',
|
|
|
+ targetType: 'product',
|
|
|
+ from: 'now-15d',
|
|
|
to: 'now',
|
|
|
- limit: 10,
|
|
|
+ limit: 15,
|
|
|
},
|
|
|
};
|
|
|
|
|
|
const getDashboard = async () => {
|
|
|
- const resp = await service.dashboard([today, thisMonth, chartData, order]);
|
|
|
+ const resp = await service.dashboard([today, thisMonth, fifteen]);
|
|
|
if (resp.status === 200) {
|
|
|
const _data = resp.result as DashboardItem[];
|
|
|
state.today = _data.find((item) => item.group === 'today')?.data.value;
|
|
|
state.thisMonth = _data.find((item) => item.group === 'thisMonth')?.data.value;
|
|
|
+
|
|
|
+ const fifteenData = _data.filter((item) => item.group === '15day').map((item) => item.data);
|
|
|
+ state.fifteenOptions = {
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: fifteenData.map((item) => item.timeString),
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: '2%',
|
|
|
+ bottom: 0,
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '告警数',
|
|
|
+ data: fifteenData.map((item, index) => index * 6),
|
|
|
+
|
|
|
+ type: 'bar',
|
|
|
+ itemStyle: {
|
|
|
+ color: '#2F54EB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -142,149 +164,205 @@ const Dashboard = observer(() => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const getCurrentAlarm = async () => {
|
|
|
+ const alarmLevel = await service.getAlarmLevel();
|
|
|
+
|
|
|
+ const currentAlarm = await service.getAlarm({});
|
|
|
+ if (currentAlarm.status === 200) {
|
|
|
+ if (alarmLevel.status === 200) {
|
|
|
+ const levels = alarmLevel.result.levels;
|
|
|
+ state.alarmList = currentAlarm.result?.data.map((item: { level: any }) => ({
|
|
|
+ ...item,
|
|
|
+ level: levels.find((l: any) => l.level === item.level)?.title,
|
|
|
+ }));
|
|
|
+ } else {
|
|
|
+ state.alarmList = currentAlarm.result?.data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
useEffect(() => {
|
|
|
getDashboard();
|
|
|
getAlarmConfig();
|
|
|
+ getCurrentAlarm();
|
|
|
}, []);
|
|
|
|
|
|
const getEcharts = async (params: any) => {
|
|
|
- // 请求数据
|
|
|
- console.log(params);
|
|
|
-
|
|
|
- setOptions({
|
|
|
- xAxis: {
|
|
|
- type: 'category',
|
|
|
- data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
|
+ // 告警趋势
|
|
|
+ const chartData = {
|
|
|
+ dashboard: 'alarm',
|
|
|
+ object: 'record',
|
|
|
+ measurement: 'trend',
|
|
|
+ dimension: 'agg',
|
|
|
+ group: 'alarmTrend',
|
|
|
+ params: {
|
|
|
+ targetType: 'device', // product、device、org、other
|
|
|
+ format: 'yyyy年-M月',
|
|
|
+ time: '1M',
|
|
|
+ // from: 'now-1y', // now-1d、now-1w、now-1M、now-1y
|
|
|
+ // to: 'now',
|
|
|
+ limit: 12,
|
|
|
+ // time: params.time.type === 'today' ? '1h' : '1d',
|
|
|
+ from: moment(params.time.start).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ to: moment(params.time.end).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ // limit: 30,
|
|
|
},
|
|
|
- yAxis: {
|
|
|
- type: 'value',
|
|
|
+ };
|
|
|
+
|
|
|
+ // 告警排名
|
|
|
+ const order = {
|
|
|
+ dashboard: 'alarm',
|
|
|
+ object: 'record',
|
|
|
+ measurement: 'rank',
|
|
|
+ dimension: 'agg',
|
|
|
+ group: 'alarmRank',
|
|
|
+ params: {
|
|
|
+ // time: '1h',
|
|
|
+ time: params.time.type === 'today' ? '1h' : '1d',
|
|
|
+ targetType: 'device',
|
|
|
+ from: moment(params.time.start).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ to: moment(params.time.end).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ limit: 9,
|
|
|
},
|
|
|
- series: [
|
|
|
- {
|
|
|
- data: [150, 230, 224, 218, 135, 147, 260],
|
|
|
- type: 'line',
|
|
|
+ };
|
|
|
+ // 请求数据
|
|
|
+ const resp = await service.dashboard([chartData, order]);
|
|
|
+
|
|
|
+ if (resp?.status === 200) {
|
|
|
+ const xData: string[] = [];
|
|
|
+ const sData: number[] = [];
|
|
|
+ resp.result
|
|
|
+ .filter((item: any) => item.group === 'alarmTrend')
|
|
|
+ .forEach((item: any) => {
|
|
|
+ xData.push(item.data.timeString);
|
|
|
+ sData.push(item.data.value);
|
|
|
+ });
|
|
|
+ setOptions({
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow',
|
|
|
+ },
|
|
|
},
|
|
|
- ],
|
|
|
- });
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: 'category',
|
|
|
+ data: xData.reverse(),
|
|
|
+ axisTick: {
|
|
|
+ alignWithLabel: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: 'Direct',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: '60%',
|
|
|
+ data: sData.reverse(),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ state.ranking = resp.result
|
|
|
+ ?.filter((item: any) => item.group === 'alarmRank')
|
|
|
+ .map((d: { data: { value: any } }) => d.data?.value)
|
|
|
+ .sort((a: { count: number }, b: { count: number }) => b.count - a.count);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
+ const alarmCountRef = useRef<RefType>();
|
|
|
return (
|
|
|
<PageContainer>
|
|
|
- <div style={{ display: 'flex' }}>
|
|
|
- <StatisticCard
|
|
|
- title="今日告警"
|
|
|
- statistic={{
|
|
|
- value: 75,
|
|
|
- suffix: '次',
|
|
|
- }}
|
|
|
- chart={
|
|
|
- <img
|
|
|
- src="https://gw.alipayobjects.com/zos/alicdn/PmKfn4qvD/mubiaowancheng-lan.svg"
|
|
|
- width="100%"
|
|
|
- alt="进度条"
|
|
|
- />
|
|
|
- }
|
|
|
- footer={
|
|
|
- <>
|
|
|
- <Statistic value={15.1} title="当月告警" suffix="次" layout="horizontal" />
|
|
|
- </>
|
|
|
- }
|
|
|
- style={{ width: '24%', marginRight: '5px' }}
|
|
|
- />
|
|
|
- <StatisticCard
|
|
|
- statistic={{
|
|
|
- title: '告警配置',
|
|
|
- value: 2176,
|
|
|
- icon: (
|
|
|
- <img
|
|
|
- style={imgStyle}
|
|
|
- src="https://gw.alipayobjects.com/mdn/rms_7bc6d8/afts/img/A*dr_0RKvVzVwAAAAAAAAAAABkARQnAQ"
|
|
|
- alt="icon"
|
|
|
- />
|
|
|
- ),
|
|
|
- }}
|
|
|
- style={{ width: '25%', marginRight: '5px' }}
|
|
|
- footer={
|
|
|
- <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
|
- <Statistic value={76} title="正常" suffix="次" layout="horizontal" />
|
|
|
- <Statistic value={76} title="禁用" suffix="次" layout="horizontal" />
|
|
|
+ <div className={'alarm-dash-board'}>
|
|
|
+ <DashBoardTopCard>
|
|
|
+ <DashBoardTopCard.Item
|
|
|
+ title="今日告警"
|
|
|
+ value={state.today}
|
|
|
+ footer={[{ title: '当月告警', value: state.thisMonth, status: 'success' }]}
|
|
|
+ span={6}
|
|
|
+ >
|
|
|
+ <Echarts options={state.fifteenOptions} />
|
|
|
+ </DashBoardTopCard.Item>
|
|
|
+ <DashBoardTopCard.Item
|
|
|
+ title="告警配置"
|
|
|
+ value={state.config}
|
|
|
+ footer={[
|
|
|
+ { title: '正常', value: state.enabledConfig, status: 'success' },
|
|
|
+ { title: '禁用', value: state.disabledConfig, status: 'error' },
|
|
|
+ ]}
|
|
|
+ span={6}
|
|
|
+ >
|
|
|
+ <img src={require('/public/images/media/dashboard-1.png')} />
|
|
|
+ </DashBoardTopCard.Item>
|
|
|
+
|
|
|
+ <Col span={12}>
|
|
|
+ <div className={'dash-board-top-item'}>
|
|
|
+ <div className={'content-left'}>
|
|
|
+ <div className={'content-left-title'}>最新告警</div>
|
|
|
+ <div>
|
|
|
+ <ul>
|
|
|
+ {state.alarmList.slice(0, 3).map((item) => (
|
|
|
+ <li key={item.id}>
|
|
|
+ <div
|
|
|
+ style={{ display: 'flex', justifyContent: 'space-between', margin: 10 }}
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <FireOutlined style={{ marginRight: 5 }} />{' '}
|
|
|
+ {moment(item.alarmTime).format('YYYY-MM-DD hh:mm:ss')}
|
|
|
+ </div>
|
|
|
+ <div>{item.alarmName}</div>
|
|
|
+ <div>{item.state?.text}</div>
|
|
|
+ <div>{item.level}</div>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- }
|
|
|
- />
|
|
|
- <div style={{ width: '50%' }}>
|
|
|
- <StatisticCard
|
|
|
- title="最新告警"
|
|
|
- statistic={{
|
|
|
- // title: '最新告警'
|
|
|
- value: undefined,
|
|
|
- }}
|
|
|
- chart={
|
|
|
- <ul>
|
|
|
- {[
|
|
|
- {
|
|
|
- dateTime: '2022-01-01 00:00:00',
|
|
|
- name: '一楼烟感告警',
|
|
|
- product: '产品',
|
|
|
- level: '1极告警',
|
|
|
- },
|
|
|
- {
|
|
|
- dateTime: '2022-01-01 00:00:00',
|
|
|
- name: '一楼烟感告警',
|
|
|
- product: '产品',
|
|
|
- level: '1极告警',
|
|
|
- },
|
|
|
- {
|
|
|
- dateTime: '2022-01-01 00:00:00',
|
|
|
- name: '一楼烟感告警',
|
|
|
- product: '产品',
|
|
|
- level: '1极告警',
|
|
|
- },
|
|
|
- {
|
|
|
- dateTime: '2022-01-01 00:00:00',
|
|
|
- name: '一楼烟感告警',
|
|
|
- product: '产品',
|
|
|
- level: '1极告警',
|
|
|
- },
|
|
|
- ].map((item) => (
|
|
|
- <li>
|
|
|
- <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
|
- <div>{item.dateTime}</div>
|
|
|
- <div>{item.name}</div>
|
|
|
- <div>{item.product}</div>
|
|
|
- <div>{item.level}</div>
|
|
|
- {JSON.stringify(state)}
|
|
|
- </div>
|
|
|
+ </Col>
|
|
|
+ </DashBoardTopCard>
|
|
|
+ </div>
|
|
|
+ <Card style={{ marginTop: 10 }}>
|
|
|
+ <DashBoard
|
|
|
+ title="告警统计"
|
|
|
+ height={600}
|
|
|
+ options={options}
|
|
|
+ onParamsChange={getEcharts}
|
|
|
+ ref={alarmCountRef}
|
|
|
+ echartsAfter={
|
|
|
+ <div className={styles.alarmRank}>
|
|
|
+ <h4>告警排名</h4>
|
|
|
+ <ul className={styles.rankingList}>
|
|
|
+ {state.ranking?.map((item, i) => (
|
|
|
+ <li key={item.targetId}>
|
|
|
+ <img
|
|
|
+ src={require(`/public/images/rule-engine/dashboard/ranking/${i + 1}.png`)}
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ {/*<span className={`${styles.rankingItemNumber} ${i < 3 ? styles.active : ''}`}>*/}
|
|
|
+ {/* {i + 1}*/}
|
|
|
+ {/*</span>*/}
|
|
|
+ <span className={styles.rankingItemTitle} title={item.targetName}>
|
|
|
+ {item.targetName}
|
|
|
+ </span>
|
|
|
+ <span className={styles.rankingItemValue}>{item.count}</span>
|
|
|
</li>
|
|
|
))}
|
|
|
</ul>
|
|
|
- }
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <Card style={{ marginTop: 10 }}>
|
|
|
- <div
|
|
|
- // className={classNames(Style['dash-board-echarts'], className)}
|
|
|
- style={{
|
|
|
- height: 200,
|
|
|
- }}
|
|
|
- >
|
|
|
- <Header
|
|
|
- title={'告警统计'}
|
|
|
- extraParams={{
|
|
|
- key: 'test',
|
|
|
- Children: (
|
|
|
- <Select
|
|
|
- options={[
|
|
|
- { label: '设备', value: 'device' },
|
|
|
- { label: '产品', value: 'product' },
|
|
|
- ]}
|
|
|
- ></Select>
|
|
|
- ),
|
|
|
- }}
|
|
|
- onParamsChange={getEcharts}
|
|
|
- />
|
|
|
- <Echarts options={options} />
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ />
|
|
|
</Card>
|
|
|
</PageContainer>
|
|
|
);
|