Merge commit '525307d1ee392efef669de8f912620e6158d142b' into iipbs/sn/dev
iipbs_sn / build (push) Has been cancelled
iipbs_sn / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import UiPageNations from "@common/business_components/ui/table/uiPageNations";
|
||||
import { Table, TableBody, TableCell, TableRow } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type UiTableProps = {
|
||||
headName: React.ReactNode | undefined;
|
||||
tableData: { [key: string]: string | number | boolean | object }[];
|
||||
headlist: UiHeadProps[];
|
||||
pageSize: number;
|
||||
total: number;
|
||||
children: React.ReactNode | undefined;
|
||||
cellClick: ((index: number | undefined, headId: string) => void) | undefined;
|
||||
onPageChange: (page: number) => void;
|
||||
className?: string;
|
||||
resetPageNo?: boolean;
|
||||
isEditable?: boolean;
|
||||
};
|
||||
|
||||
type UiHeadProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
align?: string;
|
||||
type?: string;
|
||||
options?: string[];
|
||||
};
|
||||
|
||||
export function VerticalTable({
|
||||
tableData,
|
||||
headlist,
|
||||
pageSize,
|
||||
total,
|
||||
children,
|
||||
headName,
|
||||
cellClick,
|
||||
onPageChange,
|
||||
className = "",
|
||||
resetPageNo = false,
|
||||
isEditable = false,
|
||||
}: UiTableProps) {
|
||||
const [cur, setCur] = useState<number>(1);
|
||||
const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({});
|
||||
useEffect(() => {
|
||||
if (resetPageNo) {
|
||||
setCur(1);
|
||||
onPageChange(1);
|
||||
}
|
||||
}, [onPageChange, resetPageNo]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('data',tableData)
|
||||
if (tableData.length > 0 && tableData[0]) {
|
||||
setFormData(tableData[0] as { [key: string]: string | number | boolean });
|
||||
}
|
||||
}, [tableData]);
|
||||
|
||||
const handleInputChange = (id: string, value: string | number | boolean) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[id]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCur(page);
|
||||
onPageChange(page);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{headName ? (
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{headName}</h3>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
|
||||
<Table variant="secondary">
|
||||
<TableBody className="text-center">
|
||||
{headlist?.map((head) => (
|
||||
<TableRow key={head.id}>
|
||||
{/* 컬럼 제목 */}
|
||||
<TableCell className="font-bold bg-blue-100 text-xs">
|
||||
{head.title}
|
||||
</TableCell>
|
||||
{/* 데이터 값 또는 입력 필드 */}
|
||||
<TableCell className="text-xs">
|
||||
{isEditable ? (
|
||||
head.type === "select" ? (
|
||||
<select
|
||||
value={formData[head.id] ?? "-"}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-sm px-2 py-1 text-xl border-radius-0"
|
||||
>
|
||||
{head.options?.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : head.type === "input" ? (
|
||||
<input
|
||||
type="text"
|
||||
value={formData[head.id] ?? ""}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{total > 0 && (
|
||||
<UiPageNations
|
||||
total={total}
|
||||
cur={cur}
|
||||
pageSize={pageSize}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
'use client'
|
||||
import { VerticalTable } from "@/components/verticalTable";
|
||||
import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui";
|
||||
import { useState } from "react";
|
||||
import { CommonTableColumn } from "./table";
|
||||
const inputColumn = [
|
||||
{ id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] },
|
||||
{ id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "basu", title: "배수구역", type: "input" },
|
||||
{ id: "bungu", title: "배수분구", type: "input" },
|
||||
{ id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] },
|
||||
{ id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] },
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
];
|
||||
|
||||
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData:any }) => {
|
||||
// TODO: 런타임 에러 해결 필요
|
||||
const [repairData, setRepairData] = useState([
|
||||
{ year: "2025", type: "개대체" },
|
||||
{ year: "2025", type: "보수" },
|
||||
]);
|
||||
|
||||
const [cleaningData, setCleaningData] = useState([
|
||||
{ year: "2025", type: "청소" },
|
||||
{ year: "2025", type: "준설" },
|
||||
]);
|
||||
|
||||
const [inspectionData, setInspectionData] = useState([
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
]);
|
||||
const handleInputChange = (section: string, index: number, field: string, value: string) => {
|
||||
const updateData = (data: { year: string; type: string }[], setData: React.Dispatch<React.SetStateAction<{ year: string; type: string }[]>>) => {
|
||||
const updated = [...data];
|
||||
if (updated[index]) {
|
||||
updated[index][field as keyof typeof updated[number]] = value;
|
||||
}
|
||||
setData(updated);
|
||||
};
|
||||
|
||||
if (section === "repair") {
|
||||
updateData(repairData, setRepairData);
|
||||
} else if (section === "cleaning") {
|
||||
updateData(cleaningData, setCleaningData);
|
||||
} else if (section === "inspection") {
|
||||
updateData(inspectionData, setInspectionData);
|
||||
}
|
||||
};
|
||||
function transformFeatureDataToArray(featureData: any, keys: string[]) {
|
||||
// 필요한 키만 추출하여 새로운 객체 생성
|
||||
const transformedData = keys.reduce((acc, key) => {
|
||||
acc[key] = featureData[key] !== undefined ? featureData[key] : "N/A"; // 값이 없으면 "N/A"로 처리
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
// 배열로 감싸서 반환
|
||||
return [transformedData];
|
||||
}
|
||||
const keys = CommonTableColumn.map((column) => column.id);
|
||||
const transformedFeatureData = transformFeatureDataToArray(featureData, keys);
|
||||
const renderSection = (title: string, data: { year: string; type: string }[], sectionKey: string) => (
|
||||
<div>
|
||||
{/* 섹션 제목 */}
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{title}</h3>
|
||||
</div>
|
||||
{/* 테이블 */}
|
||||
<div className="card !m-[1.2rem] !p-0r">
|
||||
<Table variant="secondary" className="w-full">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">년도</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">구분</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={row.year}
|
||||
onChange={(e) => handleInputChange(sectionKey, index, "year", e.target.value)}
|
||||
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal">{row.type}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[1000]">
|
||||
<div className="card-header bg-primary flex items-center justify-between p-4">
|
||||
<div className="flex mx-3 flex-col">
|
||||
<h3 className="text-2xl font-medium text-white text-label">하수관로 정보</h3>
|
||||
<div className="font-medium text-white text-label">(00구 00리 하수관로)</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-white hover:text-gray-700 focus:outline-none"
|
||||
aria-label="닫기"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-y-auto flex-1">
|
||||
<VerticalTable headName='LX 연계정보' tableData={transformedFeatureData} headlist={CommonTableColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
{/* here */}
|
||||
{renderSection("보수 및 갱신", repairData, "repair")}
|
||||
{renderSection("청소 및 준설", cleaningData, "cleaning")}
|
||||
{renderSection("점검 및 진단", inspectionData, "inspection")}
|
||||
</div>
|
||||
<div className="w-full mx-5 flex items-center justify-end space-x-2">
|
||||
<Button className="px-4 mr-5 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700">
|
||||
저장
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FeatureClickEventPopup;
|
||||
@@ -28,7 +28,7 @@ const Map = () => {
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis treeData={treeData}/>
|
||||
<Gis treeData={treeData} isSearch={true} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
export const CommonTableColumn = [
|
||||
{ id: 'swtrmt_fclt_cd', title: '하수처리시설코드' },
|
||||
{ id: 'swtrmt_fclt_nm', title: '하수처리시설명' },
|
||||
{ id: 'fclt_cpct', title: '시설용량' },
|
||||
{ id: 'rgn_expln', title: '지역설명' },
|
||||
{ id: 'cmcn_ymd', title: '준공일자' },
|
||||
{ id: 'oprtng_strt_ymd', title: '가동개시일자' },
|
||||
{ id: 'clsg_ymd', title: '폐쇄일자' },
|
||||
{ id: 'prcs_msrc_cd', title: '처리공법코드' },
|
||||
{ id: 'lae_cd', title: '지자체코드' },
|
||||
{ id: 'lae_nm', title: '지자체명' },
|
||||
{ id: 'ctpv_nm', title: '시도명' },
|
||||
{ id: 'sgg_nm', title: '시군구명' },
|
||||
{ id: 'sarea_cd', title: '권역코드' },
|
||||
{ id: 'sarea_nm', title: '권역명' },
|
||||
{ id: 'lrge_sarea_nm', title: '대권역명' },
|
||||
{ id: 'rvr_nm', title: '하천명' },
|
||||
{ id: 'addr', title: '주소' },
|
||||
{ id: 'daddr', title: '상세주소' },
|
||||
{ id: 'rdnm_addr', title: '도로명주소' },
|
||||
{ id: 'dtl_rdnm_addr', title: '상세도로명주소' },
|
||||
{ id: 'dschg_wtst_nm', title: '방류수계명' },
|
||||
{ id: 'plln_area', title: '부지면적' },
|
||||
{ id: 'mng_rprtv_bzenty_nm', title: '관리대행업제명' },
|
||||
{ id: 'swtrmt_ppltn_cnt', title: '하수처리인구수' },
|
||||
{ id: 'prcs_zone_area_sum', title: '처리구역면적합계' },
|
||||
{ id: 'geom', title: 'geometry' },
|
||||
{ id: 'xcrd', title: 'X좌표' },
|
||||
{ id: 'ycrd', title: 'Y좌표' },
|
||||
{ id: 'oprtng_stts_cd', title: '가동상태코드' },
|
||||
{ id: 'rprs_msrc_cn', title: '대표공법내용' },
|
||||
{ id: 'stty_crtr_bod', title: '법정기준생물화학적산소요구량' },
|
||||
{ id: 'stty_crtr_toc', title: '법정기준TOC' },
|
||||
{ id: 'stty_crtr_ss', title: '법정기준SS' },
|
||||
{ id: 'stty_crtr_tn', title: '법정기준총질소' },
|
||||
{ id: 'stty_crtr_tph', title: '법정기준총인' },
|
||||
{ id: 'stty_crtr_clbcl_cnt', title: '법정기준대장균수' },
|
||||
{ id: 'bsn_nm', title: '유역명' },
|
||||
{ id: 'obsrvn_spt_cd', title: '관측지점코드' },
|
||||
];
|
||||
@@ -10,23 +10,34 @@ import proj4 from 'proj4';
|
||||
import { register } from 'ol/proj/proj4';
|
||||
import GisApp from './lib/gis.core';
|
||||
import GisContent from './GisContent';
|
||||
import GisTree from './GisTree';
|
||||
import GisLonLat from './GisLonLat';
|
||||
import GisToolBar from './GisToolBar';
|
||||
import GisSearch from './GisSearch';
|
||||
import GisTab from './GisTab';
|
||||
import MapToggleBtn from './MapToggleBtn';
|
||||
import FeatureClickEventPopup from '@/map/featureClickEventPopup';
|
||||
|
||||
export const GisContext = createContext({gisCore: null, gisMap: null, treeData:null, checkedLayerIds:null});
|
||||
export const useGisContext = () => useContext(GisContext);
|
||||
|
||||
function Gis({ treeData = [] }) {
|
||||
function Gis({ treeData = [], isSearch = false }) {
|
||||
const mapRef = useRef();
|
||||
const overviewMapRef = useRef(null);
|
||||
const [mapObject, setMapObject] = useState({});
|
||||
const [tabVisible, setTabVisible] = useState(true);
|
||||
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
||||
const [featureData,setFeatureData] = useState(null) // 선택된 피쳐 데이터
|
||||
const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []);
|
||||
//const [treeData, setTreeData] = useState([]);
|
||||
const checkedLayerIds = useRef([]);
|
||||
let gisMap = null;
|
||||
const closePopup = () => {
|
||||
setIsOpen(false); // 팝업 닫기
|
||||
setFeatureData(null)
|
||||
};
|
||||
const handleFeatureSelect = (data) => {
|
||||
console.log('check',data)
|
||||
setFeatureData(data); // 선택된 피처 데이터 저장
|
||||
setIsOpen(true); // 팝업 열기
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
@@ -98,7 +109,7 @@ function Gis({ treeData = [] }) {
|
||||
|
||||
//setTreeData(data);
|
||||
const layerItems = getLayerItems(layerCode);
|
||||
//setEditLayerItems(prev => [...layerItems]);
|
||||
//setEditLayerItems(prev => [...layerItems])
|
||||
}, [treeData]);
|
||||
|
||||
const getLayerItems= (layerCode) => {
|
||||
@@ -126,33 +137,26 @@ function Gis({ treeData = [] }) {
|
||||
}
|
||||
}
|
||||
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GisContext.Provider value={{gisCore, gisMap, treeData, checkedLayerIds}}>
|
||||
<>
|
||||
<div className="h-[calc(100vh-10rem)] flex relative">
|
||||
<div className="absolute top-0 left-0 z-40 w-[280px] h-[calc(100vh-10rem)] bg-white border rounded-md shadow-md flex flex-col overflow-hidden">
|
||||
<Tabs defaultValue="layers" className="flex-1 flex flex-col overflow-hidden">
|
||||
<TabsList className="flex-none border-b text-[70%]">
|
||||
<TabsTrigger value="layers" className="flex-1">레이어</TabsTrigger>
|
||||
<TabsTrigger value="search" className="flex-1">시설검색</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="layers" className="grow overflow-y-auto p-2 min-h-0">
|
||||
<GisTree />
|
||||
</TabsContent>
|
||||
<TabsContent value="search" className="grow overflow-y-auto p-2 min-h-0">
|
||||
<GisSearch />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<GisTab visible={tabVisible} setVisible={setTabVisible} isSearch={isSearch} />
|
||||
<GisContent onFeatureSelect={handleFeatureSelect}/>
|
||||
{isOpen && featureData && (
|
||||
<FeatureClickEventPopup
|
||||
featureData={featureData}
|
||||
onClose={closePopup} // 닫기 콜백 전달
|
||||
/>
|
||||
)}
|
||||
<div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} />
|
||||
<GisLonLat />
|
||||
<GisToolBar />
|
||||
<MapToggleBtn />
|
||||
<GisLonLat/>
|
||||
<GisToolBar/>
|
||||
<MapToggleBtn/>
|
||||
</div>
|
||||
</>
|
||||
</GisContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,23 +6,23 @@ import GisSelect from './GisSelect';
|
||||
import GisToolBar from './GisToolBar';
|
||||
import './lib/css/gis.edit.css';
|
||||
import MapToggleBtn from './MapToggleBtn';
|
||||
//import GisEdit from './GisEdit';
|
||||
import GisEdit from './GisEdit';
|
||||
|
||||
export default function GisContent({ edit = false }) {
|
||||
export default function GisContent({ edit = false,onFeatureSelect }) {
|
||||
const { gisMap, gisCore } = useGisContext();
|
||||
const [visibleComponents, setVisibleComponents] = useState({});
|
||||
const addressResultLayer = useRef(null);
|
||||
const [playShow, setPlayShow] = useState({ show: false });
|
||||
|
||||
//셀렉트 버튼 이벤트
|
||||
const clickFeature = useCallback(feature => {
|
||||
if (feature) {
|
||||
const layer = feature.getLayer();
|
||||
setVisibleComponents(prevState => ({
|
||||
...prevState,
|
||||
}));
|
||||
}
|
||||
}, []);
|
||||
// const clickFeature = useCallback(feature => {
|
||||
// if (feature) {
|
||||
// const layer = feature.getLayer();
|
||||
// setVisibleComponents(prevState => ({
|
||||
// ...prevState,
|
||||
// }));
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
const selectRslt = useCallback(feature => {
|
||||
GisApp.Module.layer.highLightFeature(
|
||||
@@ -52,9 +52,9 @@ export default function GisContent({ edit = false }) {
|
||||
<>
|
||||
{/*<GisTree />*/}
|
||||
{/* <MapToggleBtn useCesium={useCesium} /> */}
|
||||
<GisLonLat />
|
||||
<GisToolBar />
|
||||
<GisSelect clickFeature={clickFeature} />
|
||||
{/* { <GisLonLat /> */}
|
||||
{/* <GisSelect clickFeature={clickFeature} /> */}
|
||||
<GisSelect onFeatureSelect={onFeatureSelect}/>
|
||||
{/* {edit && <GisEdit />} */}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import GisApp from './lib/gis.core';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { GisContext, setTree } from './Gis';
|
||||
import Button from '../button/Button';
|
||||
import { Button } from "@common/components";
|
||||
import { gAlert, gConfirm } from './lib/util';
|
||||
import { fclApi } from '../../js/apis/fcl/fclApi';
|
||||
// import { fclApi } from '../../js/apis/fcl/fclApi';
|
||||
|
||||
export default React.memo(function GisEdit() {
|
||||
const { editLayerItems = [], siteSn, locgovCd, checkedLayerIds } = useContext(GisContext);
|
||||
@@ -96,10 +96,10 @@ export default React.memo(function GisEdit() {
|
||||
return;
|
||||
}
|
||||
if (!gConfirm('CONFIRM', 'SAVE_QUEST')) return false;
|
||||
const result = await fclApi.saveEditFeatures(editFeatures);
|
||||
if (result.code === 200) {
|
||||
editModule.clearUndoRedo();
|
||||
}
|
||||
// const result = await fclApi.saveEditFeatures(editFeatures);
|
||||
// if (result.code === 200) {
|
||||
// editModule.clearUndoRedo();
|
||||
// }
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import GisApp from './lib/gis.core';
|
||||
import GisOlSelect from './lib/gis.select';
|
||||
import { useContext } from 'react';
|
||||
import { GisContext } from './Gis';
|
||||
export default React.memo(function GisSelect({ clickFeature }) {
|
||||
export default React.memo(function GisSelect({ onFeatureSelect }) {
|
||||
const { gisMap } = useContext(GisContext);
|
||||
const select = new GisOlSelect(gisMap);
|
||||
GisApp.Module.select = select;
|
||||
@@ -12,11 +12,15 @@ export default React.memo(function GisSelect({ clickFeature }) {
|
||||
};
|
||||
|
||||
select.getSelectedInfo(function (features) {
|
||||
clickFeature(features[0]);
|
||||
//clickFeature(features[0]);
|
||||
if(features.length > 0) {
|
||||
const layer = features[0];
|
||||
onFeatureSelect(layer.getProperties()); //피쳐 속성데이터 상위컴포넌트로 전달
|
||||
}
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div className="select_control map_control_div ol-unselectable ol-control" style={{ pointerEvents: 'auto', zIndex: '1' }}>
|
||||
{/* <div className="select_control map_control_div ol-unselectable ol-control" style={{ pointerEvents: 'auto', zIndex: '1' }}>
|
||||
<h3>선,면,다각형을 이용한 시설물 선택 영역</h3>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -30,7 +34,7 @@ export default React.memo(function GisSelect({ clickFeature }) {
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div> */}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@common/components/ui/tabs';
|
||||
import GisTree from './GisTree';
|
||||
import GisSearch from './GisSearch';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
function GisTab({ visible, setVisible, isSearch }) {
|
||||
const [activeTab, setActiveTab] = useState('layers');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`absolute top-0 left-0 w-[250px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-40
|
||||
${visible ? 'translate-x-0 opacity-100' : '-translate-x-full opacity-0 pointer-events-none'}`}
|
||||
>
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col overflow-hidden">
|
||||
<TabsList className="flex-none mt-4 border-none rounded-none">
|
||||
<TabsTrigger value="layers" className="flex-1 h-10">레이어</TabsTrigger>
|
||||
{isSearch && (
|
||||
<TabsTrigger value="search" className="flex-1 h-10">검색</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
{/* GisTree는 항상 마운트되며, 탭 상태에 따라 보여주기만 함 */}
|
||||
<div
|
||||
className={`grow overflow-y-auto p-2 pt-0 border-none shadow-none ${
|
||||
activeTab === 'layers' ? 'block' : 'hidden'
|
||||
}`}
|
||||
>
|
||||
<GisTree />
|
||||
</div>
|
||||
|
||||
{/* GisSearch도 동일하게 처리 */}
|
||||
{isSearch && (
|
||||
<div
|
||||
className={`grow overflow-y-auto p-2 pt-0 border-none shadow-none ${
|
||||
activeTab === 'search' ? 'block' : 'hidden'
|
||||
}`}
|
||||
>
|
||||
<GisSearch />
|
||||
</div>
|
||||
)}
|
||||
</Tabs>
|
||||
|
||||
{/* 접기 버튼 */}
|
||||
<button
|
||||
onClick={() => setVisible(false)}
|
||||
className="absolute top-1/2 right-[-3px] transform -translate-y-1/2 z-50 bg-white border border-l border-gray-300 rounded-l-none rounded-r px-1 py-2 shadow-sm"
|
||||
>
|
||||
<ChevronLeft size={16} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 열기 버튼 */}
|
||||
{!visible && (
|
||||
<button
|
||||
onClick={() => setVisible(true)}
|
||||
className="absolute top-1/2 left-[-3px] transform -translate-y-1/2 z-50 bg-white border border-r border-gray-300 rounded-r px-1 py-2 shadow-sm"
|
||||
>
|
||||
<ChevronRight size={16} />
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(GisTab);
|
||||
@@ -23,7 +23,6 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
setNodes(nodeData);
|
||||
gisCore.setAllLayersOnMap(false);
|
||||
}
|
||||
console.log(treeData)
|
||||
}, [treeData]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -37,7 +36,6 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
setExpanded(defaultExpanded);
|
||||
gisCore.setLayerOnMap(defaultChecked, true);
|
||||
}
|
||||
console.log(nodes)
|
||||
}, [nodes]);
|
||||
|
||||
const getDefaultCheckedNodes = (nodes, values = []) => {
|
||||
@@ -113,27 +111,11 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
parent: item.upperLyrSn,
|
||||
droppable: !!item.children,
|
||||
data: item,
|
||||
icon: src ? <img src={src} alt={item.lyrNm} className="w-4 h-4" /> : '',
|
||||
icon: src ? <img src={src} alt={item.lyrNm} /> : '',
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const findUpLyrId = (nodes, targetLyrId) => {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (node.data && node.data.lyrSn === targetLyrId) {
|
||||
return node.data.upperLyrSn;
|
||||
}
|
||||
if (node.children) {
|
||||
const upLyrId = findUpLyrId(node.children, targetLyrId);
|
||||
if (upLyrId) {
|
||||
return upLyrId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const makeLegendSrc = layerinfo => {
|
||||
if(!layerinfo.gobjTy) return null;
|
||||
|
||||
@@ -208,18 +190,13 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
style={{ marginInlineStart: depth * 16 + (!node.droppable ? 16 : 0) }}
|
||||
>
|
||||
{node.droppable && (
|
||||
<button onClick={onToggle} className="text-sm w-4 focus:outline-none" title={isOpen ? '닫기' : '열기'}>
|
||||
{isOpen ? '▾' : '▸'}
|
||||
</button>
|
||||
<i onClick={onToggle} className={`diveicon di-arrow-forward mr-2 text-base text-label ${isOpen ? 'rotate-90' : ''}`}
|
||||
title={isOpen ? '닫기' : '열기'}>
|
||||
</i>
|
||||
)}
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked.includes(node.id)}
|
||||
onChange={() => handleCheck(node.id)}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
{node.icon && <span className="inline-block w-4 h-4 mr-1">{node.icon}</span>}
|
||||
<span className="text-sm truncate">{node.text}</span>
|
||||
<input type="checkbox" checked={checked.includes(node.id)} onChange={() => handleCheck(node.id)} className="cursor-pointer"/>
|
||||
{node.icon && <span>{node.icon}</span>}
|
||||
<span className="text-[1.3rem] font-medium text-label">{node.text}</span>
|
||||
</div>
|
||||
)}
|
||||
sort={(a, b) => {
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
.tool-bar {
|
||||
position: absolute;
|
||||
top: 140px;
|
||||
top: 70px;
|
||||
right: 20px;
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -244,7 +244,7 @@
|
||||
background: url(../images/toolbar/btn_cont_zoomin_on.png) no-repeat center #67aee3;
|
||||
}
|
||||
.btn_control_zoomout {
|
||||
background: url(../images/toolbar/btn_cont_zoomout.png) no-repeat center !important;
|
||||
background: url(../images/toolbar/btn_cont_zoomout.png) no-repeat center;
|
||||
}
|
||||
.btn_control_zoomout:hover {
|
||||
background: url(../images/toolbar/btn_cont_zoomout_on.png) no-repeat center #67aee3;
|
||||
@@ -346,7 +346,7 @@
|
||||
.select_control {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 250px;
|
||||
left: 450px;
|
||||
}
|
||||
.select_control h3 {
|
||||
overflow: hidden;
|
||||
@@ -759,8 +759,6 @@
|
||||
.puGrayTb {
|
||||
position: relative;
|
||||
width: 98.3%;
|
||||
cellpadding: 0;
|
||||
cellspcing: 0;
|
||||
margin-top: 5px;
|
||||
table-layout: fixed;
|
||||
font-family: Verdana;
|
||||
|
||||
@@ -48,19 +48,31 @@ GisOlSelect.prototype = {
|
||||
this.removeInteration();
|
||||
}
|
||||
let baseStyle = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(135, 116, 243, 0.4)',
|
||||
width: 10,
|
||||
image : new CircleStyle({
|
||||
fill : new Fill({
|
||||
color : 'rgba(135, 116, 243, 0.4)',
|
||||
}),
|
||||
radius : 30
|
||||
}),
|
||||
stroke : new Stroke({
|
||||
color : 'rgba(135, 116, 243, 0.4)',
|
||||
width : 10,
|
||||
})
|
||||
});
|
||||
|
||||
this.selectInteraction = new Select({
|
||||
style: function (feature) {
|
||||
style: function (feature, resolution) {
|
||||
let styles = [];
|
||||
let layer = feature.getLayer();
|
||||
let style = GisApp.Module.style.getLayerStyle(layer, null);
|
||||
if (style) {
|
||||
styles.push(style);
|
||||
let styleFn = GisApp.Module.style.getLayerStyleFunction(layer, null);
|
||||
|
||||
if (styleFn) {
|
||||
const style = styleFn(feature, resolution);
|
||||
if (Array.isArray(style)) {
|
||||
styles.push(...style);
|
||||
} else {
|
||||
styles.push(style);
|
||||
}
|
||||
}
|
||||
styles.push(baseStyle);
|
||||
return styles;
|
||||
@@ -72,9 +84,25 @@ GisOlSelect.prototype = {
|
||||
condition: click,
|
||||
hitTolerance: me.defaults.hitTolerance,
|
||||
});
|
||||
map.addInteraction(this.selectInteraction);
|
||||
|
||||
map.addInteraction(this.selectInteraction);
|
||||
this.selectedFeatures = this.selectInteraction.getFeatures();
|
||||
|
||||
// 선택된 피처 중심으로 지도 이동
|
||||
this.selectInteraction.on('select', function (e) {
|
||||
const feature = e.selected[0];
|
||||
if (feature) {
|
||||
const geometry = feature.getGeometry();
|
||||
if (geometry) {
|
||||
const extent = geometry.getExtent();
|
||||
map.getView().fit(extent, {
|
||||
duration: 300,
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 16
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* SELECT 피처 초기화
|
||||
|
||||
Reference in New Issue
Block a user