Browse Source

관제과 운황과 페이지 구분

master
sanguu516 2 months ago
parent
commit
ac7e014fb5
  1. 2
      src/components/account/login/AccountLogin.js
  2. 2
      src/components/flight/ControlApprovalsTable.js
  3. 1149
      src/components/flight/OperationApprovalsTable.js
  4. 154
      src/containers/controlRightMenuContainer.js
  5. 655
      src/containers/flight/ControlApprovalsContainer.js
  6. 655
      src/containers/flight/OperationApprovalsContainer.js
  7. 4
      src/containers/rightMenuContainer.js
  8. 8
      src/router/hoc/authenticationCheck.tsx
  9. 2
      src/router/index.tsx
  10. 13
      src/router/routes/RouteFlight.js
  11. 21
      src/router/routes/index.js
  12. 13
      src/views/controlMenuView.js
  13. 1
      src/views/flight/FlightView.js
  14. 6
      src/views/flight/controlApprovalsView.js
  15. 15
      src/views/flight/operationApprovalsView.js

2
src/components/account/login/AccountLogin.js

@ -69,7 +69,7 @@ export const AccountLogin = ({ history }) => {
if (meta.requestStatus === 'fulfilled') { if (meta.requestStatus === 'fulfilled') {
if (loginForm?.userId === 'GMPATC') { if (loginForm?.userId === 'GMPATC') {
history.push('/flight/Approvals/new'); history.push('/operation/approvals');
} else { } else {
history.push('/control'); history.push('/control');
} }

2
src/components/flight/NewFlightApprovalsTable.js → src/components/flight/ControlApprovalsTable.js

@ -13,7 +13,7 @@ import {
} from '@src/redux/features/laanc/laancThunk'; } from '@src/redux/features/laanc/laancThunk';
import { ERROR_MESSAGE, ERROR_TITLE } from '@src/configs/msgConst'; import { ERROR_MESSAGE, ERROR_TITLE } from '@src/configs/msgConst';
export default function NewFlightApprovalsTable(props) { export default function ControlApprovalsTable(props) {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 비행승인 목록 // 비행승인 목록

1149
src/components/flight/OperationApprovalsTable.js

File diff suppressed because it is too large Load Diff

154
src/containers/controlRightMenuContainer.js

@ -0,0 +1,154 @@
import { useEffect, useState } from 'react';
import dayjs from 'dayjs';
import { useDispatch } from '@src/redux/store';
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk';
import ControlApprovalsTable from '@src/components/flight/ControlApprovalsTable';
import NewFlightApprovalsReport from '@src/components/flight/NewFlightApprovalsReport';
function ControlRightMenuContainer() {
const [filter, setFilter] = useState('');
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD'));
const [endDate, setEndDate] = useState();
const [selected, setSelected] = useState(null);
// 식별번호
const [filterId, setFilterId] = useState('');
// 지역
const [filterArea, setFilterArea] = useState('');
const dispatch = useDispatch();
useEffect(() => {
handlerOpnerPostMessage('initalState', null);
window.addEventListener('message', opnerMessage);
return () => {
window.removeEventListener('message', opnerMessage);
};
}, []);
const opnerMessage = e => {
const { type } = e.data;
const { payload } = e.data;
switch (type) {
case 'initalState':
setFilter(payload.filter);
setSelected(payload.selected);
setStartDate(payload.startDate);
setEndDate(payload.endDate);
return;
// case 'handlerSearchRs':
// console.log(payload.filter);
// setFilter(payload.filter);
// return;
default:
break;
}
};
const handlerOpnerPostMessage = (type, payload) => {
switch (type) {
case 'initalState':
window.opener.postMessage({ type, payload });
return;
case 'search':
window.opener.postMessage({ type, payload });
return;
case 'detail':
window.opener.postMessage({ type, payload });
return;
case 'closedSync':
window.opener.postMessage({ type, payload });
default:
break;
}
};
const handlerSearch = (search, searchDate, filterArea) => {
setStartDate(searchDate.startDate);
setEndDate(searchDate.endDate);
setFilter(search);
if (
search != '' &&
(search === '승인' || search === '미승인' || search === '비대상')
) {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
approvalCd: search === '승인' ? 'S' : search === '미승인' ? 'F' : 'U'
})
);
} else if (search != '') {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
applyNo: search
})
);
} else {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea
})
);
}
localStorage.setItem(
'popupState',
JSON.stringify({
filter: search,
selected: filterArea,
startDate: searchDate.startDate,
endDate: searchDate.endDate
})
);
handlerOpnerPostMessage('search', { search, searchDate, filterArea });
};
const handlerDetail = area => {
setSelected(area.planAreaSno);
handlerOpnerPostMessage('detail', { area });
};
const handleBeforeUnload = () => {
handlerOpnerPostMessage('closedSync', '');
};
return (
<div className='right-menu active'>
<div
className='right-layer active flight-approval-layer'
style={{ width: '100vw' }}
>
<div className='layer-content'>
<NewFlightApprovalsReport
handlerSearch={handlerSearch}
filterId={filterId}
setFilterId={setFilterId}
filterArea={filterArea}
setFilterArea={setFilterArea}
/>
<ControlApprovalsTable
filter={filter}
filterArea={filterArea}
filterId={filterId}
startDate={startDate}
endDate={endDate}
selected={selected}
handlerDetail={handlerDetail}
/>
</div>
</div>
</div>
);
}
export default ControlRightMenuContainer;

655
src/containers/flight/ControlApprovalsContainer.js

@ -0,0 +1,655 @@
import { useEffect, useRef, useState, lazy, Suspense } from 'react';
import { useDispatch, useSelector } from '@src/redux/store';
import NewFlightApprovalsReport from '../../components/flight/NewFlightApprovalsReport';
import {
InitFeature,
handlerFitBounds,
handlerGetCircleCoord,
flightlayerWayPoint,
flightlayerPolyline,
flightlayerPolygon,
flightlayerBuffer
} from '../../utility/MapUtils';
import { useHistory } from 'react-router-dom';
import useMapType from '@hooks/useMapType';
import { clientSaveAreaCoordinateList } from '@src/redux/features/laanc/laancSlice';
import { MapControl } from '../../components/map/MapControl';
import { clientSetIsMapLoading } from '@src/redux/features/laanc/laancSlice';
import { clientMapInit } from '@src/redux/features/control/map/mapSlice';
import ControlApprovalsTable from '@src/components/flight/ControlApprovalsTable';
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk';
import dayjs from 'dayjs';
import { setLogout } from '@src/redux/features/account/auth/authThunk';
import logo from '../../assets/images/logo/kac_logo_ icon.svg';
import { AiOutlinePoweroff } from 'react-icons/ai';
import WebsocketClient from '../../components/websocket/WebsocketClient';
import {
Card,
ButtonGroup,
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Table
} from '@component/ui';
import { FiUsers, FiFileText } from 'react-icons/fi';
export default function ControlApprovalsContainer({ mode }) {
const dispatch = useDispatch();
const history = useHistory();
const [selected, setSelected] = useState(null);
const [isMapLoading, setIsMapLoading] = useState(false);
// 비행구역 그리기
const [filter, setFilter] = useState('');
// 지도
const [mapObject, setMapObject] = useState();
const [mapType, setMapType] = useMapType();
const { areaCoordList, isOpenModal } = useSelector(state => state.laancState);
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD'));
const [endDate, setEndDate] = useState(dayjs().format('YYYY-MM-DD'));
// 식별번호
const [filterId, setFilterId] = useState('');
// 지역
const [filterArea, setFilterArea] = useState('전체');
// 시군구
const [district, setDistrict] = useState('전체');
// 미니맵 레이어
const [previewLayer, setPreviewLayer] = useState();
const map = useSelector(state => state.mapState.map);
// popup
const [isPopup, setIsPopup] = useState(false);
const [popup, setPopup] = useState(null);
const popupRef = useRef(null);
const rightMenuRef = useRef(null);
const savedRightMenuRef = useRef(null);
const [clientX, setClientX] = useState(0);
const previewGeo2 = {
type: 'FeatureCollection',
features: []
};
useEffect(() => {
const searchStDt = dayjs().format('YYYY-MM-DD');
const searchEndDt = dayjs().format('YYYY-MM-DD');
dispatch(
getLaancAprvList({
searchStDt,
searchEndDt
})
);
}, []);
useEffect(() => {
if (areaCoordList.length !== 0) {
handlerPreviewDraw();
}
}, [areaCoordList]);
useEffect(() => {
if (map) {
window._mapbox = map;
let mapInstance = mode === 'container' ? map : window.opener._mapbox;
setMapObject(mapInstance);
}
}, [map]);
useEffect(() => {
const childMessage = e => {
if (e.data.type) {
const { type } = e.data;
const { payload } = e.data;
console.log(payload);
switch (type) {
case 'initalState':
popupRef.current.postMessage({
type: 'initalState',
payload: {
filter,
selected,
startDate,
endDate
}
});
return;
case 'search':
const { search, searchDate, filterArea } = payload;
handlerSearch(search, searchDate, filterArea);
return;
case 'detail':
const { area } = payload;
handlerDetail(area);
return;
case 'closedSync':
popupRef.current.close();
// localStorage.removeItem('popupState');
return;
default:
break;
}
}
};
let timer;
if (rightMenuRef.current) {
savedRightMenuRef.current = rightMenuRef.current.getBoundingClientRect();
}
if (popup) {
timer = setInterval(() => {
if (popup.closed) {
setIsPopup(false);
clearInterval(timer);
}
if (savedRightMenuRef.current) {
const popupX = popup.screenX;
const parentX = window.screenX + savedRightMenuRef.current.left - 70;
const parentWidth = savedRightMenuRef.current.width;
if (popupX >= parentX && popupX <= parentX + parentWidth) {
popup.close();
setIsPopup(false);
clearInterval(timer);
}
}
}, 500); // 1초마다 체크
}
window.addEventListener('message', childMessage);
return () => {
clearInterval(timer);
window.removeEventListener('message', childMessage);
};
}, [popup]);
useEffect(() => {
const handleBeforeUnload = e => {
localStorage.removeItem('persist:root');
if (popupRef.current) {
popupRef.current.close();
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, []);
const handleDragEnd = e => {
setIsPopup(true);
const el = document.querySelector('.flight-approval-layer');
const popupWidth = el.offsetWidth; // 팝업의 너비
const popupHeight = el.offsetHeight; // 팝업의 너비
// const popupX = window.screenX + window.outerWidth - e.clientX; // 오른쪽 끝에 띄우기
// const popupY = Math.round(
// window.screenY + window.outerHeight / 2 - popupHeight / 2
// );
// setClientX(window.screenX + window.outerWidth - e.clientX);
const popupX =
window.screenX +
(window.outerWidth - popupWidth) / 2 +
e.clientX -
window.innerWidth / 2; // 드래그 끝나는 지점
const popupY = Math.round(
window.screenY + window.outerHeight / 2 - popupHeight / 2
);
const option = `width=${popupWidth},height=${popupHeight},left=${popupX},top=${popupY}`;
popupRef.current = window.open('/rightMenu', 'NewWindow', option);
// setPopupOption(option);
setPopup(popupRef.current);
};
const handlerSearch = (search, searchDate, filterArea) => {
setStartDate(searchDate.startDate);
setEndDate(searchDate.endDate);
if (
search != '' &&
(search === '승인' ||
search === '미승인' ||
search === '비대상' ||
search === '조건부승인')
) {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
approvalCd:
search === '승인'
? 'S'
: search === '미승인'
? 'F'
: search === '조건부승인'
? 'C'
: 'U'
})
);
} else if (search != '') {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
applyNo: search
})
);
} else {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea
})
);
}
// );
setFilter(search);
if (popup) {
popupRef.current.postMessage({
type: 'handlerSearchRs',
payload: { search }
});
}
};
const handlerDetail = area => {
setSelected(area.planAreaSno);
dispatch(clientSaveAreaCoordinateList([area]));
handlerMapInit();
};
const handlerMapInit = () => {
let mapInstance = mode === 'container' ? map : window.opener._mapbox;
if (mapInstance.getSource('preview')) {
} else {
mapInstance.addSource('preview', {
type: 'geojson',
data: previewGeo2
});
mapInstance.addLayer(flightlayerWayPoint('preview'));
mapInstance.addLayer(flightlayerBuffer('preview'));
mapInstance.addLayer(flightlayerPolygon('preview'));
mapInstance.addLayer(flightlayerPolyline('preview'));
}
dispatch(clientSetIsMapLoading(true));
const preview = mapInstance.getSource('preview');
if (preview) setPreviewLayer(preview);
setIsMapLoading(true);
setMapObject(mapInstance);
dispatch(clientMapInit(mapInstance));
};
const handlerPreviewDraw = () => {
if (areaCoordList.length > 0) {
const areas = areaCoordList[0];
previewGeo2.features = [];
let fitZoomPaths = [];
const radius = areas.bufferZone;
const circleCoords = handlerGetCircleCoord(
[areas.lon, areas.lat],
radius
);
const circle = InitFeature('Polygon', 'CIRCLE');
circle.properties.center = [areas.lon, areas.lat];
circle.geometry.coordinates = circleCoords;
previewGeo2.features.push(circle);
mapObject?.setCenter(circle.properties.center);
fitZoomPaths = circleCoords[0];
handlerFitBounds(mapObject, fitZoomPaths, 400, 'CIRCLE', 'flight');
// mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
mapObject?.getSource('preview').setData(previewGeo2);
}
};
const handlerLogout = async () => {
const { payload } = await dispatch(setLogout());
if (payload === 'SUCCESS') {
history.replace('/account/login');
}
};
const [testModal, setTestModal] = useState(false);
return (
<>
<div className='map' style={{ width: '100%' }}>
<div className='test_modal'>
<Button
color='primary'
size='lg'
onClick={() => setTestModal(!testModal)}
>
test
</Button>
</div>
<div>
<Modal
isOpen={testModal}
toggle={() => setTestModal(!testModal)}
className='modal-dialog-centered modal-lg'
>
<ModalHeader toggle={() => setTestModal(!testModal)}>
유효성 검사 상세보기
</ModalHeader>
<ModalBody className='onestop-validation'>
<div className='pal-table-warp'>
<h5 className='table-ti'>
<FiFileText />
기체 정보
</h5>
<div className='pal-table'>
<Table responsive>
<thead>
<tr>
<th width='80'>No</th>
<th width='120'>유효성 검사</th>
<th>제작 번호</th>
<th>기체 용도</th>
<th>기체 중량</th>
<th>보험 가입</th>
</tr>
</thead>
<tbody>
<tr>
<td width='80'>1</td>
<td width='120' className='success'>
성공
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='success'>가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
</tbody>
</Table>
</div>
</div>
<div className='pal-table-warp'>
<h5 className='table-ti'>
<FiUsers />
조종자 정보
</h5>
<div className='pal-table'>
<Table>
<thead>
<tr>
<th width='80'>No</th>
<th width='120'>유효성 검사</th>
<th>이름</th>
<th>생년 월일</th>
<th>자격 번호</th>
<th>조종 자격</th>
</tr>
</thead>
<tbody>
<tr>
<td width='80'>1</td>
<td width='120' className='success'>
성공
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='success'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
</tbody>
</Table>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color='primary' onClick={() => setTestModal(!testModal)}>
확인
</Button>{' '}
</ModalFooter>
</Modal>
</div>
<div className='main-data'>
<div className='data-box-btn'>
<Card>
<div className='data-box-btn-list'>
<h4>지도유형</h4>
<div className='map-btn'>
<ButtonGroup>
<Button
color={mapType === 'TERRAIN' ? 'primary' : ''}
onClick={e => setMapType('TERRAIN')}
>
지형지도
</Button>
<Button
color={mapType === 'SATELLITE' ? 'primary' : ''}
onClick={e => setMapType('SATELLITE')}
>
위성지도
</Button>
<Button
color={mapType === '2D' ? 'primary' : ''}
onClick={e => setMapType('2D')}
>
2D
</Button>
</ButtonGroup>
</div>
</div>
<div className='data-box-btn-list'>
<h4>거리측정</h4>
<div className='map-btn'>
<ButtonGroup>
<Button primary>직선</Button>
<Button primary>다각형</Button>
</ButtonGroup>
</div>
</div>
</Card>
</div>
</div>
<MapControl />
</div>
<div className='left-menu'>
<h1 className='logo'>
<img src={logo} width='80' />
<span>UTM</span>
</h1>
<ul className='left-menu-footer'>
<li>
<AiOutlinePoweroff
size={25}
className='logout-btn'
onClick={handlerLogout}
/>
</li>
<li>
<WebsocketClient />
</li>
</ul>
</div>
{!isPopup && (
<div className='right-menu active flight-approval-layer-wrap'>
<div
className='right-layer active flight-approval-layer'
ref={rightMenuRef}
>
<div className='layer-content'>
<NewFlightApprovalsReport
handlerSearch={handlerSearch}
handleDragEnd={handleDragEnd}
filterId={filterId}
setFilterId={setFilterId}
district={district}
setDistrict={setDistrict}
filterArea={filterArea}
setFilterArea={setFilterArea}
/>
<ControlApprovalsTable
filter={filter}
filterArea={filterArea}
filterId={filterId}
startDate={startDate}
endDate={endDate}
selected={selected}
handlerDetail={handlerDetail}
/>
</div>
</div>
</div>
)}
</>
);
}

655
src/containers/flight/OperationApprovalsContainer.js

@ -0,0 +1,655 @@
import { useEffect, useRef, useState, lazy, Suspense } from 'react';
import { useDispatch, useSelector } from '@src/redux/store';
import NewFlightApprovalsReport from '../../components/flight/NewFlightApprovalsReport';
import {
InitFeature,
handlerFitBounds,
handlerGetCircleCoord,
flightlayerWayPoint,
flightlayerPolyline,
flightlayerPolygon,
flightlayerBuffer
} from '../../utility/MapUtils';
import { useHistory } from 'react-router-dom';
import useMapType from '@hooks/useMapType';
import { clientSaveAreaCoordinateList } from '@src/redux/features/laanc/laancSlice';
import { MapControl } from '../../components/map/MapControl';
import { clientSetIsMapLoading } from '@src/redux/features/laanc/laancSlice';
import { clientMapInit } from '@src/redux/features/control/map/mapSlice';
import OperationApprovalsTable from '@src/components/flight/OperationApprovalsTable';
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk';
import dayjs from 'dayjs';
import { setLogout } from '@src/redux/features/account/auth/authThunk';
import logo from '../../assets/images/logo/kac_logo_ icon.svg';
import { AiOutlinePoweroff } from 'react-icons/ai';
import WebsocketClient from '../../components/websocket/WebsocketClient';
import {
Card,
ButtonGroup,
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Table
} from '@component/ui';
import { FiUsers, FiFileText } from 'react-icons/fi';
export default function OperationApprovalsContainer({ mode }) {
const dispatch = useDispatch();
const history = useHistory();
const [selected, setSelected] = useState(null);
const [isMapLoading, setIsMapLoading] = useState(false);
// 비행구역 그리기
const [filter, setFilter] = useState('');
// 지도
const [mapObject, setMapObject] = useState();
const [mapType, setMapType] = useMapType();
const { areaCoordList, isOpenModal } = useSelector(state => state.laancState);
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD'));
const [endDate, setEndDate] = useState(dayjs().format('YYYY-MM-DD'));
// 식별번호
const [filterId, setFilterId] = useState('');
// 지역
const [filterArea, setFilterArea] = useState('전체');
// 시군구
const [district, setDistrict] = useState('전체');
// 미니맵 레이어
const [previewLayer, setPreviewLayer] = useState();
const map = useSelector(state => state.mapState.map);
// popup
const [isPopup, setIsPopup] = useState(false);
const [popup, setPopup] = useState(null);
const popupRef = useRef(null);
const rightMenuRef = useRef(null);
const savedRightMenuRef = useRef(null);
const [clientX, setClientX] = useState(0);
const previewGeo2 = {
type: 'FeatureCollection',
features: []
};
useEffect(() => {
const searchStDt = dayjs().format('YYYY-MM-DD');
const searchEndDt = dayjs().format('YYYY-MM-DD');
dispatch(
getLaancAprvList({
searchStDt,
searchEndDt
})
);
}, []);
useEffect(() => {
if (areaCoordList.length !== 0) {
handlerPreviewDraw();
}
}, [areaCoordList]);
useEffect(() => {
if (map) {
window._mapbox = map;
let mapInstance = mode === 'container' ? map : window.opener._mapbox;
setMapObject(mapInstance);
}
}, [map]);
useEffect(() => {
const childMessage = e => {
if (e.data.type) {
const { type } = e.data;
const { payload } = e.data;
console.log(payload);
switch (type) {
case 'initalState':
popupRef.current.postMessage({
type: 'initalState',
payload: {
filter,
selected,
startDate,
endDate
}
});
return;
case 'search':
const { search, searchDate, filterArea } = payload;
handlerSearch(search, searchDate, filterArea);
return;
case 'detail':
const { area } = payload;
handlerDetail(area);
return;
case 'closedSync':
popupRef.current.close();
// localStorage.removeItem('popupState');
return;
default:
break;
}
}
};
let timer;
if (rightMenuRef.current) {
savedRightMenuRef.current = rightMenuRef.current.getBoundingClientRect();
}
if (popup) {
timer = setInterval(() => {
if (popup.closed) {
setIsPopup(false);
clearInterval(timer);
}
if (savedRightMenuRef.current) {
const popupX = popup.screenX;
const parentX = window.screenX + savedRightMenuRef.current.left - 70;
const parentWidth = savedRightMenuRef.current.width;
if (popupX >= parentX && popupX <= parentX + parentWidth) {
popup.close();
setIsPopup(false);
clearInterval(timer);
}
}
}, 500); // 1초마다 체크
}
window.addEventListener('message', childMessage);
return () => {
clearInterval(timer);
window.removeEventListener('message', childMessage);
};
}, [popup]);
useEffect(() => {
const handleBeforeUnload = e => {
localStorage.removeItem('persist:root');
if (popupRef.current) {
popupRef.current.close();
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, []);
const handleDragEnd = e => {
setIsPopup(true);
const el = document.querySelector('.flight-approval-layer');
const popupWidth = el.offsetWidth; // 팝업의 너비
const popupHeight = el.offsetHeight; // 팝업의 너비
// const popupX = window.screenX + window.outerWidth - e.clientX; // 오른쪽 끝에 띄우기
// const popupY = Math.round(
// window.screenY + window.outerHeight / 2 - popupHeight / 2
// );
// setClientX(window.screenX + window.outerWidth - e.clientX);
const popupX =
window.screenX +
(window.outerWidth - popupWidth) / 2 +
e.clientX -
window.innerWidth / 2; // 드래그 끝나는 지점
const popupY = Math.round(
window.screenY + window.outerHeight / 2 - popupHeight / 2
);
const option = `width=${popupWidth},height=${popupHeight},left=${popupX},top=${popupY}`;
popupRef.current = window.open('/rightMenu', 'NewWindow', option);
// setPopupOption(option);
setPopup(popupRef.current);
};
const handlerSearch = (search, searchDate, filterArea) => {
setStartDate(searchDate.startDate);
setEndDate(searchDate.endDate);
if (
search != '' &&
(search === '승인' ||
search === '미승인' ||
search === '비대상' ||
search === '조건부승인')
) {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
approvalCd:
search === '승인'
? 'S'
: search === '미승인'
? 'F'
: search === '조건부승인'
? 'C'
: 'U'
})
);
} else if (search != '') {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea,
applyNo: search
})
);
} else {
dispatch(
getLaancAprvList({
searchStDt: searchDate.startDate,
searchEndDt: searchDate.endDate,
selectZone: filterArea
})
);
}
// );
setFilter(search);
if (popup) {
popupRef.current.postMessage({
type: 'handlerSearchRs',
payload: { search }
});
}
};
const handlerDetail = area => {
setSelected(area.planAreaSno);
dispatch(clientSaveAreaCoordinateList([area]));
handlerMapInit();
};
const handlerMapInit = () => {
let mapInstance = mode === 'container' ? map : window.opener._mapbox;
if (mapInstance.getSource('preview')) {
} else {
mapInstance.addSource('preview', {
type: 'geojson',
data: previewGeo2
});
mapInstance.addLayer(flightlayerWayPoint('preview'));
mapInstance.addLayer(flightlayerBuffer('preview'));
mapInstance.addLayer(flightlayerPolygon('preview'));
mapInstance.addLayer(flightlayerPolyline('preview'));
}
dispatch(clientSetIsMapLoading(true));
const preview = mapInstance.getSource('preview');
if (preview) setPreviewLayer(preview);
setIsMapLoading(true);
setMapObject(mapInstance);
dispatch(clientMapInit(mapInstance));
};
const handlerPreviewDraw = () => {
if (areaCoordList.length > 0) {
const areas = areaCoordList[0];
previewGeo2.features = [];
let fitZoomPaths = [];
const radius = areas.bufferZone;
const circleCoords = handlerGetCircleCoord(
[areas.lon, areas.lat],
radius
);
const circle = InitFeature('Polygon', 'CIRCLE');
circle.properties.center = [areas.lon, areas.lat];
circle.geometry.coordinates = circleCoords;
previewGeo2.features.push(circle);
mapObject?.setCenter(circle.properties.center);
fitZoomPaths = circleCoords[0];
handlerFitBounds(mapObject, fitZoomPaths, 400, 'CIRCLE', 'flight');
// mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
mapObject?.getSource('preview').setData(previewGeo2);
}
};
const handlerLogout = async () => {
const { payload } = await dispatch(setLogout());
if (payload === 'SUCCESS') {
history.replace('/account/login');
}
};
const [testModal, setTestModal] = useState(false);
return (
<>
<div className='map' style={{ width: '100%' }}>
<div className='test_modal'>
<Button
color='primary'
size='lg'
onClick={() => setTestModal(!testModal)}
>
test
</Button>
</div>
<div>
<Modal
isOpen={testModal}
toggle={() => setTestModal(!testModal)}
className='modal-dialog-centered modal-lg'
>
<ModalHeader toggle={() => setTestModal(!testModal)}>
유효성 검사 상세보기
</ModalHeader>
<ModalBody className='onestop-validation'>
<div className='pal-table-warp'>
<h5 className='table-ti'>
<FiFileText />
기체 정보
</h5>
<div className='pal-table'>
<Table responsive>
<thead>
<tr>
<th width='80'>No</th>
<th width='120'>유효성 검사</th>
<th>제작 번호</th>
<th>기체 용도</th>
<th>기체 중량</th>
<th>보험 가입</th>
</tr>
</thead>
<tbody>
<tr>
<td width='80'>1</td>
<td width='120' className='success'>
성공
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='success'>가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>163CGBEROA529Y</td>
<td>영리</td>
<td>25kg이하</td>
<td className='fail'> 가입</td>
</tr>
</tbody>
</Table>
</div>
</div>
<div className='pal-table-warp'>
<h5 className='table-ti'>
<FiUsers />
조종자 정보
</h5>
<div className='pal-table'>
<Table>
<thead>
<tr>
<th width='80'>No</th>
<th width='120'>유효성 검사</th>
<th>이름</th>
<th>생년 월일</th>
<th>자격 번호</th>
<th>조종 자격</th>
</tr>
</thead>
<tbody>
<tr>
<td width='80'>1</td>
<td width='120' className='success'>
성공
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='success'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
<tr>
<td width='80'>2</td>
<td width='120' className='fail'>
실패
</td>
<td>*</td>
<td>1995****</td>
<td>91-****12</td>
<td className='fail'></td>
</tr>
</tbody>
</Table>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color='primary' onClick={() => setTestModal(!testModal)}>
확인
</Button>{' '}
</ModalFooter>
</Modal>
</div>
<div className='main-data'>
<div className='data-box-btn'>
<Card>
<div className='data-box-btn-list'>
<h4>지도유형</h4>
<div className='map-btn'>
<ButtonGroup>
<Button
color={mapType === 'TERRAIN' ? 'primary' : ''}
onClick={e => setMapType('TERRAIN')}
>
지형지도
</Button>
<Button
color={mapType === 'SATELLITE' ? 'primary' : ''}
onClick={e => setMapType('SATELLITE')}
>
위성지도
</Button>
<Button
color={mapType === '2D' ? 'primary' : ''}
onClick={e => setMapType('2D')}
>
2D
</Button>
</ButtonGroup>
</div>
</div>
<div className='data-box-btn-list'>
<h4>거리측정</h4>
<div className='map-btn'>
<ButtonGroup>
<Button primary>직선</Button>
<Button primary>다각형</Button>
</ButtonGroup>
</div>
</div>
</Card>
</div>
</div>
<MapControl />
</div>
<div className='left-menu'>
<h1 className='logo'>
<img src={logo} width='80' />
<span>UTM</span>
</h1>
<ul className='left-menu-footer'>
<li>
<AiOutlinePoweroff
size={25}
className='logout-btn'
onClick={handlerLogout}
/>
</li>
<li>
<WebsocketClient />
</li>
</ul>
</div>
{!isPopup && (
<div className='right-menu active flight-approval-layer-wrap'>
<div
className='right-layer active flight-approval-layer'
ref={rightMenuRef}
>
<div className='layer-content'>
<NewFlightApprovalsReport
handlerSearch={handlerSearch}
handleDragEnd={handleDragEnd}
filterId={filterId}
setFilterId={setFilterId}
district={district}
setDistrict={setDistrict}
filterArea={filterArea}
setFilterArea={setFilterArea}
/>
<OperationApprovalsTable
filter={filter}
filterArea={filterArea}
filterId={filterId}
startDate={startDate}
endDate={endDate}
selected={selected}
handlerDetail={handlerDetail}
/>
</div>
</div>
</div>
)}
</>
);
}

4
src/containers/rightMenuContainer.js

@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useDispatch } from '@src/redux/store'; import { useDispatch } from '@src/redux/store';
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk'; import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk';
import NewFlightApprovalsTable from '@src/components/flight/NewFlightApprovalsTable'; import OperationApprovalsTable from '@src/components/flight/OperationApprovalsTable';
import NewFlightApprovalsReport from '@src/components/flight/NewFlightApprovalsReport'; import NewFlightApprovalsReport from '@src/components/flight/NewFlightApprovalsReport';
function RightMenuContainer() { function RightMenuContainer() {
@ -136,7 +136,7 @@ function RightMenuContainer() {
filterArea={filterArea} filterArea={filterArea}
setFilterArea={setFilterArea} setFilterArea={setFilterArea}
/> />
<NewFlightApprovalsTable <OperationApprovalsTable
filter={filter} filter={filter}
filterArea={filterArea} filterArea={filterArea}
filterId={filterId} filterId={filterId}

8
src/router/hoc/authenticationCheck.tsx

@ -35,10 +35,8 @@ export default function (SpecificComponent: ReactNode, option: IOption) {
if (user?.cptAuthCode === 'DF0002') { if (user?.cptAuthCode === 'DF0002') {
const pathname = history.location.pathname; const pathname = history.location.pathname;
if ( if (!(pathname == '/operation/approvals' || pathname == '/rightMenu')) {
!(pathname == '/flight/Approvals/new' || pathname == '/rightMenu') props.history.replace('/operation/approvals');
) {
props.history.replace('/flight/Approvals/new');
return; return;
} }
} }
@ -63,7 +61,7 @@ export default function (SpecificComponent: ReactNode, option: IOption) {
if (!authRoute) { if (!authRoute) {
const cptAuthCode = localStorage.getItem('cptAuthCode'); const cptAuthCode = localStorage.getItem('cptAuthCode');
if (cptAuthCode === 'DF0002') { if (cptAuthCode === 'DF0002') {
props.history.replace('/flight/Approvals/new'); props.history.replace('/operation/approvals');
} else { } else {
localStorage.setItem('topMenuCd', '/'); localStorage.setItem('topMenuCd', '/');
dispatch(clientDispatchTopMenu('/')); dispatch(clientDispatchTopMenu('/'));

2
src/router/index.tsx

@ -30,7 +30,7 @@ export default function Router() {
<Redirect <Redirect
to={ to={
cptAuthCode === 'DF0002' cptAuthCode === 'DF0002'
? '/flight/Approvals/new' ? '/operation/approvals'
: '/main/dashboard' : '/main/dashboard'
} }
/> />

13
src/router/routes/RouteFlight.js

@ -2,8 +2,17 @@ import { lazy } from 'react';
const RouteFlight = [ const RouteFlight = [
{ {
path: '/flight/approvals/new', path: '/operation/approvals',
component: lazy(() => import('../../views/flight/NewFlightView')), component: lazy(() => import('../../views/flight/operationApprovalsView')),
layout: 'BlankLayout',
meta: {
authRoute: true
}
},
{
path: '/control/approvals',
component: lazy(() => import('../../views/flight/controlApprovalsView')),
layout: 'BlankLayout', layout: 'BlankLayout',
meta: { meta: {

21
src/router/routes/index.js

@ -105,6 +105,14 @@ const Routes = [
authRoute: true authRoute: true
} }
}, },
{
path: '/control/rightMenu',
component: lazy(() => import('../../views/rightMenuView')),
layout: 'BlankLayout',
meta: {
authRoute: true
}
},
{ {
path: '/history/record/sample1', path: '/history/record/sample1',
component: lazy(() => component: lazy(() =>
@ -231,13 +239,22 @@ const Routes = [
const GimpoControlRoutes = [ const GimpoControlRoutes = [
{ {
path: '/flight/approvals/new', path: '/operation/approvals',
component: lazy(() => import('../../views/flight/NewFlightView')), component: lazy(() => import('../../views/flight/operationApprovalsView')),
layout: 'BlankLayout', layout: 'BlankLayout',
meta: { meta: {
authRoute: true authRoute: true
} }
}, },
{
path: '/control/approvals',
component: lazy(() => import('../../views/flight/controlApprovalsView')),
layout: 'BlankLayout',
meta: {
authRoute: true
}
},
{ {
path: '/rightMenu', path: '/rightMenu',
component: lazy(() => import('../../views/rightMenuView')), component: lazy(() => import('../../views/rightMenuView')),

13
src/views/controlMenuView.js

@ -0,0 +1,13 @@
import '@styles/react/libs/flatpickr/flatpickr.scss';
import '@styles/react/libs/tables/react-dataTable-component.scss';
import '../assets/css/custom.css';
import ControlRightMenuContainer from '../containers/controlRightMenuContainer';
import FlightApprovalsContainer from '../containers/flight/flightApprovalsContainer';
export default function rightMenuView() {
return (
<div className='pal-container'>
<ControlRightMenuContainer />
</div>
);
}

1
src/views/flight/FlightView.js

@ -3,7 +3,6 @@ import '@styles/react/libs/flatpickr/flatpickr.scss';
import '@styles/react/libs/tables/react-dataTable-component.scss'; import '@styles/react/libs/tables/react-dataTable-component.scss';
import '../../assets/css/custom.css'; import '../../assets/css/custom.css';
import FlightApprovalsContainer from '../../containers/flight/flightApprovalsContainer'; import FlightApprovalsContainer from '../../containers/flight/flightApprovalsContainer';
import { MapControl } from '../../components/map/MapControl';
export default function FlightView() { export default function FlightView() {
return ( return (

6
src/views/flight/NewFlightView.js → src/views/flight/controlApprovalsView.js

@ -1,15 +1,15 @@
import '@styles/react/libs/flatpickr/flatpickr.scss'; import '@styles/react/libs/flatpickr/flatpickr.scss';
import '@styles/react/libs/tables/react-dataTable-component.scss'; import '@styles/react/libs/tables/react-dataTable-component.scss';
import '../../assets/css/custom.css'; import '../../assets/css/custom.css';
import NewFlightApprovalsContainer from '../../containers/flight/NewFlightApprovalsContainer'; import ControlApprovalsContainer from '../../containers/flight/ControlApprovalsContainer';
export default function NewFlightView() { export default function OperationApprovalsView() {
return ( return (
<div className='pal-container'> <div className='pal-container'>
{/* <Helmet> {/* <Helmet>
<title>관제시스템</title> <title>관제시스템</title>
</Helmet> */} </Helmet> */}
<NewFlightApprovalsContainer mode='container' />; <ControlApprovalsContainer mode='container' />;
</div> </div>
); );
} }

15
src/views/flight/operationApprovalsView.js

@ -0,0 +1,15 @@
import '@styles/react/libs/flatpickr/flatpickr.scss';
import '@styles/react/libs/tables/react-dataTable-component.scss';
import '../../assets/css/custom.css';
import OperationApprovalsContainer from '../../containers/flight/OperationApprovalsContainer';
export default function OperationApprovalsView() {
return (
<div className='pal-container'>
{/* <Helmet>
<title>관제시스템</title>
</Helmet> */}
<OperationApprovalsContainer mode='container' />;
</div>
);
}
Loading…
Cancel
Save