Merge remote-tracking branch 'origin/main' into iipbs/sn/dev
@@ -52,9 +52,9 @@ const LinkForm = ({ form, linkSn, updateLinkInfo, handleInsertLink, deleteLink,
|
||||
if (await confirm(confirmMsg)) {
|
||||
if (linkSn) {
|
||||
updateLinkInfo?.(data);
|
||||
} else {
|
||||
} else {
|
||||
handleInsertLink?.(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ function LinkParamsPage() {
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const dbIdList = ['link0001', 'link0002'];
|
||||
const dbIdList = ['link0001', 'link0002', 'link0007'];
|
||||
|
||||
// Link 목록 쿼리
|
||||
const query = useQuery({
|
||||
|
||||
@@ -233,6 +233,7 @@ const Map = () => {
|
||||
<>
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
height='h-screen'
|
||||
treeData={treeData}
|
||||
isSearch={false}
|
||||
gisMaps={gisMaps}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"use client";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import {
|
||||
SearchDiv,
|
||||
SearchFormLeft,
|
||||
SelectBox,
|
||||
type SelectDataType,
|
||||
} from "@common/business_components/ui";
|
||||
@@ -48,6 +46,7 @@ export function SearchBar({
|
||||
},
|
||||
[Common.selectSigunData]
|
||||
);
|
||||
|
||||
const onSelectValue = useCallback(
|
||||
(val: string, type: string) => {
|
||||
switch (type) {
|
||||
@@ -62,78 +61,58 @@ export function SearchBar({
|
||||
[initializeSigunData]
|
||||
);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log("map.ruseFcltList ", map.ruseFcltList);
|
||||
// }, [map.ruseFcltList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-4 gap-4 ">
|
||||
<div className="col-span-4">
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className="col-span-10 flex flex-row">
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/6 h-full text-2xl"
|
||||
label="시도"
|
||||
selectClass={""}
|
||||
selectArray={Common.selectSidoData || []}
|
||||
onSelectValue={(val) =>
|
||||
onSelectValue(val.toString(), "ctpvCd")
|
||||
}
|
||||
value={sidoValue || Common.selectSidoData[0]?.val || ""}
|
||||
/>
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/6 h-full text-2xl ml-10"
|
||||
label="시군구"
|
||||
selectClass={""}
|
||||
selectArray={filteredSigunData || []}
|
||||
onSelectValue={(val) =>
|
||||
onSelectValue(val.toString(), "sggCd")
|
||||
}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<div className="col-span-6 flex flex-row items-center min-w-[27rem] ml-10">
|
||||
<Label className="text-xl justify-self-center mt-2 mx-3">
|
||||
하수재이용시설
|
||||
</Label>
|
||||
<SearchInputText
|
||||
label={"시설명"}
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button
|
||||
color="sky"
|
||||
className="mt-2"
|
||||
size={"sm"}
|
||||
onClick={() => {
|
||||
onApply();
|
||||
}}
|
||||
>
|
||||
적용
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
</SearchDiv>
|
||||
</div>
|
||||
<div className="flex justify-center w-full px-4 py-3">
|
||||
<div className="flex items-center w-full max-w-screen-lg rounded-xl bg-white shadow-md px-6 py-4 space-x-3">
|
||||
<SelectBox
|
||||
labelClass="w-[40px]"
|
||||
className="w-[200px]"
|
||||
label="시도"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={Common.selectSidoData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue || Common.selectSidoData[0]?.val || ""}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[65px]"
|
||||
className="w-[200px]"
|
||||
label="시군구"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={filteredSigunData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<Label className="text-gray-600">하수재이용시설</Label>
|
||||
<SearchInputText
|
||||
label="시설명"
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
<Button
|
||||
color="sky"
|
||||
className="ml-2 text-white"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
onApply();
|
||||
}}
|
||||
>
|
||||
적용
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 923 B |
@@ -2,6 +2,7 @@ NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -15,6 +15,7 @@ NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { SearchBar } from "./searchBar/searchBar";
|
||||
|
||||
interface FeatureData {
|
||||
fid?: string;
|
||||
@@ -123,24 +124,15 @@ const Map = () => {
|
||||
setIsSearchModalOpen(false);
|
||||
};
|
||||
|
||||
// onCheckedLayersChange handler
|
||||
const handleCheckedLayersChange = (
|
||||
checkedIds: string[],
|
||||
checkedNodesData: unknown[]
|
||||
) => {
|
||||
// Handle checked layers change if needed
|
||||
console.log("Checked layers changed:", checkedIds, checkedNodesData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
treeData={treeData}
|
||||
isSearch={false}
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={true}
|
||||
searchView={null}
|
||||
detailView={null}
|
||||
isMiniMap={false}
|
||||
searchView={undefined}
|
||||
detailView={undefined}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
@@ -148,9 +140,9 @@ const Map = () => {
|
||||
setIsSearchModalOpen={setIsSearchModalOpen}
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={handleCheckedLayersChange}
|
||||
onCheckedLayersChange={undefined}
|
||||
imgVisble={false}
|
||||
searchBar={undefined}
|
||||
searchBar={(props:any) => <SearchBar onFeatureSelect={onFeatureSelect} gisMaps={gisMaps} />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
"use client";
|
||||
import { SelectBox, type SelectDataType } from "@common/business_components/ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { GEOSERVER_URL } from "@/src/shared/config/env";
|
||||
|
||||
interface SearchBarProps {
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
gisMaps: React.RefObject<any>;
|
||||
}
|
||||
|
||||
export function SearchBar({ onFeatureSelect, gisMaps }: SearchBarProps) {
|
||||
const [sidoValue, setSidoValue] = useState("0");
|
||||
const [sigunValue, setSigunValue] = useState("0");
|
||||
const [sidoData, setSidoData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [sigunData, setSigunData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [facilityData, setFacilityData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [facilityValue, setFacilityValue] = useState("0");
|
||||
|
||||
const fetchSido = async () => {
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_ctpv&outputFormat=application/json&propertyName=mctpv_korn_nm,mctpv_cd`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.mctpv_korn_nm,
|
||||
val: f.properties.mctpv_cd,
|
||||
}));
|
||||
setSidoData([{ text: "선택", val: "0" }, ...list]);
|
||||
};
|
||||
|
||||
const fetchSigun = async () => {
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_sgg&outputFormat=application/json&propertyName=sgg_cd,sgg_korn_nm`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.sgg_korn_nm,
|
||||
val: f.properties.sgg_cd,
|
||||
}));
|
||||
setSigunData(list);
|
||||
};
|
||||
|
||||
const fetchFacilitiesBySggNm = async (sggNm: string) => {
|
||||
const encodedSggNm = encodeURIComponent(sggNm);
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_il_swage_prcs_fclt&outputFormat=application/json&cql_filter=sgg_nm='${encodedSggNm}'`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.swtrmt_fclt_nm ?? "(이름없음)",
|
||||
val: f.id, // f.properties.swtrmt_fclt_cd 도 가능
|
||||
}));
|
||||
setFacilityData([{ text: "선택", val: "0" }, ...list]);
|
||||
setFacilityValue("0");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSido();
|
||||
fetchSigun();
|
||||
}, []);
|
||||
|
||||
const initializeSigunData = useCallback(
|
||||
(sidoCd: string) => {
|
||||
if (sidoCd === "0") {
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }]);
|
||||
setSigunValue("0");
|
||||
return;
|
||||
}
|
||||
const prefix = sidoCd.substring(0, 2);
|
||||
const filtered = sigunData.filter((item) => item.val.startsWith(prefix));
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }, ...filtered]);
|
||||
setSigunValue("0");
|
||||
},
|
||||
[sigunData]
|
||||
);
|
||||
|
||||
const onSelectValue = useCallback(
|
||||
(val: string, type: string) => {
|
||||
if (type === "ctpvCd") {
|
||||
setSidoValue(val);
|
||||
initializeSigunData(val);
|
||||
setFacilityData([{ text: "선택", val: "0" }]);
|
||||
} else if (type === "sggCd") {
|
||||
setSigunValue(val);
|
||||
const sggNm = sigunData.find((item) => item.val === val)?.text || "";
|
||||
if (sggNm) fetchFacilitiesBySggNm(sggNm);
|
||||
} else if (type === "fcltCd") {
|
||||
setFacilityValue(val);
|
||||
}
|
||||
},
|
||||
[initializeSigunData, sigunData]
|
||||
);
|
||||
|
||||
const handleSearch = () => {
|
||||
if (!gisMaps.current || facilityValue === "0") return;
|
||||
|
||||
const layers = gisMaps.current.getLayers().getArray();
|
||||
for (const layer of layers) {
|
||||
const source = layer.getSource?.();
|
||||
const feature = source?.getFeatureById?.(facilityValue);
|
||||
if (feature) {
|
||||
onFeatureSelect(feature);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alert("해당 처리시설을 지도에서 찾을 수 없습니다.");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-4">
|
||||
<div className="card card-border flex">
|
||||
<div className="col-span-10 flex flex-row">
|
||||
<SelectBox
|
||||
labelClass="w-[40px]"
|
||||
className="mr-10 w-[200px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시도"
|
||||
selectArray={sidoData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[65px]"
|
||||
className="mr-10 w-[200px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시군구"
|
||||
selectArray={filteredSigunData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[140px]"
|
||||
className="mr-3 w-[250px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="하수처리시설"
|
||||
selectArray={facilityData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "fcltCd")}
|
||||
value={facilityValue}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button
|
||||
color="primary"
|
||||
className="mx-2 mt-1 ml-10"
|
||||
size={"sm"}
|
||||
onClick={handleSearch}
|
||||
>
|
||||
조회
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 923 B |
@@ -2,8 +2,7 @@ export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "FO";
|
||||
export const PORT = process.env.NEXT_PUBLIC_PORT || "3000";
|
||||
export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`;
|
||||
export const SERVER_API_WR_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL ||'http://con.mind-one.co.kr:11080/api/v1';
|
||||
//export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1';
|
||||
export const SERVER_API_URL = process.env.NEXT_PUBLIC_CS_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1';
|
||||
export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1';
|
||||
export const SERVER_API_CS_URL = process.env.NEXT_PUBLIC_CS_SERVER_API_URL||'http://dev.onasys.co.kr:8000';
|
||||
export const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization";
|
||||
export const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN || "access-token";
|
||||
|
||||
|
After Width: | Height: | Size: 919 B |
|
After Width: | Height: | Size: 810 B |
|
After Width: | Height: | Size: 896 B |
|
After Width: | Height: | Size: 837 B |
|
After Width: | Height: | Size: 790 B |
|
After Width: | Height: | Size: 855 B |
|
After Width: | Height: | Size: 923 B |
@@ -10,6 +10,8 @@ NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { mapService } from "../service";
|
||||
import NewWindowPopup from "../newWindowPopup";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
import { SearchBar } from "../searchBar/searchBar";
|
||||
|
||||
const Map = () => {
|
||||
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
|
||||
@@ -61,24 +62,28 @@ const Map = () => {
|
||||
|
||||
// 2. data가 OpenLayers Feature 인스턴스인지 확인
|
||||
let foundFeature: any = null;
|
||||
console.log(data)
|
||||
console.log(data);
|
||||
//console.log(data.getId())
|
||||
let featureId = data.fid;
|
||||
const layerName = data.layerNm;
|
||||
|
||||
|
||||
if (layerName === "재이용시설") {
|
||||
// 재이용시설
|
||||
featureId = `${layerName}.${data.searchPk}`;
|
||||
} else if ( //빗물이용시설, 중수도시설
|
||||
} else if (
|
||||
//빗물이용시설, 중수도시설
|
||||
layerName === "빗물이용시설" ||
|
||||
layerName == "tb_wr_rswtr_fclt"
|
||||
) {
|
||||
featureId = `${layerName}.${data.bdstInfoSn}`;
|
||||
} else if (layerName === "tb_il_swage_prcs_fclt") { //하수처리시설
|
||||
} else if (layerName === "tb_il_swage_prcs_fclt") {
|
||||
//하수처리시설
|
||||
featureId = `${layerName}.${data.bscFctCd}`;
|
||||
} else if (layerName === "tb_wr_htdfl_fclt") { //온배수시설
|
||||
} else if (layerName === "tb_wr_htdfl_fclt") {
|
||||
//온배수시설
|
||||
featureId = `${layerName}.${data.bdstRuseFcltSn}`;
|
||||
} else if (layerName == "tb_wr_ruse_fclt_dmd_co") { //수요처
|
||||
} else if (layerName == "tb_wr_ruse_fclt_dmd_co") {
|
||||
//수요처
|
||||
featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`;
|
||||
} else {
|
||||
console.warn("해당 레이어에 대한 featureId 매핑 없음", layerName);
|
||||
@@ -120,17 +125,6 @@ const Map = () => {
|
||||
setIsSearchModalOpen(false);
|
||||
};
|
||||
|
||||
// MapSearchBar 래퍼 컴포넌트
|
||||
// const CustomMapSearchBar = (props: any) => {
|
||||
// return (
|
||||
// <MapSearchBar
|
||||
// {...props}
|
||||
// searchType="공급처 후보지 조회"
|
||||
// showApplyButton={false}
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
@@ -156,7 +150,24 @@ const Map = () => {
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={handleCheckedLayersChange}
|
||||
imgVisble={true}
|
||||
searchBar={undefined}
|
||||
searchBar={(props: any) => {
|
||||
const facilityTypes = [
|
||||
{ text: "하수처리시설", val: "tb_il_swage_prcs_fclt" },
|
||||
{ text: "하수재이용시설", val: "tb_wr_bdst_ruse_fclt" },
|
||||
{ text: "빗물이용시설", val: "tb_wr_rnwtr_utztn_fclt" },
|
||||
{ text: "중수도시설", val: "tb_wr_rswtr_fclt" },
|
||||
{ text: "온배수시설", val: "tb_wr_htdfl_fclt" },
|
||||
{ text: "수요처", val: "tb_wr_ruse_fclt_dmd_co" },
|
||||
{ text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" },
|
||||
];
|
||||
return (
|
||||
<SearchBar
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
gisMaps={gisMaps}
|
||||
facilityTypes={facilityTypes}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
"use client";
|
||||
import { SelectBox, type SelectDataType } from "@common/business_components/ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { SERVER_API_WR_URL } from "@/src/shared/config";
|
||||
|
||||
const geoserverUrl =
|
||||
"/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&outputFormat=application/json";
|
||||
|
||||
const FACILITY_TYPE_URL_MAP: Record<string, string> = {
|
||||
tb_il_swage_prcs_fclt: geoserverUrl, //하수처리시설
|
||||
tb_wr_bdst_ruse_fclt:
|
||||
"/reuse-service/reuseFc/pubDetailList/{year}/{sggCd}/1/1000", //재이용시설 post
|
||||
tb_wr_rnwtr_utztn_fclt: "/reuse-service/reuseFc/rainyInfoList/1/1000/{sggCd}", //빗물이용시설 get
|
||||
tb_wr_rswtr_fclt: "/reuse-service/reuseFc/waterreuseFc/1/1000/{sggCd}", //중수도시설 get
|
||||
tb_wr_htdfl_fclt: "/reuse-service/drain/facilityList/1/1000/{sggCd}", //온배수시설 get
|
||||
tb_wr_ruse_fclt_dmd_co: "/reuse-service/reuseFc/request/1/1000/{sggCd}", //수요처 get
|
||||
tb_wr_tmpr_dmd_co:
|
||||
"/reuse-service/reuseFc/request/temp/1/1000/{sidoNm} {sggNm}", //잠재수요처 get
|
||||
};
|
||||
|
||||
interface SearchBarProps {
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
gisMaps: React.RefObject<any>;
|
||||
facilityTypes: { text: string; val: string }[];
|
||||
}
|
||||
|
||||
export function SearchBar({
|
||||
onFeatureSelect,
|
||||
gisMaps,
|
||||
facilityTypes,
|
||||
}: SearchBarProps) {
|
||||
const [sidoValue, setSidoValue] = useState("0");
|
||||
const [sigunValue, setSigunValue] = useState("0");
|
||||
const [sidoData, setSidoData] = useState<SelectDataType[]>([
|
||||
{ text: "선택", val: "0" },
|
||||
]);
|
||||
const [sigunData, setSigunData] = useState<SelectDataType[]>([
|
||||
{ text: "선택", val: "0" },
|
||||
]);
|
||||
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([
|
||||
{ text: "선택", val: "0" },
|
||||
]);
|
||||
|
||||
const [facilityData, setFacilityData] = useState<SelectDataType[]>([
|
||||
{ text: "선택", val: "0" },
|
||||
]);
|
||||
const [facilityValue, setFacilityValue] = useState("0");
|
||||
|
||||
const [facilityType, setFacilityType] = useState(
|
||||
facilityTypes?.length === 1 && facilityTypes[0] ? facilityTypes[0].val : "0"
|
||||
);
|
||||
|
||||
//시도, 시군구 조회
|
||||
const fetchSido = async () => {
|
||||
const url = `/cms-service/api/v1/code/cache/ADSTDSTRCD/Y`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
const sidoList = json
|
||||
.filter(
|
||||
(item: any) =>
|
||||
typeof item.lwpoCd === "string" && item.lwpoCd.length === 2
|
||||
)
|
||||
.map((feature: any) => ({
|
||||
val: feature.lwpoCd,
|
||||
text: feature.lwpoCdNm,
|
||||
}));
|
||||
setSidoData([{ text: "선택", val: "0" }, ...sidoList]);
|
||||
|
||||
const sigunList = json
|
||||
.filter(
|
||||
(item: any) =>
|
||||
typeof item.lwpoCd === "string" && item.lwpoCd.length !== 2
|
||||
)
|
||||
.map((feature: any) => ({
|
||||
val: feature.lwpoCd,
|
||||
text: feature.lwpoCdNm,
|
||||
}));
|
||||
setSigunData(sigunList);
|
||||
};
|
||||
|
||||
//시설명 콤보박스 조회
|
||||
const fetchFacilitiesBySggNm = async (
|
||||
sggCd: string,
|
||||
sggNm: string,
|
||||
sidoNm: string,
|
||||
currentFacilityType?: string
|
||||
) => {
|
||||
const type = currentFacilityType ?? facilityType;
|
||||
if (!type || type === "0") return;
|
||||
|
||||
const baseUrlTemplate = FACILITY_TYPE_URL_MAP[type];
|
||||
if (!baseUrlTemplate) return;
|
||||
|
||||
const year = new Date().getFullYear().toString();
|
||||
let url = baseUrlTemplate
|
||||
.replace("{year}", year)
|
||||
.replace("{sggCd}", sggCd)
|
||||
.replace("{sidoNm}", encodeURIComponent(sidoNm))
|
||||
.replace("{sggNm}", encodeURIComponent(sggNm));
|
||||
|
||||
let list: SelectDataType[] = [];
|
||||
|
||||
if (
|
||||
type === "tb_il_swage_prcs_fclt" //하수처리시설
|
||||
) {
|
||||
const cqlFilter = `ctpv_nm='${encodeURIComponent(sidoNm)}' AND sgg_nm='${encodeURIComponent(sggNm)}'`;
|
||||
url = `${url}&typeName=hasudo:${type}&cql_filter=${cqlFilter}`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = json.features.map((f: any) => ({
|
||||
text: f.properties.swtrmt_fclt_nm ?? "(이름없음)",
|
||||
val: f.id,
|
||||
}));
|
||||
} else if (
|
||||
type === "tb_wr_bdst_ruse_fclt" // 재이용시설
|
||||
) {
|
||||
//재용에서 참조하는 하수처리시설 목록
|
||||
const paramsRes = await fetch("/reuse-service/reuseFc/pubFc");
|
||||
const paramsJson = await paramsRes.json();
|
||||
const params = paramsJson.data;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.ruseUtztnFcltNm ?? "(이름없음)",
|
||||
val: item.pbnstRuseFcltId,
|
||||
}));
|
||||
} else if (
|
||||
type === "tb_wr_ruse_fclt_dmd_co" //수요처
|
||||
) {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.dmdCoNm ?? "(이름없음)",
|
||||
val: item.dmdInfoId,
|
||||
}));
|
||||
} else if (
|
||||
type === "tb_wr_tmpr_dmd_co" //잠재수요처
|
||||
) {
|
||||
// GET 방식
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.dmdCoNm ?? "(이름없음)",
|
||||
val: item.fctryMngSn,
|
||||
}));
|
||||
} else {
|
||||
// GET 방식
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.bdstNm ?? "(이름없음)",
|
||||
val: item.bdstInfoSn,
|
||||
}));
|
||||
}
|
||||
|
||||
setFacilityData([{ text: "선택", val: "0" }, ...list]);
|
||||
setFacilityValue("0");
|
||||
};
|
||||
|
||||
//시도, 시군구 초기 데이터 조회
|
||||
useEffect(() => {
|
||||
fetchSido();
|
||||
}, []);
|
||||
|
||||
//시군구 데이터 초기화
|
||||
const initializeSigunData = useCallback(
|
||||
(sidoCd: string) => {
|
||||
if (sidoCd === "0") {
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }]);
|
||||
setSigunValue("0");
|
||||
return;
|
||||
}
|
||||
const prefix = sidoCd.substring(0, 2);
|
||||
const filtered = sigunData.filter((item) => item.val.startsWith(prefix));
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }, ...filtered]);
|
||||
setSigunValue("0");
|
||||
},
|
||||
[sigunData]
|
||||
);
|
||||
|
||||
//콤보박스 선택시 연관 콤보박스 처리
|
||||
const onSelectValue = useCallback(
|
||||
(val: string, type: string) => {
|
||||
if (type === "ctpvCd") {
|
||||
setSidoValue(val);
|
||||
initializeSigunData(val);
|
||||
setFacilityData([{ text: "선택", val: "0" }]);
|
||||
setFacilityValue("0");
|
||||
} else if (type === "sggCd") {
|
||||
setSigunValue(val);
|
||||
const sidoNm =
|
||||
sidoData.find((item) => item.val === sidoValue)?.text || "";
|
||||
const sggNm = sigunData.find((item) => item.val === val)?.text || "";
|
||||
const sggCd = sigunData.find((item) => item.val === val)?.val || "";
|
||||
if (sggNm && sidoNm)
|
||||
fetchFacilitiesBySggNm(sggCd, sggNm, sidoNm, facilityType);
|
||||
} else if (type === "facilityType") {
|
||||
setFacilityType(val);
|
||||
setFacilityValue("0");
|
||||
setFacilityData([{ text: "선택", val: "0" }]);
|
||||
const sidoNm =
|
||||
sidoData.find((item) => item.val === sidoValue)?.text || "";
|
||||
const sggNm =
|
||||
sigunData.find((item) => item.val === sigunValue)?.text || "";
|
||||
const sggCd =
|
||||
sigunData.find((item) => item.val === sigunValue)?.val || "";
|
||||
if (sggNm && sidoNm) fetchFacilitiesBySggNm(sggCd, sggNm, sidoNm, val);
|
||||
} else if (type === "fcltCd") {
|
||||
setFacilityValue(val);
|
||||
}
|
||||
},
|
||||
[initializeSigunData, sigunData, sigunValue, sidoData, sidoValue]
|
||||
);
|
||||
|
||||
//조회 버튼 클릭
|
||||
const handleSearch = () => {
|
||||
if (!gisMaps.current || facilityValue === "0") return;
|
||||
const layers = gisMaps.current.getLayers().getArray();
|
||||
for (const layer of layers) {
|
||||
const source = layer.getSource?.();
|
||||
const feature = source?.getFeatureById?.(facilityValue);
|
||||
if (feature) {
|
||||
onFeatureSelect(feature);
|
||||
return;
|
||||
}
|
||||
}
|
||||
alert("해당 처리시설을 지도에서 찾을 수 없습니다.");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-4">
|
||||
<div className="card card-border flex">
|
||||
<div className="col-span-10 flex flex-row">
|
||||
{facilityTypes.length > 1 && (
|
||||
<SelectBox
|
||||
labelClass="w-[120px]"
|
||||
className="mr-10 w-[190px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시설물유형"
|
||||
selectArray={[{ text: "선택", val: "0" }, ...facilityTypes]}
|
||||
onSelectValue={(val) =>
|
||||
onSelectValue(val.toString(), "facilityType")
|
||||
}
|
||||
value={facilityType}
|
||||
/>
|
||||
)}
|
||||
<SelectBox
|
||||
labelClass="w-[40px]"
|
||||
className="mr-10 w-[150px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시도"
|
||||
selectArray={sidoData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[65px]"
|
||||
className="mr-10 w-[150px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시군구"
|
||||
selectArray={filteredSigunData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[60px]"
|
||||
className="mr-3 w-[180px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시설명"
|
||||
selectArray={facilityData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "fcltCd")}
|
||||
value={facilityValue}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button
|
||||
color="primary"
|
||||
className="mx-2 mt-1 ml-5"
|
||||
size={"sm"}
|
||||
onClick={handleSearch}
|
||||
>
|
||||
조회
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -17,35 +17,45 @@ import {
|
||||
UiModal,
|
||||
UiTable,
|
||||
type SelectDataType,
|
||||
type SheetParameter,
|
||||
} from "@common/business_components/ui";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { Button, Label } from "@common/components/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import toast, { Toaster } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function RuseCaseMng() {
|
||||
const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state);
|
||||
const { ruseCaseMngList, status, count } = ruseCaseMng;
|
||||
const { code, ruseCaseMng, selectRuseCaseMng, ruseCaseMngInit } = useWrStore((state) => state);
|
||||
const { ruseCaseMngList, status, count, excelList } = ruseCaseMng;
|
||||
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([]);
|
||||
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
|
||||
|
||||
|
||||
const [sidoCd, setSidoCd] = useState<string>("");
|
||||
const [sigunguCd, setSigunguCd] = useState<string>("");
|
||||
const [selectUtz, setSelectUtz] = useState<string[] | []>(["조경용수"]);
|
||||
// const [selectUtz, setSelectUtz] = useState<string[] | []>(["조경용수"]);
|
||||
const [selectUtz, setSelectUtz] = useState<string>("");
|
||||
const [name, setName] = useState<string>("");
|
||||
const [resetPage, isResetPage] = useState(false);
|
||||
const { t } = useTranslation('base');
|
||||
|
||||
// 물재이용사례 조회
|
||||
const searchRuseCaseMng = async (pageNo: number, pageSize: number) => {
|
||||
const searchRuseCaseMng = async (isExcel: boolean, pageNo: number, pageSize: number) => {
|
||||
await selectRuseCaseMng(
|
||||
isExcel,
|
||||
pageNo,
|
||||
pageSize,
|
||||
sidoCd ?? "",
|
||||
sigunguCd ?? "",
|
||||
selectUtz[0] ?? "",
|
||||
sidoCd ?? "",
|
||||
sigunguCd ?? "",
|
||||
selectUtz ?? "",
|
||||
name ?? ""
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
ruseCaseMngInit();
|
||||
}, [])
|
||||
|
||||
// 시도 선택
|
||||
const getSelCity = (val: string | number) => {
|
||||
const cityValue = String(val);
|
||||
@@ -69,91 +79,97 @@ export default function RuseCaseMng() {
|
||||
isModal: boolean;
|
||||
}>({ isModal: false });
|
||||
|
||||
// 다운로드
|
||||
const download = () => {
|
||||
searchRuseCaseMng(1, 10000);;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (excelList !== undefined && excelList.length === 0) {
|
||||
toast.error(t('excelDownErrMsg'));
|
||||
return;
|
||||
}
|
||||
fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => {
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
// 받은 파일 바인너리 처리
|
||||
const blobRet = await response.arrayBuffer();
|
||||
|
||||
const excelData1 = ruseCaseMngList.map((item) => {
|
||||
return [
|
||||
item.ruseBizNm,
|
||||
item.bizBgngDate,
|
||||
item.bizEndDate,
|
||||
item.splyFcltKnd,
|
||||
item.splyFcltNm,
|
||||
item.splyFcltCpct,
|
||||
item.dmdFcltNm,
|
||||
item.dmdFcltAddr,
|
||||
item.ruseFcltCpct,
|
||||
item.ruseFcltPplLen,
|
||||
item.ruseFcltUsg,
|
||||
item.ruseFcltSplyCpct,
|
||||
item.ruseFcltPrcsMsrc,
|
||||
item.grnteWqUsg,
|
||||
item.grnteWqGrssClfg,
|
||||
item.grnteWqCombResdch,
|
||||
item.grnteWqTrbt,
|
||||
item.grnteWqBod,
|
||||
item.grnteWqSmll,
|
||||
item.grnteWqCrmty,
|
||||
item.grnteWqTn,
|
||||
item.grnteWqTp,
|
||||
item.grnteWqPh,
|
||||
item.grnteWqClIonQnt,
|
||||
item.grnteWqCndctStat,
|
||||
item.grnteWqRmrk,
|
||||
item.reqBizGrssCst,
|
||||
item.reqBizReuseFcltCst,
|
||||
item.reqBizPplCst,
|
||||
item.reqBizArchCst,
|
||||
item.reqBizMchnCst,
|
||||
item.reqBizMtrngCst,
|
||||
item.reqBizGisCst,
|
||||
item.reqBizCctvCst,
|
||||
item.reqBizCmcnCst,
|
||||
item.reqBizWstCst,
|
||||
item.reqBizDesignCst,
|
||||
item.reqBizSpvsCst,
|
||||
item.reqBizSbsdCst,
|
||||
item.reqBizEtc,
|
||||
item.prcmPlanGrssCst,
|
||||
item.prcmPlanNlep,
|
||||
item.prcmPlanLcep,
|
||||
item.prcmPlanBrdnCst,
|
||||
item.prcmPlanEcqrRt,
|
||||
item.clclnGrssCst,
|
||||
item.clclnNlep,
|
||||
item.clclnLcep,
|
||||
item.clclnBrdnCst,
|
||||
item.clclnEcqrRt,
|
||||
];
|
||||
});
|
||||
|
||||
const sheetParameter = [
|
||||
{
|
||||
data: excelData1,
|
||||
headerNum: 2,
|
||||
headerRange: 'A1:AX1',
|
||||
sheetName: '물재이용사례',
|
||||
startCol: 'A'
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
// 받은 파일 바인너리 처리
|
||||
const blobRet = await response.arrayBuffer();
|
||||
if(excelList){
|
||||
const excelData1 = excelList.map((item) => {
|
||||
return [
|
||||
item.ruseBizNm,
|
||||
item.bizBgngDate,
|
||||
item.bizEndDate,
|
||||
item.splyFcltKnd,
|
||||
item.splyFcltNm,
|
||||
item.splyFcltCpct,
|
||||
item.dmdFcltNm,
|
||||
item.dmdFcltAddr,
|
||||
item.ruseFcltCpct,
|
||||
item.ruseFcltPplLen,
|
||||
item.ruseFcltUsg,
|
||||
item.ruseFcltSplyCpct,
|
||||
item.ruseFcltPrcsMsrc,
|
||||
item.grnteWqUsg,
|
||||
item.grnteWqGrssClfg,
|
||||
item.grnteWqCombResdch,
|
||||
item.grnteWqTrbt,
|
||||
item.grnteWqBod,
|
||||
item.grnteWqSmll,
|
||||
item.grnteWqCrmty,
|
||||
item.grnteWqTn,
|
||||
item.grnteWqTp,
|
||||
item.grnteWqPh,
|
||||
item.grnteWqClIonQnt,
|
||||
item.grnteWqCndctStat,
|
||||
item.grnteWqRmrk,
|
||||
item.reqBizGrssCst,
|
||||
item.reqBizReuseFcltCst,
|
||||
item.reqBizPplCst,
|
||||
item.reqBizArchCst,
|
||||
item.reqBizMchnCst,
|
||||
item.reqBizMtrngCst,
|
||||
item.reqBizGisCst,
|
||||
item.reqBizCctvCst,
|
||||
item.reqBizCmcnCst,
|
||||
item.reqBizWstCst,
|
||||
item.reqBizDesignCst,
|
||||
item.reqBizSpvsCst,
|
||||
item.reqBizSbsdCst,
|
||||
item.reqBizEtc,
|
||||
item.prcmPlanGrssCst,
|
||||
item.prcmPlanNlep,
|
||||
item.prcmPlanLcep,
|
||||
item.prcmPlanBrdnCst,
|
||||
item.prcmPlanEcqrRt,
|
||||
item.clclnGrssCst,
|
||||
item.clclnNlep,
|
||||
item.clclnLcep,
|
||||
item.clclnBrdnCst,
|
||||
item.clclnEcqrRt,
|
||||
];
|
||||
});
|
||||
|
||||
const sheetParameter: SheetParameter[] = [
|
||||
{
|
||||
data: excelData1,
|
||||
headerNum: 2,
|
||||
headerRange: 'A1:AX1',
|
||||
sheetName: '물재이용사례',
|
||||
startCol: 'A'
|
||||
}
|
||||
];
|
||||
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
|
||||
await filePickedBlobArray(blobRet, '물재이용사례현황', sheetParameter);
|
||||
}
|
||||
];
|
||||
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
|
||||
await filePickedBlobArray(blobRet, '물재이용사례현황', sheetParameter);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}, [excelList])
|
||||
|
||||
// 양식받기
|
||||
const sampleDownLoad = () => {
|
||||
excelDownload(
|
||||
[[]],
|
||||
"물재이용사례 관리",
|
||||
"물재이용사례 관리 등록양식",
|
||||
"sample_ruseCaseMng.xlsx",
|
||||
"A1:AX1",
|
||||
2
|
||||
@@ -161,7 +177,7 @@ export default function RuseCaseMng() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if(status === 200){
|
||||
if (status === 200) {
|
||||
setIsExcelOpen({ isModal: false });
|
||||
}
|
||||
}, [status]);
|
||||
@@ -174,7 +190,7 @@ export default function RuseCaseMng() {
|
||||
<Title title={"물재이용사례 관리"} />
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className="col-span-8 flex flex-row">
|
||||
<div className="col-span-8 flex flex-row items-center">
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/3 h-full text-2xl"
|
||||
label="시도"
|
||||
@@ -183,24 +199,28 @@ export default function RuseCaseMng() {
|
||||
onSelectValue={getSelCity}
|
||||
/>
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/3 h-full text-2xl"
|
||||
className="col-span-2 w-1/3 h-full text-2xl ml-4"
|
||||
label="시군구"
|
||||
selectClass={""}
|
||||
selectArray={selectSgg || []}
|
||||
onSelectValue={getSgg}
|
||||
/>
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/3 h-full text-2xl"
|
||||
className="col-span-2 w-1/3 h-full text-2xl ml-4"
|
||||
label="용도"
|
||||
selectClass={""}
|
||||
selectArray={ruseUtzType}
|
||||
onSelectValue={(val) => {
|
||||
if (val) setSelectUtz([String(val)]);
|
||||
if (val) setSelectUtz(String(val));
|
||||
}}
|
||||
value={selectUtz || ruseUtzType[0]?.val}
|
||||
/>
|
||||
<Label className="w-28 text-xl ml-4 mr-6">
|
||||
{"수요처명"}
|
||||
</Label>
|
||||
<SearchInput
|
||||
className={"text-2xl"}
|
||||
label="수요처명"
|
||||
className={"col-span-2 w-1/3 text-2xl"}
|
||||
label=""
|
||||
textValue={name}
|
||||
setText={(e) => {
|
||||
setName(e);
|
||||
@@ -211,13 +231,29 @@ export default function RuseCaseMng() {
|
||||
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2">
|
||||
<div className="space-y">
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button size={"sm"} onClick={() => { searchRuseCaseMng(1, 20) }}>
|
||||
<Button size={"sm"} onClick={() => {
|
||||
searchRuseCaseMng(false, 1, 10);
|
||||
isResetPage(true);
|
||||
}}>
|
||||
조회
|
||||
</Button>
|
||||
<Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}>
|
||||
엑셀등록
|
||||
</Button>
|
||||
<Button size={"sm"} onClick={() => { download() }}>
|
||||
<Button size={"sm"} onClick={() => {
|
||||
selectRuseCaseMng(
|
||||
true,
|
||||
1,
|
||||
count,
|
||||
sidoCd ?? "",
|
||||
sigunguCd ?? "",
|
||||
selectUtz ?? "",
|
||||
name ?? ""
|
||||
);
|
||||
// download()
|
||||
|
||||
|
||||
}} className={"green"}>
|
||||
다운로드
|
||||
</Button>
|
||||
<Button size={"sm"} onClick={() => { sampleDownLoad() }}>
|
||||
@@ -239,10 +275,12 @@ export default function RuseCaseMng() {
|
||||
headlist={ruseCaseMng.ruseHeadList}
|
||||
pageSize={20}
|
||||
total={count}
|
||||
cellClick={() => {}}
|
||||
cellClick={undefined}
|
||||
onPageChange={(pageNo: number) => {
|
||||
searchRuseCaseMng(pageNo, 10);
|
||||
searchRuseCaseMng(false, pageNo, 10);
|
||||
isResetPage(false)
|
||||
}}
|
||||
resetPageNo={resetPage}
|
||||
/>
|
||||
</div>
|
||||
</ListDiv>
|
||||
@@ -254,9 +292,9 @@ export default function RuseCaseMng() {
|
||||
<UiModal
|
||||
open={isExcelOpen.isModal}
|
||||
onOpenChange={() => {
|
||||
setIsExcelOpen({ isModal: !isExcelOpen.isModal })
|
||||
setIsExcelOpen({ isModal: !isExcelOpen.isModal })
|
||||
}}
|
||||
bodyClass={"w-[40vw] max-w-none"}
|
||||
bodyClass={"w-[35vw] max-w-none"}
|
||||
body={<RuseCaseMngExcel />}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { SelectDataType } from "@common/business_components/ui";
|
||||
import type { TableUpperProps } from "../../common/type/header";
|
||||
|
||||
//★☆★☆ 여기서부터 수정하기 ★☆★☆
|
||||
export const ruseCaseHeadListExport: TableUpperProps[] = [
|
||||
{
|
||||
id: "ruseBizNm",
|
||||
@@ -358,6 +357,7 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
|
||||
];
|
||||
|
||||
export const ruseUtzType: SelectDataType[] = [
|
||||
{ text: "전체", val: "0" },
|
||||
{ text: "조경용수", val: "조경용수" },
|
||||
{ text: "청소화장실용수", val: "청소화장실용수" },
|
||||
{ text: "세척살수용수", val: "세척살수용수" },
|
||||
|
||||
@@ -4,90 +4,93 @@ import type { TableUpperProps } from "../../common/type/header";
|
||||
import { ruseCaseHeadListExport } from "./headList";
|
||||
export type ruseCaseState = {
|
||||
ruseCaseMng: {
|
||||
ruseHeadList:TableUpperProps[];
|
||||
ruseCaseMngList:ruseCaseMngExcelProps[];
|
||||
ruseHeadList: TableUpperProps[];
|
||||
ruseCaseMngList: ruseCaseMngExcelProps[];
|
||||
status: number;
|
||||
count: number;
|
||||
excelList?: ruseCaseMngExcelProps[];
|
||||
};
|
||||
};
|
||||
|
||||
export interface ruseCaseMngExcelProps {
|
||||
ruseBizNm: string,
|
||||
bizBgngDate: string,
|
||||
bizEndDate: string,
|
||||
splyFcltKnd: string,
|
||||
splyFcltNm: string,
|
||||
splyFcltCpct: number | 0 |'',
|
||||
dmdFcltNm: string,
|
||||
dmdFcltAddr: string,
|
||||
ruseFcltCpct: number | 0 |'',
|
||||
ruseFcltPplLen: string,
|
||||
ruseFcltUsg: string,
|
||||
ruseFcltSplyCpct: number | 0 |'',
|
||||
ruseFcltPrcsMsrc: string,
|
||||
grnteWqUsg: string,
|
||||
grnteWqGrssClfg: number | 0 |'',
|
||||
grnteWqCombResdch: number | 0 |'',
|
||||
grnteWqTrbt: string,
|
||||
grnteWqBod: string,
|
||||
grnteWqSmll: string,
|
||||
grnteWqCrmty: string,
|
||||
grnteWqTn: string,
|
||||
grnteWqTp: string,
|
||||
grnteWqPh: string,
|
||||
grnteWqClIonQnt: string,
|
||||
grnteWqCndctStat: string,
|
||||
grnteWqRmrk: string,
|
||||
reqBizReuseFcltCst: number | 0 |'',
|
||||
reqBizPplCst: number | 0 |'',
|
||||
reqBizArchCst: number | 0 |'',
|
||||
reqBizMchnCst: number | 0 |'',
|
||||
reqBizMtrngCst: number | 0 |'',
|
||||
reqBizGisCst: number | 0 |'',
|
||||
reqBizCctvCst: number | 0 |'',
|
||||
reqBizCmcnCst: number | 0 |'',
|
||||
reqBizWstCst: number | 0 |'',
|
||||
reqBizDesignCst: number | 0 |'',
|
||||
reqBizSpvsCst: number | 0 |'',
|
||||
reqBizSbsdCst: number | 0 |'',
|
||||
reqBizEtc: string,
|
||||
reqBizGrssCst: number | 0 |'',
|
||||
prcmPlanNlep: number | 0 |'',
|
||||
prcmPlanLcep: number | 0 |'',
|
||||
prcmPlanBrdnCst: number | 0 |'',
|
||||
prcmPlanEcqrRt: number | 0 |'',
|
||||
prcmPlanGrssCst: number | 0 |'',
|
||||
clclnNlep: number | 0 |'',
|
||||
clclnLcep: number | 0 |'',
|
||||
clclnBrdnCst: number | 0 |'',
|
||||
clclnEcqrRt: number | 0 |'',
|
||||
clclnGrssCst: number | 0 |'',
|
||||
ruseBizNm: string,
|
||||
bizBgngDate: string,
|
||||
bizEndDate: string,
|
||||
splyFcltKnd: string,
|
||||
splyFcltNm: string,
|
||||
splyFcltCpct: number | 0 | '',
|
||||
dmdFcltNm: string,
|
||||
dmdFcltAddr: string,
|
||||
ruseFcltCpct: number | 0 | '',
|
||||
ruseFcltPplLen: string,
|
||||
ruseFcltUsg: string,
|
||||
ruseFcltSplyCpct: number | 0 | '',
|
||||
ruseFcltPrcsMsrc: string,
|
||||
grnteWqUsg: string,
|
||||
grnteWqGrssClfg: number | 0 | '',
|
||||
grnteWqCombResdch: number | 0 | '',
|
||||
grnteWqTrbt: string,
|
||||
grnteWqBod: string,
|
||||
grnteWqSmll: string,
|
||||
grnteWqCrmty: string,
|
||||
grnteWqTn: string,
|
||||
grnteWqTp: string,
|
||||
grnteWqPh: string,
|
||||
grnteWqClIonQnt: string,
|
||||
grnteWqCndctStat: string,
|
||||
grnteWqRmrk: string,
|
||||
reqBizReuseFcltCst: number | 0 | '',
|
||||
reqBizPplCst: number | 0 | '',
|
||||
reqBizArchCst: number | 0 | '',
|
||||
reqBizMchnCst: number | 0 | '',
|
||||
reqBizMtrngCst: number | 0 | '',
|
||||
reqBizGisCst: number | 0 | '',
|
||||
reqBizCctvCst: number | 0 | '',
|
||||
reqBizCmcnCst: number | 0 | '',
|
||||
reqBizWstCst: number | 0 | '',
|
||||
reqBizDesignCst: number | 0 | '',
|
||||
reqBizSpvsCst: number | 0 | '',
|
||||
reqBizSbsdCst: number | 0 | '',
|
||||
reqBizEtc: string,
|
||||
reqBizGrssCst: number | 0 | '',
|
||||
prcmPlanNlep: number | 0 | '',
|
||||
prcmPlanLcep: number | 0 | '',
|
||||
prcmPlanBrdnCst: number | 0 | '',
|
||||
prcmPlanEcqrRt: number | 0 | '',
|
||||
prcmPlanGrssCst: number | 0 | '',
|
||||
clclnNlep: number | 0 | '',
|
||||
clclnLcep: number | 0 | '',
|
||||
clclnBrdnCst: number | 0 | '',
|
||||
clclnEcqrRt: number | 0 | '',
|
||||
clclnGrssCst: number | 0 | '',
|
||||
}
|
||||
|
||||
type ruseCaseActions = {
|
||||
selectRuseCaseMng: (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>;
|
||||
ruseCaseMngInit: () => void;
|
||||
selectRuseCaseMng: (isExcel: boolean, pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd: string, name: string) => Promise<void>;
|
||||
insertRuseCaseMng: (data: any) => Promise<void>;
|
||||
};
|
||||
|
||||
export const fetchSearchRuseCaseMng = async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
|
||||
const tailUrl = '/'+pageNo+'/'+pageSize+ ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : '');
|
||||
const resp = await getFetchWithCount(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`);
|
||||
return {data:resp.data, count:resp.count};
|
||||
export const fetchSearchRuseCaseMng = async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd: string, name: string) => {
|
||||
const tailUrl = '/' + pageNo + '/' + pageSize + ((sigunguCd !== '전체' && sigunguCd !== '') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd ? `/${usageCd}` : '/0') + (name ? `/${name}` : '');
|
||||
const resp = await getFetchWithCount(fetchType.WR, `/reuseFc/pubFcRuseCase${tailUrl}`);
|
||||
return { data: resp.data, count: resp.count };
|
||||
};
|
||||
|
||||
export const fetchInsertRuseCaseMng = async (data: any) => {
|
||||
const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR);
|
||||
return {data:response.data, status:response.status};
|
||||
return { data: response.data, status: response.status };
|
||||
};
|
||||
|
||||
export type ruseCaseType = ruseCaseState & ruseCaseActions;
|
||||
|
||||
export const ruseCaseStateExport: ruseCaseState = {
|
||||
ruseCaseMng: {
|
||||
ruseHeadList:ruseCaseHeadListExport,
|
||||
ruseCaseMngList:[],
|
||||
ruseHeadList: ruseCaseHeadListExport,
|
||||
ruseCaseMngList: [],
|
||||
status: 0,
|
||||
count: 0,
|
||||
excelList: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -95,8 +98,8 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
|
||||
set: any
|
||||
) => {
|
||||
return {
|
||||
selectRuseCaseMng: async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
|
||||
const res = await fetchSearchRuseCaseMng(pageNo, pageSize, sidoCd, sigunguCd, usageCd, name);
|
||||
ruseCaseMngInit: async () => {
|
||||
const res = await getFetchWithCount(fetchType.WR, `/reuseFc/pubFcRuseCase/1/10/0/0`);
|
||||
set((state: ruseCaseState) => ({
|
||||
ruseCaseMng: {
|
||||
...state.ruseCaseMng,
|
||||
@@ -105,12 +108,35 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
|
||||
},
|
||||
}));
|
||||
},
|
||||
selectRuseCaseMng: async (isExcel: boolean, pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd: string, name: string) => {
|
||||
const res = await fetchSearchRuseCaseMng(pageNo, pageSize, sidoCd, sigunguCd, usageCd, name);
|
||||
if (isExcel) {
|
||||
set((state: ruseCaseState) => {
|
||||
return {
|
||||
...state,
|
||||
ruseCaseMng: {
|
||||
...state.ruseCaseMng,
|
||||
count: res.count,
|
||||
excelList: res.data
|
||||
},
|
||||
};
|
||||
});
|
||||
} else {
|
||||
set((state: ruseCaseState) => ({
|
||||
ruseCaseMng: {
|
||||
...state.ruseCaseMng,
|
||||
ruseCaseMngList: res.data,
|
||||
count: res.count,
|
||||
},
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
insertRuseCaseMng: async (data: any) => {
|
||||
const response = await fetchInsertRuseCaseMng(data);
|
||||
if(response.status === 200){
|
||||
if (response.status === 200) {
|
||||
toast.success("물재이용사례 등록이 완료되었습니다. ");
|
||||
}else{
|
||||
} else {
|
||||
toast.error("물재이용사례 등록에 실패하였습니다. ");
|
||||
}
|
||||
set((state: ruseCaseState) => ({
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
isInvalidNumber,
|
||||
parseRawValue,
|
||||
} from "@/components/lib/func/excelFunc";
|
||||
import toast from "react-hot-toast";
|
||||
export const useTypes = [
|
||||
"조경용수",
|
||||
"청소화장실용수",
|
||||
@@ -16,6 +17,18 @@ export const useTypes = [
|
||||
"기타",
|
||||
];
|
||||
|
||||
|
||||
export const chkMngType =[
|
||||
'위탁',
|
||||
'자체'
|
||||
]
|
||||
export const chkSpwtPositnType = [
|
||||
'유량계',
|
||||
'수위계',
|
||||
'펌프적산전력계',
|
||||
'기타'
|
||||
]
|
||||
|
||||
export type Buildingtype = {
|
||||
bdstInfoSn: number;
|
||||
ctpvCd: 1;
|
||||
@@ -143,43 +156,56 @@ export const excelToRainChkData = (excelData: any, year: string) => {
|
||||
})
|
||||
.filter((el: any) => el !== undefined);
|
||||
|
||||
console.log("rainChkgdData", rainChkgdData);
|
||||
if (rainChkgdData.length > 0) {
|
||||
rainChkgdData.forEach((rain: any) => {
|
||||
const defaultMessage = `[시설명: ${rain.bdstNm}, 점검일: ${rain.chckYmd}, 이용용도: ${rain.guideDetails[0]?.utztnUsgCd}] 항목에서 `;
|
||||
|
||||
if (rain.chckYmd && isInvalidDate(rain.chckYmd)) {
|
||||
issues.push(
|
||||
defaultMessage + `점검일: 날짜 형식 아님 (${rain.chckYmd})`
|
||||
defaultMessage + `점검일: 날짜 형식 아님 입력값 : (${rain.chckYmd})`
|
||||
);
|
||||
}
|
||||
if (rain.anlOperCst.length > 2 && isInvalidNumber(rain.anlOperCst)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
` 연간운영비(원/년): 숫자 형식 아님 (${rain.anlOperCst})`
|
||||
` 연간운영비(원/년): 숫자 형식 아님 /입력값:${rain.anlOperCst}`
|
||||
);
|
||||
}
|
||||
if (rain.oilqMeasMthd.length > 1 && !chkMngType.includes(rain.oilqMeasMthd)) {
|
||||
issues.push(
|
||||
defaultMessage + `유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rain.oilqMeasMthd}`
|
||||
);
|
||||
}
|
||||
if (rain.spwtPositn.length > 1 && !chkSpwtPositnType.includes(rain.spwtPositn)) {
|
||||
issues.push(
|
||||
defaultMessage + `측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rain.spwtPositn}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
rain.rainyDetails?.forEach((key) => {
|
||||
if (key.ph?.length > 1 && isInvalidNumber(key.ph)) {
|
||||
issues.push(defaultMessage + `PH: 숫자 형식 아님 (${key.ph})`);
|
||||
issues.push(defaultMessage + `PH: 숫자 형식 아님 /입력값:${key.ph}`);
|
||||
}
|
||||
if (key.trbt?.length > 1 && isInvalidNumber(key.trbt)) {
|
||||
issues.push(defaultMessage + `탁도: 숫자 형식 아님 (${key.trbt})`);
|
||||
issues.push(defaultMessage + `탁도: 숫자 형식 아님 /입력값:${key.trbt}`);
|
||||
}
|
||||
if (key.grssClfg?.length > 1 && isInvalidNumber(key.grssClfg)) {
|
||||
issues.push(
|
||||
defaultMessage + `총대장균군수: 숫자 형식 아님 (${key.grssClfg})`
|
||||
defaultMessage + `총대장균군수: 숫자 형식 아님 /입력값:${key.grssClfg}`
|
||||
);
|
||||
}
|
||||
if (key.levyAmt?.length > 1 && isInvalidNumber(key.levyAmt)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`과태료부과금액(천원): 숫자 형식 아님 (${key.levyAmt})`
|
||||
`과태료부과금액(천원): 숫자 형식 아님 /입력값:${key.levyAmt}`
|
||||
);
|
||||
}
|
||||
if (key.vltnDt?.length > 2 && isInvalidDate(key.vltnDt)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`과태료 부과일(위반일): 날짜 형식 아님 (${key.vltnDt})`
|
||||
`과태료 부과일(위반일): 날짜 형식 아님 /입력값:${key.vltnDt}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -188,17 +214,19 @@ export const excelToRainChkData = (excelData: any, year: string) => {
|
||||
if (isInvalidNumber(guide.splyUswtrUtztnQnt)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`대체_${mm}: 숫자 형식 아님 (${guide.splyUswtrUtztnQnt})`
|
||||
`대체_${mm}: 숫자 형식 아님 /입력값:${guide.splyUswtrUtztnQnt}`
|
||||
);
|
||||
}
|
||||
if (isInvalidNumber(guide.ruseFcltUtztnQnt)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`빗물_${mm}: 숫자 형식 아님 (${guide.ruseFcltUtztnQnt})`
|
||||
`빗물_${mm}: 숫자 형식 아님 /입력값:${guide.ruseFcltUtztnQnt}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}else{
|
||||
toast.error("데이터가 없습니다. 점검 데이터를 확인해주세요.");
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { SelectDataType } from "@common/business_components/ui";
|
||||
import { useTypes, type Buildingtype } from "../rainChkgdn/excel";
|
||||
import {
|
||||
chkMngType,
|
||||
chkSpwtPositnType,
|
||||
useTypes,
|
||||
type Buildingtype,
|
||||
} from "../rainChkgdn/excel";
|
||||
import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
|
||||
import type { rswtChkZodType } from ".";
|
||||
import {
|
||||
@@ -171,14 +176,14 @@ export const excelToRswtGuideData = (
|
||||
.filter((el: any) => el !== undefined);
|
||||
|
||||
if (rswtChkgdData.length > 0) {
|
||||
rswtExcelDataCheck(rswtChkgdData,issues)
|
||||
rswtExcelDataCheck(rswtChkgdData, issues);
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
return { data: [], issues };
|
||||
}
|
||||
|
||||
return { data:rswtChkgdData, issues: [] };
|
||||
return { data: rswtChkgdData, issues: [] };
|
||||
};
|
||||
|
||||
export const excelToRswtPolution = (excelData: any, issues: string[]) => {
|
||||
@@ -248,25 +253,25 @@ export const excelToRswtPolution = (excelData: any, issues: string[]) => {
|
||||
})
|
||||
.filter((el: any) => el !== undefined);
|
||||
if (rswtChkgdData.length > 0) {
|
||||
rswtExcelDataCheck(rswtChkgdData,issues)
|
||||
rswtExcelDataCheck(rswtChkgdData, issues);
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
return { data: [], issues };
|
||||
}
|
||||
|
||||
return { data:rswtChkgdData, issues: [] };
|
||||
return { data: rswtChkgdData, issues: [] };
|
||||
};
|
||||
|
||||
|
||||
const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
|
||||
const rswtExcelDataCheck = (
|
||||
rswtChkgdData: rswtChkZodType[],
|
||||
issues: string[]
|
||||
) => {
|
||||
rswtChkgdData.forEach((rswt: any) => {
|
||||
const defaultMessage = `*중수도 수질 [NO: ${rswt.bdstRuseFcltSn}, 점검일: ${rswt.chckYmd}, 이용용도: ${rswt.utztnUsgCd}] 항목에서 `;
|
||||
|
||||
if (rswt.chckYmd && isInvalidDate(rswt.chckYmd)) {
|
||||
issues.push(
|
||||
defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`
|
||||
);
|
||||
issues.push(defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`);
|
||||
}
|
||||
if (rswt.anlOperCst.length > 2 && isInvalidNumber(rswt.anlOperCst)) {
|
||||
issues.push(
|
||||
@@ -279,6 +284,25 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
|
||||
defaultMessage + ` 처리용량(m³): 숫자 형식 아님 (${rswt.prcsCpct})`
|
||||
);
|
||||
}
|
||||
if (
|
||||
rswt.oilqMeasMthd.length > 1 &&
|
||||
!chkMngType.includes(rswt.oilqMeasMthd)
|
||||
) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rswt.oilqMeasMthd}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
rswt.spwtPositn.length > 1 &&
|
||||
!chkSpwtPositnType.includes(rswt.spwtPositn)
|
||||
) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rswt.spwtPositn}`
|
||||
);
|
||||
}
|
||||
|
||||
if (rswt?.rswtrPollutions && rswt?.rswtrPollutions.length > 0) {
|
||||
rswt.rswtrPollutions.forEach((pol) => {
|
||||
if (isInvalidNumber(pol.grssClfgUswtr)) {
|
||||
@@ -301,8 +325,7 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
|
||||
}
|
||||
if (isInvalidNumber(pol.bod)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`${pol.node}분기_BOD: 숫자 형식 아님 (${pol.bod})`
|
||||
defaultMessage + `${pol.node}분기_BOD: 숫자 형식 아님 (${pol.bod})`
|
||||
);
|
||||
}
|
||||
if (isInvalidNumber(pol.smll)) {
|
||||
@@ -319,8 +342,7 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
|
||||
}
|
||||
if (isInvalidNumber(pol.tph)) {
|
||||
issues.push(
|
||||
defaultMessage +
|
||||
`${pol.node}분기_T-P: 숫자 형식 아님 (${pol.tph})`
|
||||
defaultMessage + `${pol.node}분기_T-P: 숫자 형식 아님 (${pol.tph})`
|
||||
);
|
||||
}
|
||||
if (isInvalidNumber(pol.tn)) {
|
||||
@@ -358,7 +380,5 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useWrStore } from "@/app/store";
|
||||
import { coRegExcel } from "@/app/store/matDmd/coSttn/headList";
|
||||
import type { ruseCaseMngExcelProps } from "@/app/store/matDmd/ruseCaseMng/index";
|
||||
import { reUseCaseNames } from "@/app/store/matDmd/ruseCaseMng/excel";
|
||||
import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui";
|
||||
import { Form, FormField } from "@common/business_components/ui/form";
|
||||
import { Button } from "@common/components/ui";
|
||||
import type { ruseCaseMngExcelProps } from "@/app/store/matDmd/ruseCaseMng/index";
|
||||
import { filePicked, Title } from "@common/business_components/ui";
|
||||
import { Form } from "@common/business_components/ui/form";
|
||||
import { Button, Input } from "@common/components/ui";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import type { z } from "zod";
|
||||
import { useWrStore } from "@/app/store";
|
||||
|
||||
export default function RuseCaseMngExcel() {
|
||||
const { insertRuseCaseMng } = useWrStore((state) => state);
|
||||
@@ -79,6 +79,7 @@ export default function RuseCaseMngExcel() {
|
||||
};
|
||||
|
||||
Object.keys(reUseCaseNames).forEach((key: string) => {
|
||||
console.log("reUseCaseNames", reUseCaseNames)
|
||||
if (item && reUseCaseNames[key] !== undefined) {
|
||||
const mappedKey = reUseCaseNames[key];
|
||||
if (mappedKey && object.hasOwnProperty(mappedKey)) {
|
||||
@@ -146,23 +147,16 @@ export default function RuseCaseMngExcel() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="file"
|
||||
render={() => (
|
||||
<FormUi label="">
|
||||
<FileUpload
|
||||
files={file ? [file] : []}
|
||||
setFiles={(files: File[] | null) => {
|
||||
console.log("files", files)
|
||||
if (files && files?.length === 1) setFiles(files[0]);
|
||||
}}
|
||||
fileTypes=""
|
||||
maxFiles={1}
|
||||
></FileUpload>
|
||||
</FormUi>
|
||||
)}
|
||||
/>
|
||||
<div className="space-y-4 p-4">
|
||||
<Input
|
||||
type="file"
|
||||
onChange={(e) => {
|
||||
const selectedFile = e.target.files?.[0];
|
||||
if (selectedFile) setFiles(selectedFile);
|
||||
}}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -8,6 +8,7 @@ const serverApiUrl = CS_URL;
|
||||
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
|
||||
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
|
||||
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||
const REUSE_API_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL || "";
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
const pathname = req.nextUrl.pathname;
|
||||
@@ -135,6 +136,15 @@ export function middleware(req: NextRequest) {
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/reuse-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${REUSE_API_URL}${pathname.replace("/reuse-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/reuse-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
@@ -146,6 +156,7 @@ export const config = {
|
||||
"/attach-service/:path*",
|
||||
"/geoserver/:path*",
|
||||
"/sewerage-service/:path*",
|
||||
"/reuse-service/:path*",
|
||||
"/"
|
||||
],
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 923 B |
@@ -1,323 +1,357 @@
|
||||
import { Search } from 'lucide-react';
|
||||
import OlMap from 'ol/Map';
|
||||
import View from 'ol/View';
|
||||
import { defaults as defaultControls, OverviewMap } from 'ol/control';
|
||||
import { defaults } from 'ol/interaction';
|
||||
import * as olProj from 'ol/proj';
|
||||
import { register } from 'ol/proj/proj4';
|
||||
import proj4 from 'proj4';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import GisMapToggle from './GisMapToggle';
|
||||
import GisSelect from './GisSelect';
|
||||
import GisToolBar from './GisToolBar';
|
||||
import GisTree from './GisTree';
|
||||
import './lib/css/gis.common.css';
|
||||
import GisApp from './lib/gis.core';
|
||||
import { Search } from "lucide-react";
|
||||
import OlMap from "ol/Map";
|
||||
import View from "ol/View";
|
||||
import { defaults as defaultControls, OverviewMap } from "ol/control";
|
||||
import { defaults } from "ol/interaction";
|
||||
import * as olProj from "ol/proj";
|
||||
import { register } from "ol/proj/proj4";
|
||||
import proj4 from "proj4";
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import GisMapToggle from "./GisMapToggle";
|
||||
import GisSelect from "./GisSelect";
|
||||
import GisToolBar from "./GisToolBar";
|
||||
import GisTree from "./GisTree";
|
||||
import "./lib/css/gis.common.css";
|
||||
import GisApp from "./lib/gis.core";
|
||||
|
||||
export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null });
|
||||
export const GisContext = createContext({
|
||||
gisCore: null,
|
||||
gisMap: null,
|
||||
treeData: null,
|
||||
checkedLayerIds: null,
|
||||
});
|
||||
export const useGisContext = () => useContext(GisContext);
|
||||
export const searchUpperPpls = async (fcltMngNo) => {
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
|
||||
method: "GET",
|
||||
});
|
||||
let data = null;
|
||||
if (res.ok) {
|
||||
try {
|
||||
// 응답이 비어있는지 확인
|
||||
const contentLength = res.headers.get("content-length");
|
||||
if (contentLength === "0")
|
||||
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
let data = null;
|
||||
if (res.ok) {
|
||||
try {
|
||||
// 응답이 비어있는지 확인
|
||||
const contentLength = res.headers.get('content-length');
|
||||
if (contentLength === '0') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
||||
const text = await res.text();
|
||||
if (!text || text.trim() === "")
|
||||
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
||||
const text = await res.text();
|
||||
if (!text || text.trim() === '') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
// JSON 파싱 시도
|
||||
data = JSON.parse(text);
|
||||
} catch (error) {
|
||||
console.error("JSON parsing error:", error);
|
||||
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
}
|
||||
}
|
||||
|
||||
// JSON 파싱 시도
|
||||
data = JSON.parse(text);
|
||||
} catch (error) {
|
||||
console.error('JSON parsing error:', error);
|
||||
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
}
|
||||
}
|
||||
const select = GisApp.Module.select;
|
||||
const gisMap = GisApp.Module.core?.map;
|
||||
let foundFeature = null;
|
||||
|
||||
const select = GisApp.Module.select;
|
||||
data.forEach((item) => {
|
||||
const pplMngNo = item.ppl_mng_no.trim();
|
||||
|
||||
const gisMap = GisApp.Module.core?.map;
|
||||
gisMap
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.filter((e) => e.get("lyrTyNm") == "tb_ms_swage_ppl")
|
||||
.forEach((layer) => {
|
||||
const source = layer.getSource?.();
|
||||
if (source?.getFeatures) {
|
||||
const features = source.getFeatures();
|
||||
for (const f of features) {
|
||||
if (f.values_.fclt_mng_no.trim() === pplMngNo) {
|
||||
foundFeature = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let foundFeature = null;
|
||||
if (foundFeature) select.selectMultiFeature(foundFeature, true);
|
||||
});
|
||||
|
||||
data.forEach(item => {
|
||||
const pplMngNo = item.ppl_mng_no.trim();
|
||||
if (!foundFeature) alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
gisMap.getLayers().getArray().filter(e => e.get("lyrTyNm") == 'tb_ms_swage_ppl').forEach(layer => {
|
||||
const source = layer.getSource?.();
|
||||
if (source?.getFeatures) {
|
||||
const features = source.getFeatures();
|
||||
for (const f of features) {
|
||||
if (f.values_.fclt_mng_no.trim() === pplMngNo) {
|
||||
foundFeature = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let geometry = foundFeature?.getGeometry();
|
||||
if (geometry) {
|
||||
let extent = geometry.getExtent();
|
||||
|
||||
if (foundFeature) select.selectMultiFeature(foundFeature, true);
|
||||
});
|
||||
|
||||
if (!foundFeature) alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
let geometry = foundFeature?.getGeometry();
|
||||
if (geometry) {
|
||||
let extent = geometry.getExtent();
|
||||
|
||||
gisMap.getView().fit(extent, {
|
||||
duration: 300,
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 14,
|
||||
constrainResolution: true,
|
||||
nearest: true,
|
||||
});
|
||||
}
|
||||
gisMap.getView().fit(extent, {
|
||||
duration: 300,
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 14,
|
||||
constrainResolution: true,
|
||||
nearest: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function Gis({
|
||||
isOpen, //우측 상세 레이어 팝업
|
||||
gisMaps,
|
||||
isSearchModalOpen,
|
||||
treeData = [],
|
||||
isSearch = false,
|
||||
isMiniMap = false,
|
||||
setIsOpen,
|
||||
setFeatureData,
|
||||
featureData,
|
||||
imgVisble = false,
|
||||
searchView: SearchView,
|
||||
detailView: DetailView,
|
||||
onFeatureSelect,
|
||||
setIsSearchModalOpen,
|
||||
onCheckedLayersChange,
|
||||
searchBar: SearchBar,
|
||||
waterDepth: WaterDepth,
|
||||
isOpen, //우측 상세 레이어 팝업
|
||||
gisMaps,
|
||||
isSearchModalOpen,
|
||||
treeData = [],
|
||||
isSearch = false,
|
||||
isMiniMap = false,
|
||||
setIsOpen,
|
||||
setFeatureData,
|
||||
featureData,
|
||||
imgVisble = false,
|
||||
searchView: SearchView,
|
||||
detailView: DetailView,
|
||||
onFeatureSelect,
|
||||
setIsSearchModalOpen,
|
||||
onCheckedLayersChange,
|
||||
searchBar: SearchBar,
|
||||
waterDepth: WaterDepth,,
|
||||
height:height="h-[calc(100vh-10rem)]",
|
||||
}) {
|
||||
const mapRef = useRef();
|
||||
const [mapObject, setMapObject] = useState({});
|
||||
const [tabVisible, setTabVisible] = useState(true);
|
||||
const [layerNm, setLayerNm] = useState('')
|
||||
// const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
||||
// const [featureData, setFeatureData] = useState(null) // 선택된 피쳐 데이터
|
||||
// const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
|
||||
const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []);
|
||||
const mapInstanceRef = useRef(null);
|
||||
const checkedLayerIds = useRef([]);
|
||||
const layerNmRef = useRef('')
|
||||
const mapRef = useRef();
|
||||
const [mapObject, setMapObject] = useState({});
|
||||
const [tabVisible, setTabVisible] = useState(true);
|
||||
const [layerNm, setLayerNm] = useState("");
|
||||
// const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
||||
// const [featureData, setFeatureData] = useState(null) // 선택된 피쳐 데이터
|
||||
// const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
|
||||
const gisCore = useMemo(
|
||||
() => new GisApp.core({ targetId: mapRef.current }),
|
||||
[],
|
||||
);
|
||||
const mapInstanceRef = useRef(null);
|
||||
const checkedLayerIds = useRef([]);
|
||||
const layerNmRef = useRef("");
|
||||
|
||||
// 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가
|
||||
// const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]);
|
||||
// const facilityOverlaysRef = useRef([]);
|
||||
// 체크된 레이어 상태 관리
|
||||
const [checkedLayers, setCheckedLayers] = useState([]);
|
||||
const [checkedLayersData, setCheckedLayersData] = useState([]);
|
||||
// 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가
|
||||
// const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]);
|
||||
// const facilityOverlaysRef = useRef([]);
|
||||
// 체크된 레이어 상태 관리
|
||||
const [checkedLayers, setCheckedLayers] = useState([]);
|
||||
const [checkedLayersData, setCheckedLayersData] = useState([]);
|
||||
|
||||
let gisMap = null;
|
||||
const closePopup = () => {
|
||||
setIsOpen(false); // 팝업 닫기
|
||||
setFeatureData(null)
|
||||
};
|
||||
let gisMap = null;
|
||||
const closePopup = () => {
|
||||
setIsOpen(false); // 팝업 닫기
|
||||
setFeatureData(null);
|
||||
};
|
||||
|
||||
// Feature Click Event
|
||||
const handleFeatureSelect = useCallback((data) => {
|
||||
onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출
|
||||
}, [mapInstanceRef]);
|
||||
// Feature Click Event
|
||||
const handleFeatureSelect = useCallback(
|
||||
(data) => {
|
||||
onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출
|
||||
},
|
||||
[mapInstanceRef],
|
||||
);
|
||||
|
||||
// 지도 생성 및 초기화
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단
|
||||
// 지도 생성 및 초기화
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단
|
||||
|
||||
// EPSG:5179 등록
|
||||
proj4.defs([
|
||||
[
|
||||
'EPSG:5179',
|
||||
'+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs',
|
||||
],
|
||||
]);
|
||||
register(proj4);
|
||||
// EPSG:5179 등록
|
||||
proj4.defs([
|
||||
[
|
||||
"EPSG:5179",
|
||||
"+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs",
|
||||
],
|
||||
]);
|
||||
register(proj4);
|
||||
|
||||
// OpenLayers 지도 객체 생성
|
||||
const map = new OlMap({
|
||||
layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']],
|
||||
renderer: 'webgl',
|
||||
target: mapRef.current,
|
||||
interactions: defaults({ doubleClickZoom: false }),
|
||||
controls: defaultControls({
|
||||
zoom: false,
|
||||
rotate: false,
|
||||
attributionOptions: {
|
||||
collapsible: false,
|
||||
},
|
||||
}).extend([]),
|
||||
view: new View({
|
||||
projection: GisApp.TO_PROJ,
|
||||
zoom: gisCore.defaults.zoom,
|
||||
center: olProj.transform(
|
||||
[127.4875, 37.4916],
|
||||
GisApp.FROM_PROJ,
|
||||
GisApp.TO_PROJ
|
||||
),
|
||||
minZoom: gisCore.defaults.minZoom,
|
||||
maxZoom: gisCore.defaults.maxZoom,
|
||||
}),
|
||||
});
|
||||
// OpenLayers 지도 객체 생성
|
||||
const map = new OlMap({
|
||||
layers: [gisCore.baseLayers["base"], gisCore.baseLayers["satellite"]],
|
||||
renderer: "webgl",
|
||||
target: mapRef.current,
|
||||
interactions: defaults({ doubleClickZoom: false }),
|
||||
controls: defaultControls({
|
||||
zoom: false,
|
||||
rotate: false,
|
||||
attributionOptions: {
|
||||
collapsible: false,
|
||||
},
|
||||
}).extend([]),
|
||||
view: new View({
|
||||
projection: GisApp.TO_PROJ,
|
||||
zoom: gisCore.defaults.zoom,
|
||||
center: olProj.transform(
|
||||
[127.4875, 37.4916],
|
||||
GisApp.FROM_PROJ,
|
||||
GisApp.TO_PROJ,
|
||||
),
|
||||
minZoom: gisCore.defaults.minZoom,
|
||||
maxZoom: gisCore.defaults.maxZoom,
|
||||
}),
|
||||
});
|
||||
|
||||
gisMaps.current = map
|
||||
gisMaps.current = map;
|
||||
|
||||
// 미니맵 추가
|
||||
if (isMiniMap) {
|
||||
const overviewMap = new OverviewMap({
|
||||
layers: [GisApp.Module.core.baseMiniMapLayers['base']],
|
||||
view: new View({
|
||||
projection: GisApp.TO_PROJ,
|
||||
minZoom: 9,
|
||||
}),
|
||||
collapsed: false,
|
||||
collapseLabel: '\u00BB',
|
||||
label: '\u00AB',
|
||||
});
|
||||
map.addControl(overviewMap);
|
||||
}
|
||||
// 미니맵 추가
|
||||
if (isMiniMap) {
|
||||
const overviewMap = new OverviewMap({
|
||||
layers: [GisApp.Module.core.baseMiniMapLayers["base"]],
|
||||
view: new View({
|
||||
projection: GisApp.TO_PROJ,
|
||||
minZoom: 9,
|
||||
}),
|
||||
collapsed: false,
|
||||
collapseLabel: "\u00BB",
|
||||
label: "\u00AB",
|
||||
});
|
||||
map.addControl(overviewMap);
|
||||
}
|
||||
|
||||
// 스케일 컨트롤 및 베이스 레이어 설정
|
||||
gisCore.__proto__.map = map;
|
||||
gisCore.scaleControl();
|
||||
gisCore.changeBaseLayer('base');
|
||||
setMapObject({ map: map });
|
||||
mapInstanceRef.current = map; // 생성 완료 플래그 설정
|
||||
// 스케일 컨트롤 및 베이스 레이어 설정
|
||||
gisCore.__proto__.map = map;
|
||||
gisCore.scaleControl();
|
||||
gisCore.changeBaseLayer("base");
|
||||
setMapObject({ map: map });
|
||||
mapInstanceRef.current = map; // 생성 완료 플래그 설정
|
||||
}, []);
|
||||
|
||||
}, []);
|
||||
// treeData 변경 시 레이어 코드 생성
|
||||
useEffect(() => {
|
||||
if (!treeData || treeData.length === 0) return;
|
||||
|
||||
// treeData 변경 시 레이어 코드 생성
|
||||
useEffect(() => {
|
||||
if (!treeData || treeData.length === 0) return;
|
||||
// 기존 오버레이 및 레이어 모두 삭제
|
||||
gisCore.deleteAllOverlay();
|
||||
gisCore.deleteAllLayer();
|
||||
|
||||
// 기존 오버레이 및 레이어 모두 삭제
|
||||
gisCore.deleteAllOverlay();
|
||||
gisCore.deleteAllLayer();
|
||||
// 트리 데이터를 레이어 코드 객체로 변환
|
||||
const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, "lyrSn");
|
||||
GisApp.layerCode = layerCode; // 전역 레이어 코드 설정
|
||||
|
||||
// 트리 데이터를 레이어 코드 객체로 변환
|
||||
const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn');
|
||||
GisApp.layerCode = layerCode; // 전역 레이어 코드 설정
|
||||
//setTreeData(data);
|
||||
// 편집 가능한 레이어 목록 생성
|
||||
//const layerItems = getLayerItems(layerCode);
|
||||
//setEditLayerItems(prev => [...layerItems])
|
||||
}, [treeData]);
|
||||
|
||||
//setTreeData(data);
|
||||
// 편집 가능한 레이어 목록 생성
|
||||
//const layerItems = getLayerItems(layerCode);
|
||||
//setEditLayerItems(prev => [...layerItems])
|
||||
}, [treeData]);
|
||||
const getLayerItems = (layerCode) => {
|
||||
const layerItems = Object.entries(layerCode)
|
||||
.filter(([key, obj], idx) => obj?.updtPsblYn === "Y")
|
||||
.map(([key, obj]) => ({
|
||||
...obj,
|
||||
name: obj.lyrNm,
|
||||
label: obj.lyrNm,
|
||||
value: obj.lyrSn + "",
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.sortOrdr > b.sortOrdr) return 1;
|
||||
if (a.sortOrdr < b.sortOrdr) return -1;
|
||||
return 0;
|
||||
});
|
||||
return layerItems;
|
||||
};
|
||||
|
||||
const getLayerItems = (layerCode) => {
|
||||
const layerItems = Object.entries(layerCode)
|
||||
.filter(([key, obj], idx) => obj?.updtPsblYn === 'Y')
|
||||
.map(([key, obj]) => ({
|
||||
...obj,
|
||||
name: obj.lyrNm,
|
||||
label: obj.lyrNm,
|
||||
value: obj.lyrSn + '',
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.sortOrdr > b.sortOrdr) return 1;
|
||||
if (a.sortOrdr < b.sortOrdr) return -1;
|
||||
return 0;
|
||||
});
|
||||
return layerItems;
|
||||
};
|
||||
if (mapObject.map) {
|
||||
try {
|
||||
gisMap = mapObject.map;
|
||||
} catch (e) {
|
||||
console.error("오류 발생:", e); // 시큐어코딩 관련 수정 : 오류 정보를 로그로 출력
|
||||
}
|
||||
}
|
||||
|
||||
if (mapObject.map) {
|
||||
try {
|
||||
gisMap = mapObject.map;
|
||||
} catch (e) {
|
||||
console.error('오류 발생:', e); // 시큐어코딩 관련 수정 : 오류 정보를 로그로 출력
|
||||
}
|
||||
}
|
||||
// 체크된 레이어 정보를 처리하는 함수
|
||||
const handleCheckedLayersChange = useCallback(
|
||||
(checkedIds, checkedNodesData) => {
|
||||
setTimeout(() => {
|
||||
setCheckedLayers(checkedIds);
|
||||
setCheckedLayersData(checkedNodesData);
|
||||
onCheckedLayersChange?.(checkedIds, checkedNodesData);
|
||||
}, 1000);
|
||||
},
|
||||
[onCheckedLayersChange],
|
||||
);
|
||||
|
||||
// 체크된 레이어 정보를 처리하는 함수
|
||||
const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => {
|
||||
setTimeout(() => {
|
||||
setCheckedLayers(checkedIds);
|
||||
setCheckedLayersData(checkedNodesData);
|
||||
onCheckedLayersChange?.(checkedIds, checkedNodesData);
|
||||
}, 1000);
|
||||
}, [onCheckedLayersChange]);
|
||||
return (
|
||||
<>
|
||||
<GisContext.Provider
|
||||
value={{
|
||||
gisCore,
|
||||
gisMap: mapInstanceRef.current,
|
||||
treeData,
|
||||
checkedLayerIds,
|
||||
}}
|
||||
>
|
||||
<div className={`relative flex ${height}`}>
|
||||
<div
|
||||
ref={mapRef}
|
||||
className="gis-map relative flex-1"
|
||||
style={{ height: "100%", padding: 0 }}
|
||||
>
|
||||
<GisTree
|
||||
visible={tabVisible}
|
||||
setVisible={setTabVisible}
|
||||
onCheckedChange={handleCheckedLayersChange}
|
||||
onLayerIconClick={(layerNm) => {
|
||||
layerNmRef.current = layerNm;
|
||||
setLayerNm(layerNm);
|
||||
setIsOpen(true);
|
||||
const dummyFeature = {
|
||||
getProperties: () => ({}), // 최소 구조
|
||||
};
|
||||
onFeatureSelect(dummyFeature);
|
||||
}}
|
||||
imageVisible={imgVisble}
|
||||
/>
|
||||
|
||||
return (
|
||||
<>
|
||||
<GisContext.Provider value={{ gisCore, gisMap: mapInstanceRef.current, treeData, checkedLayerIds }}>
|
||||
<div className="h-[calc(100vh-10rem)] flex relative">
|
||||
<div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} >
|
||||
<GisTree
|
||||
visible={tabVisible}
|
||||
setVisible={setTabVisible}
|
||||
onCheckedChange={handleCheckedLayersChange}
|
||||
onLayerIconClick={(layerNm) => {
|
||||
layerNmRef.current = layerNm
|
||||
setLayerNm(layerNm); setIsOpen(true); const dummyFeature = {
|
||||
getProperties: () => ({}), // 최소 구조
|
||||
};
|
||||
onFeatureSelect(dummyFeature);
|
||||
}}
|
||||
imageVisible={imgVisble}
|
||||
/>
|
||||
|
||||
{/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
|
||||
{isOpen && !isSearchModalOpen && (
|
||||
<DetailView
|
||||
featureData={featureData || { getProperties: () => ({}) }}
|
||||
seletectedLayerNm={layerNm}
|
||||
onClose={closePopup} // 닫기 콜백 전달
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* SearchView가 존재할 때만 렌더링 */}
|
||||
{SearchView && (
|
||||
<SearchView
|
||||
open={isSearchModalOpen}
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* 시도/시군구 조회 영역 */}
|
||||
{SearchBar && (
|
||||
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-30 bg-gray-300 rounded " style={{ maxWidth: 'calc(100% - 2rem)' }}>
|
||||
<SearchBar/>
|
||||
</div>
|
||||
)}
|
||||
<GisToolBar />
|
||||
<GisMapToggle />
|
||||
<GisSelect onFeatureSelect={handleFeatureSelect} />
|
||||
{/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
|
||||
{isOpen && !isSearchModalOpen && (
|
||||
<DetailView
|
||||
featureData={featureData || { getProperties: () => ({}) }}
|
||||
seletectedLayerNm={layerNm}
|
||||
onClose={closePopup} // 닫기 콜백 전달
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* SearchView가 존재할 때만 렌더링 */}
|
||||
{SearchView && (
|
||||
<SearchView
|
||||
open={isSearchModalOpen}
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* 시도/시군구 조회 영역 */}
|
||||
{SearchBar && (
|
||||
<div
|
||||
className="absolute left-1/2 top-4 z-30 -translate-x-1/2 rounded w-max"
|
||||
>
|
||||
<SearchBar />
|
||||
</div>
|
||||
)}
|
||||
<GisToolBar />
|
||||
<GisMapToggle />
|
||||
<GisSelect onFeatureSelect={handleFeatureSelect} />
|
||||
{typeof WaterDepth === "function" && <WaterDepth onClose={() => setIsOpen(false)} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isSearch && (
|
||||
<div className="absolute right-8 top-[305px] z-20">
|
||||
<button
|
||||
onClick={() => setIsSearchModalOpen(true)}
|
||||
className="overflow-hidden w-[40px] h-[40px] bg-white
|
||||
border border-[#9aa7b8]
|
||||
rounded
|
||||
shadow-[0_0_5px_#999]
|
||||
flex items-center justify-center
|
||||
p-0
|
||||
"
|
||||
>
|
||||
<Search size={24} className="text-gray-700" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</GisContext.Provider>
|
||||
</>
|
||||
);
|
||||
{isSearch && (
|
||||
<div className="absolute right-8 top-[305px] z-20">
|
||||
<button
|
||||
onClick={() => setIsSearchModalOpen(true)}
|
||||
className="flex h-[40px] w-[40px] items-center justify-center overflow-hidden rounded border border-[#9aa7b8] bg-white p-0 shadow-[0_0_5px_#999]"
|
||||
>
|
||||
<Search size={24} className="text-gray-700" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</GisContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Gis);
|
||||
|
||||
@@ -196,7 +196,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
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
|
||||
className={`absolute top-0 left-0 w-[250px] h-full 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'}`}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -143,11 +143,11 @@ GisOlLayer.prototype = {
|
||||
let source;
|
||||
|
||||
let lyrTyNm = option.lyrTyNm;
|
||||
if(lyrTyNm == 'tb_ms_flood_dngr_map'
|
||||
|| lyrTyNm == 'tb_ms_sbmrg_dngr_map'
|
||||
|| lyrTyNm == 'tb_ms_swage_mhl'
|
||||
|| lyrTyNm == 'tb_ms_swage_ppl'
|
||||
|| lyrTyNm == 'tb_ms_swage_pmstn'
|
||||
if(lyrTyNm == 'tb_ms_flood_dngr_map' //침수위험지도
|
||||
|| lyrTyNm == 'tb_ms_sbmrg_dngr_map' //도시침수지도
|
||||
|| lyrTyNm == 'tb_ms_swage_mhl' //맨홀
|
||||
|| lyrTyNm == 'tb_ms_swage_ppl' //관로
|
||||
|| lyrTyNm == 'tb_ms_swage_pmstn' //펌프장
|
||||
) {
|
||||
source = this.getVectorSourceBbox(option);
|
||||
}else {
|
||||
|
||||