After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 9.5 MiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 353 KiB |
After Width: | Height: | Size: 257 KiB |
After Width: | Height: | Size: 192 KiB |
After Width: | Height: | Size: 253 KiB |
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 251 KiB |
After Width: | Height: | Size: 185 KiB |
After Width: | Height: | Size: 63 KiB |
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
// export function getMembers(orgId) {
|
|||
// return dispatch => basicAction({
|
|||
// type: 'get',
|
|||
// dispatch: dispatch,
|
|||
// actionType: 'GET_MEMBERS',
|
|||
// url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`,
|
|||
// msg: { error: '获取用户列表失败' },
|
|||
// reducer: { name: 'members' }
|
|||
// });
|
|||
// }
|
@ -0,0 +1,7 @@ |
|||
'use strict'; |
|||
|
|||
import * as example from './example' |
|||
|
|||
export default { |
|||
...example, |
|||
} |
@ -0,0 +1,45 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
|
|||
function BasicInfo() { |
|||
|
|||
return <Box title={"基本信息"} > |
|||
<div className='_basic_info'> |
|||
<div className='_basic_row1'> |
|||
<div className='_item1' /> |
|||
<div className='_item2'> |
|||
<div className='_basictitlebg'> |
|||
<div className='_basic_title'>南昌县</div> |
|||
<span className='_basic_sub_title'>Nanchang county</span> |
|||
</div> |
|||
|
|||
<div className='_basic_text'> |
|||
<span>区域面积:</span> |
|||
<span className='_text_number_color'>1742.3km³</span> |
|||
</div> |
|||
|
|||
<div className='_basic_text'> |
|||
<span>人口:</span> |
|||
<span className='_text_number_color'>14.43万</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className='_basic_row2'> |
|||
<div className='_item1'> |
|||
<span>社区数量</span> |
|||
<span className='_number'>53个</span> |
|||
</div> |
|||
<div className='basicinterval' /> |
|||
<div className='_item2'> |
|||
<span>房屋数量</span> |
|||
<span className='_number'>630座</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</Box> |
|||
} |
|||
|
|||
export default BasicInfo; |
|||
|
|||
|
@ -0,0 +1,14 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import './style.less'; |
|||
|
|||
function CitySafty(props) { |
|||
|
|||
return <Box title={"城市安全"} > |
|||
城市安全 |
|||
</Box> |
|||
} |
|||
|
|||
export default CitySafty; |
|||
|
|||
|
@ -0,0 +1,32 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
|
|||
function Infrastructure(props) { |
|||
|
|||
const data = [ |
|||
{ name: '烟感设备', number: 32 }, |
|||
{ name: '温度设备', number: 32 }, |
|||
{ name: '摄像头设备', number: 32 }, |
|||
{ name: '电梯设备', number: 32 }, |
|||
] |
|||
return <Box title={"基础设施"} > |
|||
<div className='_basic_device'> |
|||
{ |
|||
data.map((s, index) => { |
|||
return <div className='_device_item'> |
|||
<div className={'_device_img' + (index + 1)} /> |
|||
<div className='_device_text'> |
|||
<div>{s.name}</div> |
|||
<div><span className='_device_number'>{s.number}</span> 个</div> |
|||
</div> |
|||
</div> |
|||
}) |
|||
} |
|||
|
|||
</div> |
|||
</Box> |
|||
} |
|||
|
|||
export default Infrastructure; |
|||
|
|||
|
@ -0,0 +1,35 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
|
|||
function PopulationDynamics() { |
|||
|
|||
const data = [ |
|||
{ title: '常驻人口', number: 447 }, |
|||
{ title: '流动人口', number: 447 }, |
|||
{ title: '境外人口', number: 447 }, |
|||
{ title: '未落户人口', number: 447 }, |
|||
{ title: '贫困人口', number: 447 }, |
|||
{ title: '老龄人口', number: 447 }, |
|||
] |
|||
return <Box title={"人口动态"} > |
|||
<div className='_person_trends'> |
|||
<div className='_person_tends_item1' /> |
|||
<div className='_person_tends_item2'> |
|||
{ |
|||
data.map(s => { |
|||
return <div className='_person_text'> |
|||
<div className='_person_title'>{s.title}</div> |
|||
<div className='_person_number'>{s.number}</div> |
|||
<span>万人</span> |
|||
</div> |
|||
}) |
|||
} |
|||
|
|||
</div> |
|||
</div> |
|||
</Box> |
|||
} |
|||
|
|||
export default PopulationDynamics; |
|||
|
|||
|
@ -0,0 +1,33 @@ |
|||
/* 轮播列表组件 */ |
|||
import React from 'react'; |
|||
import ScrollBoard from './scrollBoard'; |
|||
import NoData from './noData'; |
|||
import './index.less'; |
|||
function CarouselList(props) { |
|||
const { |
|||
header = [], data = [], rowNum = 4, height, columnWidth, multiellipsis, waitTime = 2000, marginTop, ...restProps |
|||
} = props; |
|||
|
|||
const config = { |
|||
header, |
|||
rowNum, |
|||
headerBGC: 'rgba(81, 200, 247, 0.2)', |
|||
oddRowBGC: 'transparent', |
|||
evenRowBGC: 'transparent', |
|||
headerHeight: 30, |
|||
data, |
|||
waitTime, |
|||
columnWidth: columnWidth || [], |
|||
}; |
|||
|
|||
return data.length > 0 ? ( |
|||
<ScrollBoard |
|||
config={config} |
|||
style={{ height }} |
|||
className={multiellipsis ? 'scroll-board-multi' : 'scroll-board'} |
|||
{...restProps} |
|||
/> |
|||
) : <NoData marginTop={marginTop || 0} />; |
|||
} |
|||
|
|||
export default CarouselList; |
@ -0,0 +1,77 @@ |
|||
.opcityBackground { |
|||
background-color: rgba(8, 27, 55, 0.6); |
|||
} |
|||
|
|||
.card-title { |
|||
height: 31px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 24px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 2px; |
|||
} |
|||
|
|||
/* 滚动列表 */ |
|||
.scroll-board { |
|||
width: 533px; |
|||
height: 220px; |
|||
margin-top: 10px; |
|||
margin-left: 6px; |
|||
|
|||
.header { |
|||
height: 30px; |
|||
border-top: 1px solid #0047ba; |
|||
border-bottom: 1px solid #0047ba; |
|||
|
|||
.header-item { |
|||
// background: rgba(12, 49, 110, 0.3); |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
.rows { |
|||
.row-item { |
|||
font-size: 16px; |
|||
} |
|||
|
|||
.row-item:hover { |
|||
background: linear-gradient(270deg, rgba(17, 183, 247, 0) 0%, rgba(17, 183, 247, 0.85) 100%); |
|||
color: #9ac8fc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.scroll-board-multi { |
|||
padding: 5px 0px 5px; |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
font-size: 12px !important; |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
// border-bottom: 1px solid #124C79 !important; |
|||
} |
|||
|
|||
.rows { |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
|
|||
.row-item { |
|||
border-bottom: 1px solid #124C79 !important; |
|||
} |
|||
|
|||
.row-item:hover { |
|||
background: linear-gradient(270deg, rgba(17, 183, 247, 0) 0%, rgba(17, 183, 247, 0.85) 100%); |
|||
color: #9ac8fc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
._sorrow { |
|||
display: inline-block; |
|||
width: 15px; |
|||
height: 15px; |
|||
background: url('/assets/images/homePage/bigscreen/sorrow.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-left: 13px; |
|||
} |
@ -0,0 +1,18 @@ |
|||
/* 公共模块暂无数据组件 */ |
|||
import React from 'react'; |
|||
import { Empty } from 'antd'; |
|||
|
|||
function NoData({ height = 180, marginTop = 0 }) { |
|||
return ( |
|||
<Empty |
|||
image="/assets/images/homePage/bigscreen/empty.png" |
|||
imageStyle={{ |
|||
height, |
|||
marginTop |
|||
}} |
|||
description={false} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
export default NoData; |
@ -0,0 +1,469 @@ |
|||
import React, { |
|||
useEffect, useState, useRef, useMemo, forwardRef, |
|||
} from 'react'; |
|||
|
|||
import PropTypes from 'prop-types'; |
|||
|
|||
import classnames from 'classnames'; |
|||
|
|||
import { deepMerge } from '@jiaminghi/charts/lib/util/index'; |
|||
|
|||
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'; |
|||
|
|||
import { useAutoResize, co } from '@jiaminghi/data-view-react'; |
|||
|
|||
import './style.less'; |
|||
|
|||
const defaultConfig = { |
|||
/** |
|||
* @description Board header |
|||
* @type {Array<String>} |
|||
* @default header = [] |
|||
* @example header = ['column1', 'column2', 'column3'] |
|||
*/ |
|||
header: [], |
|||
/** |
|||
* @description Board data |
|||
* @type {Array<Array>} |
|||
* @default data = [] |
|||
*/ |
|||
data: [], |
|||
/** |
|||
* @description Row num |
|||
* @type {Number} |
|||
* @default rowNum = 5 |
|||
*/ |
|||
rowNum: 5, |
|||
/** |
|||
* @description Header background color |
|||
* @type {String} |
|||
* @default headerBGC = '#00BAFF' |
|||
*/ |
|||
headerBGC: '#00BAFF', |
|||
/** |
|||
* @description Odd row background color |
|||
* @type {String} |
|||
* @default oddRowBGC = '#003B51' |
|||
*/ |
|||
oddRowBGC: '#003B51', |
|||
/** |
|||
* @description Even row background color |
|||
* @type {String} |
|||
* @default evenRowBGC = '#003B51' |
|||
*/ |
|||
evenRowBGC: '#0A2732', |
|||
/** |
|||
* @description Scroll wait time |
|||
* @type {Number} |
|||
* @default waitTime = 2000 |
|||
*/ |
|||
waitTime: 2000, |
|||
/** |
|||
* @description Header height |
|||
* @type {Number} |
|||
* @default headerHeight = 35 |
|||
*/ |
|||
headerHeight: 35, |
|||
/** |
|||
* @description Column width |
|||
* @type {Array<Number>} |
|||
* @default columnWidth = [] |
|||
*/ |
|||
columnWidth: [], |
|||
/** |
|||
* @description Column align |
|||
* @type {Array<String>} |
|||
* @default align = [] |
|||
* @example align = ['left', 'center', 'right'] |
|||
*/ |
|||
align: [], |
|||
/** |
|||
* @description Show index |
|||
* @type {Boolean} |
|||
* @default index = false |
|||
*/ |
|||
index: false, |
|||
/** |
|||
* @description index Header |
|||
* @type {String} |
|||
* @default indexHeader = '#' |
|||
*/ |
|||
indexHeader: '#', |
|||
/** |
|||
* @description Carousel type |
|||
* @type {String} |
|||
* @default carousel = 'single' |
|||
* @example carousel = 'single' | 'page' |
|||
*/ |
|||
carousel: 'single', |
|||
/** |
|||
* @description Pause scroll when mouse hovered |
|||
* @type {Boolean} |
|||
* @default hoverPause = true |
|||
* @example hoverPause = true | false |
|||
*/ |
|||
hoverPause: true, |
|||
}; |
|||
|
|||
function calcHeaderData({ header, index, indexHeader }) { |
|||
if (!header.length) { |
|||
return []; |
|||
} |
|||
|
|||
header = [...header]; |
|||
|
|||
if (index) header.unshift(indexHeader); |
|||
|
|||
return header; |
|||
} |
|||
|
|||
function calcRows({ |
|||
data, index, headerBGC, rowNum, |
|||
}) { |
|||
if (index) { |
|||
data = data.map((row, i) => { |
|||
row = [...row]; |
|||
|
|||
const indexTag = `<span class="index" style="background-color: ${headerBGC};">${i |
|||
+ 1}</span>`; |
|||
|
|||
row.unshift(indexTag); |
|||
|
|||
return row; |
|||
}); |
|||
} |
|||
|
|||
data = data.map((ceils, i) => ({ ceils, rowIndex: i })); |
|||
|
|||
const rowLength = data.length; |
|||
|
|||
if (rowLength > rowNum && rowLength < 2 * rowNum) { |
|||
data = [...data, ...data]; |
|||
} |
|||
|
|||
return data.map((d, i) => ({ ...d, scroll: i })); |
|||
} |
|||
|
|||
function calcAligns(mergedConfig, header) { |
|||
const columnNum = header.length; |
|||
|
|||
const aligns = new Array(columnNum).fill('left'); |
|||
|
|||
const { align } = mergedConfig; |
|||
|
|||
return deepMerge(aligns, align); |
|||
} |
|||
|
|||
const ScrollBoard = forwardRef(({ |
|||
onClick, config = {}, className, style, onMouseOver, |
|||
}, ref) => { |
|||
const { width, height, domRef } = useAutoResize(ref); |
|||
|
|||
const [state, setState] = useState({ |
|||
mergedConfig: null, |
|||
|
|||
header: [], |
|||
|
|||
rows: [], |
|||
|
|||
rowsShow: [], |
|||
|
|||
widths: [], |
|||
|
|||
heights: [], |
|||
|
|||
aligns: [], |
|||
}); |
|||
|
|||
const { |
|||
mergedConfig, header, rows, widths, heights, aligns, rowsShow, |
|||
} = state; |
|||
|
|||
const stateRef = useRef({ |
|||
...state, |
|||
rowsData: [], |
|||
avgHeight: 0, |
|||
animationIndex: 0, |
|||
}); |
|||
|
|||
Object.assign(stateRef.current, state); |
|||
|
|||
function onResize() { |
|||
if (!mergedConfig) return; |
|||
|
|||
const widths = calcWidths(mergedConfig, stateRef.current.rowsData); |
|||
|
|||
const heights = calcHeights(mergedConfig, header); |
|||
|
|||
const data = { widths, heights }; |
|||
|
|||
Object.assign(stateRef.current, data); |
|||
setState((state) => ({ ...state, ...data })); |
|||
} |
|||
const [init, setInit] = useState(true); |
|||
|
|||
function calcData() { |
|||
// const mergedConfig = deepMerge(
|
|||
// deepClone(defaultConfig, true),
|
|||
// config || {},
|
|||
// );
|
|||
const mergedConfig = { |
|||
...defaultConfig, |
|||
...config, |
|||
}; |
|||
|
|||
const header = calcHeaderData(mergedConfig); |
|||
|
|||
const rows = calcRows(mergedConfig); |
|||
|
|||
const widths = calcWidths(mergedConfig, stateRef.current.rowsData); |
|||
|
|||
const heights = calcHeights(mergedConfig, header); |
|||
|
|||
const aligns = calcAligns(mergedConfig, header); |
|||
|
|||
const data = { |
|||
mergedConfig, |
|||
header, |
|||
rows, |
|||
widths, |
|||
aligns, |
|||
heights: init ? heights : state.heights.concat(heights), |
|||
rowsShow: init ? rows : state.rowsShow, |
|||
}; |
|||
setInit(false); |
|||
Object.assign(stateRef.current, data, { |
|||
rowsData: rows, |
|||
animationIndex: stateRef.current.animationIndex, |
|||
}); |
|||
|
|||
setState((state) => ({ ...state, ...data })); |
|||
} |
|||
|
|||
function calcWidths({ columnWidth, header }, rowsData) { |
|||
const usedWidth = columnWidth.reduce((all, w) => all + w, 0); |
|||
|
|||
let columnNum = 0; |
|||
if (rowsData[0]) { |
|||
columnNum = rowsData[0].ceils.length; |
|||
} else if (header.length) { |
|||
columnNum = header.length; |
|||
} |
|||
|
|||
const avgWidth = (width - usedWidth) / (columnNum - columnWidth.length); |
|||
|
|||
const widths = new Array(columnNum).fill(avgWidth); |
|||
|
|||
return deepMerge(widths, columnWidth); |
|||
} |
|||
|
|||
function calcHeights({ headerHeight, rowNum, data }, header) { |
|||
let allHeight = height; |
|||
|
|||
if (header.length) allHeight -= headerHeight; |
|||
|
|||
const avgHeight = allHeight / rowNum; |
|||
|
|||
Object.assign(stateRef.current, { avgHeight }); |
|||
|
|||
return new Array(data.length).fill(avgHeight); |
|||
} |
|||
|
|||
function* animation(start = false) { |
|||
let { |
|||
avgHeight, |
|||
animationIndex, |
|||
mergedConfig: { waitTime, carousel, rowNum }, |
|||
rowsData, |
|||
} = stateRef.current; |
|||
|
|||
const rowLength = rowsData.length; |
|||
|
|||
if (start) yield new Promise((resolve) => setTimeout(resolve, waitTime)); |
|||
|
|||
const animationNum = carousel === 'single' ? 1 : rowNum; |
|||
|
|||
let rows = rowsData.slice(animationIndex); |
|||
rows.push(...rowsData.slice(0, animationIndex)); |
|||
rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1); |
|||
|
|||
const heights = new Array(rowLength).fill(avgHeight); |
|||
setState((state) => ({ |
|||
...state, rows, heights, rowsShow: rows, |
|||
})); |
|||
|
|||
yield new Promise((resolve) => setTimeout(resolve, 300)); |
|||
|
|||
animationIndex += animationNum; |
|||
|
|||
const back = animationIndex - rowLength; |
|||
if (back >= 0) animationIndex = back; |
|||
|
|||
const newHeights = [...heights]; |
|||
newHeights.splice(0, animationNum, ...new Array(animationNum).fill(0)); |
|||
|
|||
Object.assign(stateRef.current, { animationIndex }); |
|||
setState((state) => ({ ...state, heights: newHeights })); |
|||
} |
|||
|
|||
function emitEvent(handle, ri, ci, row, ceil) { |
|||
const { ceils, rowIndex } = row; |
|||
|
|||
handle && handle({ |
|||
row: ceils, ceil, rowIndex, columnIndex: ci, |
|||
}); |
|||
} |
|||
|
|||
function handleHover(enter, ri, ci, row, ceil) { |
|||
if (enter) emitEvent(onMouseOver, ri, ci, row, ceil); |
|||
|
|||
if (!mergedConfig.hoverPause) return; |
|||
|
|||
const { pause, resume } = task.current; |
|||
|
|||
enter && pause && resume ? pause() : resume && resume(); |
|||
} |
|||
|
|||
// updateRows(rows, animationIndex) {
|
|||
// const { mergedConfig, animationHandler, animation } = this
|
|||
// this.mergedConfig = {
|
|||
// ...mergedConfig,
|
|||
// data: [...rows]
|
|||
// }
|
|||
// this.needCalc = true
|
|||
// if (typeof animationIndex === 'number') this.animationIndex = animationIndex
|
|||
// if (!animationHandler) animation(true)
|
|||
// }
|
|||
|
|||
const getBackgroundColor = (rowIndex) => mergedConfig[rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']; |
|||
|
|||
const task = useRef({}); |
|||
|
|||
useEffect(() => { |
|||
calcData(); |
|||
|
|||
let start = true; |
|||
|
|||
function* loop() { |
|||
while (true) { |
|||
yield* animation(start); |
|||
|
|||
start = false; |
|||
|
|||
const { waitTime } = stateRef.current.mergedConfig; |
|||
|
|||
yield new Promise((resolve) => setTimeout(resolve, waitTime - 300)); |
|||
} |
|||
} |
|||
|
|||
const { |
|||
mergedConfig: { rowNum }, |
|||
rows: rowsData, |
|||
} = stateRef.current; |
|||
|
|||
const rowLength = rowsData.length; |
|||
|
|||
if (rowNum >= rowLength) { |
|||
setState((prestate) => ({ |
|||
...prestate, rowsShow: state.rows, |
|||
})); |
|||
return; |
|||
} |
|||
|
|||
task.current = co(loop); |
|||
|
|||
return task.current.end; |
|||
}, [config, domRef.current]); |
|||
|
|||
useEffect(onResize, [width, height, domRef.current]); |
|||
|
|||
const classNames = useMemo(() => classnames('dv-scroll-board', className), [ |
|||
className, |
|||
]); |
|||
|
|||
return ( |
|||
<div className={classNames} style={style} ref={domRef}> |
|||
{!!header.length && !!mergedConfig && ( |
|||
<div |
|||
className="header" |
|||
style={{ backgroundColor: `${mergedConfig.headerBGC}` }} |
|||
> |
|||
{header.map((headerItem, i) => ( |
|||
<div |
|||
className="header-item" |
|||
key={`${headerItem}-${i}`} |
|||
style={{ |
|||
height: `${mergedConfig.headerHeight}px`, |
|||
lineHeight: `${mergedConfig.headerHeight}px`, |
|||
width: `${widths[i]}px`, |
|||
}} |
|||
align={aligns[i]} |
|||
dangerouslySetInnerHTML={{ __html: headerItem }} |
|||
/> |
|||
))} |
|||
</div> |
|||
)} |
|||
|
|||
{!!mergedConfig && ( |
|||
<div |
|||
className="rows" |
|||
style={{ |
|||
height: `${height |
|||
- (header.length ? mergedConfig.headerHeight : 0)}px`,
|
|||
}} |
|||
> |
|||
{rowsShow.map((row, ri) => ( |
|||
<div |
|||
className="row-item" |
|||
key={`${row.toString()}-${row.scroll}`} |
|||
style={{ |
|||
height: `${heights[ri]}px`, |
|||
lineHeight: `${heights[ri]}px`, |
|||
backgroundColor: `${getBackgroundColor(row.rowIndex)}`, |
|||
}} |
|||
> |
|||
{row.ceils.map((ceil, ci) => { |
|||
if (typeof (ceil) === 'string') { |
|||
return ( |
|||
<div |
|||
className="ceil" |
|||
key={`${ceil}-${ri}-${ci}`} |
|||
style={{ width: `${widths[ci]}px` }} |
|||
align={aligns[ci]} |
|||
dangerouslySetInnerHTML={{ __html: ceil }} |
|||
onClick={() => emitEvent(onClick, ri, ci, row, ceil)} |
|||
onMouseEnter={() => handleHover(true, ri, ci, row, ceil)} |
|||
onMouseLeave={() => handleHover(false)} |
|||
/> |
|||
); |
|||
} |
|||
return ( |
|||
<div |
|||
className="ceil" |
|||
style={{ width: `${widths[ci]}px` }} |
|||
align={aligns[ci]} |
|||
key={`${ri}-${ci}`} |
|||
onMouseEnter={() => handleHover(true, ri, ci, row, ceil)} |
|||
onMouseLeave={() => handleHover(false)} |
|||
> |
|||
{ceil} |
|||
</div> |
|||
); |
|||
})} |
|||
</div> |
|||
))} |
|||
</div> |
|||
)} |
|||
</div> |
|||
); |
|||
}); |
|||
|
|||
ScrollBoard.propTypes = { |
|||
config: PropTypes.object, |
|||
onClick: PropTypes.func, |
|||
onMouseOver: PropTypes.func, |
|||
className: PropTypes.string, |
|||
style: PropTypes.object, |
|||
}; |
|||
|
|||
export default ScrollBoard; |
@ -0,0 +1,44 @@ |
|||
.dv-scroll-board { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
color: #fff; |
|||
|
|||
.text { |
|||
padding: 0 10px; |
|||
box-sizing: border-box; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
font-size: 15px; |
|||
|
|||
.header-item { |
|||
.text; |
|||
transition: all 0.3s; |
|||
} |
|||
} |
|||
|
|||
.rows { |
|||
overflow: hidden; |
|||
|
|||
.row-item { |
|||
display: flex; |
|||
font-size: 14px; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.ceil { |
|||
.text; |
|||
} |
|||
|
|||
.index { |
|||
border-radius: 3px; |
|||
padding: 0px 3px; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
'use strict' |
|||
|
|||
import React from 'react' |
|||
import './index.less' |
|||
class Box extends React.Component { |
|||
render() { |
|||
const { title, height = '100%', children, bodyPaddingTop = 1, titlePaddingTop, margin, overflow } = this.props |
|||
|
|||
const headerbg = { |
|||
background: 'url(/assets/images/homePage/bigscreen/headertitlebg.png) no-repeat', |
|||
backgroundSize: '100% 100%', |
|||
} |
|||
return ( |
|||
<div style={{ height, width: '100%', margin: margin || "0px 0px 28px" }}> |
|||
<div style={{ |
|||
height: height, listStyle: 'none', overflow: overflow || 'hidden', |
|||
|
|||
}}> |
|||
<div style={{ height: 42, paddingTop: '4px', wordBreak: 'keep-all', whiteSpace: 'nowrap', width: '100%', ...headerbg, }}> |
|||
<span className='card-title'>{title}</span> |
|||
</div> |
|||
<div style={{ |
|||
height: 'calc(100% - 42px)', |
|||
backgroundImage: 'linear-gradient(180deg, #04377ecc 1%, #001241 100%)', |
|||
}}> |
|||
{children} |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
export default Box |
|||
|
@ -0,0 +1,31 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import './style.less'; |
|||
|
|||
function SpecialPerson(props) { |
|||
|
|||
const data = [ |
|||
{ name: '刑满释放', number: 447 }, |
|||
{ name: '社区矫正', number: 447 }, |
|||
{ name: '吸毒人员', number: 447 }, |
|||
{ name: '重点青少年', number: 447 }, |
|||
{ name: '艾滋病人', number: 447 }, |
|||
] |
|||
return <Box title={"特殊人群统计"}> |
|||
<div className='_special'> |
|||
{ |
|||
data.map((s, index) => { |
|||
return <div className={`_special_item _special_bg${index % 2 == 0 ? 1 : 2}`}> |
|||
<span>{s.name}</span> |
|||
<span><span className='_number'>{s.number}</span>人</span> |
|||
</div> |
|||
}) |
|||
} |
|||
|
|||
</div> |
|||
</Box> |
|||
} |
|||
|
|||
export default SpecialPerson; |
|||
|
|||
|
@ -0,0 +1,286 @@ |
|||
@card-height: calc(100% - 42px - 13px); //左右卡片内容高度定义 目前卡片为等高 |
|||
|
|||
//基本信息 |
|||
._basic_info { |
|||
padding: 21px 7px 0px 12px; |
|||
|
|||
//第一行 |
|||
._basic_row1 { |
|||
display: flex; |
|||
width: 100%; |
|||
|
|||
._item1 { |
|||
width: 128px; |
|||
height: 125px; |
|||
background: url('/assets/images/homePage/communtity/basicimg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._item2 { |
|||
width: calc(100% - 128px); |
|||
height: 125px; |
|||
padding-left: 20px; |
|||
|
|||
._basictitlebg { |
|||
background: url('/assets/images/homePage/communtity/basictitlebg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
width: 100%; |
|||
height: 17.5px; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
|
|||
._basic_title { |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 21px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0.74px; |
|||
width: 70px; |
|||
margin-top: -13px; |
|||
} |
|||
|
|||
._basic_sub_title { |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 14px; |
|||
color: #C0E2FF; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
|
|||
._basic_text { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
padding-bottom: 14px; |
|||
border-bottom: 1px solid #c3e6ff69; |
|||
font-size: 14px; |
|||
color: #C0E2FF; |
|||
margin-top: 14px; |
|||
padding-left: 5px; |
|||
padding-right: 5px; |
|||
|
|||
._text_number_color { |
|||
font-family: D-DIN-Italic; |
|||
font-weight: DIN; |
|||
font-size: 16px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0.5px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//第二行 |
|||
._basic_row2 { |
|||
width: 100%; |
|||
display: flex; |
|||
font-family: SourceHanSansCN-Medium; |
|||
font-weight: 500; |
|||
font-size: 13px; |
|||
color: #C0E2FF; |
|||
letter-spacing: 0; |
|||
margin-top: 19px; |
|||
background: url('/assets/images/homePage/communtity/basictextbg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
height: 43px; |
|||
align-items: center; |
|||
|
|||
._item1 { |
|||
width: 50%; |
|||
display: flex; |
|||
justify-content: space-around; |
|||
} |
|||
|
|||
._item2 { |
|||
width: calc(50% - 1px); |
|||
display: flex; |
|||
justify-content: space-around; |
|||
} |
|||
|
|||
.basicinterval { |
|||
background: url('/assets/images/homePage/communtity/basicinterval.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
width: 1px; |
|||
height: 19px; |
|||
} |
|||
|
|||
._number { |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 16px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0; |
|||
text-align: right; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//人口动态 |
|||
._person_trends { |
|||
display: flex; |
|||
padding: 10px 18px 0px 10px; |
|||
height: 100%; |
|||
|
|||
._person_tends_item1 { |
|||
width: 123.5px; |
|||
height: 100%; |
|||
background: url('/assets/images/homePage/communtity/person.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._person_tends_item2 { |
|||
width: calc(100% - 123.5px); |
|||
padding-left: 21px; |
|||
padding-top: 30px; |
|||
|
|||
._person_text { |
|||
margin-bottom: 10%; |
|||
background: url('/assets/images/homePage/communtity/personbg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
width: 100%; |
|||
height: 15px; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
padding-left: 10px; |
|||
padding-right: 8px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 12px; |
|||
color: #C0E2FF; |
|||
letter-spacing: 0; |
|||
|
|||
._person_title { |
|||
width: 100px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 16px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0.56px; |
|||
margin-top: -11px; |
|||
} |
|||
|
|||
._person_number { |
|||
width: calc(100% - 150px); |
|||
height: 28px; |
|||
font-family: D-DIN-Italic; |
|||
font-weight: Italic; |
|||
font-size: 28px; |
|||
color: #24DCF7; |
|||
letter-spacing: 0.98px; |
|||
margin-top: -37px; |
|||
text-align: right; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
//基础设施 |
|||
._basic_device { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
height: 100%; |
|||
align-items: center; |
|||
|
|||
._device_item { |
|||
width: 50%; |
|||
height: 82px; |
|||
display: flex; |
|||
|
|||
._device_img1 { |
|||
width: 93.12px; |
|||
height: 82px; |
|||
background: url('/assets/images/homePage/communtity/smoke.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._device_img2 { |
|||
width: 93.12px; |
|||
height: 82px; |
|||
background: url('/assets/images/homePage/communtity/temp.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._device_img3 { |
|||
width: 93.12px; |
|||
height: 82px; |
|||
background: url('/assets/images/homePage/communtity/video.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._device_img4 { |
|||
width: 93.12px; |
|||
height: 82px; |
|||
background: url('/assets/images/homePage/communtity/lift.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._device_text { |
|||
color: #ECF7FF; |
|||
width: calc(100% - 94px); |
|||
display: flex; |
|||
align-items: center; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
|
|||
._device_number { |
|||
font-family: D-DIN-Italic; |
|||
font-weight: Italic; |
|||
font-size: 28px; |
|||
color: #29E4FF; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
._special { |
|||
height: 100%; |
|||
padding-left: 16px; |
|||
padding-right: 9px; |
|||
|
|||
._special_item { |
|||
height: 19%; |
|||
margin-bottom: 0.6%; |
|||
display: flex; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 18px; |
|||
color: #ECF7FF; |
|||
letter-spacing: 0.4px; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding-right: 3%; |
|||
padding-left: 15%; |
|||
|
|||
._number { |
|||
font-family: D-DIN-Italic; |
|||
font-weight: Italic; |
|||
font-size: 26px; |
|||
color: #24DCF7; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
|
|||
._special_bg1 { |
|||
background: url('/assets/images/homePage/communtity/special1.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._special_bg2 { |
|||
background: url('/assets/images/homePage/communtity/special2.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import './style.less'; |
|||
|
|||
function DataTop5(props) { |
|||
|
|||
|
|||
return <Box title={"小区人流量排名"} bodyPaddingTop={1} > |
|||
小区人流量排名 |
|||
</Box> |
|||
} |
|||
|
|||
export default DataTop5; |
|||
|
|||
|
@ -0,0 +1,3 @@ |
|||
export const mathRound = (number) => { |
|||
return number ? Math.round(number / 1000) / 10 : 0 |
|||
} |
@ -0,0 +1,48 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
const MAPDOMID = 'fs-amap-container'; |
|||
let map = null; |
|||
let interval = null; |
|||
function Map(props) { |
|||
const [delay, setDelay] = useState(true) |
|||
// 地图初始化
|
|||
const loadMap = () => { |
|||
map = new AMap.Map(MAPDOMID, { |
|||
center: [115.89, 28.68], |
|||
zoomEnable: true, |
|||
dragEnable: true, |
|||
viewMode: '3D', |
|||
pitch: 30, |
|||
labelzIndex: 130, |
|||
zoom: 18, |
|||
cursor: 'pointer', |
|||
mapStyle: 'amap://styles/4eb48d1ef0a024c73376fd2256d0b5a5', |
|||
}); |
|||
map.on('complete', () => { |
|||
setTimeout(() => { |
|||
setDelay(false) |
|||
}, 1000); |
|||
|
|||
}); |
|||
}; |
|||
|
|||
|
|||
// 初始化GIS 组件销毁清空定时器
|
|||
useEffect(() => { |
|||
loadMap(); |
|||
}, []); |
|||
|
|||
return ( |
|||
<> |
|||
{delay && <div style={{ |
|||
width: '100%', height: '100%', left: 0, top: 0, zIndex: 1000, background: '#02152f', position: 'absolute', |
|||
display: 'flex', alignItems: 'center', justifyContent: 'center' |
|||
}}> |
|||
</div>} |
|||
<div className="gis" id={MAPDOMID} /> |
|||
</> |
|||
); |
|||
} |
|||
|
|||
export default connect()(Map); |
@ -0,0 +1,77 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import { connect } from 'react-redux'; |
|||
import { push } from 'react-router-redux'; |
|||
import LeftTop from '../components/basic-info' |
|||
import LeftMiddle from '../components/population-dynamics' |
|||
import LeftBottom from '../components/infrastructure' |
|||
import RightTop from '../components/city-safty' |
|||
import RightMiddle from '../components/special-person' |
|||
import RightBottom from '../components/traffic-ranking' |
|||
import Gis from './gis'; |
|||
import './style.less' |
|||
|
|||
function homePage(props) { |
|||
const { dispatch } = props; |
|||
const childStyle = { height: '32%', color: '#fff', marginBottom: 17 } |
|||
const cardHeight = document.body.clientHeight * 0.896 * 0.32 |
|||
const cardContentHeight = cardHeight - 42 - 13 |
|||
return <> |
|||
<div className='homepage'> |
|||
<div className='_title'> |
|||
<div className='_title_text'> |
|||
<span>社区安全</span> |
|||
<div className='_title_dot'></div> |
|||
<span>流动人口</span> |
|||
</div> |
|||
<div onClick={() => { dispatch(push('/metadataManagement/latestMetadata')) }} className='_exit' >返回平台</div> |
|||
</div> |
|||
<div className='homepage-left homepage-left-left'> |
|||
<div className="list"> |
|||
<div className='child' style={childStyle}> |
|||
<LeftTop /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<LeftMiddle /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<LeftBottom cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className='homepage-center'> |
|||
<Gis /> |
|||
</div> |
|||
<div className='homepage-left homepage-left-right'> |
|||
<div className="list"> |
|||
<div className='child-right' style={childStyle}> |
|||
<RightTop /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<RightMiddle cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<RightBottom /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</> |
|||
|
|||
|
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { |
|||
auth, global |
|||
} = state; |
|||
return { |
|||
clientHeight: global.clientHeight, |
|||
actions: global.actions, |
|||
|
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(homePage); |
|||
|
|||
|
@ -0,0 +1,6 @@ |
|||
'use strict'; |
|||
|
|||
import homePage from './homePage'; |
|||
|
|||
|
|||
export default homePage ; |
@ -0,0 +1,260 @@ |
|||
.homepage { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
background: url('/assets/images/homePage/communtity/bg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
overflow: hidden; |
|||
// display: flex; |
|||
// flex-direction: column; |
|||
// justify-content: center; |
|||
// align-items: center; |
|||
padding-top: 5%; |
|||
|
|||
._title_text { |
|||
position: absolute; |
|||
top: 0%; |
|||
width: 100%; |
|||
height: 62px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 48px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 12px; |
|||
z-index: 300; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
._title_dot { |
|||
display: inline-block; |
|||
width: 8px; |
|||
height: 8px; |
|||
background-image: linear-gradient(180deg, #FFFFFF 0%, #0d71ef00 100%); |
|||
margin-left: 14px; |
|||
margin-right: 14px; |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
} |
|||
|
|||
._exit { |
|||
position: absolute; |
|||
right: 60px; |
|||
top: 38px; |
|||
cursor: pointer; |
|||
color: #C8F0FF; |
|||
display: flex; |
|||
width: 102px; |
|||
height: 33px; |
|||
background: url('/assets/images/homePage/bigscreen/exit.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
padding-left: 35px; |
|||
align-items: center; |
|||
z-index: 400; |
|||
} |
|||
|
|||
.homepage-left { |
|||
width: 21.8%; |
|||
height: 84.6%; |
|||
position: absolute; |
|||
top: 10.1%; |
|||
z-index: 300; |
|||
} |
|||
|
|||
.homepage-center { |
|||
width: 93.9%; |
|||
height: 84%; |
|||
position: absolute; |
|||
top: 10.4%; |
|||
left: 58px; |
|||
padding-left: 16px; |
|||
padding-right: 16px; |
|||
z-index: 200; |
|||
|
|||
._top { |
|||
margin-top: 5%; |
|||
height: calc(100% - 200px); |
|||
background: url('/assets/images/homePage/bigscreen/centerbg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
.homepage-left-left { |
|||
left: 58px; |
|||
} |
|||
|
|||
.homepage-left-right { |
|||
right: 58px; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
.list { |
|||
list-style: none; |
|||
height: 100%; |
|||
} |
|||
|
|||
.list .child { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateX(-300px); |
|||
animation: show .5s forwards; |
|||
} |
|||
|
|||
.list .child.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child.hide { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
/*animation-delay*/ |
|||
.list .child:not(.hide):nth-child(5n + 1) { |
|||
animation-delay: .3s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 2) { |
|||
animation-delay: .6s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 3) { |
|||
animation-delay: .9s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 4) { |
|||
animation-delay: 1.2s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 5) { |
|||
animation-delay: 1.5s; |
|||
} |
|||
|
|||
.list .child-right { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateX(300px); |
|||
animation: show .5s forwards; |
|||
} |
|||
|
|||
.list .child-right.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child-right.hide { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
/*animation-delay*/ |
|||
.list .child-right:not(.hide):nth-child(5n + 1) { |
|||
animation-delay: .3s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 2) { |
|||
animation-delay: .6s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 3) { |
|||
animation-delay: .9s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 4) { |
|||
animation-delay: 1.2s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 5) { |
|||
animation-delay: 1.5s; |
|||
} |
|||
|
|||
.list .child-top { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateY(300px); |
|||
animation: show 1s forwards; |
|||
} |
|||
|
|||
.list .child-top.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child-top.hide { |
|||
opacity: 1; |
|||
transform: translateY(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
|
|||
@keyframes show { |
|||
to { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
} |
|||
} |
|||
|
|||
@keyframes hide { |
|||
to { |
|||
opacity: 0; |
|||
transform: translateX(100px); |
|||
max-height: 0; |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.center-card-title { |
|||
height: 31px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 24px; |
|||
color: #FFFFFF; |
|||
display: flex; |
|||
align-items: center; |
|||
margin-top: 5px; |
|||
|
|||
._icon_left { |
|||
width: 32px; |
|||
height: 17px; |
|||
background: url('/assets/images/homePage/bigscreen/center-left.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-right: 11px; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
._icon_right { |
|||
width: 32px; |
|||
height: 17px; |
|||
background: url('/assets/images/homePage/bigscreen/center-right.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-right: 11px; |
|||
margin-left: 10px; |
|||
} |
|||
} |
|||
|
|||
.gis { |
|||
position: absolute; |
|||
width: 100vw; |
|||
height: calc(~"100% - 114px"); |
|||
left: 0; |
|||
top: 114px; |
|||
z-index: 0; |
|||
display: flex; |
|||
justify-content: center; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
import reducers from './reducers'; |
|||
import routes from './routes'; |
|||
import actions from './actions'; |
|||
import { getNavItem } from './nav-item'; |
|||
|
|||
export default { |
|||
key: 'communitysafty', |
|||
name: '首页', |
|||
reducers: reducers, |
|||
routes: routes, |
|||
actions: actions, |
|||
getNavItem: getNavItem |
|||
}; |
@ -0,0 +1,11 @@ |
|||
import React from 'react'; |
|||
import { Link } from 'react-router-dom'; |
|||
import { Menu } from 'antd'; |
|||
import { HomeOutlined } from '@ant-design/icons'; |
|||
export function getNavItem(user) { |
|||
return ( |
|||
<Menu.Item key="communitysafty" icon={<HomeOutlined />}> |
|||
<Link to="/communitysafty">社区安全</Link> |
|||
</Menu.Item> |
|||
); |
|||
} |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
export default { |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
'use strict'; |
|||
import homePage from './containers/index'; |
|||
|
|||
export default [{ |
|||
type: 'outer', |
|||
route: { |
|||
path: '/communitysafty', |
|||
key: 'communitysafty', |
|||
breadcrumb: '数据监控平台', |
|||
// 不设置 component 则面包屑禁止跳转
|
|||
component: homePage |
|||
} |
|||
}]; |
@ -0,0 +1,74 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import { connect } from 'react-redux'; |
|||
import { push } from 'react-router-redux'; |
|||
import AccessData from '../components/accessData' |
|||
import AlarmList from '../components/alarmList' |
|||
import DataShare from '../components/dataShare' |
|||
import DataTop5 from '../components/dataTop5' |
|||
import HotspotData from '../components/hotspotData' |
|||
import NodeResource from '../components/nodeResource' |
|||
import AbnormalMonitoring from '../components/abnormalMonitoring' |
|||
import CenterTop from '../components/centerTop' |
|||
import './style.less' |
|||
|
|||
function homePage(props) { |
|||
const { dispatch } = props; |
|||
const childStyle = { height: '32%', color: '#fff', marginBottom: 17 } |
|||
const cardHeight = document.body.clientHeight * 0.896 * 0.32 |
|||
const cardContentHeight = cardHeight - 42 - 13 |
|||
return <div className='homepage'> |
|||
<div className='_title'> |
|||
<div onClick={() => { dispatch(push('/metadataManagement/latestMetadata')) }} className='_exit' ><div className='_icon' />进入后台</div> |
|||
</div> |
|||
<div className='homepage-left homepage-left-left'> |
|||
<div className="list"> |
|||
<div className='child' style={childStyle}> |
|||
<AccessData /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<NodeResource /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<AlarmList cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className='homepage-center'> |
|||
<CenterTop /> |
|||
<div className="list"> |
|||
<div className='child-top'> |
|||
<AbnormalMonitoring /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className='homepage-left homepage-left-right'> |
|||
<div className="list"> |
|||
<div className='child-right' style={childStyle}> |
|||
<DataShare /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<DataTop5 cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<HotspotData /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { |
|||
auth, global |
|||
} = state; |
|||
return { |
|||
clientHeight: global.clientHeight, |
|||
actions: global.actions, |
|||
|
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(homePage); |
|||
|
|||
|
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
// export function getMembers(orgId) {
|
|||
// return dispatch => basicAction({
|
|||
// type: 'get',
|
|||
// dispatch: dispatch,
|
|||
// actionType: 'GET_MEMBERS',
|
|||
// url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`,
|
|||
// msg: { error: '获取用户列表失败' },
|
|||
// reducer: { name: 'members' }
|
|||
// });
|
|||
// }
|
@ -0,0 +1,7 @@ |
|||
'use strict'; |
|||
|
|||
import * as example from './example' |
|||
|
|||
export default { |
|||
...example, |
|||
} |
@ -0,0 +1,49 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import CarouselList from './public/carousel-list'; |
|||
import { Tooltip } from 'antd'; |
|||
import { ApiTable, useFsRequest } from '$utils'; |
|||
import moment from 'moment'; |
|||
function AbnormalMonitoring(props) { |
|||
|
|||
const { data: logs = {} } = useFsRequest({ |
|||
url: ApiTable.getLogs, |
|||
query: { |
|||
logState: false, |
|||
startTime: moment().subtract(7, 'days').format('YYYY-MM-DD HH:mm:ss'), |
|||
endTime: moment().format('YYYY-MM-DD HH:mm:ss') |
|||
}, |
|||
pollingInterval: 1000 * 60 |
|||
}); |
|||
|
|||
const dataSource = logs?.rows ? logs?.rows?.map(s => { |
|||
return [ |
|||
<div style={{ color: '#fff' }}> |
|||
<Tooltip placement="top" title={s?.acquisitionTask?.taskName}> |
|||
{s?.acquisitionTask?.taskName?.length > 20 ? s?.acquisitionTask?.taskNamesubstring(0, 20) + '...' : s?.acquisitionTask?.taskName} |
|||
</Tooltip> |
|||
</div>, |
|||
moment(s?.startTime).format('YYYY-MM-DD HH:mm:ss'), |
|||
moment(s?.endTime).valueOf() - moment(s?.startTime).valueOf() + '毫秒', |
|||
<div style={{ color: 'rgba(245, 27, 27, 1)' }}> |
|||
<Tooltip placement="top" title={s?.details}> |
|||
{s?.details?.length > 20 ? s?.details.substring(0, 20) + '...' : s?.details} |
|||
</Tooltip> |
|||
</div> |
|||
] |
|||
}) : [] |
|||
return <div style={{ height: 149, border: '1px solid #50c9d74d', backgroundImage: 'linear-gradient(180deg, rgba(0, 32, 74, 0) 3%, rgba(80, 201, 247, 0.1) 100%)' }}> |
|||
<div className='center-card-title' style={{ marginBottom: 6 }}><div className='_icon_left' />异常监控<div className='_icon_right' /></div> |
|||
<CarouselList |
|||
header={['任务名称', '采集时间', '耗时', '异常日志']} |
|||
data={dataSource} |
|||
rowNum={2} |
|||
height={100} |
|||
multiellipsis |
|||
marginTop={-50} |
|||
/> |
|||
</div> |
|||
} |
|||
|
|||
export default AbnormalMonitoring; |
|||
|
|||
|
@ -0,0 +1,36 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
import { useFsRequest } from '$utils'; |
|||
import { mathRound } from './util' |
|||
function AccessData() { |
|||
|
|||
const { data: accessdata = [] } = useFsRequest({ |
|||
url: 'homepage/accessdata', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'accessdata', |
|||
}); |
|||
|
|||
|
|||
const renderBody = () => { |
|||
return <div className='access_data'> |
|||
<div className='_img'></div> |
|||
<div className='data_unit'> |
|||
数据单位<div className='data_number'>{!accessdata?.projects ? '-' : accessdata?.projects?.split(',')?.length}</div>个 |
|||
</div> |
|||
<div className='data_today'> |
|||
今日数据<div className='data_number'>{!accessdata?.res?.stat?.today ? '-' : accessdata?.res?.stat?.today > 1000 ? mathRound(accessdata?.res?.stat?.today) : accessdata?.res?.stat?.today}</div>{accessdata?.res?.stat?.today > 1000 ? '万条' : '条'} |
|||
</div> |
|||
<div className='data_total'> |
|||
数据总量<div className='data_number'>{accessdata?.res?.stat?.datas ? Math.round(accessdata?.res?.stat?.datas / 10000) : '-'}</div>万条 |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
return <Box title={"接入数据统计"} > |
|||
{renderBody()} |
|||
</Box> |
|||
} |
|||
|
|||
export default AccessData; |
|||
|
|||
|
@ -0,0 +1,52 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
import CarouselList from './public/carousel-list'; |
|||
import { Tooltip } from 'antd'; |
|||
import moment from 'moment'; |
|||
import NoData from './public/noData'; |
|||
import { useFsRequest } from '$utils'; |
|||
|
|||
function AlarmList(props) { |
|||
const { cardContentHeight } = props; |
|||
const { data: alarms = [] } = useFsRequest({ |
|||
url: 'homepage/alarms', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'alarms', |
|||
}); |
|||
|
|||
|
|||
const data = alarms.map(s => { |
|||
return [ |
|||
s.content, |
|||
s.level == 1 ? '一级' : s.level == 2 ? '二级' : s.level == 3 ? '三级' : '四级', |
|||
moment(s.time).format('YYYY-MM-DD HH:mm:ss') |
|||
] |
|||
}) |
|||
|
|||
const renderBody = () => { |
|||
return <CarouselList |
|||
header={['预警内容', '预警等级', '预警时间']} |
|||
data={data?.map(s => { |
|||
return [ |
|||
<Tooltip placement="top" title={s[0]}> |
|||
{s[0].length > 20 ? s[0]?.substring(0, 20) + '...' : s[0]} |
|||
</Tooltip>, |
|||
<div style={{ color: s[1] == '一级' ? 'rgba(245, 27, 27, 1)' : s[1] == '二级' ? '#FF7900' : s[1] == '三级' ? '#FFCD00' : '#00DA9F' }}>{s[1]}</div>, |
|||
s[2] |
|||
] |
|||
})} |
|||
rowNum={6} |
|||
height={cardContentHeight} |
|||
multiellipsis |
|||
columnWidth={[180, 80, 150]} |
|||
/> |
|||
} |
|||
|
|||
return <Box title={"预警列表"}> |
|||
{alarms?.length > 0 ? renderBody() : <NoData />} |
|||
</Box> |
|||
} |
|||
|
|||
export default AlarmList; |
|||
|
|||
|
@ -0,0 +1,19 @@ |
|||
import React from 'react' |
|||
import './style.less' |
|||
|
|||
function CenterTop(props) { |
|||
|
|||
|
|||
return <div className='_top'> |
|||
<div className='center_top_data'> |
|||
<div className='_center_card1'>共享交换</div> |
|||
<div className='_center_card2'>数据监控</div> |
|||
<div className='_center_card3'>数据治理</div> |
|||
<div className='_center_card4'>数据采集</div> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
export default CenterTop; |
|||
|
|||
|
@ -0,0 +1,54 @@ |
|||
import React from 'react' |
|||
import Box from './public/table-card'; |
|||
import { useFsRequest } from '$utils'; |
|||
import { mathRound } from './util'; |
|||
function DataShare(props) { |
|||
|
|||
const { data: dataTotal = {} } = useFsRequest({ |
|||
url: 'homepage/datatotal/top5', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'datatotal', |
|||
}); |
|||
|
|||
const { data: restfulInfo = {} } = useFsRequest({ |
|||
url: 'homepage/restful/info', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'restfulInfo', |
|||
}); |
|||
|
|||
const renderItem = (s) => { |
|||
return <div className='_item_content'> |
|||
<div className={'_item_icon' + s.key} /> |
|||
<div className='_item_text'> |
|||
{s.title} |
|||
<div className='number_container'> |
|||
<span className='_number'>{s.data}</span>{s.unit} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
const leftData = [ |
|||
{ key: '1', data: mathRound(dataTotal?.total), unit: '万条', title: '共享库数据总量' }, |
|||
{ key: '2', data: restfulInfo?.total, unit: '次', title: '访问接口总次数' }, |
|||
{ key: '3', data: restfulInfo?.totalUser, unit: '个', title: '访问接口用户总数' }] |
|||
const rightData = [ |
|||
{ key: '2', data: restfulInfo?.todayTotal, unit: '次', title: '接口访问次数' }, |
|||
{ key: '3', data: restfulInfo?.todayUser, unit: '个', title: '访问接口用户总数' }] |
|||
|
|||
return <Box title={"数据共享"} > |
|||
<div className='data_share'> |
|||
<div className='_left_content'> |
|||
{leftData.map(s => renderItem(s))} |
|||
</div> |
|||
<div className='_right_content'> |
|||
<div className='_today_text'>今日</div> |
|||
{rightData.map(s => renderItem(s))} |
|||
</div> |
|||
</div> |
|||
</Box> |
|||
} |
|||
|
|||
export default DataShare; |
|||
|
|||
|
@ -0,0 +1,262 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import ReactEcharts from 'echarts-for-react'; |
|||
import './style.less'; |
|||
import { useFsRequest } from '$utils'; |
|||
import { mathRound } from './util'; |
|||
import NoData from './public/noData'; |
|||
function DataTop5(props) { |
|||
const { cardContentHeight } = props; |
|||
const { data: dataTotal = {} } = useFsRequest({ |
|||
url: 'homepage/datatotal/top5', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'datatotal', |
|||
}); |
|||
|
|||
const renderBody = () => { |
|||
let chartData = dataTotal?.top5?.map(x => { |
|||
return { |
|||
name: x?.dataSource?.resourceCatalog?.name, |
|||
value: mathRound(x.dbRecordCount), |
|||
} |
|||
}) || [] |
|||
|
|||
let options = { |
|||
xAxis: { |
|||
splitLine: { |
|||
show: false, |
|||
}, |
|||
axisLabel: { |
|||
show: false, |
|||
}, |
|||
axisTick: { |
|||
show: false, |
|||
}, |
|||
splitArea: { show: false }, |
|||
axisLine: { |
|||
show: false, |
|||
}, |
|||
}, |
|||
tooltip: { |
|||
confine: true, |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'shadow', |
|||
}, |
|||
backgroundColor: 'rgba(13,30,44, 0.7)', |
|||
borderColor: 'rgba(3, 65, 118, 0.8)', |
|||
textStyle: { |
|||
color: '#fff', |
|||
}, |
|||
formatter: function (params) { |
|||
var name = params[0].name |
|||
if (name.length > 20) { |
|||
name = name.replace(/(.{20})/g, '$1<br>') // 每 30 个字符添加一个换行符
|
|||
} |
|||
var content = name |
|||
|
|||
return content + ' : <b>' + params[0].value + '</b>万条' |
|||
} |
|||
}, |
|||
grid: { |
|||
top: 13, |
|||
bottom: -10, |
|||
left: '5%', |
|||
}, |
|||
yAxis: { |
|||
inverse: true, |
|||
axisLine: { |
|||
show: false, |
|||
}, |
|||
axisTick: { |
|||
show: false, |
|||
}, |
|||
axisLabel: { |
|||
textStyle: { |
|||
color: '#fff', |
|||
padding: [-5, 0, 35, 18], |
|||
}, |
|||
formatter(value, index) { |
|||
let str = '', num = 'TOP' + (index + 1) |
|||
let valueHandle = value.length > 10 ? value.substring(0, 10) + '...' : value |
|||
if (index === 0) { |
|||
str = '{a| ' + num + '}{title| ' + valueHandle + '}' |
|||
} else if (index === 1) { |
|||
str = '{b| ' + num + '}{title| ' + valueHandle + '}' |
|||
} else if (index === 2) { |
|||
str = '{c| ' + num + '}{title| ' + valueHandle + '}' |
|||
} else { |
|||
str = '{d| ' + num + '}{title| ' + valueHandle + '}' |
|||
} |
|||
return str |
|||
}, |
|||
rich: { |
|||
a: { |
|||
borderColor: '#EE6F7C', |
|||
borderWidth: 1, |
|||
borderRadius: [0, 10, 10, 0], |
|||
padding: [3.5, 10, 1, -13], |
|||
backgroundColor: 'rgba(238, 111, 124, 0.8)', |
|||
}, |
|||
b: { |
|||
borderColor: '#FFCF5F', |
|||
borderWidth: 1, |
|||
borderRadius: [0, 10, 10, 0], |
|||
padding: [3.5, 10, 1, -13], |
|||
backgroundColor: 'rgba(255, 207, 95, 0.7)', |
|||
}, |
|||
c: { |
|||
borderColor: '#00E8FF', |
|||
borderWidth: 1, |
|||
borderRadius: [0, 10, 10, 0], |
|||
padding: [3.5, 10, 1, -13], |
|||
backgroundColor: 'rgba(0, 232, 255, 0.7)', |
|||
}, |
|||
d: { |
|||
borderColor: '#1A90FF', |
|||
borderWidth: 1, |
|||
borderRadius: [0, 10, 10, 0], |
|||
padding: [3.5, 10, 1, -13], |
|||
backgroundColor: 'rgba(26, 144, 255, 0.7)', |
|||
}, |
|||
title: { |
|||
padding: [0, 0, 0, 3], |
|||
}, |
|||
}, |
|||
align: 'left', |
|||
}, |
|||
data: chartData.map((item) => item.name), |
|||
}, |
|||
series: [ |
|||
{ |
|||
type: 'pictorialBar', |
|||
symbol: 'rect', |
|||
symbolRotate: 30, |
|||
symbolRepeat: 'fixed', |
|||
symbolClip: true, |
|||
symbolOffset: [0, -1.5], |
|||
symbolSize: [2, 12], |
|||
symbolMargin: '3', |
|||
itemStyle: { |
|||
normal: { |
|||
color: '#000726', |
|||
}, |
|||
}, |
|||
label: { |
|||
show: true, |
|||
color: '#C8F0FF', |
|||
fontFamily: 'Bebas', |
|||
fontSize: 12, |
|||
offset: [-9, 1], |
|||
position: 'right', |
|||
formatter(params) { |
|||
let result = '' |
|||
switch (params.dataIndex) { |
|||
case 0: |
|||
result = '{img|}{index0|' + params.value + '}{unit|}' |
|||
break |
|||
case 1: |
|||
result = '{img|}{index1|' + params.value + '}{unit|}' |
|||
break |
|||
case 2: |
|||
result = '{img|}{index2|' + params.value + '}{unit|}' |
|||
break |
|||
default: |
|||
result = '{img|}{index3|' + params.value + '}{unit|}' |
|||
break |
|||
} |
|||
return result |
|||
}, |
|||
rich: { |
|||
img: { |
|||
height: 18, |
|||
width: 20, |
|||
// backgroundColor: { image: arrow },这个图片自己切,这里上传不了(加了一个尾巴的形状)
|
|||
}, |
|||
unit: { |
|||
color: '#C8F0FF', |
|||
fontSize: 11, |
|||
}, |
|||
index0: { |
|||
color: '#FFF', |
|||
fontFamily: 'Bebas', |
|||
padding: [-2, 2, 0, 0], |
|||
fontWeight: 'bold', |
|||
fontSize: 16, |
|||
}, |
|||
index1: { |
|||
color: '#FFF', |
|||
fontFamily: 'Bebas', |
|||
padding: [-2, 2, 0, 0], |
|||
fontWeight: 'bold', |
|||
fontSize: 16, |
|||
}, |
|||
index2: { |
|||
color: '#FFF', |
|||
fontFamily: 'Bebas', |
|||
padding: [-2, 2, 0, 0], |
|||
fontWeight: 'bold', |
|||
fontSize: 16, |
|||
}, |
|||
index3: { |
|||
color: '#FFF', |
|||
fontFamily: 'Bebas', |
|||
padding: [-2, 2, 0, 0], |
|||
fontWeight: 'bold', |
|||
fontSize: 16, |
|||
}, |
|||
}, |
|||
}, |
|||
symbolBoundingData: Math.max(...chartData.map((item) => item.value)) * 1.3, |
|||
data: chartData.map((item) => item.value), |
|||
z: 2, |
|||
}, |
|||
{ |
|||
type: 'bar', |
|||
barWidth: 10, |
|||
data: chartData.map((item) => item.value), |
|||
itemStyle: { |
|||
normal: { |
|||
color: '#54DEFA', |
|||
}, |
|||
}, |
|||
z: 1, |
|||
}, |
|||
{ |
|||
type: 'bar', |
|||
barGap: '-125%', // 设置外框粗细
|
|||
data: chartData.map((items) => Math.max(...chartData.map((item) => item.value)) * 1.3), |
|||
barWidth: 15, |
|||
itemStyle: { |
|||
color: 'none', |
|||
borderColor: '#979797', |
|||
}, |
|||
z: 0, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
return <ReactEcharts |
|||
option={options} |
|||
notMerge |
|||
lazyUpdate |
|||
style={{ height: cardContentHeight }} |
|||
/> |
|||
} |
|||
|
|||
return <Box title={"数据量TOP5单位"} bodyPaddingTop={1} > |
|||
{ |
|||
dataTotal?.top5?.length > 0 ? |
|||
<> |
|||
<div className='data_top5_unit'>数据量:万条</div> |
|||
{renderBody()} |
|||
</> |
|||
: <NoData /> |
|||
} |
|||
|
|||
</Box> |
|||
} |
|||
|
|||
export default DataTop5; |
|||
|
|||
|
@ -0,0 +1,42 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import NoData from './public/noData'; |
|||
import './style.less'; |
|||
import { ApiTable, useFsRequest } from '$utils'; |
|||
function HotspotData(props) { |
|||
|
|||
const { data: restfulInfo = {} } = useFsRequest({ |
|||
url: 'homepage/restful/info', |
|||
pollingInterval: 1000 * 60, |
|||
cacheKey: 'restfulInfo', |
|||
}); |
|||
|
|||
const top3 = restfulInfo?.top3 |
|||
return <Box title={"热点数据"} bodyPaddingTop={25} > |
|||
{top3?.length > 0 ? |
|||
<div className='hotspot_data_container'> |
|||
<div className='_img'></div> |
|||
<div className='_top1'> |
|||
<span className='hotspot_title' title={top3[0].name}>{top3[0].name?.length > 8 ? top3[0].name.substring(0, 8) + '...' : top3[0].name}</span> |
|||
<div className='hotspot_data_number'>{top3[0].count}</div> |
|||
</div> |
|||
<div className='_top2'> |
|||
{top3?.length > 2 && <> |
|||
<span className='hotspot_title' title={top3[2].name}>{top3[2].name?.length > 8 ? top3[2].name.substring(0, 8) + '...' : top3[2].name}</span> |
|||
<div className='hotspot_data_number'>{top3[2].count}</div> |
|||
</>} |
|||
</div> |
|||
<div className='_top3'> |
|||
{top3?.length > 1 && <> |
|||
<span className='hotspot_title' title={top3[1].name}>{top3[1].name?.length > 8 ? top3[1].name.substring(0, 8) + '...' : top3[1].name}</span> |
|||
<div className='hotspot_data_number'>{top3[1].count}</div> |
|||
</>} |
|||
</div> |
|||
</div> : <NoData /> |
|||
} |
|||
</Box> |
|||
} |
|||
|
|||
export default HotspotData; |
|||
|
|||
|
@ -0,0 +1,39 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import Box from './public/table-card'; |
|||
import { ApiTable, useFsRequest } from '$utils'; |
|||
import './style.less'; |
|||
function NodeResource(props) { |
|||
const { data: cluters = {} } = useFsRequest({ |
|||
url: 'homepage/datatotal/cluters', |
|||
pollingInterval: 1000 * 20, |
|||
}); |
|||
|
|||
const renderBody = () => { |
|||
return <div className='node-resource-container'> |
|||
<div className='_item'> |
|||
<div className='_noderesource_data'>{cluters?.disk}<span className='_percent'>%</span></div> |
|||
<div className='_noderesource_title'>硬盘</div> |
|||
<div className='disk_icon' /> |
|||
</div> |
|||
<div className='_item'> |
|||
<div className='_noderesource_data'>{cluters?.memory}<span className='_percent'>%</span></div> |
|||
<div className='_noderesource_title'>内存</div> |
|||
<div className='memory_icon' /> |
|||
</div> |
|||
<div className='_item'> |
|||
<div className='_noderesource_data'>{cluters?.cpu}<span className='_percent'>%</span></div> |
|||
<div className='_noderesource_title'>CPU</div> |
|||
<div className='cpu_icon' /> |
|||
</div> |
|||
</div> |
|||
|
|||
} |
|||
|
|||
return <Box title={"节点资源"} > |
|||
{renderBody()} |
|||
</Box> |
|||
} |
|||
|
|||
export default NodeResource; |
|||
|
|||
|
@ -0,0 +1,33 @@ |
|||
/* 轮播列表组件 */ |
|||
import React from 'react'; |
|||
import ScrollBoard from './scrollBoard'; |
|||
import NoData from './noData'; |
|||
import './index.less'; |
|||
function CarouselList(props) { |
|||
const { |
|||
header = [], data = [], rowNum = 4, height, columnWidth, multiellipsis, waitTime = 2000, marginTop, ...restProps |
|||
} = props; |
|||
|
|||
const config = { |
|||
header, |
|||
rowNum, |
|||
headerBGC: 'rgba(81, 200, 247, 0.2)', |
|||
oddRowBGC: 'transparent', |
|||
evenRowBGC: 'transparent', |
|||
headerHeight: 30, |
|||
data, |
|||
waitTime, |
|||
columnWidth: columnWidth || [], |
|||
}; |
|||
|
|||
return data.length > 0 ? ( |
|||
<ScrollBoard |
|||
config={config} |
|||
style={{ height }} |
|||
className={multiellipsis ? 'scroll-board-multi' : 'scroll-board'} |
|||
{...restProps} |
|||
/> |
|||
) : <NoData marginTop={marginTop || 0} />; |
|||
} |
|||
|
|||
export default CarouselList; |
@ -0,0 +1,80 @@ |
|||
.opcityBackground { |
|||
background-color: rgba(8, 27, 55, 0.6); |
|||
} |
|||
|
|||
.card-title { |
|||
// background: linear-gradient(to bottom, #fafafb, #92cbff); |
|||
// background-clip: border-box; |
|||
// -webkit-background-clip: text; |
|||
color: #fff; |
|||
font-size: 22px; |
|||
font-family: YouSheBiaoTiHei; |
|||
padding-left: 20px; |
|||
// font-weight: 600; |
|||
} |
|||
|
|||
/* 滚动列表 */ |
|||
.scroll-board { |
|||
width: 533px; |
|||
height: 220px; |
|||
margin-top: 10px; |
|||
margin-left: 6px; |
|||
|
|||
.header { |
|||
height: 30px; |
|||
border-top: 1px solid #0047ba; |
|||
border-bottom: 1px solid #0047ba; |
|||
|
|||
.header-item { |
|||
// background: rgba(12, 49, 110, 0.3); |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
.rows { |
|||
.row-item { |
|||
font-size: 16px; |
|||
} |
|||
|
|||
.row-item:hover { |
|||
background: linear-gradient(270deg, rgba(17, 183, 247, 0) 0%, rgba(17, 183, 247, 0.85) 100%); |
|||
color: #9ac8fc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.scroll-board-multi { |
|||
padding: 5px 0px 5px; |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
font-size: 12px !important; |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
// border-bottom: 1px solid #124C79 !important; |
|||
} |
|||
|
|||
.rows { |
|||
color: rgba(204, 228, 255, 1) !important; |
|||
|
|||
.row-item { |
|||
border-bottom: 1px solid #124C79 !important; |
|||
} |
|||
|
|||
.row-item:hover { |
|||
background: linear-gradient(270deg, rgba(17, 183, 247, 0) 0%, rgba(17, 183, 247, 0.85) 100%); |
|||
color: #9ac8fc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
._sorrow { |
|||
display: inline-block; |
|||
width: 15px; |
|||
height: 15px; |
|||
background: url('/assets/images/homePage/bigscreen/sorrow.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-left: 13px; |
|||
} |
@ -0,0 +1,18 @@ |
|||
/* 公共模块暂无数据组件 */ |
|||
import React from 'react'; |
|||
import { Empty } from 'antd'; |
|||
|
|||
function NoData({ height = 180, marginTop = 0 }) { |
|||
return ( |
|||
<Empty |
|||
image="/assets/images/homePage/bigscreen/empty.png" |
|||
imageStyle={{ |
|||
height, |
|||
marginTop |
|||
}} |
|||
description={false} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
export default NoData; |
@ -0,0 +1,469 @@ |
|||
import React, { |
|||
useEffect, useState, useRef, useMemo, forwardRef, |
|||
} from 'react'; |
|||
|
|||
import PropTypes from 'prop-types'; |
|||
|
|||
import classnames from 'classnames'; |
|||
|
|||
import { deepMerge } from '@jiaminghi/charts/lib/util/index'; |
|||
|
|||
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'; |
|||
|
|||
import { useAutoResize, co } from '@jiaminghi/data-view-react'; |
|||
|
|||
import './style.less'; |
|||
|
|||
const defaultConfig = { |
|||
/** |
|||
* @description Board header |
|||
* @type {Array<String>} |
|||
* @default header = [] |
|||
* @example header = ['column1', 'column2', 'column3'] |
|||
*/ |
|||
header: [], |
|||
/** |
|||
* @description Board data |
|||
* @type {Array<Array>} |
|||
* @default data = [] |
|||
*/ |
|||
data: [], |
|||
/** |
|||
* @description Row num |
|||
* @type {Number} |
|||
* @default rowNum = 5 |
|||
*/ |
|||
rowNum: 5, |
|||
/** |
|||
* @description Header background color |
|||
* @type {String} |
|||
* @default headerBGC = '#00BAFF' |
|||
*/ |
|||
headerBGC: '#00BAFF', |
|||
/** |
|||
* @description Odd row background color |
|||
* @type {String} |
|||
* @default oddRowBGC = '#003B51' |
|||
*/ |
|||
oddRowBGC: '#003B51', |
|||
/** |
|||
* @description Even row background color |
|||
* @type {String} |
|||
* @default evenRowBGC = '#003B51' |
|||
*/ |
|||
evenRowBGC: '#0A2732', |
|||
/** |
|||
* @description Scroll wait time |
|||
* @type {Number} |
|||
* @default waitTime = 2000 |
|||
*/ |
|||
waitTime: 2000, |
|||
/** |
|||
* @description Header height |
|||
* @type {Number} |
|||
* @default headerHeight = 35 |
|||
*/ |
|||
headerHeight: 35, |
|||
/** |
|||
* @description Column width |
|||
* @type {Array<Number>} |
|||
* @default columnWidth = [] |
|||
*/ |
|||
columnWidth: [], |
|||
/** |
|||
* @description Column align |
|||
* @type {Array<String>} |
|||
* @default align = [] |
|||
* @example align = ['left', 'center', 'right'] |
|||
*/ |
|||
align: [], |
|||
/** |
|||
* @description Show index |
|||
* @type {Boolean} |
|||
* @default index = false |
|||
*/ |
|||
index: false, |
|||
/** |
|||
* @description index Header |
|||
* @type {String} |
|||
* @default indexHeader = '#' |
|||
*/ |
|||
indexHeader: '#', |
|||
/** |
|||
* @description Carousel type |
|||
* @type {String} |
|||
* @default carousel = 'single' |
|||
* @example carousel = 'single' | 'page' |
|||
*/ |
|||
carousel: 'single', |
|||
/** |
|||
* @description Pause scroll when mouse hovered |
|||
* @type {Boolean} |
|||
* @default hoverPause = true |
|||
* @example hoverPause = true | false |
|||
*/ |
|||
hoverPause: true, |
|||
}; |
|||
|
|||
function calcHeaderData({ header, index, indexHeader }) { |
|||
if (!header.length) { |
|||
return []; |
|||
} |
|||
|
|||
header = [...header]; |
|||
|
|||
if (index) header.unshift(indexHeader); |
|||
|
|||
return header; |
|||
} |
|||
|
|||
function calcRows({ |
|||
data, index, headerBGC, rowNum, |
|||
}) { |
|||
if (index) { |
|||
data = data.map((row, i) => { |
|||
row = [...row]; |
|||
|
|||
const indexTag = `<span class="index" style="background-color: ${headerBGC};">${i |
|||
+ 1}</span>`; |
|||
|
|||
row.unshift(indexTag); |
|||
|
|||
return row; |
|||
}); |
|||
} |
|||
|
|||
data = data.map((ceils, i) => ({ ceils, rowIndex: i })); |
|||
|
|||
const rowLength = data.length; |
|||
|
|||
if (rowLength > rowNum && rowLength < 2 * rowNum) { |
|||
data = [...data, ...data]; |
|||
} |
|||
|
|||
return data.map((d, i) => ({ ...d, scroll: i })); |
|||
} |
|||
|
|||
function calcAligns(mergedConfig, header) { |
|||
const columnNum = header.length; |
|||
|
|||
const aligns = new Array(columnNum).fill('left'); |
|||
|
|||
const { align } = mergedConfig; |
|||
|
|||
return deepMerge(aligns, align); |
|||
} |
|||
|
|||
const ScrollBoard = forwardRef(({ |
|||
onClick, config = {}, className, style, onMouseOver, |
|||
}, ref) => { |
|||
const { width, height, domRef } = useAutoResize(ref); |
|||
|
|||
const [state, setState] = useState({ |
|||
mergedConfig: null, |
|||
|
|||
header: [], |
|||
|
|||
rows: [], |
|||
|
|||
rowsShow: [], |
|||
|
|||
widths: [], |
|||
|
|||
heights: [], |
|||
|
|||
aligns: [], |
|||
}); |
|||
|
|||
const { |
|||
mergedConfig, header, rows, widths, heights, aligns, rowsShow, |
|||
} = state; |
|||
|
|||
const stateRef = useRef({ |
|||
...state, |
|||
rowsData: [], |
|||
avgHeight: 0, |
|||
animationIndex: 0, |
|||
}); |
|||
|
|||
Object.assign(stateRef.current, state); |
|||
|
|||
function onResize() { |
|||
if (!mergedConfig) return; |
|||
|
|||
const widths = calcWidths(mergedConfig, stateRef.current.rowsData); |
|||
|
|||
const heights = calcHeights(mergedConfig, header); |
|||
|
|||
const data = { widths, heights }; |
|||
|
|||
Object.assign(stateRef.current, data); |
|||
setState((state) => ({ ...state, ...data })); |
|||
} |
|||
const [init, setInit] = useState(true); |
|||
|
|||
function calcData() { |
|||
// const mergedConfig = deepMerge(
|
|||
// deepClone(defaultConfig, true),
|
|||
// config || {},
|
|||
// );
|
|||
const mergedConfig = { |
|||
...defaultConfig, |
|||
...config, |
|||
}; |
|||
|
|||
const header = calcHeaderData(mergedConfig); |
|||
|
|||
const rows = calcRows(mergedConfig); |
|||
|
|||
const widths = calcWidths(mergedConfig, stateRef.current.rowsData); |
|||
|
|||
const heights = calcHeights(mergedConfig, header); |
|||
|
|||
const aligns = calcAligns(mergedConfig, header); |
|||
|
|||
const data = { |
|||
mergedConfig, |
|||
header, |
|||
rows, |
|||
widths, |
|||
aligns, |
|||
heights: init ? heights : state.heights.concat(heights), |
|||
rowsShow: init ? rows : state.rowsShow, |
|||
}; |
|||
setInit(false); |
|||
Object.assign(stateRef.current, data, { |
|||
rowsData: rows, |
|||
animationIndex: stateRef.current.animationIndex, |
|||
}); |
|||
|
|||
setState((state) => ({ ...state, ...data })); |
|||
} |
|||
|
|||
function calcWidths({ columnWidth, header }, rowsData) { |
|||
const usedWidth = columnWidth.reduce((all, w) => all + w, 0); |
|||
|
|||
let columnNum = 0; |
|||
if (rowsData[0]) { |
|||
columnNum = rowsData[0].ceils.length; |
|||
} else if (header.length) { |
|||
columnNum = header.length; |
|||
} |
|||
|
|||
const avgWidth = (width - usedWidth) / (columnNum - columnWidth.length); |
|||
|
|||
const widths = new Array(columnNum).fill(avgWidth); |
|||
|
|||
return deepMerge(widths, columnWidth); |
|||
} |
|||
|
|||
function calcHeights({ headerHeight, rowNum, data }, header) { |
|||
let allHeight = height; |
|||
|
|||
if (header.length) allHeight -= headerHeight; |
|||
|
|||
const avgHeight = allHeight / rowNum; |
|||
|
|||
Object.assign(stateRef.current, { avgHeight }); |
|||
|
|||
return new Array(data.length).fill(avgHeight); |
|||
} |
|||
|
|||
function* animation(start = false) { |
|||
let { |
|||
avgHeight, |
|||
animationIndex, |
|||
mergedConfig: { waitTime, carousel, rowNum }, |
|||
rowsData, |
|||
} = stateRef.current; |
|||
|
|||
const rowLength = rowsData.length; |
|||
|
|||
if (start) yield new Promise((resolve) => setTimeout(resolve, waitTime)); |
|||
|
|||
const animationNum = carousel === 'single' ? 1 : rowNum; |
|||
|
|||
let rows = rowsData.slice(animationIndex); |
|||
rows.push(...rowsData.slice(0, animationIndex)); |
|||
rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1); |
|||
|
|||
const heights = new Array(rowLength).fill(avgHeight); |
|||
setState((state) => ({ |
|||
...state, rows, heights, rowsShow: rows, |
|||
})); |
|||
|
|||
yield new Promise((resolve) => setTimeout(resolve, 300)); |
|||
|
|||
animationIndex += animationNum; |
|||
|
|||
const back = animationIndex - rowLength; |
|||
if (back >= 0) animationIndex = back; |
|||
|
|||
const newHeights = [...heights]; |
|||
newHeights.splice(0, animationNum, ...new Array(animationNum).fill(0)); |
|||
|
|||
Object.assign(stateRef.current, { animationIndex }); |
|||
setState((state) => ({ ...state, heights: newHeights })); |
|||
} |
|||
|
|||
function emitEvent(handle, ri, ci, row, ceil) { |
|||
const { ceils, rowIndex } = row; |
|||
|
|||
handle && handle({ |
|||
row: ceils, ceil, rowIndex, columnIndex: ci, |
|||
}); |
|||
} |
|||
|
|||
function handleHover(enter, ri, ci, row, ceil) { |
|||
if (enter) emitEvent(onMouseOver, ri, ci, row, ceil); |
|||
|
|||
if (!mergedConfig.hoverPause) return; |
|||
|
|||
const { pause, resume } = task.current; |
|||
|
|||
enter && pause && resume ? pause() : resume && resume(); |
|||
} |
|||
|
|||
// updateRows(rows, animationIndex) {
|
|||
// const { mergedConfig, animationHandler, animation } = this
|
|||
// this.mergedConfig = {
|
|||
// ...mergedConfig,
|
|||
// data: [...rows]
|
|||
// }
|
|||
// this.needCalc = true
|
|||
// if (typeof animationIndex === 'number') this.animationIndex = animationIndex
|
|||
// if (!animationHandler) animation(true)
|
|||
// }
|
|||
|
|||
const getBackgroundColor = (rowIndex) => mergedConfig[rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']; |
|||
|
|||
const task = useRef({}); |
|||
|
|||
useEffect(() => { |
|||
calcData(); |
|||
|
|||
let start = true; |
|||
|
|||
function* loop() { |
|||
while (true) { |
|||
yield* animation(start); |
|||
|
|||
start = false; |
|||
|
|||
const { waitTime } = stateRef.current.mergedConfig; |
|||
|
|||
yield new Promise((resolve) => setTimeout(resolve, waitTime - 300)); |
|||
} |
|||
} |
|||
|
|||
const { |
|||
mergedConfig: { rowNum }, |
|||
rows: rowsData, |
|||
} = stateRef.current; |
|||
|
|||
const rowLength = rowsData.length; |
|||
|
|||
if (rowNum >= rowLength) { |
|||
setState((prestate) => ({ |
|||
...prestate, rowsShow: state.rows, |
|||
})); |
|||
return; |
|||
} |
|||
|
|||
task.current = co(loop); |
|||
|
|||
return task.current.end; |
|||
}, [config, domRef.current]); |
|||
|
|||
useEffect(onResize, [width, height, domRef.current]); |
|||
|
|||
const classNames = useMemo(() => classnames('dv-scroll-board', className), [ |
|||
className, |
|||
]); |
|||
|
|||
return ( |
|||
<div className={classNames} style={style} ref={domRef}> |
|||
{!!header.length && !!mergedConfig && ( |
|||
<div |
|||
className="header" |
|||
style={{ backgroundColor: `${mergedConfig.headerBGC}` }} |
|||
> |
|||
{header.map((headerItem, i) => ( |
|||
<div |
|||
className="header-item" |
|||
key={`${headerItem}-${i}`} |
|||
style={{ |
|||
height: `${mergedConfig.headerHeight}px`, |
|||
lineHeight: `${mergedConfig.headerHeight}px`, |
|||
width: `${widths[i]}px`, |
|||
}} |
|||
align={aligns[i]} |
|||
dangerouslySetInnerHTML={{ __html: headerItem }} |
|||
/> |
|||
))} |
|||
</div> |
|||
)} |
|||
|
|||
{!!mergedConfig && ( |
|||
<div |
|||
className="rows" |
|||
style={{ |
|||
height: `${height |
|||
- (header.length ? mergedConfig.headerHeight : 0)}px`,
|
|||
}} |
|||
> |
|||
{rowsShow.map((row, ri) => ( |
|||
<div |
|||
className="row-item" |
|||
key={`${row.toString()}-${row.scroll}`} |
|||
style={{ |
|||
height: `${heights[ri]}px`, |
|||
lineHeight: `${heights[ri]}px`, |
|||
backgroundColor: `${getBackgroundColor(row.rowIndex)}`, |
|||
}} |
|||
> |
|||
{row.ceils.map((ceil, ci) => { |
|||
if (typeof (ceil) === 'string') { |
|||
return ( |
|||
<div |
|||
className="ceil" |
|||
key={`${ceil}-${ri}-${ci}`} |
|||
style={{ width: `${widths[ci]}px` }} |
|||
align={aligns[ci]} |
|||
dangerouslySetInnerHTML={{ __html: ceil }} |
|||
onClick={() => emitEvent(onClick, ri, ci, row, ceil)} |
|||
onMouseEnter={() => handleHover(true, ri, ci, row, ceil)} |
|||
onMouseLeave={() => handleHover(false)} |
|||
/> |
|||
); |
|||
} |
|||
return ( |
|||
<div |
|||
className="ceil" |
|||
style={{ width: `${widths[ci]}px` }} |
|||
align={aligns[ci]} |
|||
key={`${ri}-${ci}`} |
|||
onMouseEnter={() => handleHover(true, ri, ci, row, ceil)} |
|||
onMouseLeave={() => handleHover(false)} |
|||
> |
|||
{ceil} |
|||
</div> |
|||
); |
|||
})} |
|||
</div> |
|||
))} |
|||
</div> |
|||
)} |
|||
</div> |
|||
); |
|||
}); |
|||
|
|||
ScrollBoard.propTypes = { |
|||
config: PropTypes.object, |
|||
onClick: PropTypes.func, |
|||
onMouseOver: PropTypes.func, |
|||
className: PropTypes.string, |
|||
style: PropTypes.object, |
|||
}; |
|||
|
|||
export default ScrollBoard; |
@ -0,0 +1,44 @@ |
|||
.dv-scroll-board { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
color: #fff; |
|||
|
|||
.text { |
|||
padding: 0 10px; |
|||
box-sizing: border-box; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
font-size: 15px; |
|||
|
|||
.header-item { |
|||
.text; |
|||
transition: all 0.3s; |
|||
} |
|||
} |
|||
|
|||
.rows { |
|||
overflow: hidden; |
|||
|
|||
.row-item { |
|||
display: flex; |
|||
font-size: 14px; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.ceil { |
|||
.text; |
|||
} |
|||
|
|||
.index { |
|||
border-radius: 3px; |
|||
padding: 0px 3px; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
'use strict' |
|||
|
|||
import React from 'react' |
|||
import './index.less' |
|||
class Box extends React.Component { |
|||
render() { |
|||
const { title, height = '100%', children, bodyPaddingTop = 1, titlePaddingTop, margin, overflow } = this.props |
|||
|
|||
const headerbg = { |
|||
background: 'url(/assets/images/homePage/bigscreen/headertitlebg.png) no-repeat', |
|||
backgroundSize: '100% 100%', |
|||
} |
|||
return ( |
|||
<div style={{ height, width: '100%', margin: margin || "0px 0px 28px" }}> |
|||
<div style={{ |
|||
height: height, listStyle: 'none', overflow: overflow || 'hidden', |
|||
backgroundImage: 'linear-gradient(180deg, #00204a00 3%, #50c9f71a 100%)', |
|||
}}> |
|||
<div style={{ height: 42, paddingLeft: 24, paddingTop: '4px', wordBreak: 'keep-all', whiteSpace: 'nowrap', width: '100%', ...headerbg }}> |
|||
<span className='card-title'>{title}</span><div className='_sorrow' /> |
|||
</div> |
|||
<div |
|||
style={{ |
|||
width: '100%', height: 2, |
|||
marginTop: titlePaddingTop || 10, marginBottom: bodyPaddingTop || 25, |
|||
}} /> |
|||
{children} |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
export default Box |
|||
|
@ -0,0 +1,373 @@ |
|||
@card-height: calc(100% - 42px - 13px); //左右卡片内容高度定义 目前卡片为等高 |
|||
|
|||
//节点资源 |
|||
.node-resource-container { |
|||
display: flex; |
|||
height: @card-height; |
|||
width: 100%; |
|||
align-items: center; |
|||
|
|||
._item { |
|||
width: 33%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
|
|||
._noderesource_data { |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 600; |
|||
font-size: 24px; |
|||
color: #FFFFFF; |
|||
line-height: 43.2px; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
._percent { |
|||
opacity: 0.8; |
|||
font-family: PingFangSC-Regular; |
|||
font-weight: 400; |
|||
font-size: 12px; |
|||
color: #C8F0FF; |
|||
text-align: left; |
|||
line-height: 24px; |
|||
} |
|||
} |
|||
|
|||
._noderesource_title { |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 20px; |
|||
color: #D8F0FF; |
|||
letter-spacing: 1.54px; |
|||
text-align: center; |
|||
text-shadow: 0 0 10px rgba(0, 145, 255, 0.5); |
|||
margin-bottom: 17px; |
|||
} |
|||
|
|||
.disk_icon { |
|||
width: 68.73px; |
|||
height: 62.77px; |
|||
background: url('/assets/images/homePage/bigscreen/disk.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.cpu_icon { |
|||
width: 68.73px; |
|||
height: 62.77px; |
|||
background: url('/assets/images/homePage/bigscreen/cpu.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.memory_icon { |
|||
width: 68.73px; |
|||
height: 62.77px; |
|||
background: url('/assets/images/homePage/bigscreen/memory.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//接入数据统计 |
|||
.access_data { |
|||
display: flex; |
|||
height: @card-height; |
|||
width: 100%; |
|||
justify-content: center; |
|||
|
|||
font-family: PingFangSC-Regular; |
|||
font-weight: 400; |
|||
font-size: 14px; |
|||
color: #FFFFFF; |
|||
|
|||
._img { |
|||
width: 230px; |
|||
height: 95%; |
|||
background: url('/assets/images/homePage/bigscreen/accessdata.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.data_unit { |
|||
position: absolute; |
|||
top: 27%; |
|||
right: 21%; |
|||
|
|||
.data_number { |
|||
line-height: 25px; |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 700; |
|||
font-size: 20px; |
|||
color: #3E86FF; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
|
|||
.data_today { |
|||
position: absolute; |
|||
bottom: 13%; |
|||
right: 79%; |
|||
text-align: right; |
|||
|
|||
.data_number { |
|||
line-height: 25px; |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 700; |
|||
font-size: 20px; |
|||
color: #00F6E4; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
|
|||
.data_total { |
|||
position: absolute; |
|||
bottom: 13%; |
|||
left: 79%; |
|||
|
|||
.data_number { |
|||
line-height: 25px; |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 700; |
|||
font-size: 20px; |
|||
color: #FFDC4E; |
|||
letter-spacing: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.data_top5_unit { |
|||
position: absolute; |
|||
right: 4%; |
|||
top: 18%; |
|||
font-family: PingFangSC-Regular; |
|||
font-weight: 400; |
|||
font-size: 12px; |
|||
color: #C8F0FF; |
|||
} |
|||
|
|||
.hotspot_data_container { |
|||
display: flex; |
|||
height: @card-height; |
|||
width: 100%; |
|||
justify-content: center; |
|||
|
|||
font-family: PingFangSC-Regular; |
|||
font-weight: 400; |
|||
font-size: 14px; |
|||
color: #FFFFFF; |
|||
|
|||
._img { |
|||
width: 203px; |
|||
height: 80%; |
|||
background: url('/assets/images/homePage/bigscreen/hotspotdatabg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.hotspot_title { |
|||
padding: 2px; |
|||
padding-left: 6px; |
|||
padding-right: 6px; |
|||
background: rgba(77, 241, 227, 0.08); |
|||
border: 1px solid rgba(77, 241, 227, 0.1); |
|||
box-shadow: inset 0 0 20px 0 rgba(28, 185, 196, 0.23); |
|||
} |
|||
|
|||
|
|||
.hotspot_data_number { |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 700; |
|||
font-size: 18px; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
._top1 { |
|||
position: absolute; |
|||
top: 25%; |
|||
right: 63%; |
|||
text-align: right; |
|||
} |
|||
|
|||
._top2 { |
|||
position: absolute; |
|||
bottom: 22%; |
|||
right: 67%; |
|||
text-align: center; |
|||
} |
|||
|
|||
._top3 { |
|||
position: absolute; |
|||
bottom: 34%; |
|||
left: 73%; |
|||
} |
|||
} |
|||
|
|||
//数据共享 |
|||
.data_share { |
|||
display: flex; |
|||
height: @card-height; |
|||
|
|||
._left_content { |
|||
width: 50%; |
|||
height: 90%; |
|||
padding-left: 30px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
._right_content { |
|||
._today_text { |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 24px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0.5px; |
|||
position: absolute; |
|||
right: 6%; |
|||
top: 21%; |
|||
} |
|||
|
|||
padding-top: 23px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
width: 47%; |
|||
height: 95%; |
|||
background: url(/assets/images/homePage/bigscreen/todaybg.png); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
justify-content: space-evenly; |
|||
align-items: center; |
|||
|
|||
} |
|||
|
|||
._item_content { |
|||
display: flex; |
|||
|
|||
|
|||
._item_icon1 { |
|||
width: 52px; |
|||
height: 52px; |
|||
background: url('/assets/images/homePage/bigscreen/1.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._item_icon2 { |
|||
width: 52px; |
|||
height: 52px; |
|||
background: url('/assets/images/homePage/bigscreen/2.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._item_icon3 { |
|||
width: 52px; |
|||
height: 52px; |
|||
background: url('/assets/images/homePage/bigscreen/3.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._item_text { |
|||
color: #C8F0FF; |
|||
padding-left: 6px; |
|||
|
|||
.number_container { |
|||
._number { |
|||
font-family: D-DINExp-Bold; |
|||
font-weight: 700; |
|||
font-size: 22px; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-evenly; |
|||
width: 112px; |
|||
height: 28px; |
|||
background-image: linear-gradient(227deg, #3196AB 0%, #2091cd00 100%); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//大屏中间上部分 |
|||
.center_top_data { |
|||
|
|||
._center_card1 { |
|||
width: 353px; |
|||
height: 74px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 22px; |
|||
color: #FFFFFF; |
|||
letter-spacing: 0.46px; |
|||
text-align: center; |
|||
position: absolute; |
|||
top: -3%; |
|||
left: 32%; |
|||
background: url(/assets/images/homePage/bigscreen/centerdatabg1.png); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
._center_card2 { |
|||
width: 146px; |
|||
height: 35px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 16px; |
|||
color: #35D0FF; |
|||
letter-spacing: 0.46px; |
|||
text-align: center; |
|||
position: absolute; |
|||
top: 26%; |
|||
left: 42%; |
|||
background: url(/assets/images/homePage/bigscreen/centerdatabg2.png); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
._center_card3 { |
|||
width: 146px; |
|||
height: 35px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 16px; |
|||
color: #35D0FF; |
|||
letter-spacing: 0.46px; |
|||
text-align: center; |
|||
position: absolute; |
|||
top: 52%; |
|||
left: 42%; |
|||
background: url(/assets/images/homePage/bigscreen/centerdatabg2.png); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
._center_card4 { |
|||
width: 146px; |
|||
height: 35px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 16px; |
|||
color: #35D0FF; |
|||
letter-spacing: 0.46px; |
|||
text-align: center; |
|||
position: absolute; |
|||
top: 74%; |
|||
left: 42%; |
|||
background: url(/assets/images/homePage/bigscreen/centerdatabg2.png); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
export const mathRound = (number) => { |
|||
return number ? Math.round(number / 1000) / 10 : 0 |
|||
} |
@ -0,0 +1,74 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import { connect } from 'react-redux'; |
|||
import { push } from 'react-router-redux'; |
|||
import AccessData from '../components/accessData' |
|||
import AlarmList from '../components/alarmList' |
|||
import DataShare from '../components/dataShare' |
|||
import DataTop5 from '../components/dataTop5' |
|||
import HotspotData from '../components/hotspotData' |
|||
import NodeResource from '../components/nodeResource' |
|||
import AbnormalMonitoring from '../components/abnormalMonitoring' |
|||
import CenterTop from '../components/centerTop' |
|||
import './style.less' |
|||
|
|||
function homePage(props) { |
|||
const { dispatch } = props; |
|||
const childStyle = { height: '32%', color: '#fff', marginBottom: 17 } |
|||
const cardHeight = document.body.clientHeight * 0.896 * 0.32 |
|||
const cardContentHeight = cardHeight - 42 - 13 |
|||
return <div className='homepage'> |
|||
<div className='_title'> |
|||
<div onClick={() => { dispatch(push('/metadataManagement/latestMetadata')) }} className='_exit' ><div className='_icon' />进入后台</div> |
|||
</div> |
|||
<div className='homepage-left homepage-left-left'> |
|||
<div className="list"> |
|||
<div className='child' style={childStyle}> |
|||
<AccessData /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<NodeResource /> |
|||
</div> |
|||
<div className='child' style={childStyle}> |
|||
<AlarmList cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className='homepage-center'> |
|||
<CenterTop /> |
|||
<div className="list"> |
|||
<div className='child-top'> |
|||
<AbnormalMonitoring /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className='homepage-left homepage-left-right'> |
|||
<div className="list"> |
|||
<div className='child-right' style={childStyle}> |
|||
<DataShare /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<DataTop5 cardContentHeight={cardContentHeight} /> |
|||
</div> |
|||
<div className='child-right' style={childStyle}> |
|||
<HotspotData /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { |
|||
auth, global |
|||
} = state; |
|||
return { |
|||
clientHeight: global.clientHeight, |
|||
actions: global.actions, |
|||
|
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(homePage); |
|||
|
|||
|
@ -0,0 +1,6 @@ |
|||
'use strict'; |
|||
|
|||
import homePage from './homePage'; |
|||
|
|||
|
|||
export default homePage ; |
@ -0,0 +1,324 @@ |
|||
.homepage { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
background: url('/assets/images/homePage/enter/bg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
padding-top: 5%; |
|||
|
|||
._enter_row1 { |
|||
height: 302px; |
|||
|
|||
._row1_item { |
|||
width: 1434px; |
|||
height: 241px; |
|||
// opacity: 0.8; |
|||
background: url('/assets/images/homePage/enter/row1.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
._row1_item:hover { |
|||
width: 1434px; |
|||
height: 302.6px; |
|||
background: url('/assets/images/homePage/enter/row1-select.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
._enter_row2 { |
|||
height: 427px; |
|||
display: flex; |
|||
width: 100%; |
|||
padding-left: 4%; |
|||
|
|||
._row2_item1 { |
|||
width: 454px; |
|||
height: 371px; |
|||
// opacity: 0.8; |
|||
background: url('/assets/images/homePage/enter/row2-1.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
margin-left: 60px; |
|||
} |
|||
|
|||
._row2_item1:hover { |
|||
background: url('/assets/images/homePage/enter/row2-1-select.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
._row2_item2 { |
|||
margin-left: 60px; |
|||
width: 454px; |
|||
height: 371px; |
|||
// opacity: 0.8; |
|||
background: url('/assets/images/homePage/enter/row2-2.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
._row2_item2:hover { |
|||
background: url('/assets/images/homePage/enter/row2-2-select.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
._row2_item3 { |
|||
margin-left: 60px; |
|||
width: 454px; |
|||
height: 371px; |
|||
// opacity: 0.8; |
|||
background: url('/assets/images/homePage/enter/row2-3.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
._row2_item3:hover { |
|||
background: url('/assets/images/homePage/enter/row2-3-select.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
._enter_title { |
|||
position: absolute; |
|||
top: 4%; |
|||
left: 3%; |
|||
width: 575.35px; |
|||
height: 78px; |
|||
background: url('/assets/images/homePage/enter/title.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._title { |
|||
width: 100%; |
|||
height: 88px; |
|||
background: url('/assets/images/homePage/bigscreen/top.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
._exit { |
|||
position: absolute; |
|||
right: 60px; |
|||
top: 38px; |
|||
cursor: pointer; |
|||
color: #C8F0FF; |
|||
display: flex; |
|||
|
|||
._icon { |
|||
display: inline-block; |
|||
width: 28px; |
|||
height: 28px; |
|||
background: url('/assets/images/homePage/bigscreen/exit.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-right: 3px; |
|||
} |
|||
} |
|||
|
|||
.homepage-left { |
|||
width: 21.8%; |
|||
height: 89.6%; |
|||
position: absolute; |
|||
top: 8.2%; |
|||
z-index: 300; |
|||
} |
|||
|
|||
.homepage-center { |
|||
width: 49.16%; |
|||
height: 89.6%; |
|||
position: absolute; |
|||
bottom: 2.4%; |
|||
left: 25.5%; |
|||
padding-left: 16px; |
|||
padding-right: 16px; |
|||
z-index: 400; |
|||
|
|||
._top { |
|||
margin-top: 5%; |
|||
height: calc(100% - 200px); |
|||
background: url('/assets/images/homePage/bigscreen/centerbg.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
.homepage-left-left { |
|||
left: 48px; |
|||
} |
|||
|
|||
.homepage-left-right { |
|||
right: 48px; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
.list { |
|||
list-style: none; |
|||
height: 100%; |
|||
} |
|||
|
|||
.list .child { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateX(-300px); |
|||
animation: show .5s forwards; |
|||
} |
|||
|
|||
.list .child.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child.hide { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
/*animation-delay*/ |
|||
.list .child:not(.hide):nth-child(5n + 1) { |
|||
animation-delay: .3s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 2) { |
|||
animation-delay: .6s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 3) { |
|||
animation-delay: .9s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 4) { |
|||
animation-delay: 1.2s; |
|||
} |
|||
|
|||
.list .child:not(.hide):nth-child(5n + 5) { |
|||
animation-delay: 1.5s; |
|||
} |
|||
|
|||
.list .child-right { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateX(300px); |
|||
animation: show .5s forwards; |
|||
} |
|||
|
|||
.list .child-right.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child-right.hide { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
/*animation-delay*/ |
|||
.list .child-right:not(.hide):nth-child(5n + 1) { |
|||
animation-delay: .3s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 2) { |
|||
animation-delay: .6s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 3) { |
|||
animation-delay: .9s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 4) { |
|||
animation-delay: 1.2s; |
|||
} |
|||
|
|||
.list .child-right:not(.hide):nth-child(5n + 5) { |
|||
animation-delay: 1.5s; |
|||
} |
|||
|
|||
.list .child-top { |
|||
box-sizing: border-box; |
|||
opacity: 0; |
|||
transform: translateY(300px); |
|||
animation: show 1s forwards; |
|||
} |
|||
|
|||
.list .child-top.show { |
|||
animation-delay: 0s !important; |
|||
} |
|||
|
|||
.list .child-top.hide { |
|||
opacity: 1; |
|||
transform: translateY(0); |
|||
animation-name: hide; |
|||
animation-delay: 0s; |
|||
} |
|||
|
|||
|
|||
@keyframes show { |
|||
to { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
} |
|||
} |
|||
|
|||
@keyframes hide { |
|||
to { |
|||
opacity: 0; |
|||
transform: translateX(100px); |
|||
max-height: 0; |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.center-card-title { |
|||
height: 31px; |
|||
font-family: YouSheBiaoTiHei; |
|||
font-size: 24px; |
|||
color: #FFFFFF; |
|||
display: flex; |
|||
align-items: center; |
|||
margin-top: 5px; |
|||
|
|||
._icon_left { |
|||
width: 32px; |
|||
height: 17px; |
|||
background: url('/assets/images/homePage/bigscreen/center-left.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-right: 11px; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
._icon_right { |
|||
width: 32px; |
|||
height: 17px; |
|||
background: url('/assets/images/homePage/bigscreen/center-right.png'); |
|||
background-repeat: no-repeat; |
|||
background-size: 100% 100%; |
|||
margin-right: 11px; |
|||
margin-left: 10px; |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
import reducers from './reducers'; |
|||
import routes from './routes'; |
|||
import actions from './actions'; |
|||
import { getNavItem } from './nav-item'; |
|||
|
|||
export default { |
|||
key: 'waterprevention', |
|||
name: '首页', |
|||
reducers: reducers, |
|||
routes: routes, |
|||
actions: actions, |
|||
getNavItem: getNavItem |
|||
}; |
@ -0,0 +1,11 @@ |
|||
import React from 'react'; |
|||
import { Link } from 'react-router-dom'; |
|||
import { Menu } from 'antd'; |
|||
import { HomeOutlined } from '@ant-design/icons'; |
|||
export function getNavItem(user) { |
|||
return ( |
|||
<Menu.Item key="waterprevention" icon={<HomeOutlined />}> |
|||
<Link to="/waterprevention">数据监控平台</Link> |
|||
</Menu.Item> |
|||
); |
|||
} |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
export default { |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
'use strict'; |
|||
import homePage from './containers/index'; |
|||
|
|||
export default [{ |
|||
type: 'outer', |
|||
route: { |
|||
path: '/waterprevention', |
|||
key: 'waterprevention', |
|||
breadcrumb: '数据监控平台', |
|||
// 不设置 component 则面包屑禁止跳转
|
|||
component: homePage |
|||
} |
|||
}]; |