tsg-web/src/views/rcgl/byfz/bypc/index.js

568 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { Fragment, useRef, useMemo, useEffect, useState } from 'react';
import BasicCrudModal from '../../../../components/crud/BasicCrudModal';
import { Table, Card, Modal, Form, Input, Button, Row, Col, Timeline, message, Tabs, Image, Tag } from 'antd';
import { FileWordOutlined, FilePdfOutlined, FileZipOutlined, FileExcelOutlined } from '@ant-design/icons';
import { useSelector, useDispatch } from 'react-redux';
import ToolBar from './toolbar';
import ModalForm from './form';
import apiurl from '../../../../service/apiurl';
import usePageTable from '../../../../components/crud/usePageTable2';
import { createCrudService } from '../../../../components/crud/_';
import { httppost2, httpget2 } from '../../../../utils/request';
import { CrudOpRender_text } from '../../../../components/crud/CrudOpRender';
import PointHistory from './pointHistory';
import PrecessForm from './precessForm'
import './index.less';
import moment from 'moment';
// 引入OpenLayers相关依赖
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import * as proj from 'ol/proj';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import { Vector as VectorLayer } from 'ol/layer';
import { Vector as VectorSource } from 'ol/source';
import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style';
import Overlay from 'ol/Overlay';
const url = "http://223.75.53.141:9100/gs-tsg"
const Page = () => {
const role = useSelector(state => state.auth.role);
const editBtn = role?.rule?.find(item => item.menuName == "编辑") || true;
const viewBtn = role?.rule?.find(item => item.menuName == "查看") || true;
const delBtn = role?.rule?.find(item => item.menuName == "删除") || true;
const initData = {
obDate: moment().format('YYYY-MM-DD')
}
// 添加地图相关的状态和引用
const mapContainerRef = useRef(null);
const [map, setMap] = useState(null);
const [vectorLayer, setVectorLayer] = useState(null);
const [selectedFeature, setSelectedFeature] = useState(null);
const [popupOverlay, setPopupOverlay] = useState(null);
const popupRef = useRef(null);
// 在组件顶部的状态声明中添加一个新的状态
const [highlightLayer, setHighlightLayer] = useState(null);
// 点位弹框
const [modalVisible, setModalVisible] = useState(false)
const [detailPoint, setDetailPoint] = useState({})
// 白蚁处理弹框
const [precessVisible, setPrecessVisible] = useState(false)
const [mode, setMode] = useState('save')
const dispatch = useDispatch();
const refModal = useRef();
const [searchVal, setSearchVal] = useState({ ...initData })
const [count, setCount] = useState({})
const [allCdList, setAllCdList] = useState([])
// const columns = [
// { title: '序号', key: 'inx', dataIndex: 'inx', width: 60, align: "center" },
// {title: '填报日期', key: 'reportDate', dataIndex: 'reportDate', width: 140,},
// {
// title: '普查类型', key: 'surveyType', dataIndex: 'surveyType', width: 200,
// render: (value) => <span>{value ? surveyType[value] : ''}</span>
// },
// {
// title: '普查方式', key: 'surveyWay', dataIndex: 'surveyWay', width: 200,
// render: (value) => <span>{value ? surveyWay[value] : ''}</span>
// },
// {
// title: '危害情况', key: 'isHarm', dataIndex: 'isHarm', width: 200,
// render: (value, row) =>(
// <span style={row.harmNum > 0 ? { color: "red" } : {}}>{isHarm[row.harmNum > 0 ? 1 : 0]}</span>)
// },
// {title: '白蚁危害处数', key: 'harmNum', dataIndex: 'harmNum', width: 100},
// {title: '已处置处数', key: 'handleNum', dataIndex: 'handleNum', width: 100},
// {title: '上报人', key: 'reportUserName', dataIndex: 'reportUserName', width: 100},
// {
// title: '操作', key: 'operation', width: 200, fixed: 'right',align: 'center',
// render: (value, row, index) => (
// <CrudOpRender_text
// edit={editBtn ? true : false}
// del={delBtn ? true : false}
// view={viewBtn ? true : false}
// command={(cmd) => () => command(cmd)(row)} />)
// },
// ];
const columns = [
{
title: '监测时间',
dataIndex: 'obDate',
key: 'obDate',
width: 180,
align: 'center'
},
{
title: '测点编号',
dataIndex: 'order',
key: 'order',
width: 120,
align: 'center'
},
{
title: '有无白蚁',
dataIndex: 'status',
align: 'center',
key: 'status',
width: 100,
render: (text, record) => {
// 如果 isHarm 为 null 或 undefined显示无
if (text == null) {
return <Tag color="#04d919" style={{ borderRadius: '50%', padding: '4px 8px' }}></Tag>;
}
// 如果 isHandle 为 null 或 undefined当作 false 处理
const isHandle = record.isHandle ?? false;
return (
<Tag
color={!text ? '#04d919' : '#d9001b'}
style={{ borderRadius: '50%', padding: '4px 8px' }}
>
{!text ? '无' : '有'}
</Tag>
);
}
},
{
title: '操作',
dataIndex: 'opreate',
key: 'opreate',
width: 120,
align: 'center',
render: (v, r) => {
let renderTag;
if (!r.isProcess && r.status == 1) {
renderTag = <Button style={{ marginRight: 4 }} type="link" size="small" onClick={()=> handlerPrecess(r,'save')} title="处理">处理</Button>;
}
if (r.hasInspectTask && r.jcskByRProcessVo) {
renderTag = <Button style={{ marginRight: 4 }} type="link" size="small" onClick={()=> handlerPrecess(r,'view')} title="工单详情">工单详情</Button>;
}
return (<>
{renderTag}
<Button style={{ marginRight: 4 }} type="link" size="small" title="查看" onClick={()=> viewDetail(r)}>查看</Button>
</>)
}
},
];
const width = useMemo(() => columns.reduce((total, cur) => total + (cur.width), 0), [columns]);
const command = (type) => (params) => {
if (type === 'save') {
refModal.current.showSave();
} else if (type === 'edit') {
refModal.current.showEdit({ ...params });
} else if (type === 'view') {
refModal.current.showView(params);
} else if (type === 'del') {
refModal.current.onDeleteGet(apiurl.rcgl.byfz.bypc.delete + `/${params.id}`);
}
}
const { tableProps, search, refresh } = usePageTable(createCrudService(apiurl.rcgl.byfz.bypc.page).find_noCode);
/**
* @description 处理成功的回调
*/
const successCallback = () => {
refresh()
}
// 表格操作栏 查看按钮回调
const viewDetail = (r) => {
setDetailPoint(r);
setModalVisible(true)
}
// 表格操作栏 处理按钮回调
const handlerPrecess = (r,type) => {
setDetailPoint(r);
setPrecessVisible(true)
setMode(type)
}
// 获取白蚁统计数量
const getCount = async () => {
const params = {
pageSo: {
pageNumber: 1,
pageSize: 99999,
},
...searchVal
}
try {
const res = await httppost2(apiurl.rcgl.byfz.bypc.count, params);
setCount(res.data);
} catch (error) {
console.log(error);
}
}
// 获取白蚁所有测点
const getCdList = async (params) => {
try {
const res = await httppost2(apiurl.rcgl.byfz.bypc.allCd,params);
if (res.code == 200) {
setAllCdList(res.data);
// 添加标记点到地图
if (map && res.data && res.data.length > 0) {
addMarkersToMap(res.data);
}
}
} catch (error) {
console.log(error);
}
}
// 添加标记点到地图
const addMarkersToMap = (points) => {
if (!map) return;
// 如果已存在矢量图层,先移除
if (vectorLayer) {
map.removeLayer(vectorLayer);
}
// 创建矢量数据源
const vectorSource = new VectorSource();
// 遍历所有点位数据
points.forEach(point => {
// 确保点位有经纬度
if (point.lgtd && point.lttd) {
// 创建要素
const feature = new Feature({
geometry: new Point(proj.fromLonLat([point.lgtd, point.lttd])),
properties: point // 保存点位的所有属性
});
// 根据status设置样式
const color = point.status === 1 ? '#d9001b' :
point.status === 0 ? '#04d919':'#8c8c8c'
; // 1为红色0为绿色 null 为灰色
feature.setStyle(new Style({
image: new CircleStyle({
radius: 4,
fill: new Fill({ color: color }),
stroke: new Stroke({ color: '#ffffff', width: 1 })
})
}));
// 添加要素到数据源
vectorSource.addFeature(feature);
}
});
// 创建矢量图层
const newVectorLayer = new VectorLayer({
source: vectorSource,
zIndex: 10 // 确保点位在地图上层显示
});
// 添加图层到地图
map.addLayer(newVectorLayer);
// 保存矢量图层引用
setVectorLayer(newVectorLayer);
// 添加点击事件处理
// map.on('click', (event) => {
// const feature = map.forEachFeatureAtPixel(event.pixel, function(feature) {
// return feature;
// });
// if (feature) {
// const properties = feature.get('properties');
// if (properties) {
// // 可以在这里处理点击事件,比如显示详情等
// console.log('点击了标记点:', properties);
// // 可以添加弹窗或其他交互
// }
// }
// });
};
// 修改高亮显示选中的测点函数
const highlightFeature = (feature) => {
if (!feature || !highlightLayer) return;
// 清除之前的高亮
highlightLayer.getSource().clear();
// 获取要素属性
const properties = feature.get('properties');
const color = properties.status === 1 ? '#d9001b' : '#04d919';
// 创建一个新的要素用于高亮显示
const highlightFeature = new Feature({
geometry: feature.getGeometry().clone(),
properties: properties
});
// 设置高亮样式
highlightFeature.setStyle(new Style({
image: new CircleStyle({
radius: 10, // 放大图标
fill: new Fill({ color: color }),
stroke: new Stroke({ color: '#ffffff', width: 3 })
}),
text: new Text({
text: properties.order || '未命名测点',
offsetY: -20,
font: 'bold 14px Arial',
fill: new Fill({ color: '#333' }),
stroke: new Stroke({ color: '#fff', width: 3 })
})
}));
// 添加到高亮图层
highlightLayer.getSource().addFeature(highlightFeature);
// 更新选中的要素引用
setSelectedFeature(feature);
// 平移地图到选中的要素位置
const geometry = feature.getGeometry();
const coordinate = geometry.getCoordinates();
map.getView().animate({
center: coordinate,
duration: 500,
zoom: map.getView().getZoom() < 18 ? 18 : map.getView().getZoom()
});
};
// 根据测点ID查找对应的要素
const findFeatureByDeviceId = (deviceId) => {
if (!vectorLayer) return null;
let targetFeature = null;
vectorLayer.getSource().forEachFeature((feature) => {
const properties = feature.get('properties');
if (properties && properties.order === deviceId) {
targetFeature = feature;
}
});
return targetFeature;
};
// 处理表格行点击事件
const handleRowClick = (record,event) => {
// 如果点击的是操作列中的元素,不触发地图交互
if (event && (event.target.closest('button') || event.target.closest('.ant-btn'))) {
return;
}
const feature = findFeatureByDeviceId(record.order);
if (feature) {
highlightFeature(feature);
}
};
useEffect(() => {
const params = {
search: {
...searchVal,
}
};
search(params)
getCount();
}, [searchVal])
// 初始化地图
useEffect(() => {
if (!mapContainerRef.current) return;
// 创建地图实例
const mapInstance = new Map({
target: mapContainerRef.current,
controls: [],
view: new View({
center: proj.fromLonLat([114.764317000, 31.496800000]), // 设置地图中心点坐标
zoom: 18.4, // 设置初始缩放级别
minZoom: 5.5,
maxZoom: 30,
}),
});
// 添加天地图卫星影像图层
const satelliteLayer = new TileLayer({
source: new XYZSource({
url: "https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=efc861f25f96dc6e5f884f0403ebfefd",
}),
maxZoom: 30,
});
// 设置图层名称
satelliteLayer.set('name', 'SatelliteImage');
// 添加图层到地图
mapInstance.addLayer(satelliteLayer);
// 创建高亮图层
const highlightVectorLayer = new VectorLayer({
source: new VectorSource(),
zIndex: 999, // 确保高亮图层在最上面
style: null
});
// 添加高亮图层到地图
mapInstance.addLayer(highlightVectorLayer);
setHighlightLayer(highlightVectorLayer);
// 创建弹出层
const overlay = new Overlay({
element: popupRef.current,
positioning: 'bottom-center',
stopEvent: false,
offset: [0, -10]
});
mapInstance.addOverlay(overlay);
setPopupOverlay(overlay);
// 添加地图点击事件
mapInstance.on('click', (event) => {
const feature = mapInstance.forEachFeatureAtPixel(event.pixel, function (feature) {
return feature;
});
if (feature) {
setModalVisible(true)
setDetailPoint(feature?.values_?.properties)
highlightFeature(feature);
}
});
// 保存地图实例
setMap(mapInstance);
// 组件卸载时清理地图
return () => {
if (mapInstance) {
mapInstance.setTarget(null);
}
};
}, []);
// 当地图实例创建完成后获取测点数据
useEffect(() => {
if (map && searchVal) {
getCdList(searchVal);
}
}, [map,searchVal]);
return (
<>
<div className='content-root clearFloat xybm' style={{ paddingRight: "0", paddingBottom: "0" }}>
<div className='lf CrudAdcdTreeTableBox' style={{ width: "100%", display: 'flex' }}>
<div className='by-left' style={{ width: "60%", height: "calc(100vh - 180px)" }} ref={mapContainerRef}></div>
<div className='by-right' style={{ width: "40%", marginLeft: 10 }}>
<Row gutter={16} className='statsRow'>
<Col span={6}>
<div className='statItem'>
<div className='valueWrapper'>
<span className='number' style={{ color: '#722ed1' }}>{count.totalPoint}</span>
<span className="unit"></span>
</div>
<span className='label'>总监测点数</span>
</div>
</Col>
<Col span={6}>
<div className='statItem'>
<div className='valueWrapper'>
<span className='number' style={{ color: '#f5222d' }}>{count.hasAnt}</span>
<span className="unit"></span>
</div>
<span className='label'>有白蚁</span>
</div>
</Col>
<Col span={6}>
<div className='statItem'>
<div className='valueWrapper'>
<span className='number' style={{ color: '#52c41a' }}>{count.notAnt}</span>
<span className="unit"></span>
</div>
<span className='label'>无白蚁</span>
</div>
</Col>
<Col span={6}>
<div className='statItem'>
<div className='valueWrapper'>
<span className='number' style={{ color: '#8c8c8c' }}>{count.noData}</span>
<span className="unit"></span>
</div>
<span className='label'>离线</span>
</div>
</Col>
</Row>
<Card className='nonebox'>
<ToolBar
setSearchVal={setSearchVal}
initData={initData}
// onSave={command('save')}
// role={role}
/>
</Card>
<div className="ant-card-body" style={{ padding: "20px 0 0 0", marginRight: 20 }}>
<Table
columns={columns}
rowKey="inx"
{...tableProps}
scroll={{ x: width, y: "calc( 100vh - 400px )" }}
onRow={(record) => ({
onClick: (event) => handleRowClick(record,event), // 添加行点击事件
style: { cursor: 'pointer' } // 添加鼠标指针样式
})}
/>
</div>
</div>
</div>
{/* 添加测点详情Modal */}
<Modal
title={detailPoint?.order}
open={modalVisible}
onCancel={() => setModalVisible(false)}
width={1000}
footer={null}
destroyOnClose
>
<PointHistory data={detailPoint} />
</Modal>
{/*处理弹框 */}
<Modal
title={mode == 'save' ? "处理" : '工单详情'}
open={precessVisible}
onCancel={() => setPrecessVisible(false)}
width={1000}
footer={null}
destroyOnClose
>
<PrecessForm record={detailPoint} mode={mode} refresh={refresh} setPrecessVisible={setPrecessVisible} />
</Modal>
<BasicCrudModal
width={1000}
ref={refModal}
title=""
component={ModalForm}
onCrudSuccess={successCallback}
// onCrudSuccess={()=>{refresh({addvcd:localStorage.getItem('ADCD6')})}}
/>
</div>
</>
);
}
export default Page;