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 |
||||
|
} |
||||
|
}]; |