Compare commits
No commits in common. "9181d55cad590e8b56a1752c664b5dd3cf51a14a" and "83372f9739d399e9fc4c045b0af1b16e364e0a45" have entirely different histories.
9181d55cad
...
83372f9739
Binary file not shown.
|
Before Width: | Height: | Size: 824 B |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
|
|
@ -337,13 +337,6 @@ input:-webkit-autofill:active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-picker-cell-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
.ant-picker-cell-inner {
|
|
||||||
color: rgba(255, 255, 255, 0.25) !important;
|
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Footer/Ranges
|
// Footer/Ranges
|
||||||
.ant-picker-footer {
|
.ant-picker-footer {
|
||||||
|
|
|
||||||
|
|
@ -157,17 +157,33 @@ const apiurl = {
|
||||||
managePic: service + '/screen/manageHouseImg/get',
|
managePic: service + '/screen/manageHouseImg/get',
|
||||||
wzPage:service + '/rescue/goods/page/query'
|
wzPage:service + '/rescue/goods/page/query'
|
||||||
},
|
},
|
||||||
qhfz: {
|
zrrgl: {
|
||||||
lawPage: service + '/SzRuleByLaw/page',
|
page: service + "/resPerson/page",
|
||||||
systemPage: service + '/SzRegulatoryFramework/page',
|
edit: service + "/resPerson/update",
|
||||||
caseSta:service + '/szCase/statistics/1'
|
delete: service + "/resPerson/del",
|
||||||
|
save: service + "/resPerson/insert",
|
||||||
},
|
},
|
||||||
zrz: {
|
},
|
||||||
respPerson: service + '/screen/responsibility/getPerson',
|
sy: {
|
||||||
floodPerson: service + '/screen/responsibility/getFxPerson',
|
fxya:{
|
||||||
train:service + '/screen/responsibility/getTraining'
|
page: service + "/resPlanB/list",
|
||||||
}
|
update: service + "/resPlanB/update",
|
||||||
}
|
save: service + "/resPlanB/insert",
|
||||||
|
delete: service + "/resPlanB/del",
|
||||||
|
getFile: service + "/resPlanB/file/get"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gcaqjc: {
|
||||||
|
gcaqyj: {
|
||||||
|
yjgzpz:{
|
||||||
|
page: service + '/osmoticWarnRule/page',
|
||||||
|
save: service + '/osmoticWarnRule/insert',
|
||||||
|
edit: service + "/osmoticWarnRule/update",
|
||||||
|
delete: service + "/osmoticWarnRule/del",
|
||||||
|
list: service + "/osmoticPressDevice/list"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiurl
|
export default apiurl
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import centerOfMass from '@turf/center-of-mass';
|
||||||
import bbox from '@turf/bbox';
|
import bbox from '@turf/bbox';
|
||||||
import turfLength from '@turf/length';
|
import turfLength from '@turf/length';
|
||||||
import along from '@turf/along';
|
import along from '@turf/along';
|
||||||
import { config } from '@/config';
|
|
||||||
const class2type = {};
|
const class2type = {};
|
||||||
const { toString } = class2type;
|
const { toString } = class2type;
|
||||||
'Boolean Number String Function Array Date RegExp Object Error'.split(' ').forEach((name) => {
|
'Boolean Number String Function Array Date RegExp Object Error'.split(' ').forEach((name) => {
|
||||||
|
|
@ -613,17 +613,4 @@ export const myFiltrate = (data,params)=>{
|
||||||
return query.substring(iStart);
|
return query.substring(iStart);
|
||||||
|
|
||||||
return query.substring(iStart, iEnd);
|
return query.substring(iStart, iEnd);
|
||||||
}
|
|
||||||
|
|
||||||
export const download = (url) => {
|
|
||||||
let downloadLink = document.createElement("a");
|
|
||||||
// downloadLink.href = `${process.env.REACT_APP_API_URL}/gunshiApp/ss/personnelPlan/file/download/${params}`;
|
|
||||||
downloadLink.href = config.ip +url;
|
|
||||||
// downloadLink.download = `${params.fileName}`;
|
|
||||||
downloadLink.style.display = "none";
|
|
||||||
// 将链接添加到页面中
|
|
||||||
document.body.appendChild(downloadLink);
|
|
||||||
|
|
||||||
// 模拟点击事件,开始下载
|
|
||||||
downloadLink.click();
|
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,6 @@ import { treeList, srcData, videoList, ysyToken } from './http'
|
||||||
import VideoControler from "@/components/VideoCom/VideoControler"
|
import VideoControler from "@/components/VideoCom/VideoControler"
|
||||||
import { httppost } from "@/utils/request"
|
import { httppost } from "@/utils/request"
|
||||||
import apiurl from "@/service/apiurl"
|
import apiurl from "@/service/apiurl"
|
||||||
import { Spin } from "antd"
|
|
||||||
|
|
||||||
|
|
||||||
const VideoList = () => {
|
const VideoList = () => {
|
||||||
|
|
@ -16,7 +15,6 @@ const VideoList = () => {
|
||||||
const [size, setSize] = useState(1)
|
const [size, setSize] = useState(1)
|
||||||
const [treeListData, setTreeData] = useState([])
|
const [treeListData, setTreeData] = useState([])
|
||||||
const [selectList, setSelectList] = useState()
|
const [selectList, setSelectList] = useState()
|
||||||
const [loading, setLoading] = useState(true)
|
|
||||||
|
|
||||||
const selectedKeys = async (list, node) => {
|
const selectedKeys = async (list, node) => {
|
||||||
console.log(node, 'node');
|
console.log(node, 'node');
|
||||||
|
|
@ -76,24 +74,18 @@ const VideoList = () => {
|
||||||
|
|
||||||
}
|
}
|
||||||
const getTreeData = async () => {
|
const getTreeData = async () => {
|
||||||
try {
|
const res = await treeList()
|
||||||
const res = await treeList()
|
const res1 = await videoList()
|
||||||
const res1 = await videoList()
|
const arr = res1.data.filter(item => item.menuId).map((item, index) => {
|
||||||
const arr = res1.data.filter(item => item.menuId).map((item, index) => {
|
item.parentId = item.menuId
|
||||||
item.parentId = item.menuId
|
item.id = 999 + index
|
||||||
item.id = 999 + index
|
item.isLeaf = true
|
||||||
item.isLeaf = true
|
// item.icon=<VideoCameraFilled />
|
||||||
// item.icon=<VideoCameraFilled />
|
return item
|
||||||
return item
|
})
|
||||||
})
|
const arr1 = [...arr, ...res.data]
|
||||||
const arr1 = [...arr, ...res.data]
|
console.log("before", arr1);
|
||||||
console.log("before", arr1);
|
setTreeData(buildTree(arr1))
|
||||||
setTreeData(buildTree(arr1))
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function buildTree(data, parentId = "0") {
|
function buildTree(data, parentId = "0") {
|
||||||
const tree = [];
|
const tree = [];
|
||||||
|
|
@ -145,28 +137,20 @@ const VideoList = () => {
|
||||||
}, [])
|
}, [])
|
||||||
return (
|
return (
|
||||||
<div className='flex videoList'>
|
<div className='flex videoList'>
|
||||||
{loading ? (
|
<div className={['treeRight', (selectList && selectList.type == 1) ? 'ptz-visible' : ''].join(' ')}>
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
<TreeData size={size} selectedKeys={selectedKeys} treeListData={treeListData} videoArr={videoArr} />
|
||||||
<Spin tip="加载中..." />
|
{
|
||||||
</div>
|
selectList && selectList.type == 1 ?
|
||||||
) : (
|
<div style={{ position: "absolute", bottom: 0, left: 0 }}>
|
||||||
<>
|
<VideoControler
|
||||||
<div className={['treeRight', (selectList && selectList.type == 1) ? 'ptz-visible' : ''].join(' ')}>
|
selectItem={selectList}
|
||||||
<TreeData size={size} selectedKeys={selectedKeys} treeListData={treeListData} videoArr={videoArr} />
|
onOperation={onOperation}
|
||||||
{
|
/>
|
||||||
selectList && selectList.type == 1 ?
|
</div>
|
||||||
<div style={{ position: "absolute", bottom: 0, left: 0 }}>
|
: null
|
||||||
<VideoControler
|
}
|
||||||
selectItem={selectList}
|
</div>
|
||||||
onOperation={onOperation}
|
<div className='treeLeft'><SplitScreen count={count} videoArr={videoArr} clickIndex={clickIndex} getType={getType} /></div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className='treeLeft'><SplitScreen count={count} videoArr={videoArr} clickIndex={clickIndex} getType={getType} /></div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import selectedBg from '@/assets/images/modal/selected.png';
|
|
||||||
import skbg from '@/assets/images/card/skbg.png';
|
|
||||||
import gth from '@/assets/images/card/gth.png';
|
|
||||||
import syl from '@/assets/images/card/syl.png';
|
|
||||||
import wy from '@/assets/images/card/wy.png';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const WarningSection = ({ type = 'monitor' }) => {
|
|
||||||
const [activeTab, setActiveTab] = useState('water'); // 'water' or 'safety' for monitor; 'flood' or 'weather' for forecast
|
|
||||||
|
|
||||||
// Mock Data
|
|
||||||
const waterWarnings = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '双石水库',
|
|
||||||
limitLevel: '108.89',
|
|
||||||
currentLevel: '109.09',
|
|
||||||
exceedance: '0.20',
|
|
||||||
time: '2025-10-28 09:00:00'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const safetyMonitorItems = [
|
|
||||||
{ label: '渗压监测', value: 2, icon: syl },
|
|
||||||
{ label: '渗流监测', value: 1, icon: syl },
|
|
||||||
{ label: '位移监测', value: 0, icon: wy }
|
|
||||||
];
|
|
||||||
|
|
||||||
const floodWarnings = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
message: '根据上游流量站实测和产汇流预测结果,未来24小时预计总入库564.2万m³,平均入库流量65.2m³/s,达到橙色预警级别。',
|
|
||||||
time: '2025-10-28 08:10:00'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const weatherWarnings = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '赤壁市气象台发布暴雨红色预警[Ⅰ级/特别...',
|
|
||||||
time: '2025-07-28 06:35:30',
|
|
||||||
content: '赤壁市2025年08月10日07时33分08秒发布暴雨红色预警信号:过去3小时最大降水出现在XXXXX为71毫米。受强降雨云团持续影响,预计未来3小时,上述地区及周边乡镇仍将有50-80毫米的降雨,累计雨量可达150毫米以上,局地阵风7-9级。城乡积涝、山区山洪、地质灾害、中小河流洪水风险极高,请特别加强防范。'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const renderContent = () => {
|
|
||||||
if (type === 'monitor') {
|
|
||||||
if (activeTab === 'water') {
|
|
||||||
return (
|
|
||||||
<div className="warning-list">
|
|
||||||
{waterWarnings.map((item) => (
|
|
||||||
<div key={item.id} className="warning-item">
|
|
||||||
<div className="item-header">
|
|
||||||
<img src={gth} alt="warning" className="warning-icon" />
|
|
||||||
<div className="item-title" style={{ backgroundImage: `url(${skbg})` }}>{item.name}</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-content">
|
|
||||||
<p>水库汛限水位 {item.limitLevel} m。</p>
|
|
||||||
<p>实时监测水位 {item.currentLevel} m,超出汛限水位 <span className="highlight"> {item.exceedance} m</span></p>
|
|
||||||
<div className="item-time">{item.time}</div>
|
|
||||||
</div>
|
|
||||||
<div className="divider" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className="safety-monitor-view">
|
|
||||||
{safetyMonitorItems.map((item, index) => (
|
|
||||||
<div key={index} className="monitor-item">
|
|
||||||
<div className="monitor-value" style={{ color: item.value > 0 ? '#ff4d4f' : '#fff' }}>{item.value}</div>
|
|
||||||
<img src={item.icon} alt={item.label} className="monitor-icon" />
|
|
||||||
<div className="monitor-label">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Forecast Warning
|
|
||||||
if (activeTab === 'flood') {
|
|
||||||
return (
|
|
||||||
<div className="warning-list">
|
|
||||||
{floodWarnings.map(item => (
|
|
||||||
<div key={item.id} className="warning-item">
|
|
||||||
<div className="item-content full-width">
|
|
||||||
<div className="warning-text-wrapper">
|
|
||||||
<img src={gth} alt="warning" className="warning-icon-inline" />
|
|
||||||
<span className="warning-text">{item.message}</span>
|
|
||||||
</div>
|
|
||||||
<div className="item-time" style={{marginLeft:25,marginTop:0}}>预报时间:{item.time}</div>
|
|
||||||
</div>
|
|
||||||
<div className="divider" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className="warning-list">
|
|
||||||
{weatherWarnings.map(item => (
|
|
||||||
<div key={item.id} className="warning-item">
|
|
||||||
<div className="item-header weather-header">
|
|
||||||
<img src={gth} alt="rainstorm" className="weather-icon" />
|
|
||||||
<div className="item-title" title={item.title} style={{ backgroundImage: `url(${skbg})`,paddingLeft:38 }}>{item.title}</div>
|
|
||||||
</div>
|
|
||||||
<div className="item-time weather-time">{item.time}</div>
|
|
||||||
<div className="item-content weather-content">
|
|
||||||
{item.content}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTabs = () => {
|
|
||||||
if (type === 'monitor') {
|
|
||||||
return [
|
|
||||||
{ key: 'water', label: `水库水情 (${waterWarnings.length})` },
|
|
||||||
{ key: 'safety', label: `安全监测 (${safetyMonitorItems.reduce((acc, cur) => acc + cur.value, 0)})` } // Sum of values or just count of items? Design shows (3) which matches items count 3. Let's use 3.
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
{ key: 'flood', label: `洪水预报 (${floodWarnings.length})` },
|
|
||||||
{ key: 'weather', label: `气象预报 (${weatherWarnings.length})` }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset active tab when type changes
|
|
||||||
React.useEffect(() => {
|
|
||||||
setActiveTab(type === 'monitor' ? 'water' : 'flood');
|
|
||||||
}, [type]);
|
|
||||||
|
|
||||||
const tabs = getTabs();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="warning-section">
|
|
||||||
{/* Tabs */}
|
|
||||||
<div className="tabs-container">
|
|
||||||
{tabs.map(tab => (
|
|
||||||
<div
|
|
||||||
key={tab.key}
|
|
||||||
className={`tab-item ${activeTab === tab.key ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab(tab.key)}
|
|
||||||
style={activeTab === tab.key ? { backgroundImage: `url(${selectedBg})` } : {}}
|
|
||||||
>
|
|
||||||
{tab.label}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
{renderContent()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WarningSection;
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
.warning-section {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.tabs-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 0 10px;
|
|
||||||
|
|
||||||
.tab-item {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
padding: 5px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
cursor: pointer;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
transition: all 0.3s;
|
|
||||||
margin: 0 5px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 10px #00a0e9;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-list {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 0 10px;
|
|
||||||
max-height: 180px;
|
|
||||||
|
|
||||||
.warning-item {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
.item-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
.warning-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin-right: 10px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 30px;
|
|
||||||
margin-right: 10px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-title {
|
|
||||||
width: 180px;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #fff;
|
|
||||||
padding: 2px 20px 2px 20px;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.weather-header {
|
|
||||||
.item-title {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
padding-left: 34px; /* Align with title (icon width + margin) */
|
|
||||||
|
|
||||||
&.full-width {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.weather-content {
|
|
||||||
padding-left: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
line-height: 1.5;
|
|
||||||
text-indent: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-text-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.warning-icon-inline {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
margin-right: 8px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
.highlight {
|
|
||||||
color: #ff4d4f; /* Red for warning values */
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-time {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather-time {
|
|
||||||
padding-left: 50px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin-top: 10px;
|
|
||||||
height: 1px;
|
|
||||||
background: repeating-linear-gradient(to right, rgba(0, 160, 233, 0.5) 0, rgba(0, 160, 233, 0.5) 5px, transparent 5px, transparent 10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.safety-monitor-view {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px 10px;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.monitor-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.monitor-value {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monitor-icon {
|
|
||||||
width: 80px;
|
|
||||||
height: 60px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monitor-label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +1,56 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CommonCard from '../../UI/CommonCard';
|
import CommonCard from '../../UI/CommonCard';
|
||||||
import ThreeDots from '../../UI/ThreeDots';
|
import ThreeDots from '../../UI/ThreeDots';
|
||||||
import WarningSection from './components/WarningSection';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
|
|
||||||
const WarningToggles = ({ activeType, onToggle }) => {
|
const WarningToggles = () => {
|
||||||
return (
|
return (
|
||||||
<div className="warning-toggles">
|
<div className="warning-toggles">
|
||||||
<div
|
<div className="toggle-item active">
|
||||||
className={`toggle-item ${activeType === 'monitor' ? 'active' : ''}`}
|
<span>监测预警</span>
|
||||||
onClick={() => onToggle('monitor')}
|
<span className="badge">4</span>
|
||||||
>
|
</div>
|
||||||
<span>监测预警</span>
|
<div className="toggle-item">
|
||||||
<span className="badge">4</span>
|
<span>预报预警</span>
|
||||||
</div>
|
<span className="badge bg-red">2</span>
|
||||||
<div
|
</div>
|
||||||
className={`toggle-item ${activeType === 'forecast' ? 'active' : ''}`}
|
</div>
|
||||||
onClick={() => onToggle('forecast')}
|
)
|
||||||
>
|
|
||||||
<span>预报预警</span>
|
|
||||||
<span className="badge bg-red">2</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SiYu = () => {
|
const SiYu = () => {
|
||||||
const [warningType, setWarningType] = React.useState('monitor');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="siyu-view">
|
<div className="siyu-view">
|
||||||
<div className="side-panel left">
|
<div className="side-panel left">
|
||||||
<CommonCard title="预报" className="panel-card card-1">
|
<CommonCard title="预报" className="panel-card card-1">
|
||||||
<div className="placeholder-content">内容填充区域</div>
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard title="预演" className="panel-card card-2">
|
<CommonCard title="预演" className="panel-card card-2">
|
||||||
<div className="placeholder-content">内容填充区域</div>
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="side-panel right">
|
<div className="side-panel right">
|
||||||
<CommonCard
|
<CommonCard
|
||||||
title="预警"
|
title="预警"
|
||||||
className="panel-card card-1"
|
className="panel-card card-1"
|
||||||
headerExtra={<WarningToggles activeType={warningType} onToggle={setWarningType} />}
|
headerExtra={<WarningToggles />}
|
||||||
>
|
>
|
||||||
<WarningSection type={warningType} />
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard
|
<CommonCard
|
||||||
title="实时水雨情"
|
title="实时水雨情"
|
||||||
className="panel-card card-2"
|
className="panel-card card-2"
|
||||||
headerExtra={<ThreeDots onClick={() => console.log('实时水雨情 clicked')} />}
|
headerExtra={<ThreeDots onClick={() => console.log('实时水雨情 clicked')} />}
|
||||||
>
|
>
|
||||||
<div className="placeholder-content">内容填充区域</div>
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
|
|
||||||
<CommonCard title="预案" className="panel-card card-3">
|
<CommonCard title="预案" className="panel-card card-3">
|
||||||
<div className="placeholder-content">内容填充区域</div>
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,44 +11,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
.card-1 {
|
.card-1 { flex: 1; }
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.warning-toggles {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.toggle-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-left: 15px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255,0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 10px #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
line-height: 16px;
|
|
||||||
text-align: center;
|
|
||||||
background: #ff4d4f; /* Default red */
|
|
||||||
border-radius: 7px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5px;
|
|
||||||
padding: 0 4px;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-2 { flex: 1; }
|
.card-2 { flex: 1; }
|
||||||
.card-3 { flex: 1; }
|
.card-3 { flex: 1; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,212 +0,0 @@
|
||||||
import React, { useState,useEffect } from 'react';
|
|
||||||
import { Image } from 'antd';
|
|
||||||
import arrowIcon from '@/assets/images/card/arrow.png';
|
|
||||||
import selectedBg from '@/assets/images/modal/selected.png';
|
|
||||||
import resperson from '@/assets/images/card/resperson.png';
|
|
||||||
import smallCard from '@/assets/images/card/smallCard.png';
|
|
||||||
import qys from '@/assets/images/business/qys.png';
|
|
||||||
import PdfView from '@/views/Home/components/UI/PdfView';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import { httpget } from '@/utils/request';
|
|
||||||
import { download } from '@/utils/tools';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const ImplementResponsibility = () => {
|
|
||||||
const types = {
|
|
||||||
1:"行政责任人",
|
|
||||||
2:"主管部门责任人",
|
|
||||||
3:"管理单位责任人",
|
|
||||||
4:"巡查责任人",
|
|
||||||
5:"技术责任人",
|
|
||||||
}
|
|
||||||
const [activeTab, setActiveTab] = useState('dam'); // 'dam' or 'flood'
|
|
||||||
const [pdfInfo, setPdfInfo] = useState({ visible: false, title: '', fileId: '' });
|
|
||||||
const [imagePreview, setImagePreview] = useState({ visible: false, src: '' });
|
|
||||||
const [respData, setRespData] = useState([])
|
|
||||||
const [floodData, setFloodData] = useState([])
|
|
||||||
const [trainData, setTrainData] = useState({})
|
|
||||||
const [trainFileData, setTrainFileData] = useState({})
|
|
||||||
|
|
||||||
// 获取大坝安全责任人
|
|
||||||
const getRespData = async () => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httpget(apiurl.sz.zrz.respPerson)
|
|
||||||
if (code == 200 && data.length > 0) {
|
|
||||||
const list = data.map(item => ({name:item.name,phone:item.contactInfo,role:types[item.type]}))
|
|
||||||
setRespData(list)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取防汛安全责任人
|
|
||||||
const getFloodData = async () => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httpget(apiurl.sz.zrz.floodPerson)
|
|
||||||
if (code == 200 && data.length > 0) {
|
|
||||||
const list = data.map(item => ({name:item.name,phone:item.contactInfo,role:types[item.type]}))
|
|
||||||
setFloodData(list)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取岗位培训
|
|
||||||
const getTrainData = async () => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httpget(apiurl.sz.zrz.train)
|
|
||||||
if (code == 200) {
|
|
||||||
setTrainData(data)
|
|
||||||
setTrainFileData(data?.latestPersonnelPlan?.files[0])
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const {trainingCount,hasTraining,hasNoTraining,totalTraining,latestPersonnelPlan} = trainData||{}
|
|
||||||
const trainingStats = [
|
|
||||||
{ label: '培训计划', value: trainingCount??'-' },
|
|
||||||
{ label: '已开展', value: hasTraining??'-' },
|
|
||||||
{ label: '未开展', value: hasNoTraining??'-' },
|
|
||||||
{ label: '参训总人次', value: totalTraining??'-' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleTrainingClick = () => {
|
|
||||||
if (!trainFileData || !trainFileData.fileName) return;
|
|
||||||
|
|
||||||
const fileName = trainFileData.fileName;
|
|
||||||
const fileId = trainFileData.fileId;
|
|
||||||
const extension = fileName.split('.').pop().toLowerCase();
|
|
||||||
const downloadUrl = `/gunshiApp/ss/personnelPlan/file/download/${fileId}`;
|
|
||||||
|
|
||||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(extension)) {
|
|
||||||
setImagePreview({
|
|
||||||
visible: true,
|
|
||||||
src: downloadUrl
|
|
||||||
});
|
|
||||||
} else if (extension === 'pdf') {
|
|
||||||
setPdfInfo({
|
|
||||||
visible: true,
|
|
||||||
title: fileName,
|
|
||||||
fileId: fileId
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
download(downloadUrl)
|
|
||||||
// window.location.href = downloadUrl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentPersonList = activeTab === 'dam' ? respData : floodData;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeTab === 'dam') {
|
|
||||||
getRespData()
|
|
||||||
} else {
|
|
||||||
getFloodData()
|
|
||||||
}
|
|
||||||
}, [activeTab])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getTrainData()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="implement-responsibility">
|
|
||||||
{/* Top Tabs */}
|
|
||||||
<div className="tabs-container">
|
|
||||||
<div
|
|
||||||
className={`tab-item ${activeTab === 'dam' ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab('dam')}
|
|
||||||
style={activeTab === 'dam' ? { backgroundImage: `url(${selectedBg})` } : {}}
|
|
||||||
>
|
|
||||||
大坝安全责任人
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={`tab-item ${activeTab === 'flood' ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab('flood')}
|
|
||||||
style={activeTab === 'flood' ? { backgroundImage: `url(${selectedBg})` } : {}}
|
|
||||||
>
|
|
||||||
防汛 “三个责任人”
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Person List */}
|
|
||||||
<div className="person-list">
|
|
||||||
{currentPersonList.map((person, index) => (
|
|
||||||
<div key={index} className="person-item">
|
|
||||||
<img src={resperson} alt="avatar" className="avatar" />
|
|
||||||
<span className="name">{person.name}</span>
|
|
||||||
<span className="phone">{person.phone}</span>
|
|
||||||
<div
|
|
||||||
className="role-label"
|
|
||||||
style={{ backgroundImage: `url(${smallCard})` }}
|
|
||||||
title={person.role}
|
|
||||||
>
|
|
||||||
{person.role}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Job Training Section */}
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>岗位培训</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Training Stats */}
|
|
||||||
<div className="training-stats">
|
|
||||||
{trainingStats.map((stat, index) => (
|
|
||||||
<div key={index} className="stat-item">
|
|
||||||
<span className="stat-value">{stat.value}</span>
|
|
||||||
<img src={qys} alt="icon" className="stat-icon" />
|
|
||||||
<span className="stat-label">{stat.label}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Latest Training Content */}
|
|
||||||
<div className="latest-training" style={{ backgroundImage: `url(${smallCard})` }}>
|
|
||||||
<span className="label">最新培训内容:</span>
|
|
||||||
<div
|
|
||||||
className="value-box"
|
|
||||||
onClick={handleTrainingClick}
|
|
||||||
title={trainFileData.fileName?.replace(/\.[^/.]+$/, '')}
|
|
||||||
>
|
|
||||||
{trainFileData.fileName?.replace(/\.[^/.]+$/, '')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PDF Viewer */}
|
|
||||||
{pdfInfo.visible && (
|
|
||||||
<PdfView
|
|
||||||
visible={pdfInfo.visible}
|
|
||||||
onClose={() => setPdfInfo({ ...pdfInfo, visible: false })}
|
|
||||||
title={pdfInfo.title}
|
|
||||||
fileId={pdfInfo.fileId}
|
|
||||||
url="/gunshiApp/ss/personnelPlan/file/download/"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Image Preview */}
|
|
||||||
<div style={{ display: 'none' }}>
|
|
||||||
<Image
|
|
||||||
src={imagePreview.src}
|
|
||||||
preview={{
|
|
||||||
visible: imagePreview.visible,
|
|
||||||
src: imagePreview.src,
|
|
||||||
onVisibleChange: (value) => {
|
|
||||||
setImagePreview({ ...imagePreview, visible: value });
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImplementResponsibility;
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
.implement-responsibility {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.tabs-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.tab-item {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
padding: 3px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
cursor: pointer;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 10px #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.person-list {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.person-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 30%;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 45px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.phone {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.role-label {
|
|
||||||
width: 100%;
|
|
||||||
height: 24px;
|
|
||||||
line-height: 24px;
|
|
||||||
text-align: center;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 8px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.training-stats {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
.stat-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #00eaff;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: -5px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-icon {
|
|
||||||
width: 100px;
|
|
||||||
height: 60px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.latest-training {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
margin-right: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-box {
|
|
||||||
flex: 1;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0 10px;
|
|
||||||
color: #00eaff;
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,362 +0,0 @@
|
||||||
import React, { useState,useEffect } from 'react';
|
|
||||||
import ReactEcharts from 'echarts-for-react';
|
|
||||||
import arrowIcon from '@/assets/images/card/arrow.png';
|
|
||||||
import selectedBg from '@/assets/images/modal/selected.png';
|
|
||||||
import YearSelect from '@/views/Home/components/UI/YearSelect';
|
|
||||||
import PdfView from '@/views/Home/components/UI/PdfView';
|
|
||||||
import { FileTextOutlined } from '@ant-design/icons';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import { httppost, httpget } from '@/utils/request';
|
|
||||||
import { Empty } from 'antd';
|
|
||||||
import './index.less';
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
const StrengthenRuleOfLaw = () => {
|
|
||||||
const params = {
|
|
||||||
pageSo:{ pageSize: 9999, pageNumber: 1 }
|
|
||||||
}
|
|
||||||
const [activeTab, setActiveTab] = useState('laws');
|
|
||||||
const [lawData, setLawData] = useState([]);
|
|
||||||
const [systemData, setSystemData] = useState([]);
|
|
||||||
const [year, setYear] = useState(moment().format('YYYY'));
|
|
||||||
const [pdfInfo, setPdfInfo] = useState({ visible: false, title: '', fileId: '' });
|
|
||||||
|
|
||||||
// 获取法律法规文件
|
|
||||||
const lawsFiles = async () => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httppost(apiurl.sz.qhfz.lawPage, params)
|
|
||||||
if (code == 200) {
|
|
||||||
const list = data?.records.map(item => {
|
|
||||||
if (item?.files?.length === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return { name: item?.files[0]?.fileName?.replace(/\.pdf$/i, ''), fileId: item?.files[0]?.fileId }
|
|
||||||
}).flat()
|
|
||||||
setLawData(list)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取系统文件
|
|
||||||
const systemFiles = async () => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httppost(apiurl.sz.qhfz.systemPage, params)
|
|
||||||
if (code == 200) {
|
|
||||||
const list = data?.records.map(item => {
|
|
||||||
if (item?.files?.length === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return { name: item?.files[0]?.fileName?.replace(/\.pdf$/i, ''), fileId: item?.files[0]?.fileId }
|
|
||||||
}).flat()
|
|
||||||
|
|
||||||
setSystemData(list)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [chartData, setChartData] = useState([]);
|
|
||||||
const [selectedPieItem, setSelectedPieItem] = useState(null);
|
|
||||||
const [startAngle, setStartAngle] = useState(90);
|
|
||||||
|
|
||||||
const typeMapping = {
|
|
||||||
0: { name: '违建', color: '#1890ff' },
|
|
||||||
1: { name: '毁林垦荒', color: '#00eaff' },
|
|
||||||
2: { name: '筑坝拦汊', color: '#3155fb' },
|
|
||||||
3: { name: '填占库容', color: '#bcebf7' },
|
|
||||||
4: { name: '违法取水', color: '#00d085' },
|
|
||||||
5: { name: '其他', color: '#1890ff' },
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取水政执法数据 0:违建,1:毀林垦荒,2:筑坝拦汊,3:填占库容,4:违法取水,5:其他
|
|
||||||
const caseList = async (params) => {
|
|
||||||
try {
|
|
||||||
const { code, data } = await httppost(apiurl.sz.qhfz.caseSta, params)
|
|
||||||
if (code == 200) {
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
setChartData([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newChartData = data.map(item => {
|
|
||||||
const typeInfo = typeMapping[item.type] || { name: '未知', color: '#ccc' };
|
|
||||||
return {
|
|
||||||
name: typeInfo.name,
|
|
||||||
value: item.count,
|
|
||||||
color: typeInfo.color
|
|
||||||
};
|
|
||||||
});
|
|
||||||
setChartData(newChartData);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const total = chartData.reduce((acc, cur) => acc + cur.value, 0);
|
|
||||||
|
|
||||||
const getOption = () => {
|
|
||||||
let centerName = '案件总数';
|
|
||||||
let centerValue = total;
|
|
||||||
let centerPercent = '100%';
|
|
||||||
|
|
||||||
if (selectedPieItem) {
|
|
||||||
centerName = selectedPieItem.name;
|
|
||||||
centerValue = selectedPieItem.value;
|
|
||||||
centerPercent = selectedPieItem.percent;
|
|
||||||
} else if (chartData.length === 1) {
|
|
||||||
centerName = chartData[0].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: '{b}: {c} ({d}%)'
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '水政执法-外圈',
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['48%', '82%'],
|
|
||||||
center: ['34%', '50%'],
|
|
||||||
startAngle: startAngle,
|
|
||||||
avoidLabelOverlap: false,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'inside',
|
|
||||||
formatter: '{b}',
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 10
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
opacity: 0.4
|
|
||||||
},
|
|
||||||
data: chartData.map(item => ({
|
|
||||||
value: item.value,
|
|
||||||
name: item.name,
|
|
||||||
itemStyle: { color: item.color }
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '水政执法-内圈',
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['50%', '56%'],
|
|
||||||
center: ['34%', '50%'],
|
|
||||||
startAngle: startAngle,
|
|
||||||
avoidLabelOverlap: false,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'center',
|
|
||||||
formatter: () => `{name|${centerName}}\n{value|${centerValue}}\n{percent|${centerPercent}}`,
|
|
||||||
rich: {
|
|
||||||
name: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: 'rgba(255,255,255,0.6)',
|
|
||||||
lineHeight: 16
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#fff',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
percent: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#fff',
|
|
||||||
lineHeight: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: '14',
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labelLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
data: chartData.map(item => ({
|
|
||||||
value: item.value,
|
|
||||||
name: item.name,
|
|
||||||
itemStyle: { color: item.color }
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}};
|
|
||||||
|
|
||||||
const onChartClick = (params) => {
|
|
||||||
if (params.componentType !== 'series') return;
|
|
||||||
|
|
||||||
const { name, value, dataIndex } = params;
|
|
||||||
|
|
||||||
// Toggle selection
|
|
||||||
if (selectedPieItem && selectedPieItem.name === name) {
|
|
||||||
setSelectedPieItem(null);
|
|
||||||
setStartAngle(90);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sumBefore = 0;
|
|
||||||
for (let i = 0; i < dataIndex; i++) {
|
|
||||||
sumBefore += chartData[i].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset = (sumBefore + value / 2) / total * 360;
|
|
||||||
const newStartAngle = 90 + offset;
|
|
||||||
|
|
||||||
setStartAngle(newStartAngle);
|
|
||||||
setSelectedPieItem({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
percent: ((value / total) * 100).toFixed(0) + '%'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleItemClick = (item) => {
|
|
||||||
setPdfInfo({
|
|
||||||
visible: true,
|
|
||||||
title: item.name,
|
|
||||||
fileId: item.fileId
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (chartData && chartData.length > 0) {
|
|
||||||
const totalVal = chartData.reduce((acc, cur) => acc + cur.value, 0);
|
|
||||||
const firstItem = chartData[0];
|
|
||||||
const value = firstItem.value;
|
|
||||||
|
|
||||||
const offset = (value / 2) / totalVal * 360;
|
|
||||||
const newStartAngle = 90 + offset;
|
|
||||||
|
|
||||||
setStartAngle(newStartAngle);
|
|
||||||
setSelectedPieItem({
|
|
||||||
name: firstItem.name,
|
|
||||||
value: value,
|
|
||||||
percent: ((value / totalVal) * 100).toFixed(0) + '%'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setSelectedPieItem(null);
|
|
||||||
setStartAngle(90);
|
|
||||||
}
|
|
||||||
}, [chartData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeTab == 'laws') {
|
|
||||||
lawsFiles()
|
|
||||||
} else {
|
|
||||||
systemFiles()
|
|
||||||
}
|
|
||||||
}, [activeTab])
|
|
||||||
useEffect(() => {
|
|
||||||
if (year) {
|
|
||||||
const params = {
|
|
||||||
stm: moment(year).format('YYYY-01-01 00:00:00'),
|
|
||||||
etm: moment(year).format('YYYY-12-31 23:59:59'),
|
|
||||||
}
|
|
||||||
caseList(params)
|
|
||||||
}
|
|
||||||
}, [year])
|
|
||||||
return (
|
|
||||||
<div className="strengthen-rule-of-law">
|
|
||||||
{/* Top Tabs */}
|
|
||||||
<div className="tabs-container">
|
|
||||||
<div
|
|
||||||
className={`tab-item ${activeTab === 'laws' ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab('laws')}
|
|
||||||
style={activeTab === 'laws' ? { backgroundImage: `url(${selectedBg})` } : {}}
|
|
||||||
>
|
|
||||||
法律法规
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={`tab-item ${activeTab === 'system' ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab('system')}
|
|
||||||
style={activeTab === 'system' ? { backgroundImage: `url(${selectedBg})` } : {}}
|
|
||||||
>
|
|
||||||
制度管理
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* List Content */}
|
|
||||||
<div className="list-content">
|
|
||||||
{(activeTab === 'laws' ? lawData : systemData).length > 0 ? (
|
|
||||||
(activeTab === 'laws' ? lawData : systemData).map((item, index) => (
|
|
||||||
<div key={index} className="list-item" onClick={() => handleItemClick(item)}>
|
|
||||||
<div className="icon-wrapper">
|
|
||||||
<FileTextOutlined style={{ color: '#1890ff', fontSize: '16px' }} />
|
|
||||||
</div>
|
|
||||||
<span className="text">{item.name}</span>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div style={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={<span style={{color:'#fff'}}>暂无数据</span>} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Water Administration Law Enforcement */}
|
|
||||||
<div className="enforcement-section">
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>水政执法</span>
|
|
||||||
</div>
|
|
||||||
<YearSelect
|
|
||||||
value={year}
|
|
||||||
onChange={setYear}
|
|
||||||
style={{ width: 100 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="chart-legend-container">
|
|
||||||
{chartData.length > 0 ? (
|
|
||||||
<>
|
|
||||||
<div className="chart-wrapper">
|
|
||||||
<ReactEcharts
|
|
||||||
option={getOption()}
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
notMerge={true}
|
|
||||||
onEvents={{
|
|
||||||
'click': onChartClick
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="legend-wrapper">
|
|
||||||
{chartData.map((item, index) => (
|
|
||||||
<div key={index} className="legend-item">
|
|
||||||
<span className="color-dot" style={{ backgroundColor: item.color }}></span>
|
|
||||||
<span className="name">{item.name}</span>
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={<span style={{color:'#fff'}}>暂无数据</span>} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PDF Viewer */}
|
|
||||||
{pdfInfo.visible && (
|
|
||||||
<PdfView
|
|
||||||
visible={pdfInfo.visible}
|
|
||||||
onClose={() => setPdfInfo({ ...pdfInfo, visible: false })}
|
|
||||||
title={pdfInfo.title}
|
|
||||||
fileId={pdfInfo.fileId}
|
|
||||||
url="/gunshiApp/ss/projectEvents/file/download/"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StrengthenRuleOfLaw;
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
.strengthen-rule-of-law {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: #fff;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.tabs-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
|
|
||||||
.tab-item {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
padding: 3px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
cursor: pointer;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 10px #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-content {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
max-height: 120px; /* Limit height to show scroll if needed, though items are few */
|
|
||||||
|
|
||||||
.list-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.05);
|
|
||||||
.text {
|
|
||||||
color: #00a0e9;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wrapper {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-right: 10px;
|
|
||||||
background: rgba(24, 144, 255, 0.1);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.enforcement-section {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 8px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-legend-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.chart-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 140px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-wrapper {
|
|
||||||
width: 140px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
padding-left: 10px;
|
|
||||||
|
|
||||||
.legend-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
.color-dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
margin-right: 8px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
flex: 1;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,6 @@ import React, { useState, useEffect } from 'react';
|
||||||
import CommonCard from '../../UI/CommonCard';
|
import CommonCard from '../../UI/CommonCard';
|
||||||
import PerfectSystem from './components/PerfectSystem';
|
import PerfectSystem from './components/PerfectSystem';
|
||||||
import SoundMechanism from './components/SoundMechanism';
|
import SoundMechanism from './components/SoundMechanism';
|
||||||
import StrengthenRuleOfLaw from './components/StrengthenRuleOfLaw';
|
|
||||||
import ImplementResponsibility from './components/ImplementResponsibility';
|
|
||||||
import { httppost } from '@/utils/request';
|
import { httppost } from '@/utils/request';
|
||||||
import apiurl from '@/service/apiurl';
|
import apiurl from '@/service/apiurl';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
@ -38,10 +36,10 @@ const SiZhi = () => {
|
||||||
|
|
||||||
<div className="side-panel right">
|
<div className="side-panel right">
|
||||||
<CommonCard title="强化法治" className="panel-card card-1">
|
<CommonCard title="强化法治" className="panel-card card-1">
|
||||||
<StrengthenRuleOfLaw />
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard title="落实责任制" className="panel-card card-2">
|
<CommonCard title="落实责任制" className="panel-card card-2">
|
||||||
<ImplementResponsibility />
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
||||||
return (
|
return (
|
||||||
<CommonModal
|
<CommonModal
|
||||||
visible={visible}
|
visible={visible}
|
||||||
title={title?.replace(/\.pdf$/i, '')}
|
title={title}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
width="60%"
|
width="60%"
|
||||||
bodyStyle={{ padding: 0, overflow: 'hidden' }}
|
bodyStyle={{ padding: 0, overflow: 'hidden' }}
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,14 @@ const YearSelect = ({ value, onChange, className, style, ...props }) => {
|
||||||
onChange(dateString);
|
onChange(dateString);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const disabledDate = (current) => {
|
|
||||||
return current && current > moment().endOf('day');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DatePicker
|
<DatePicker
|
||||||
picker="year"
|
picker="year"
|
||||||
disabledDate={disabledDate}
|
|
||||||
value={value ? moment(value, 'YYYY') : null}
|
value={value ? moment(value, 'YYYY') : null}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={`custom-year-select ${className || ''}`}
|
className={`custom-year-select ${className || ''}`}
|
||||||
// dropdownClassName="custom-year-select-dropdown"
|
dropdownClassName="custom-year-select-dropdown"
|
||||||
style={style}
|
style={style}
|
||||||
allowClear={false}
|
allowClear={false}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,6 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-picker-cell-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
.ant-picker-cell-inner {
|
|
||||||
color: rgba(255, 255, 255, 0.25) !important;
|
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell-selected .ant-picker-cell-inner {
|
.ant-picker-cell-selected .ant-picker-cell-inner {
|
||||||
background-color: #00a0e9 !important;
|
background-color: #00a0e9 !important;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue