Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
This commit is contained in:
@@ -17,3 +17,6 @@ NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/ap
|
|||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||||
|
|||||||
@@ -20,3 +20,6 @@ NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/ap
|
|||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||||
|
|||||||
@@ -16,3 +16,6 @@ NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BE
|
|||||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
|
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||||
|
|||||||
@@ -15,4 +15,40 @@ export const sanitizeInput = (val: string, allowDecimal = false) => {
|
|||||||
|
|
||||||
// 정수만 허용
|
// 정수만 허용
|
||||||
return val.replace(/[^0-9]/g, "");
|
return val.replace(/[^0-9]/g, "");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// utils: format Date <-> "YYYY-MM-DD" without moment
|
||||||
|
export const formatDateToYMD=(value: Date): string => {
|
||||||
|
if (!value) return "";
|
||||||
|
// value can be Date | string ("YYYY-MM-DD")
|
||||||
|
if (typeof value === "string") {
|
||||||
|
// assume already in yyyy-mm-dd
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
|
||||||
|
const d = new Date(value);
|
||||||
|
if (isNaN(d.getTime())) return "";
|
||||||
|
return toYMD(d);
|
||||||
|
}
|
||||||
|
if (value instanceof Date) {
|
||||||
|
if (isNaN(value.getTime())) return "";
|
||||||
|
return toYMD(value);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const toYMD=(date: Date): string => {
|
||||||
|
// avoid timezone shift by normalizing to local date parts
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const m = String(date.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
|
return `${y}-${m}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const parseYMDToDate=(ymd: string): Date | null => {
|
||||||
|
// if (!/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return null;
|
||||||
|
// const [y, m, d] = ymd.split("-").map(Number);
|
||||||
|
// const date = new Date(y, (m - 1), d);
|
||||||
|
// // validate round-trip
|
||||||
|
// return (date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d) ? date : null;
|
||||||
|
// }
|
||||||
@@ -2,7 +2,7 @@ import ModalSimilarPage from "@/app/(base-layout)/pages/sdmMng/sdmInfoInsert/mod
|
|||||||
import { useDmStore } from "@/app/store";
|
import { useDmStore } from "@/app/store";
|
||||||
import {
|
import {
|
||||||
CANDIDATE,
|
CANDIDATE,
|
||||||
DatePicker,
|
// DatePicker,
|
||||||
FormField,
|
FormField,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormUi,
|
FormUi,
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
UiModal,
|
UiModal,
|
||||||
} from "@common/business_components";
|
} from "@common/business_components";
|
||||||
import { Input } from "@common/components";
|
import { Input } from "@common/components";
|
||||||
import { Button, Label } from "@common/components/ui";
|
import { Button, Label } from "@common/components/ui";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { AccessibleFormField } from "./AccessibleFormField";
|
import { AccessibleFormField } from "./AccessibleFormField";
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
import SearchInputText from "../common/searhDemandInput/component";
|
import SearchInputText from "../common/searhDemandInput/component";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { sanitizeInput } from "../common/utils/utils";
|
import { formatDateToYMD, sanitizeInput } from "../common/utils/utils";
|
||||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||||
import type { SdmInfoFormData } from "@/app/store/sdmMng";
|
import type { SdmInfoFormData } from "@/app/store/sdmMng";
|
||||||
|
|
||||||
@@ -161,7 +161,9 @@ export function SdmInfoRegForm({
|
|||||||
|
|
||||||
if (defaults) {
|
if (defaults) {
|
||||||
Object.entries(defaults).forEach(([field, value]) => {
|
Object.entries(defaults).forEach(([field, value]) => {
|
||||||
form.setValue(field as keyof SdmInfoFormData, value, { shouldDirty: true });
|
form.setValue(field as keyof SdmInfoFormData, value, {
|
||||||
|
shouldDirty: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form.getValues("usePurpose")]);
|
}, [form.getValues("usePurpose")]);
|
||||||
@@ -208,8 +210,8 @@ export function SdmInfoRegForm({
|
|||||||
formIndex: 0,
|
formIndex: 0,
|
||||||
required: true,
|
required: true,
|
||||||
component: (field) => (
|
component: (field) => (
|
||||||
<div className="">
|
<div className="relative w-64">
|
||||||
<DatePicker
|
{/* <DatePicker
|
||||||
mode="single"
|
mode="single"
|
||||||
selected={field.value as Date}
|
selected={field.value as Date}
|
||||||
onSelect={(date) => {
|
onSelect={(date) => {
|
||||||
@@ -224,8 +226,18 @@ export function SdmInfoRegForm({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
showOutsideDays={true}
|
showOutsideDays={true}
|
||||||
selectClass={undefined}
|
selectClass={undefined}
|
||||||
/>
|
/> */}
|
||||||
|
{/* 네이티브 input */}
|
||||||
|
<Input
|
||||||
|
className="w-full !text-xl text-right"
|
||||||
|
type="date"
|
||||||
|
onChange= {(e) => {
|
||||||
|
const selectedDate = e.target.value ? new Date(e.target.value) : null;
|
||||||
|
field.onChange(selectedDate);
|
||||||
|
}}
|
||||||
|
value={ formatDateToYMD(field.value as Date) }
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -363,13 +375,11 @@ export function SdmInfoRegForm({
|
|||||||
render={({ field }) =>
|
render={({ field }) =>
|
||||||
name === "money" ? (
|
name === "money" ? (
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-black" >
|
<Label className="text-black">희망단가(원)*</Label>
|
||||||
희망단가(원)*
|
{component(field)}
|
||||||
</Label>
|
|
||||||
{ component(field)}
|
|
||||||
</div>
|
</div>
|
||||||
// <FormUi label={label + "*"}>{component(field)}</FormUi>
|
|
||||||
) : (
|
) : (
|
||||||
|
// <FormUi label={label + "*"}>{component(field)}</FormUi>
|
||||||
<AccessibleFormField
|
<AccessibleFormField
|
||||||
field={field}
|
field={field}
|
||||||
label={label}
|
label={label}
|
||||||
@@ -773,7 +783,7 @@ export function SdmInfoRegForm({
|
|||||||
onSelectValue={(val: string | number) => {
|
onSelectValue={(val: string | number) => {
|
||||||
field.onChange(val.toString());
|
field.onChange(val.toString());
|
||||||
}}
|
}}
|
||||||
value={field.value }
|
value={field.value}
|
||||||
readOnly={isDisabled}
|
readOnly={isDisabled}
|
||||||
onBlur={field.onBlur}
|
onBlur={field.onBlur}
|
||||||
ref={field.ref}
|
ref={field.ref}
|
||||||
|
|||||||
+54
-29
@@ -7,6 +7,8 @@ const serverApiUrl = process.env.NEXT_PUBLIC_CS_SERVER_API_URL;
|
|||||||
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
|
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
|
||||||
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_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 SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||||
|
const BUSINESS_JUSO_API_URL = process.env.NEXT_PUBLIC_FINDADRESS_URL || "";
|
||||||
|
const AdressKey = process.env.NEXT_PUBLIC_WR_ADDRESS_KEY || "";
|
||||||
|
|
||||||
export function middleware(req: NextRequest) {
|
export function middleware(req: NextRequest) {
|
||||||
const pathname = req.nextUrl.pathname;
|
const pathname = req.nextUrl.pathname;
|
||||||
@@ -24,8 +26,8 @@ export function middleware(req: NextRequest) {
|
|||||||
|
|
||||||
if (pathname.startsWith("/server")) {
|
if (pathname.startsWith("/server")) {
|
||||||
const decodedParams = Array.from(searchParams.entries())
|
const decodedParams = Array.from(searchParams.entries())
|
||||||
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
||||||
.join("&");
|
.join("&");
|
||||||
|
|
||||||
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
|
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
|
||||||
decodedParams ? `?${decodedParams}` : ""
|
decodedParams ? `?${decodedParams}` : ""
|
||||||
@@ -38,7 +40,6 @@ export function middleware(req: NextRequest) {
|
|||||||
return NextResponse.redirect(new URL("/pages/wrStatus", req.url));
|
return NextResponse.redirect(new URL("/pages/wrStatus", req.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (pathname.startsWith("/attach-service")) {
|
if (pathname.startsWith("/attach-service")) {
|
||||||
const requestHeaders = new Headers(req.headers);
|
const requestHeaders = new Headers(req.headers);
|
||||||
// const token =
|
// const token =
|
||||||
@@ -69,7 +70,7 @@ export function middleware(req: NextRequest) {
|
|||||||
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`
|
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`
|
||||||
);
|
);
|
||||||
return NextResponse.rewrite(new URL(newUrl), {
|
return NextResponse.rewrite(new URL(newUrl), {
|
||||||
headers: requestHeaders
|
headers: requestHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,33 +82,31 @@ export function middleware(req: NextRequest) {
|
|||||||
let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설
|
typename == "hasudo:tb_il_swage_prcs_fclt" || //하수처리시설
|
||||||
|| typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역
|
typename == "hasudo:tb_ms_swage_prcs_zone" || //하수처리구역
|
||||||
|| typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도
|
typename == "hasudo:tb_ms_sbmrg_dngr_map" || //침수위험지도
|
||||||
|| typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
|
typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
|
||||||
) {
|
) {
|
||||||
newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||||
}
|
} else if (
|
||||||
else if (
|
typename == "hasudo:tb_ms_swage_ppl" || //관로
|
||||||
typename == "hasudo:tb_ms_swage_ppl" //관로
|
typename == "hasudo:tb_ms_swage_mhl" || //맨홀
|
||||||
|| typename == "hasudo:tb_ms_swage_mhl" //맨홀
|
typename == "hasudo:tb_ms_swage_pmstn" || //펌프장
|
||||||
|| typename == "hasudo:tb_ms_swage_pmstn" //펌프장
|
typename == "hasudo:tb_ms_stofcbr" //우수토실
|
||||||
|| typename == "hasudo:tb_ms_stofcbr" //우수토실
|
|
||||||
) {
|
) {
|
||||||
newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||||
}
|
} else if (
|
||||||
else if (
|
typename == "hasudo:om_dt_untrt_swg_rain" || //중점우수토실
|
||||||
typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실
|
typename == "hasudo:om_wqlt_station" || //관측소
|
||||||
|| typename == "hasudo:om_wqlt_station" //관측소
|
typename == "hasudo:tb_wr_bdst_ruse_fclt" || //재이용시설
|
||||||
|| typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설
|
typename == "hasudo:tb_wr_rnwtr_utztn_fclt" || //빗물이용시설
|
||||||
|| typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설
|
typename == "hasudo:tb_wr_rswtr_fclt" || //중수도시설
|
||||||
|| typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설
|
typename == "hasudo:tb_wr_htdfl_fclt" || //온배수시설
|
||||||
|| typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설
|
typename == "hasudo:tb_wr_ruse_fclt_dmd_co" || //수요처
|
||||||
|| typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처
|
typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
|
||||||
|| typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
|
) {
|
||||||
){
|
|
||||||
const url = req.nextUrl.clone();
|
const url = req.nextUrl.clone();
|
||||||
url.searchParams.set('TYPENAME', typename.replace("hasudo:",""));
|
url.searchParams.set("TYPENAME", typename.replace("hasudo:", ""));
|
||||||
queryParams = url.searchParams.toString();
|
queryParams = url.searchParams.toString();
|
||||||
newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`;
|
newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`;
|
||||||
}
|
}
|
||||||
@@ -118,15 +117,41 @@ export function middleware(req: NextRequest) {
|
|||||||
|
|
||||||
if (pathname.startsWith("/sewerage-service")) {
|
if (pathname.startsWith("/sewerage-service")) {
|
||||||
const queryParams = searchParams.toString();
|
const queryParams = searchParams.toString();
|
||||||
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${queryParams ? `?${queryParams}` : ""
|
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${
|
||||||
}`;
|
queryParams ? `?${queryParams}` : ""
|
||||||
|
}`;
|
||||||
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
|
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
|
||||||
return NextResponse.rewrite(new URL(newUrl));
|
return NextResponse.rewrite(new URL(newUrl));
|
||||||
}
|
}
|
||||||
|
if (pathname.startsWith("/bussiness-juso-service")) {
|
||||||
|
console.log("AdressKey ", AdressKey);
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
currentPage: "1", // 원하는 페이지
|
||||||
|
countPerPage: "5", // 페이지당 결과 수
|
||||||
|
keyword: searchParams.get("keyword") || "", // 검색어(주소)
|
||||||
|
confmKey: AdressKey, // 인증키 인터넷용 패쇄망용은 따로 발급 필요 즉 middleware에서 치환해서 사용해야할듯
|
||||||
|
resultType: "json", // 결과 형식 기본은 xml
|
||||||
|
});
|
||||||
|
const newUrl = `${BUSINESS_JUSO_API_URL}?${params.toString()}`;
|
||||||
|
console.log(`[Middleware] Request to business.juso.go.kr`);
|
||||||
|
console.log(req.url);
|
||||||
|
console.log(
|
||||||
|
`[Middleware] Rewriting from '/business.juso.go.kr' path to: ${newUrl}`
|
||||||
|
);
|
||||||
|
return NextResponse.rewrite(new URL(newUrl));
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/server/:path*", "/user-service/:path*", "/attach-service/:path*", "/geoserver/:path*", "/sewerage-service/:path*", "/"],
|
matcher: [
|
||||||
|
"/server/:path*",
|
||||||
|
"/user-service/:path*",
|
||||||
|
"/attach-service/:path*",
|
||||||
|
"/geoserver/:path*",
|
||||||
|
"/sewerage-service/:path*",
|
||||||
|
"/bussiness-juso-service/:path*",
|
||||||
|
"/",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
+104
-83
@@ -12,8 +12,8 @@ import RdiiChart from './chart/rdiiChart';
|
|||||||
import { useInfilChartModalStore, useRdiiChartModalStore } from "./chartModalStore";
|
import { useInfilChartModalStore, useRdiiChartModalStore } from "./chartModalStore";
|
||||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||||
import { codeMapper } from "../common/codeMapper";
|
import { codeMapper } from "../common/codeMapper";
|
||||||
import { searchUpperPpls } from '@common/business_components/ui/map/Gis';
|
|
||||||
import LoadingOverlay from '@/components/ui/LoadingBar';
|
import LoadingOverlay from '@/components/ui/LoadingBar';
|
||||||
|
import { searchUpperPpls } from "./smh";
|
||||||
|
|
||||||
|
|
||||||
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
|
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
|
||||||
@@ -58,7 +58,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
|||||||
const getSelectedLayers = () => {
|
const getSelectedLayers = () => {
|
||||||
let result = null;
|
let result = null;
|
||||||
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
||||||
let featureLayer: { get: (key: string) => any }; // 피처가 속한 레이어를 찾기 위한 변수
|
let featureLayer: any = null; // 피처가 속한 레이어를 찾기 위한 변수
|
||||||
layers.some((layer: any) => {
|
layers.some((layer: any) => {
|
||||||
const source = layer.getSource();
|
const source = layer.getSource();
|
||||||
// if (source && featureData && featureData.fid) {
|
// if (source && featureData && featureData.fid) {
|
||||||
@@ -77,98 +77,119 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
|||||||
setIsClicked(true);
|
setIsClicked(true);
|
||||||
console.log('gis data', featureData.values_);
|
console.log('gis data', featureData.values_);
|
||||||
const lyrTyNm = featureData.id_.split('.')[0];
|
const lyrTyNm = featureData.id_.split('.')[0];
|
||||||
if (lyrTyNm === 'tb_ms_swage_ppl') {
|
|
||||||
result = '하수관로';
|
switch (lyrTyNm) {
|
||||||
setHeadlist(MsSwagePplCol);
|
case 'tb_ms_swage_ppl': {
|
||||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
result = '하수관로';
|
||||||
}
|
setHeadlist(MsSwagePplCol);
|
||||||
else if (lyrTyNm === 'tb_ms_swage_mhl') {
|
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||||
result = '하수맨홀';
|
break;
|
||||||
setHeadlist(MsSwageMhlCol);
|
|
||||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
|
||||||
}
|
|
||||||
else if (lyrTyNm === 'tb_ms_swage_pmstn') { // 하수펌프장
|
|
||||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
|
||||||
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
|
|
||||||
setIsDrtwSewagePump(true);
|
|
||||||
setHeadlist(DrtwSewagePumpCol);
|
|
||||||
}
|
}
|
||||||
else {
|
case 'tb_ms_swage_mhl': {
|
||||||
setIsDrtwSewagePump(false);
|
result = '하수맨홀';
|
||||||
setHeadlist(ExclnSewagePumpCol);
|
setHeadlist(MsSwageMhlCol);
|
||||||
|
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_swage_pmstn': {
|
||||||
|
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||||
|
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
|
||||||
|
setIsDrtwSewagePump(true);
|
||||||
|
setHeadlist(DrtwSewagePumpCol);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setIsDrtwSewagePump(false);
|
||||||
|
setHeadlist(ExclnSewagePumpCol);
|
||||||
|
}
|
||||||
|
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||||
|
result = '하수펌프장';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_il_swage_prcs_fclt': {
|
||||||
|
result = '하수처리시설';
|
||||||
|
setHeadlist(SwtrmtFcltCol);
|
||||||
|
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_swage_prcs_zone': {
|
||||||
|
result = '하수처리구역';
|
||||||
|
setHeadlist(SwtrmtPrcsZoneCol);
|
||||||
|
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_stofcbr': {
|
||||||
|
result = '우수토실';
|
||||||
|
setHeadlist(stofcbrCol);
|
||||||
|
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'om_dt_untrt_swg_rain': {
|
||||||
|
result = '중점우수토실';
|
||||||
|
setHeadlist(untrtSwgRainCol1);
|
||||||
|
setHeadlist2(untrtSwgRainCol2);
|
||||||
|
setLayerSpecificNm(featureData.values_.manage_no);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
result = featureLayer ? featureLayer.get('lyrNm') : null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
|
||||||
result = '하수펌프장';
|
|
||||||
}
|
}
|
||||||
else if (lyrTyNm === 'tb_il_swage_prcs_fclt') {
|
|
||||||
result = '하수처리시설';
|
|
||||||
setHeadlist(SwtrmtFcltCol);
|
|
||||||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
|
||||||
}
|
|
||||||
else if (lyrTyNm === 'tb_ms_swage_prcs_zone') {
|
|
||||||
result = '하수처리구역';
|
|
||||||
setHeadlist(SwtrmtPrcsZoneCol);
|
|
||||||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
|
||||||
}
|
|
||||||
else if (lyrTyNm === 'tb_ms_stofcbr') {
|
|
||||||
result = '우수토실';
|
|
||||||
setHeadlist(stofcbrCol);
|
|
||||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
|
||||||
}
|
|
||||||
else if (lyrTyNm === 'om_dt_untrt_swg_rain') {
|
|
||||||
result = '중점우수토실';
|
|
||||||
setHeadlist(untrtSwgRainCol1);
|
|
||||||
setHeadlist2(untrtSwgRainCol2);
|
|
||||||
setLayerSpecificNm(featureData.values_.manage_no);
|
|
||||||
}
|
|
||||||
else result = featureLayer.get('lyrNm');
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// SearchModal 클릭 이벤트
|
// SearchModal 클릭 이벤트
|
||||||
setIsClicked(false);
|
setIsClicked(false);
|
||||||
console.log('SearchModal data', featureData);
|
console.log('SearchModal data', featureData);
|
||||||
const lyrTyNm = featureData.lyrTypeNm;
|
const lyrTyNm = featureData.lyrTypeNm;
|
||||||
if (featureData.lyrTypeNm === 'tb_ms_swage_ppl') {
|
|
||||||
result = '하수관로';
|
switch (lyrTyNm) {
|
||||||
setHeadlist(MsSwagePplCol2)
|
case 'tb_ms_swage_ppl': {
|
||||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
result = '하수관로';
|
||||||
}
|
setHeadlist(MsSwagePplCol2)
|
||||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_mhl') {
|
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||||
result = '하수맨홀';
|
break;
|
||||||
setHeadlist(MsSwageMhlCol2)
|
|
||||||
setLayerSpecificNm(featureData.fcltMngNo);
|
|
||||||
}
|
|
||||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_pmstn') { // 하수펌프장
|
|
||||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
|
||||||
if (drtwPumpCode && featureData.usgCd.trim() === drtwPumpCode.val) {
|
|
||||||
console.log('오수펌프장');
|
|
||||||
setIsDrtwSewagePump(false);
|
|
||||||
setHeadlist(ExclnSewagePumpCol2)
|
|
||||||
}
|
}
|
||||||
else {
|
case 'tb_ms_swage_mhl': {
|
||||||
console.log('빗물펌프장');
|
result = '하수맨홀';
|
||||||
setIsDrtwSewagePump(true);
|
setHeadlist(MsSwageMhlCol2)
|
||||||
setHeadlist(DrtwSewagePumpCol2)
|
setLayerSpecificNm(featureData.fcltMngNo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_swage_pmstn': {
|
||||||
|
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||||
|
if (drtwPumpCode && featureData.usgCd.trim() === drtwPumpCode.val) {
|
||||||
|
console.log('오수펌프장');
|
||||||
|
setIsDrtwSewagePump(false);
|
||||||
|
setHeadlist(ExclnSewagePumpCol2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('빗물펌프장');
|
||||||
|
setIsDrtwSewagePump(true);
|
||||||
|
setHeadlist(DrtwSewagePumpCol2)
|
||||||
|
}
|
||||||
|
setLayerSpecificNm(featureData.fcltMngNo);
|
||||||
|
result = '하수펌프장';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_il_swage_prcs_fclt': {
|
||||||
|
result = '하수처리시설';
|
||||||
|
setHeadlist(SwtrmtFcltCol2)
|
||||||
|
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_swage_prcs_zone': {
|
||||||
|
result = '하수처리구역';
|
||||||
|
setHeadlist(SwtrmtPrcsZoneCol2);
|
||||||
|
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'tb_ms_stofcbr': {
|
||||||
|
result = '우수토실';
|
||||||
|
setHeadlist(stofcbrCol2)
|
||||||
|
setLayerSpecificNm(featureData.fcltMngNo);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setLayerSpecificNm(featureData.fcltMngNo);
|
|
||||||
result = '하수펌프장';
|
|
||||||
}
|
}
|
||||||
else if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') {
|
|
||||||
result = '하수처리시설';
|
|
||||||
setHeadlist(SwtrmtFcltCol2)
|
|
||||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
|
||||||
}
|
|
||||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') {
|
|
||||||
result = '하수처리구역';
|
|
||||||
setHeadlist(SwtrmtPrcsZoneCol2);
|
|
||||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
|
||||||
}
|
|
||||||
else if (featureData.lyrTypeNm === 'tb_ms_stofcbr') {
|
|
||||||
result = '우수토실';
|
|
||||||
setHeadlist(stofcbrCol2)
|
|
||||||
setLayerSpecificNm(featureData.fcltMngNo);
|
|
||||||
}
|
|
||||||
// else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -424,7 +445,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
|||||||
className="px-2 py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700 mr-4"
|
className="px-2 py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700 mr-4"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
searchUpperPpls(layerSpecificNm).finally(() => setIsLoading(false));
|
searchUpperPpls(gisMap, layerSpecificNm).finally(() => setIsLoading(false));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
상류관로 보기
|
상류관로 보기
|
||||||
|
|||||||
@@ -1,6 +1,84 @@
|
|||||||
// 하수맨홀
|
// 하수맨홀
|
||||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||||
|
|
||||||
|
// 상류관로 보기 함수 (FeatureClickEventPopup에서 사용)
|
||||||
|
export async function searchUpperPpls(gisMap: any, fcltMngNo: any) {
|
||||||
|
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||||
|
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
|
||||||
|
let data: any = null;
|
||||||
|
if (res.ok) {
|
||||||
|
try {
|
||||||
|
// 응답이 비어있는지 확인
|
||||||
|
const contentLength = res.headers.get("content-length");
|
||||||
|
if (contentLength === "0") {
|
||||||
|
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
||||||
|
const text = await res.text();
|
||||||
|
if (!text || text.trim() === "") {
|
||||||
|
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON 파싱 시도
|
||||||
|
data = JSON.parse(text);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("JSON parsing error:", error);
|
||||||
|
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const select: any = GisApp.Module.select;
|
||||||
|
let foundFeature: any = null;
|
||||||
|
|
||||||
|
(data || []).forEach((item: any) => {
|
||||||
|
const pplMngNo = item.ppl_mng_no?.trim?.();
|
||||||
|
|
||||||
|
gisMap
|
||||||
|
.getLayers()
|
||||||
|
.getArray()
|
||||||
|
.filter((e: any) => e.get("lyrTyNm") == "tb_ms_swage_ppl")
|
||||||
|
.forEach((layer: any) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (foundFeature) select?.selectMultiFeature?.(foundFeature, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!foundFeature) alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||||
|
|
||||||
|
const geometry = foundFeature?.getGeometry();
|
||||||
|
if (geometry) {
|
||||||
|
const extent = geometry.getExtent();
|
||||||
|
gisMap.getView().fit(extent, {
|
||||||
|
duration: 300,
|
||||||
|
padding: [50, 50, 50, 50],
|
||||||
|
maxZoom: 14,
|
||||||
|
constrainResolution: true,
|
||||||
|
nearest: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const inputColumn = [
|
const inputColumn = [
|
||||||
{ id: "swtrmtZoneNm", title: "처리구역", type: "input" },
|
{ id: "swtrmtZoneNm", title: "처리구역", type: "input" },
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|||||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||||
import SearchModal from "./searchModal/searchModal";
|
import SearchModal from "./searchModal/searchModal";
|
||||||
import FeatureClickEventPopup from "./components/popupComponent/featureClickEventPopup";
|
import FeatureClickEventPopup from "./components/popupComponent/featureClickEventPopup";
|
||||||
import { WaterDepth, WATER_DEPTH_LAYER_TYPES, ORDERED_LAYERS } from "./waterDepth/waterDepth";
|
import { WaterDepth, WATER_DEPTH_LAYER_TYPES } from "./waterDepth/waterDepth";
|
||||||
import { ProcSearchBar } from "@common/business_components/ui/map/searchBar";
|
import { ProcSearchBar } from "@common/business_components/ui/map/searchBar";
|
||||||
|
|
||||||
// 타입 정의 추가
|
// 타입 정의 추가
|
||||||
@@ -160,7 +160,6 @@ const Map = () => {
|
|||||||
isPlaying={isPlaying}
|
isPlaying={isPlaying}
|
||||||
setIsPlaying={setIsPlaying as any}
|
setIsPlaying={setIsPlaying as any}
|
||||||
targetLayerTypes={WATER_DEPTH_LAYER_TYPES as any}
|
targetLayerTypes={WATER_DEPTH_LAYER_TYPES as any}
|
||||||
orderedLayers={ORDERED_LAYERS as any}
|
|
||||||
checkInterval={2500}
|
checkInterval={2500}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -149,7 +149,6 @@ const ReportTab: React.FC<ReportTabProps> = ({ open, isSearch = true, ctpvNm, sg
|
|||||||
// if (gubun !== "") { // 구분검색 (추후 사용할 수도 있을 것 같아 남겨둠)
|
// if (gubun !== "") { // 구분검색 (추후 사용할 수도 있을 것 같아 남겨둠)
|
||||||
// filterReportData = filterReportData.filter(item => (item.plantZoneGubun) ? String(item.plantZoneGubun).includes(String(gubun)) : null);
|
// filterReportData = filterReportData.filter(item => (item.plantZoneGubun) ? String(item.plantZoneGubun).includes(String(gubun)) : null);
|
||||||
// }
|
// }
|
||||||
console.log('report count:', filterReportData.length);
|
|
||||||
|
|
||||||
setTableCount(filterReportData.length || 0);
|
setTableCount(filterReportData.length || 0);
|
||||||
|
|
||||||
|
|||||||
@@ -80,12 +80,9 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
const [end2, setEnd2] = useState("");
|
const [end2, setEnd2] = useState("");
|
||||||
const [var1, setVar1] = useState("");
|
const [var1, setVar1] = useState("");
|
||||||
const [var2, setVar2] = useState("");
|
const [var2, setVar2] = useState("");
|
||||||
const [var3, setVar3] = useState("");
|
|
||||||
const [var4, setVar4] = useState("");
|
|
||||||
// 시작, 종료
|
|
||||||
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
|
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
|
||||||
|
|
||||||
const { ctpv, sgg, BIZ_TY_CD, BIZ_SE_CD } = useStore();
|
const { ctpv, sgg, BIZ_TY_CD } = useStore();
|
||||||
|
|
||||||
|
|
||||||
// 오늘 날짜 구하기
|
// 오늘 날짜 구하기
|
||||||
@@ -128,8 +125,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
setEnd2("");
|
setEnd2("");
|
||||||
setVar1("");
|
setVar1("");
|
||||||
setVar2("");
|
setVar2("");
|
||||||
setVar3("");
|
|
||||||
setVar4("");
|
|
||||||
setCtpvCd("0");
|
setCtpvCd("0");
|
||||||
setSggCd("0");
|
setSggCd("0");
|
||||||
setYearFilter({
|
setYearFilter({
|
||||||
@@ -196,7 +191,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
||||||
|
|
||||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||||
console.log('api:', `${API_URL}/v1/fclts?${queryParams.toString()}`);
|
|
||||||
|
|
||||||
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
|
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
|
||||||
const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => {
|
const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => {
|
||||||
@@ -231,7 +225,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("dataWithRowNumber: ", dataWithRowNumber)
|
|
||||||
setTableData(dataWithRowNumber);
|
setTableData(dataWithRowNumber);
|
||||||
setTableCount(res.data.count || []);
|
setTableCount(res.data.count || []);
|
||||||
|
|
||||||
@@ -264,7 +257,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
const params = new URLSearchParams(RequestDTO).toString();
|
const params = new URLSearchParams(RequestDTO).toString();
|
||||||
|
|
||||||
const res = await getData(`v1/bizInfo?${params}`);
|
const res = await getData(`v1/bizInfo?${params}`);
|
||||||
console.log('운영정보 사업 데이터:', res.content);
|
|
||||||
|
|
||||||
const mappedContent = (res.content || []).map((item: any, index: number) => {
|
const mappedContent = (res.content || []).map((item: any, index: number) => {
|
||||||
const formattedItem = formatDisplayData(item);
|
const formattedItem = formatDisplayData(item);
|
||||||
@@ -512,7 +504,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"'));
|
RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"'));
|
||||||
|
|
||||||
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
|
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
|
||||||
console.log('운영정보 cellClick 데이터:', res.data);
|
|
||||||
|
|
||||||
const mappedData = (res.data || []).map((item: any, detailIndex: number) => {
|
const mappedData = (res.data || []).map((item: any, detailIndex: number) => {
|
||||||
const formattedItem = formatDisplayData(item);
|
const formattedItem = formatDisplayData(item);
|
||||||
@@ -527,7 +518,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-',
|
swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log('운영정보 cellClick 데이터:', mappedData);
|
|
||||||
|
|
||||||
setDetailTableData(mappedData);
|
setDetailTableData(mappedData);
|
||||||
setDetailTableCount(res.count || 0);
|
setDetailTableCount(res.count || 0);
|
||||||
@@ -565,7 +555,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-',
|
swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log('운영정보 PageNation 데이터:', mappedData);
|
|
||||||
|
|
||||||
setDetailTableData(mappedData);
|
setDetailTableData(mappedData);
|
||||||
setDetailTableCount(res.count || 0);
|
setDetailTableCount(res.count || 0);
|
||||||
@@ -667,7 +656,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
||||||
|
|
||||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||||
console.log('res.data.data', res.data.data);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -785,7 +773,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
|
|
||||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||||
const excelData = mappedContent; // 배열로 감싸서 전달
|
const excelData = mappedContent; // 배열로 감싸서 전달
|
||||||
console.log('excelData', excelData);
|
|
||||||
|
|
||||||
// 파일명 생성 (분석번호 기반)
|
// 파일명 생성 (분석번호 기반)
|
||||||
const label = BASIC_TYPES.find((type) => type.val === basicType)?.text
|
const label = BASIC_TYPES.find((type) => type.val === basicType)?.text
|
||||||
@@ -929,8 +916,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
const apiStartTime = performance.now();
|
const apiStartTime = performance.now();
|
||||||
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
|
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
|
||||||
const apiEndTime = performance.now();
|
const apiEndTime = performance.now();
|
||||||
console.log(`🚀 1. API 호출 시간: ${(apiEndTime - apiStartTime).toFixed(2)}ms`);
|
|
||||||
console.log('운영정보 결과 다운로드.data:', res.data);
|
|
||||||
|
|
||||||
const mappingStartTime = performance.now();
|
const mappingStartTime = performance.now();
|
||||||
// 헤더 매핑 정의
|
// 헤더 매핑 정의
|
||||||
@@ -974,13 +959,10 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
const mappingEndTime = performance.now();
|
const mappingEndTime = performance.now();
|
||||||
console.log(`🔄 2. 데이터 매핑 시간: ${(mappingEndTime - mappingStartTime).toFixed(2)}ms`);
|
|
||||||
|
|
||||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||||
const excelData = mappedData; // 배열로 감싸서 전달
|
const excelData = mappedData; // 배열로 감싸서 전달
|
||||||
|
|
||||||
console.log('excelData', excelData);
|
|
||||||
|
|
||||||
const excelStartTime = performance.now();
|
const excelStartTime = performance.now();
|
||||||
// 파일명 생성 (분석번호 기반)
|
// 파일명 생성 (분석번호 기반)
|
||||||
const label = WORK_TYPES.find((type) => type.val === workType)?.text
|
const label = WORK_TYPES.find((type) => type.val === workType)?.text
|
||||||
@@ -994,7 +976,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
headerMapping: headerMapping
|
headerMapping: headerMapping
|
||||||
});
|
});
|
||||||
const excelEndTime = performance.now();
|
const excelEndTime = performance.now();
|
||||||
console.log(`📊 3. Excel 다운로드 시간: ${(excelEndTime - excelStartTime).toFixed(2)}ms`);
|
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1023,7 +1004,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const row = detailTableData[0];
|
const row = detailTableData[0];
|
||||||
console.log('row', row);
|
|
||||||
const layerNm = row.lyrTypeNm;
|
const layerNm = row.lyrTypeNm;
|
||||||
const fids = detailTableData.map((item) => item.fid);
|
const fids = detailTableData.map((item) => item.fid);
|
||||||
const core = GisApp.Module.layer as any;
|
const core = GisApp.Module.layer as any;
|
||||||
@@ -1116,6 +1096,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
onPageChange={setCurPage}
|
onPageChange={setCurPage}
|
||||||
resetPageNo={isResetPage}
|
resetPageNo={isResetPage}
|
||||||
resetSelection={isResetSelection}
|
resetSelection={isResetSelection}
|
||||||
|
curPage={curPage}
|
||||||
headButton={
|
headButton={
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button>
|
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button>
|
||||||
@@ -1147,6 +1128,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
onPageChange={setCurPage}
|
onPageChange={setCurPage}
|
||||||
resetPageNo={isResetPage}
|
resetPageNo={isResetPage}
|
||||||
resetSelection={isResetSelection}
|
resetSelection={isResetSelection}
|
||||||
|
curPage={curPage}
|
||||||
headButton={
|
headButton={
|
||||||
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={operationExcelDownload}>파일 다운로드</Button>
|
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={operationExcelDownload}>파일 다운로드</Button>
|
||||||
}
|
}
|
||||||
@@ -1173,6 +1155,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
|||||||
onPageChange={setDetailCurPage}
|
onPageChange={setDetailCurPage}
|
||||||
resetPageNo={isResetDetailPage}
|
resetPageNo={isResetDetailPage}
|
||||||
resetSelection={isResetDetailSelection}
|
resetSelection={isResetDetailSelection}
|
||||||
|
curPage={detailCurPage}
|
||||||
headButton={
|
headButton={
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button>
|
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button>
|
||||||
|
|||||||
@@ -7,16 +7,6 @@ export const WATER_DEPTH_LAYER_TYPES: string[] = [
|
|||||||
'tb_ms_flood_dngr_map',
|
'tb_ms_flood_dngr_map',
|
||||||
'tb_ms_sbmrg_dngr_map'
|
'tb_ms_sbmrg_dngr_map'
|
||||||
];
|
];
|
||||||
// 지정된 순서에 따른 lyr_sn 배열
|
|
||||||
export const ORDERED_LAYERS: string[] = [
|
|
||||||
'27', // 침수위험지도(tb_ms_sbmrg_dngr_map) - 30년 빈도
|
|
||||||
'28', // 침수위험지도(tb_ms_sbmrg_dngr_map) - 50년 빈도
|
|
||||||
'3', // 침수위험지도(tb_ms_sbmrg_dngr_map) - 80년 빈도
|
|
||||||
'4', // 침수위험지도(tb_ms_sbmrg_dngr_map) - 100년 빈도
|
|
||||||
'59', // 도시침수지도(tb_ms_flood_dngr_map) - 30년 빈도
|
|
||||||
'60', // 도시침수지도(tb_ms_flood_dngr_map) - 50년 빈도
|
|
||||||
'58' // 도시침수지도(tb_ms_flood_dngr_map) - 80년 빈도
|
|
||||||
];
|
|
||||||
|
|
||||||
interface WaterDepthProps {
|
interface WaterDepthProps {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { TableHead, TableRow } from "@common/components/ui";
|
||||||
|
|
||||||
|
|
||||||
|
export type TableUpperProps = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
upName?: string;
|
||||||
|
upSequnce?: number;
|
||||||
|
edit?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
rowSpan?: number;
|
||||||
|
colSpan?: number;
|
||||||
|
type?: string;
|
||||||
|
selectData?: string; // type 이 seelct 일경우 selectarray 정하기 위해
|
||||||
|
row?: number;
|
||||||
|
mergeCell?: boolean // 셀을 합칠경우
|
||||||
|
maxLength?: number
|
||||||
|
readonly?: boolean
|
||||||
|
};
|
||||||
|
type chHeadType = {
|
||||||
|
headNm: string;
|
||||||
|
text: string;
|
||||||
|
length: number;
|
||||||
|
rows?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HeadMakeFloodPre = (
|
||||||
|
upHeadList: TableUpperProps[],
|
||||||
|
makeHeadNm: chHeadType[]
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TableRow>
|
||||||
|
{upHeadList
|
||||||
|
.filter((data) => data.visible != false)
|
||||||
|
.map((head: TableUpperProps) =>
|
||||||
|
!head.upSequnce ? (
|
||||||
|
<TableHead
|
||||||
|
className={`border border-gray-600 px-2 py-1 `}
|
||||||
|
rowSpan={2}
|
||||||
|
key={head.id}
|
||||||
|
>
|
||||||
|
{head.title}
|
||||||
|
</TableHead>
|
||||||
|
) : head.upSequnce === 1 ? (
|
||||||
|
<TableHead
|
||||||
|
className={`border border-gray-600 px-2 py-1 `}
|
||||||
|
key={head.id}
|
||||||
|
rowSpan={1}
|
||||||
|
colSpan={
|
||||||
|
makeHeadNm?.find((ch) => ch.headNm === head.upName)?.length
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{makeHeadNm?.find((ch) => ch.headNm === head.upName)?.text}
|
||||||
|
</TableHead>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
{upHeadList
|
||||||
|
.filter((data) => data.visible != false)
|
||||||
|
.map((head: TableUpperProps) =>
|
||||||
|
makeHeadNm?.find((ch) => ch.headNm === head.upName) ? (
|
||||||
|
<TableHead
|
||||||
|
className={`border border-gray-600 px-2 py-1 `}
|
||||||
|
key={head.id}
|
||||||
|
>
|
||||||
|
{head.title}
|
||||||
|
</TableHead>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</TableRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import type { TableUpperProps } from "./headMakeFloodPre";
|
||||||
|
import { Input, TableCell, TableRow } from "@common/components/ui";
|
||||||
|
|
||||||
|
|
||||||
|
// 숫자입력시 , 삽입
|
||||||
|
const formatCommaNumber = (
|
||||||
|
value: number | string | undefined | null
|
||||||
|
): string => {
|
||||||
|
if (value === undefined || value === null || value === "") return "";
|
||||||
|
const num = Number(value);
|
||||||
|
// 숫자로 변환할 수 없는 값이 들어올 경우 빈 문자열을 반환하여 예상치 못한 "NaN" 출력을 방지합니다.
|
||||||
|
if (isNaN(num)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return num.toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// onchange 텍스트를 숫자로 and ,제거
|
||||||
|
const handleCommaNumberChange = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
onChange: (value: number | "") => void
|
||||||
|
) => {
|
||||||
|
const raw = e.target.value.replace(/,/g, "");
|
||||||
|
if (!/^\d*$/.test(raw)) return;
|
||||||
|
onChange(raw === "" ? "" : Number(raw));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (
|
||||||
|
rowIndex: number,
|
||||||
|
field: any,
|
||||||
|
value: string,
|
||||||
|
setInputData: (data: any) => void
|
||||||
|
) => {
|
||||||
|
if (Number(value) < 0 || !Number(value)) {
|
||||||
|
value = "0";
|
||||||
|
}
|
||||||
|
setInputData((prevData: any) =>
|
||||||
|
prevData?.map((row: any, index: number) => {
|
||||||
|
if (index === rowIndex) {
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
[field]: Number(value.replace(/,/g, "")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InputTableBody = (
|
||||||
|
chgRes: any[] | [],
|
||||||
|
headList: TableUpperProps[],
|
||||||
|
setInputData: (data: any[]) => void,
|
||||||
|
isEditable: boolean = true
|
||||||
|
) => {
|
||||||
|
// mergeCell 처리용
|
||||||
|
const getRowSpan = (data: any[], key: string, rowIndex: number) => {
|
||||||
|
if (rowIndex > 0 && data[rowIndex - 1][key] && String(data[rowIndex][key]).trim() === "") {
|
||||||
|
return 0; // 이전과 같으면 셀을 그리지 않음
|
||||||
|
}
|
||||||
|
let span = 1;
|
||||||
|
for (let i = rowIndex + 1; i < data.length; i++) {
|
||||||
|
if (data[i][key] === data[rowIndex][key]) span++;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{chgRes.map((data, dataIndex) => {
|
||||||
|
// 소계/총합계 행 판별
|
||||||
|
const isSubTotal = data["용수명"] === "소계";
|
||||||
|
const isTotal = data["구분"] === 0;
|
||||||
|
|
||||||
|
const bgClass = isTotal
|
||||||
|
? "bg-[#d5e1eb] font-bold"
|
||||||
|
: isSubTotal
|
||||||
|
? "bg-[#e6eef5] font-semibold"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={dataIndex + "row"}>
|
||||||
|
{headList.map((head, headIndex) => {
|
||||||
|
// console.log('head, head.id', head, head.id);
|
||||||
|
const isTextColumn = head.id === "__EMPTY_3";
|
||||||
|
const textAlignClass = isTextColumn
|
||||||
|
? "text-center"
|
||||||
|
: "text-right";
|
||||||
|
|
||||||
|
// // 총합계 행이면 "구분"에서 colSpan=2로 병합
|
||||||
|
// if (
|
||||||
|
// (data["구분"] === 0 || data["용수명"] === 0) &&
|
||||||
|
// head.id === "구분"
|
||||||
|
// ) {
|
||||||
|
// return (
|
||||||
|
// <TableCell
|
||||||
|
// key={head.id + headIndex}
|
||||||
|
// colSpan={2}
|
||||||
|
// className="font-bold text-center bg-[#d5e1eb]"
|
||||||
|
// >
|
||||||
|
// 총합계
|
||||||
|
// </TableCell>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// // 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
|
||||||
|
// if (
|
||||||
|
// (data["구분"] === 0 || data["용수명"] === 0) &&
|
||||||
|
// head.id === "용수명"
|
||||||
|
// ) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 총합계 행의 나머지 셀에도 색상 적용
|
||||||
|
// if (data["구분"] === 0 || data["용수명"] === 0) {
|
||||||
|
// return (
|
||||||
|
// <TableCell
|
||||||
|
// key={head.id + headIndex}
|
||||||
|
// className="bg-[#d5e1eb] font-bold text-right"
|
||||||
|
// >
|
||||||
|
// {Number(data[head.id])
|
||||||
|
// ? formatCommaNumber(data[head.id as keyof any])
|
||||||
|
// : (data[head.id as keyof any] ?? "")}
|
||||||
|
// </TableCell>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// mergeCell 처리
|
||||||
|
if (head.mergeCell === true) {
|
||||||
|
const rowSpan = getRowSpan(chgRes, head.id, dataIndex);
|
||||||
|
if (rowSpan === 0) return null;
|
||||||
|
return (
|
||||||
|
<TableCell
|
||||||
|
key={head.id + headIndex}
|
||||||
|
rowSpan={rowSpan}
|
||||||
|
className={`w-36 align-middle text-center whitespace-nowrap ${bgClass}`}
|
||||||
|
>
|
||||||
|
{Number(data[head.id])
|
||||||
|
? formatCommaNumber(data[head.id as keyof any])
|
||||||
|
: (data[head.id as keyof any] ?? "")}
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 기본 셀
|
||||||
|
// return head.edit === true && data.edit !== false ? (
|
||||||
|
return isEditable && head.edit === true && data.edit !== false ? (
|
||||||
|
<TableCell
|
||||||
|
key={head.id + headIndex}
|
||||||
|
className={`w-36 ${bgClass}`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
// className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`}
|
||||||
|
className={`${textAlignClass} !text-xl h-10 w-full`}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleCommaNumberChange(e, (val) =>
|
||||||
|
handleChange(
|
||||||
|
dataIndex,
|
||||||
|
head.id,
|
||||||
|
String(val),
|
||||||
|
setInputData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value={formatCommaNumber(data[head.id as keyof any]) ?? ""}
|
||||||
|
maxLength={12}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
) : (
|
||||||
|
<TableCell
|
||||||
|
key={head.id + headIndex}
|
||||||
|
className={`w-36 ${bgClass}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
// className={`
|
||||||
|
// ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
|
||||||
|
// !text-xl w-full h-10
|
||||||
|
// `}
|
||||||
|
className={`${textAlignClass} !text-xl w-full h-10`}
|
||||||
|
>
|
||||||
|
{Number(data[head.id])
|
||||||
|
? formatCommaNumber(data[head.id as keyof any])
|
||||||
|
: (data[head.id as keyof any] ?? "")}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{chgRes.length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={headList.length}
|
||||||
|
className="h-24 text-center text-gray-500"
|
||||||
|
>
|
||||||
|
조회된 결과가 없습니다.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,7 +2,10 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||||
import { UITab, UiTable } from '@common/business_components/ui'
|
import { UITab, UiTable } from '@common/business_components/ui'
|
||||||
|
import { Table, TableBody, TableHeader } from "@common/components/ui";
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
|
import { HeadMakeFloodPre } from './headMakeFloodPre';
|
||||||
|
import { InputTableBody } from './inputTableBody';
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ val: "management", text: "관리SHEET" },
|
{ val: "management", text: "관리SHEET" },
|
||||||
@@ -19,7 +22,7 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
|||||||
const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "management");
|
const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "management");
|
||||||
const [managementTableHeader, setManagementTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더
|
const [managementTableHeader, setManagementTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더
|
||||||
const [managementTableData, setManagementTableData] = useState<any>([{}]) //테이블 데이터
|
const [managementTableData, setManagementTableData] = useState<any>([{}]) //테이블 데이터
|
||||||
const [resultTableHeader, setResultTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더
|
const [resultTableHeader, setResultTableHeader] = useState<{ id: string; title: string, mergeCell?: boolean }[]>([]) //테이블 헤더
|
||||||
const [resultTableData, setResultTableData] = useState<any>([{}]) //테이블 데이터
|
const [resultTableData, setResultTableData] = useState<any>([{}]) //테이블 데이터
|
||||||
const [isLoading, setIsLoading] = useState(false) //로딩 상태
|
const [isLoading, setIsLoading] = useState(false) //로딩 상태
|
||||||
|
|
||||||
@@ -53,13 +56,12 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
|||||||
|
|
||||||
// 관리 SHEET
|
// 관리 SHEET
|
||||||
const managementHeader = jsonDataBySheet['관리SHEET']?.[7] || [];
|
const managementHeader = jsonDataBySheet['관리SHEET']?.[7] || [];
|
||||||
// 1번째~47번째, 74번째~80번째 데이터만 필터링
|
|
||||||
const filteredManagementHeader = Object.entries(managementHeader)
|
const filteredManagementHeader = Object.entries(managementHeader)
|
||||||
.filter(([key, value]) => {
|
.filter(([key, value]) => {
|
||||||
const match = key.match(/^__EMPTY_(\d+)$/);
|
const match = key.match(/^__EMPTY_(\d+)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
const index = parseInt(match[1] ?? '', 10);
|
const index = parseInt(match[1] ?? '', 10);
|
||||||
return (index > 0 && index < 47) || (index > 73);
|
return (0 < index && index < 47) || (73 < index);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
@@ -81,17 +83,20 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
|||||||
|
|
||||||
// 운영관리현황 점검결과
|
// 운영관리현황 점검결과
|
||||||
const resultHeader = jsonDataBySheet['운영관리현황 점검결과']?.[2] || [];
|
const resultHeader = jsonDataBySheet['운영관리현황 점검결과']?.[2] || [];
|
||||||
const dynamicInspectionHeader = Object.entries(resultHeader)
|
const dynamicResultHeader = Object.entries(resultHeader)
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
id: key,
|
id: key,
|
||||||
title: String(value).replace(/\r\n/g, '\n')
|
title: String(value).replace(/\r\n/g, '\n'),
|
||||||
|
mergeCell: false,
|
||||||
|
...(key === '__EMPTY_3' && { mergeCell: true }) // __EMPTY_3인 경우 mergeCell: true 추가
|
||||||
}));
|
}));
|
||||||
dynamicInspectionHeader.splice(1, 0, {
|
dynamicResultHeader.splice(1, 0, {
|
||||||
id: "__EMPTY_2",
|
id: "__EMPTY_2",
|
||||||
title: ''
|
title: '',
|
||||||
|
mergeCell: false
|
||||||
});
|
});
|
||||||
setResultTableHeader(dynamicInspectionHeader);
|
setResultTableHeader(dynamicResultHeader);
|
||||||
console.log('resultTableHeader', dynamicInspectionHeader);
|
console.log('resultTableHeader', dynamicResultHeader);
|
||||||
|
|
||||||
const resultData = Array.isArray(jsonDataBySheet['운영관리현황 점검결과'])
|
const resultData = Array.isArray(jsonDataBySheet['운영관리현황 점검결과'])
|
||||||
? jsonDataBySheet['운영관리현황 점검결과'].slice(3)
|
? jsonDataBySheet['운영관리현황 점검결과'].slice(3)
|
||||||
@@ -225,7 +230,7 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
|||||||
)}
|
)}
|
||||||
{/* 운영관리현황 점검결과 탭 */}
|
{/* 운영관리현황 점검결과 탭 */}
|
||||||
{activeTab === 'Inspection results' && (
|
{activeTab === 'Inspection results' && (
|
||||||
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
<div className="h-[79vh] w-full overflow-x-hidden overflow-y-auto rounded-lg border">
|
||||||
<div className="flex-1 px-2 pb-2">
|
<div className="flex-1 px-2 pb-2">
|
||||||
<LoadingOverlay isLoading={isLoading} />
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
<UiTable
|
<UiTable
|
||||||
@@ -238,6 +243,14 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
|||||||
cellClick={undefined}
|
cellClick={undefined}
|
||||||
onPageChange={() => { }}
|
onPageChange={() => { }}
|
||||||
/>
|
/>
|
||||||
|
{/* <Table variant="secondary">
|
||||||
|
<TableHeader className="font-bold">
|
||||||
|
{HeadMakeFloodPre(resultTableHeader, [])}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{InputTableBody(resultTableData ?? [], resultTableHeader, setResultTableData, false)}
|
||||||
|
</TableBody>
|
||||||
|
</Table> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { deleteData, postData, useCookieAuth } from '@/components/lib/api'
|
import { deleteData, postData, useCookieAuth } from '@/components/lib/api'
|
||||||
import { searchData } from '@/components/lib/common/apiHelpers'
|
import { searchData } from '@/components/lib/common/apiHelpers'
|
||||||
import { formatDate, handleDownload, yearOptions } from '@/components/lib/utils'
|
import { formatDate, handleDownload } from '@/components/lib/utils'
|
||||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||||
import { SearchDiv, SearchFormLeft, SearchFormRight, UiTable, UiModal } from '@common/business_components/ui'
|
import { SearchDiv, SearchFormLeft, SearchFormRight, UiTable, UiModal } from '@common/business_components/ui'
|
||||||
import ListDiv from '@common/business_components/ui/list_div'
|
import ListDiv from '@common/business_components/ui/list_div'
|
||||||
@@ -14,6 +14,7 @@ import { AUTH_USER_ID } from "@/shared/config";
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { ENDPOINT } from "@common/components/config";
|
import { ENDPOINT } from "@common/components/config";
|
||||||
import { getUserInfo } from "@common/components/api";
|
import { getUserInfo } from "@common/components/api";
|
||||||
|
import TableModal from './tableModal';
|
||||||
|
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
@@ -30,7 +31,8 @@ const Page = () => {
|
|||||||
const [curPage, setCurPage] = useState(1)
|
const [curPage, setCurPage] = useState(1)
|
||||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||||
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false) // 모달 상태
|
const [isModalOpen, setIsModalOpen] = useState(false) // 등록 모달 상태
|
||||||
|
const [isTableModalOpen, setIsTableModalOpen] = useState(false) // 테이블 모달 상태
|
||||||
const [resetSelection, setResetSelection] = useState<boolean>(false); // 선택 초기화 상태
|
const [resetSelection, setResetSelection] = useState<boolean>(false); // 선택 초기화 상태
|
||||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -223,6 +225,7 @@ const Page = () => {
|
|||||||
setFileNm(fileName);
|
setFileNm(fileName);
|
||||||
setAtflId(id);
|
setAtflId(id);
|
||||||
setDcrpPplMtncBizSn(tableData[index]?.dcrpPplMtncBizSn);
|
setDcrpPplMtncBizSn(tableData[index]?.dcrpPplMtncBizSn);
|
||||||
|
setIsTableModalOpen(true);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPageChange={setCurPage}
|
onPageChange={setCurPage}
|
||||||
@@ -245,6 +248,21 @@ const Page = () => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{/* 엑셀 데이터 테이블 모달 */}
|
||||||
|
<UiModal
|
||||||
|
open={isTableModalOpen}
|
||||||
|
onOpenChange={setIsTableModalOpen}
|
||||||
|
bodyClass={`
|
||||||
|
h-[100dvh]
|
||||||
|
w-[90dvw]
|
||||||
|
max-w-[100dvw]
|
||||||
|
max-h-[100dvh]
|
||||||
|
overflow-hidden
|
||||||
|
z-[101]
|
||||||
|
`}
|
||||||
|
body={<TableModal fileNm={fileNm} atflId={atflId} />}
|
||||||
|
useDirectRender={true}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,465 @@
|
|||||||
|
'use client'
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||||
|
import { UITab, UiTable } from '@common/business_components/ui'
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan';
|
||||||
|
import { oldPpTable2HeadList, oldPpTable2NodeList, oldPpTable6HeadList, oldPpTable6NodeList } from './headList';
|
||||||
|
|
||||||
|
const TABS = [
|
||||||
|
{ val: "1", text: "row data" },
|
||||||
|
{ val: "2", text: "1.정밀조사" },
|
||||||
|
{ val: "3", text: "2.정밀조사_정비사업연계" },
|
||||||
|
{ val: "4", text: " 3.정비사업" },
|
||||||
|
{ val: "5", text: "4.정비사업요약" },
|
||||||
|
{ val: "6", text: "4.정비사업최종" },
|
||||||
|
];
|
||||||
|
|
||||||
|
interface TableModalProps {
|
||||||
|
fileNm: string;
|
||||||
|
atflId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "1");
|
||||||
|
const [tableHeader1, setTableHeader1] = useState<{ id: string; title: string }[]>([])
|
||||||
|
const [tableHeader2, setTableHeader2] = useState<{ id: string; title: string }[]>([])
|
||||||
|
const [tableHeader4, setTableHeader4] = useState<{ id: string; title: string }[]>([])
|
||||||
|
const [tableHeader5, setTableHeader5] = useState<{ id: string; title: string }[]>([])
|
||||||
|
const [tableData1, setTableData1] = useState<any>([{}])
|
||||||
|
const [tableData2, setTableData2] = useState<any>([{}])
|
||||||
|
const [tableData3, setTableData3] = useState<any>([{}])
|
||||||
|
const [tableData4, setTableData4] = useState<any>([{}])
|
||||||
|
const [tableData5, setTableData5] = useState<any>([{}])
|
||||||
|
const [tableData6, setTableData6] = useState<any>([{}])
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isChckFile();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 파일 다운로드
|
||||||
|
const isChckFile = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
if (fileNm === '' || fileNm === undefined) {
|
||||||
|
alert('파일을 선택해주세요');
|
||||||
|
setIsLoading(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/attach-service/api/v1/download/${atflId}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('파일을 가져오는 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
const file = new File([blob], fileNm, {
|
||||||
|
type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetSheets = ['row data', '1.정밀조사', '2.정밀조사_정비사업연계', ' 3.정비사업', '4.정비사업요약', '4.정비사업최종'];
|
||||||
|
const jsonDataBySheet = await filePickedSelectedSheets(file, 0, targetSheets);
|
||||||
|
|
||||||
|
const header1 = jsonDataBySheet['row data']?.[1] || [];
|
||||||
|
const header2 = jsonDataBySheet['1.정밀조사']?.[1] || [];
|
||||||
|
const header4 = jsonDataBySheet[' 3.정비사업']?.[1] || [];
|
||||||
|
const header5 = jsonDataBySheet['4.정비사업요약']?.[2] || [];
|
||||||
|
|
||||||
|
// row data
|
||||||
|
const allEntries1 = Object.entries(header1);
|
||||||
|
const filteredHeader1 = [
|
||||||
|
allEntries1[0],
|
||||||
|
allEntries1[1],
|
||||||
|
allEntries1[2],
|
||||||
|
allEntries1[3],
|
||||||
|
allEntries1[5],
|
||||||
|
allEntries1[6],
|
||||||
|
allEntries1[7],
|
||||||
|
allEntries1[8],
|
||||||
|
allEntries1[9],
|
||||||
|
allEntries1[10],
|
||||||
|
allEntries1[11],
|
||||||
|
allEntries1[12],
|
||||||
|
allEntries1[13],
|
||||||
|
allEntries1[14],
|
||||||
|
allEntries1[15]
|
||||||
|
].filter((e): e is [string, any] => Boolean(e))
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: key,
|
||||||
|
title: String(value).replace(/\r\n/g, '\n')
|
||||||
|
}));
|
||||||
|
filteredHeader1.splice(0, 0, {
|
||||||
|
id: "rowNumber",
|
||||||
|
title: '일련번호'
|
||||||
|
});
|
||||||
|
setTableHeader1(filteredHeader1);
|
||||||
|
|
||||||
|
const data1 = Array.isArray(jsonDataBySheet['row data'])
|
||||||
|
? jsonDataBySheet['row data'].slice(2)
|
||||||
|
: [];
|
||||||
|
const filteredData1 = filterEmptyRows(data1).map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
rowNumber: index + 1,
|
||||||
|
__EMPTY_1: item.__EMPTY_1 || '-'
|
||||||
|
}));
|
||||||
|
setTableData1(filteredData1);
|
||||||
|
|
||||||
|
// 1.정밀조사
|
||||||
|
const allEntries2 = Object.entries(header2);
|
||||||
|
let filteredHeader2 = [
|
||||||
|
allEntries2[0],
|
||||||
|
allEntries2[1],
|
||||||
|
allEntries2[2],
|
||||||
|
allEntries2[3],
|
||||||
|
allEntries2[5],
|
||||||
|
allEntries2[6],
|
||||||
|
allEntries2[7],
|
||||||
|
allEntries2[8],
|
||||||
|
allEntries2[9],
|
||||||
|
allEntries2[10],
|
||||||
|
allEntries2[11],
|
||||||
|
allEntries2[12],
|
||||||
|
allEntries2[13],
|
||||||
|
allEntries2[14],
|
||||||
|
allEntries2[15]
|
||||||
|
].filter((e): e is [string, any] => Boolean(e))
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: key,
|
||||||
|
title: String(value).replace(/\r\n/g, '\n')
|
||||||
|
}));
|
||||||
|
filteredHeader2.splice(0, 0, {
|
||||||
|
id: "rowNumber",
|
||||||
|
title: '일련번호'
|
||||||
|
});
|
||||||
|
setTableHeader2(filteredHeader2);
|
||||||
|
|
||||||
|
const data2 = Array.isArray(jsonDataBySheet['1.정밀조사'])
|
||||||
|
? jsonDataBySheet['1.정밀조사'].slice(2)
|
||||||
|
: [];
|
||||||
|
const filteredData2 = filterEmptyRows(data2).map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
rowNumber: index + 1
|
||||||
|
}));
|
||||||
|
setTableData2(filteredData2);
|
||||||
|
|
||||||
|
// 2.정밀조사_정비사업연계 (헤더는 import)
|
||||||
|
const data3 = Array.isArray(jsonDataBySheet['2.정밀조사_정비사업연계'])
|
||||||
|
? jsonDataBySheet['2.정밀조사_정비사업연계'].slice(5)
|
||||||
|
: [];
|
||||||
|
const filteredData3 = filterEmptyRows(data3).map((item: any, index: number) => {
|
||||||
|
const newItem = { ...item };
|
||||||
|
// 매핑 에러로 인해 특정 키를 "__EMPTY_0"으로 변경
|
||||||
|
const targetKey = "*1차 출처: 공단자료\r\n*2차 출처: 정산서, 정밀조사보고서 등.\r\n (파랑) 2024년말 기준 실적 제출 자료. 준공 후 재확인 필요\r\n (노랑셀) 준공(또는 정산) 후에 재확인 필요";
|
||||||
|
if (newItem[targetKey] !== undefined) {
|
||||||
|
newItem["__EMPTY_0"] = newItem[targetKey];
|
||||||
|
delete newItem[targetKey];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...newItem,
|
||||||
|
__EMPTY_4: newItem?.__EMPTY_4 || '-',
|
||||||
|
rowNumber: index + 1
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setTableData3(filteredData3);
|
||||||
|
|
||||||
|
// 3.정비사업
|
||||||
|
const allEntries4 = Object.entries(header4);
|
||||||
|
const filteredHeader4 = allEntries4.filter((e): e is [string, any] => Boolean(e))
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: key,
|
||||||
|
title: String(value).replace(/\r\n/g, '\n')
|
||||||
|
}));
|
||||||
|
|
||||||
|
filteredHeader4.splice(0, 0, {
|
||||||
|
id: "rowNumber",
|
||||||
|
title: '일련번호'
|
||||||
|
});
|
||||||
|
setTableHeader4(filteredHeader4);
|
||||||
|
|
||||||
|
const data4 = Array.isArray(jsonDataBySheet[' 3.정비사업'])
|
||||||
|
? jsonDataBySheet[' 3.정비사업'].slice(2)
|
||||||
|
: [];
|
||||||
|
const filteredData4 = filterEmptyRows(data4).map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
rowNumber: index + 1,
|
||||||
|
__EMPTY_2: item?.__EMPTY_2 || '-',
|
||||||
|
__EMPTY_11: item?.__EMPTY_11 || '-',
|
||||||
|
}));
|
||||||
|
setTableData4(filteredData4);
|
||||||
|
|
||||||
|
// 4.정비사업요약
|
||||||
|
const allEntries5 = Object.entries(header5);
|
||||||
|
const excludeKeys5 = [
|
||||||
|
": \"정비사업\"sheet에서 사업코드 ctrl+c, Ctrl+v 한 다음 중복된 데이터 제거실시",
|
||||||
|
"__EMPTY",
|
||||||
|
"__EMPTY_8",
|
||||||
|
"__EMPTY_15"
|
||||||
|
];
|
||||||
|
const filteredHeader5 = allEntries5
|
||||||
|
.filter(([key]) => !excludeKeys5.includes(key))
|
||||||
|
.filter((e): e is [string, any] => Boolean(e))
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: key,
|
||||||
|
title: String(value).replace(/\r\n/g, '\n'),
|
||||||
|
}));
|
||||||
|
filteredHeader5.splice(0, 0, {
|
||||||
|
id: "rowNumber",
|
||||||
|
title: '일련번호'
|
||||||
|
});
|
||||||
|
setTableHeader5(filteredHeader5);
|
||||||
|
|
||||||
|
const data5 = Array.isArray(jsonDataBySheet['4.정비사업요약'])
|
||||||
|
? jsonDataBySheet['4.정비사업요약'].slice(3)
|
||||||
|
: [];
|
||||||
|
const filteredData5 =
|
||||||
|
filterEmptyRows(data5).map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
rowNumber: index + 1,
|
||||||
|
}));
|
||||||
|
setTableData5(filteredData5);
|
||||||
|
|
||||||
|
// 4.정비사업최종 (헤더는 import)
|
||||||
|
const data6 = Array.isArray(jsonDataBySheet['4.정비사업최종'])
|
||||||
|
? jsonDataBySheet['4.정비사업최종'].slice(6)
|
||||||
|
: [];
|
||||||
|
const filteredData6 =
|
||||||
|
filterEmptyRows(data6).map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
rowNumber: index + 1,
|
||||||
|
}));
|
||||||
|
setTableData6(filteredData6);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('파일 처리 중 오류 발생:', error);
|
||||||
|
alert('파일 처리 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterEmptyRows(data: any[]) {
|
||||||
|
return data
|
||||||
|
.filter(row => {
|
||||||
|
return Object.entries(row).some(([key, value]) => {
|
||||||
|
const match = key.match(/^__EMPTY_(\d+)$/);
|
||||||
|
if (!match || parseInt(match[1] ?? '', 10) < 2) return false;
|
||||||
|
|
||||||
|
return value !== undefined &&
|
||||||
|
value !== null &&
|
||||||
|
String(value).trim() !== '' &&
|
||||||
|
String(value).trim() !== '-' &&
|
||||||
|
String(value).trim() !== '';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.map((row) => {
|
||||||
|
const processedRow = { ...row };
|
||||||
|
const keys = Object.keys(processedRow);
|
||||||
|
|
||||||
|
keys.forEach((key, keyIndex) => {
|
||||||
|
// 마지막 속성(사업 근거)이 아닌 경우에만 빈 값을 '-'으로 변경
|
||||||
|
if (keyIndex !== keys.length - 1) {
|
||||||
|
const value = processedRow[key];
|
||||||
|
if (value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
String(value).trim() === '') {
|
||||||
|
processedRow[key] = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return processedRow;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 특정 시트만 선택적으로 JSON으로 변환하는 함수
|
||||||
|
function filePickedSelectedSheets(file: File, headerNum: number = 0, sheetNames: string[]): Promise<Record<string, any[]>> {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
reader.onload = function (e) {
|
||||||
|
const data = e.target?.result;
|
||||||
|
if (!data) {
|
||||||
|
console.error("Failed to read file: e.target.result is null");
|
||||||
|
reject(new Error("Failed to read file"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excel 파일 읽기
|
||||||
|
const wb = XLSX.read(data, { type: 'binary', cellDates: true });
|
||||||
|
const result: Record<string, any[]> = {};
|
||||||
|
|
||||||
|
// 지정된 시트 이름만 찾아서 처리
|
||||||
|
sheetNames.forEach(targetSheetName => {
|
||||||
|
// 대소문자 구분 없이 시트 이름 찾기
|
||||||
|
const sheetName = wb.SheetNames.find(name =>
|
||||||
|
name.toLowerCase() === targetSheetName.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sheetName) {
|
||||||
|
const sheet = wb.Sheets[sheetName];
|
||||||
|
if (sheet) {
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw: false });
|
||||||
|
result[targetSheetName] = jsonData;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Sheet "${targetSheetName}" not found in workbook`);
|
||||||
|
result[targetSheetName] = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = function () {
|
||||||
|
console.error("Error reading file");
|
||||||
|
reject(new Error("Error reading file"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 파일 읽기 시작
|
||||||
|
reader.readAsBinaryString(file);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 탭 영역 */}
|
||||||
|
<UITab
|
||||||
|
defaultValue={activeTab}
|
||||||
|
tabList={TABS.map((tab) => ({
|
||||||
|
value: tab.val,
|
||||||
|
label: tab.text
|
||||||
|
}))}
|
||||||
|
onValueChange={(value: string) => setActiveTab(value)}
|
||||||
|
/>
|
||||||
|
{/* row data 탭 */}
|
||||||
|
{activeTab === '1' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[150%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'row data'}
|
||||||
|
tableData={tableData1}
|
||||||
|
headlist={tableHeader1}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 1.정밀조사 탭 */}
|
||||||
|
{activeTab === '2' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[150%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'1.정밀조사'}
|
||||||
|
tableData={tableData2}
|
||||||
|
headlist={tableHeader2}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
className={"overflow-x-auto overflow-y-auto"}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 2.정밀조사_정비사업연계 탭 */}
|
||||||
|
{activeTab === '3' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[550%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'2.정밀조사_정비사업연계'}
|
||||||
|
tableData={tableData3}
|
||||||
|
headlist={oldPpTable2HeadList}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
className={"overflow-x-auto overflow-y-auto"}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
>
|
||||||
|
<HeadMakeColSpan upHeadList={oldPpTable2NodeList} />
|
||||||
|
</UiTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 3.정비사업 탭 */}
|
||||||
|
{activeTab === '4' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[160%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'3.정비사업'}
|
||||||
|
tableData={tableData4}
|
||||||
|
headlist={tableHeader4}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
className={"overflow-x-auto overflow-y-auto"}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 4.정비사업요약 탭 */}
|
||||||
|
{activeTab === '5' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[130%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'4.정비사업요약'}
|
||||||
|
tableData={tableData5}
|
||||||
|
headlist={tableHeader5}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
className={"overflow-x-auto overflow-y-auto"}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 4.정비사업최종 탭 */}
|
||||||
|
{activeTab === '6' && (
|
||||||
|
<div className="h-[79vh] w-full overflow-auto rounded-lg border">
|
||||||
|
<div className="flex-1 px-2 pb-2">
|
||||||
|
<LoadingOverlay isLoading={isLoading} />
|
||||||
|
<div className="min-w-[200%]">
|
||||||
|
<UiTable
|
||||||
|
headName={'4.정비사업최종'}
|
||||||
|
tableData={tableData6}
|
||||||
|
headlist={oldPpTable6HeadList}
|
||||||
|
pageSize={10}
|
||||||
|
total={0}
|
||||||
|
className={"overflow-x-auto overflow-y-auto"}
|
||||||
|
cellClick={undefined}
|
||||||
|
onPageChange={() => { }}
|
||||||
|
>
|
||||||
|
<HeadMakeColSpan upHeadList={oldPpTable6NodeList} />
|
||||||
|
</UiTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableModal;
|
||||||
@@ -27,3 +27,5 @@ NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
|||||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
|||||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||||
|
|
||||||
|
|
||||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BE
|
|||||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
|
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||||
|
NEXT_PUBLIC_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const ruseBizMthdCd: SelectDataType[] = [
|
|||||||
{ val: "자체", text: "자체" },
|
{ val: "자체", text: "자체" },
|
||||||
];
|
];
|
||||||
|
|
||||||
//공통 건축물 용도
|
// 공통 건축물 용도
|
||||||
export const bdstUsgCd: SelectDataType[] = [
|
export const bdstUsgCd: SelectDataType[] = [
|
||||||
{ val: "-1", text: "확인불가" },
|
{ val: "-1", text: "확인불가" },
|
||||||
{ val: "1", text: "운동장" },
|
{ val: "1", text: "운동장" },
|
||||||
@@ -71,67 +71,67 @@ export const bdstUsgCd: SelectDataType[] = [
|
|||||||
{ val: "14", text: "골프장" },
|
{ val: "14", text: "골프장" },
|
||||||
{ val: "15", text: "대규모점포" },
|
{ val: "15", text: "대규모점포" },
|
||||||
{ val: "16", text: "기타" },
|
{ val: "16", text: "기타" },
|
||||||
{ text: "숙박업", val: "17" },
|
{ val: "17", text: "숙박업" },
|
||||||
{ text: "목욕장업", val: "18" },
|
{ val: "18", text: "목욕장업" },
|
||||||
{ text: "공장", val: "19" },
|
{ val: "19", text: "공장" },
|
||||||
{ text: "발전시설", val: "20" },
|
{ val: "20", text: "발전시설" },
|
||||||
{ text: "관광단지개발사업", val: "21" },
|
{ val: "21", text: "관광단지개발사업" },
|
||||||
{ text: "도시개발사업", val: "22" },
|
{ val: "22", text: "도시개발사업" },
|
||||||
{ text: "산업단지개발사업", val: "23" },
|
{ val: "23", text: "산업단지개발사업" },
|
||||||
{ text: "택지개발사업", val: "24" },
|
{ val: "24", text: "택지개발사업" },
|
||||||
{ text: "대규모점포(대형마트)", val: "25" },
|
{ val: "25", text: "대규모점포(대형마트)" },
|
||||||
{ text: "대규모점포(전문점)", val: "26" },
|
{ val: "26", text: "대규모점포(전문점)" },
|
||||||
{ text: "대규모점포(백화점)", val: "27" },
|
{ val: "27", text: "대규모점포(백화점)" },
|
||||||
{ text: "대규모점포(쇼핑센터)", val: "28" },
|
{ val: "28", text: "대규모점포(쇼핑센터)" },
|
||||||
{ text: "대규모점포(복합쇼핑몰)", val: "29" },
|
{ val: "29", text: "대규모점포(복합쇼핑몰)" },
|
||||||
{ text: "물류시설", val: "30" },
|
{ val: "30", text: "물류시설" },
|
||||||
{ text: "운수시설(여객자동차터미널)", val: "31" },
|
{ val: "31", text: "운수시설(여객자동차터미널)" },
|
||||||
{ text: "운수시설(철도시설)", val: "32" },
|
{ val: "32", text: "운수시설(철도시설)" },
|
||||||
{ text: "운수시설(공항시설)", val: "33" },
|
{ val: "33", text: "운수시설(공항시설)" },
|
||||||
{ text: "운수시설(항만시설)", val: "34" },
|
{ val: "34", text: "운수시설(항만시설)" },
|
||||||
{ text: "업무시설(공공업무시설)", val: "35" },
|
{ val: "35", text: "업무시설(공공업무시설)" },
|
||||||
{ text: "업무시설(금융업소)", val: "36" },
|
{ val: "36", text: "업무시설(금융업소)" },
|
||||||
{ text: "업무시설(사무소)", val: "37" },
|
{ val: "37", text: "업무시설(사무소)" },
|
||||||
{ text: "업무시설(결혼상담소 등 소개업소)", val: "38" },
|
{ val: "38", text: "업무시설(결혼상담소 등 소개업소)" },
|
||||||
{ text: "업무시설(출판사)", val: "39" },
|
{ val: "39", text: "업무시설(출판사)" },
|
||||||
{ text: "업무시설(신문사)", val: "40" },
|
{ val: "40", text: "업무시설(신문사)" },
|
||||||
{ text: "업무시설(오피스텔)", val: "41" },
|
{ val: "41", text: "업무시설(오피스텔)" },
|
||||||
{ text: "교정시설", val: "42" },
|
{ val: "42", text: "교정시설" },
|
||||||
{ text: "방송국", val: "43" },
|
{ val: "43", text: "방송국" },
|
||||||
{ text: "전신전화국", val: "44" },
|
{ val: "44", text: "전신전화국" },
|
||||||
{ text: "기타", val: "45" },
|
{ val: "45", text: "기타" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const rswtBdstType: SelectDataType[] = [
|
export const rswtBdstType: SelectDataType[] = [
|
||||||
{ text: "숙박업", val: "17" },
|
{ val: "17", text: "숙박업" },
|
||||||
{ text: "목욕장업", val: "18" },
|
{ val: "18", text: "목욕장업" },
|
||||||
{ text: "공장", val: "19" },
|
{ val: "19", text: "공장" },
|
||||||
{ text: "발전시설", val: "20" },
|
{ val: "20", text: "발전시설" },
|
||||||
{ text: "관광단지개발사업", val: "21" },
|
{ val: "21", text: "관광단지개발사업" },
|
||||||
{ text: "도시개발사업", val: "22" },
|
{ val: "22", text: "도시개발사업" },
|
||||||
{ text: "산업단지개발사업", val: "23" },
|
{ val: "23", text: "산업단지개발사업" },
|
||||||
{ text: "택지개발사업", val: "24" },
|
{ val: "24", text: "택지개발사업" },
|
||||||
{ text: "대규모점포(대형마트)", val: "25" },
|
{ val: "25", text: "대규모점포(대형마트)" },
|
||||||
{ text: "대규모점포(전문점)", val: "26" },
|
{ val: "26", text: "대규모점포(전문점)" },
|
||||||
{ text: "대규모점포(백화점)", val: "27" },
|
{ val: "27", text: "대규모점포(백화점)" },
|
||||||
{ text: "대규모점포(쇼핑센터)", val: "28" },
|
{ val: "28", text: "대규모점포(쇼핑센터)" },
|
||||||
{ text: "대규모점포(복합쇼핑몰)", val: "29" },
|
{ val: "29", text: "대규모점포(복합쇼핑몰)" },
|
||||||
{ text: "물류시설", val: "30" },
|
{ val: "30", text: "물류시설" },
|
||||||
{ text: "운수시설(여객자동차터미널)", val: "31" },
|
{ val: "31", text: "운수시설(여객자동차터미널)" },
|
||||||
{ text: "운수시설(철도시설)", val: "32" },
|
{ val: "32", text: "운수시설(철도시설)" },
|
||||||
{ text: "운수시설(공항시설)", val: "33" },
|
{ val: "33", text: "운수시설(공항시설)" },
|
||||||
{ text: "운수시설(항만시설)", val: "34" },
|
{ val: "34", text: "운수시설(항만시설)" },
|
||||||
{ text: "업무시설(공공업무시설)", val: "35" },
|
{ val: "35", text: "업무시설(공공업무시설)" },
|
||||||
{ text: "업무시설(금융업소)", val: "36" },
|
{ val: "36", text: "업무시설(금융업소)" },
|
||||||
{ text: "업무시설(사무소)", val: "37" },
|
{ val: "37", text: "업무시설(사무소)" },
|
||||||
{ text: "업무시설(결혼상담소 등 소개업소)", val: "38" },
|
{ val: "38", text: "업무시설(결혼상담소 등 소개업소)" },
|
||||||
{ text: "업무시설(출판사)", val: "39" },
|
{ val: "39", text: "업무시설(출판사)" },
|
||||||
{ text: "업무시설(신문사)", val: "40" },
|
{ val: "40", text: "업무시설(신문사)" },
|
||||||
{ text: "업무시설(오피스텔)", val: "41" },
|
{ val: "41", text: "업무시설(오피스텔)" },
|
||||||
{ text: "교정시설", val: "42" },
|
{ val: "42", text: "교정시설" },
|
||||||
{ text: "방송국", val: "43" },
|
{ val: "43", text: "방송국" },
|
||||||
{ text: "전신전화국", val: "44" },
|
{ val: "44", text: "전신전화국" },
|
||||||
{ text: "기타", val: "45" },
|
{ val: "45", text: "기타" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const orgwtKndCd = [
|
export const orgwtKndCd = [
|
||||||
@@ -186,6 +186,7 @@ export const pplLenght = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const rainFcltCd = [
|
export const rainFcltCd = [
|
||||||
|
{ val: "10", text: "확인불가" },
|
||||||
{ val: "01", text: "지붕" },
|
{ val: "01", text: "지붕" },
|
||||||
{ val: "02", text: "건물옥상" },
|
{ val: "02", text: "건물옥상" },
|
||||||
{ val: "03", text: "골프장 연못" },
|
{ val: "03", text: "골프장 연못" },
|
||||||
@@ -195,7 +196,6 @@ export const rainFcltCd = [
|
|||||||
{ val: "07", text: "태양광패널" },
|
{ val: "07", text: "태양광패널" },
|
||||||
{ val: "08", text: "연못" },
|
{ val: "08", text: "연못" },
|
||||||
{ val: "09", text: "기타" },
|
{ val: "09", text: "기타" },
|
||||||
{ val: "10", text: "확인불가" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ const hdtflObjSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.min(1, { message: validationMessages.required("에너지원 종류") }),
|
.min(1, { message: validationMessages.required("에너지원 종류") }),
|
||||||
fclCpct: z
|
fclCpct: z
|
||||||
.number()
|
.number().nullish(),
|
||||||
.positive({ message: validationMessages.required("시설 용량") }),
|
//.positive({ message: validationMessages.required("시설 용량") }),
|
||||||
plwtYn: z.enum(["Y", "N"]).default("Y"),
|
plwtYn: z.enum(["Y", "N"]).default("Y"),
|
||||||
orgwtWtritSpt: z
|
orgwtWtritSpt: z
|
||||||
.string()
|
.string().nullish(),
|
||||||
.min(1, { message: validationMessages.required("원수 취수 지점") }),
|
// .min(1, { message: validationMessages.required("원수 취수 지점") }),
|
||||||
orgwtOcrnProcs: z
|
orgwtOcrnProcs: z
|
||||||
.string()
|
.string().nullish(),
|
||||||
.min(1, { message: validationMessages.required("원수 발생 공정") }),
|
// .min(1, { message: validationMessages.required("원수 발생 공정") }),
|
||||||
wtritEntrcTp: z
|
wtritEntrcTp: z
|
||||||
.number()
|
.number()
|
||||||
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
|
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
|
||||||
@@ -33,12 +33,12 @@ const hdtflObjSchema = z.object({
|
|||||||
amtgnr: z.number().default(0),
|
amtgnr: z.number().default(0),
|
||||||
prcsProcsYn: z.enum(["Y", "N"]).default("Y"),
|
prcsProcsYn: z.enum(["Y", "N"]).default("Y"),
|
||||||
prcsFcltCpct: z
|
prcsFcltCpct: z
|
||||||
.number()
|
.number().nullish(),
|
||||||
.positive({ message: validationMessages.required("처리시설 용량") }),
|
// .positive({ message: validationMessages.required("처리시설 용량") }),
|
||||||
fmYn: z.string().default(""),
|
fmYn: z.string().default(""),
|
||||||
utztnCpct: z
|
utztnCpct: z
|
||||||
.number()
|
.number().nullish(),
|
||||||
.positive({ message: validationMessages.required("이용 용량") }),
|
// .positive({ message: validationMessages.required("이용 용량") }),
|
||||||
utztnUsg: z
|
utztnUsg: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, { message: validationMessages.required("이용 용도") }),
|
.min(1, { message: validationMessages.required("이용 용도") }),
|
||||||
|
|||||||
@@ -19,14 +19,9 @@ export const baseReuseBuildingSchema = z.object({
|
|||||||
instlCstSprtYn: z.string().default("Y"),
|
instlCstSprtYn: z.string().default("Y"),
|
||||||
instlCstSprtAmt: z.number().nullish(),
|
instlCstSprtAmt: z.number().nullish(),
|
||||||
grntchBdstCertYn: z.string().default("Y"),
|
grntchBdstCertYn: z.string().default("Y"),
|
||||||
grntchBdstCertYr: z
|
grntchBdstCertYr: z.string().refine(
|
||||||
.string()
|
(val) => val === "" || /^\d{4}$/.test(val),
|
||||||
.max(4, { message: validationMessages.yearFormat })
|
{ message: "연도는 빈값 또는 4자리 숫자여야 합니다." }
|
||||||
.refine(
|
|
||||||
(year) => year.length === 0 || /^\d{4}$/.test(year),
|
|
||||||
{
|
|
||||||
message: validationMessages.yearFormat,
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
grntchBdstCertNo: z.string().default(""),
|
grntchBdstCertNo: z.string().default(""),
|
||||||
grntchBdstCertGrd: z.string().default(""),
|
grntchBdstCertGrd: z.string().default(""),
|
||||||
@@ -59,7 +54,7 @@ export const chgBuildingSchema = z.object({
|
|||||||
export const htdflReuseBuildingSchema = z.object({
|
export const htdflReuseBuildingSchema = z.object({
|
||||||
bdstRuseFcltSn: z.number().nullish(),
|
bdstRuseFcltSn: z.number().nullish(),
|
||||||
bdstInfoSn: z.number().nullish(),
|
bdstInfoSn: z.number().nullish(),
|
||||||
instlCmptnDt: z.coerce.date().default(() => new Date()),
|
//instlCmptnDt: z.coerce.date().default(() => new Date()),
|
||||||
instlCst: z.number().nullish(),
|
instlCst: z.number().nullish(),
|
||||||
operMngCd: z.string().optional().nullish(),
|
operMngCd: z.string().optional().nullish(),
|
||||||
anlOperCst: z.number().default(0),
|
anlOperCst: z.number().default(0),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const rswtrObjSchema = z.object({
|
|||||||
//wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }),
|
//wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }),
|
||||||
//oblgTrgtWtrUsqty: z.number().nullish().default(0),
|
//oblgTrgtWtrUsqty: z.number().nullish().default(0),
|
||||||
orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }),
|
orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }),
|
||||||
filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }),
|
filtrTrtmfctCpct: z.number().nullish(),
|
||||||
filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }),
|
filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }),
|
||||||
filtrTrtmfctUsgCd: z.string().default("").nullish(),
|
filtrTrtmfctUsgCd: z.string().default("").nullish(),
|
||||||
rswtrUsqty: z.number().default(0),
|
rswtrUsqty: z.number().default(0),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
|
|||||||
const { userSiSggCd } = code;
|
const { userSiSggCd } = code;
|
||||||
const [useCityCd, setUserCityCd] = useState<SelectDataType[]>([]);
|
const [useCityCd, setUserCityCd] = useState<SelectDataType[]>([]);
|
||||||
const [useSggCd, setUserSggCd] = useState<SelectDataType[]>([]);
|
const [useSggCd, setUserSggCd] = useState<SelectDataType[]>([]);
|
||||||
|
const [initDaddr, setInitDaddr] = useState<string>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userSiSggCd.length === 2 && code.cityCd.length > 0) {
|
if (userSiSggCd.length === 2 && code.cityCd.length > 0) {
|
||||||
@@ -56,6 +56,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
|
|||||||
form.setValue("ctpvCd", Number(city[0]?.val));
|
form.setValue("ctpvCd", Number(city[0]?.val));
|
||||||
form.setValue("sggCd", Number(sgg[0]?.val));
|
form.setValue("sggCd", Number(sgg[0]?.val));
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [form, userSiSggCd, code.cityCd, code.sggCd]);
|
}, [form, userSiSggCd, code.cityCd, code.sggCd]);
|
||||||
|
|
||||||
|
|
||||||
@@ -67,9 +68,16 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
|
|||||||
return () => subscription?.unsubscribe();
|
return () => subscription?.unsubscribe();
|
||||||
}, [form.form]);
|
}, [form.form]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// console.log("form ", form.watch());
|
||||||
|
// }, [form.formState]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("form.formState ", form.watch("archArea"));
|
const bdstInfoSn = form.getValues("bdstInfoSn");
|
||||||
}, [form.formState]);
|
if(bdstInfoSn&&bdstInfoSn>0)
|
||||||
|
setInitDaddr(form.getValues("daddr"));
|
||||||
|
}, [form.getValues("bdstInfoSn")]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -166,7 +174,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
|
|||||||
/> */}
|
/> */}
|
||||||
<div>
|
<div>
|
||||||
<SearchInputAddress
|
<SearchInputAddress
|
||||||
initValue={undefined}
|
initValue={initDaddr}
|
||||||
setRtnData={(rtnData: addressType) => {
|
setRtnData={(rtnData: addressType) => {
|
||||||
console.log("rtnData", rtnData);
|
console.log("rtnData", rtnData);
|
||||||
field.onChange(rtnData.roadAddrPart1);
|
field.onChange(rtnData.roadAddrPart1);
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ export default function HtdflRegForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cleanHtdflData = (raw: any): z.infer<typeof hdtflZod> => {
|
const cleanHtdflData = (raw: any): z.infer<typeof hdtflZod> => {
|
||||||
|
const buildings =
|
||||||
|
raw?.reuseBuilding && raw.reuseBuilding.length > 0
|
||||||
|
? raw.reuseBuilding
|
||||||
|
: [{}];
|
||||||
return {
|
return {
|
||||||
...raw,
|
...raw,
|
||||||
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
|
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
|
||||||
@@ -92,7 +96,7 @@ export default function HtdflRegForm({
|
|||||||
: new Date(item?.archChgPrmsnDt),
|
: new Date(item?.archChgPrmsnDt),
|
||||||
bdstChgNm: item?.bdstChgNm ?? "", // null → ""
|
bdstChgNm: item?.bdstChgNm ?? "", // null → ""
|
||||||
})),
|
})),
|
||||||
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
|
reuseBuilding:buildings.map((item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
instlCmptnDt: new Date(item?.instlCmptnDt),
|
instlCmptnDt: new Date(item?.instlCmptnDt),
|
||||||
htdflObj: {
|
htdflObj: {
|
||||||
|
|||||||
@@ -84,13 +84,14 @@ export default function RainRegForm({
|
|||||||
typeof item?.grntchBdstCertYr === "string"
|
typeof item?.grntchBdstCertYr === "string"
|
||||||
? item?.grntchBdstCertYr
|
? item?.grntchBdstCertYr
|
||||||
: "", // null → ""
|
: "", // null → ""
|
||||||
|
bdstUsgCd: item?.bdstUsgCd ?? Number("-1"), // null → "-1"
|
||||||
instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y"
|
instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y"
|
||||||
grntchBdstCertYn: item?.grntchBdstCertYn ?? "Y",
|
grntchBdstCertYn: item?.grntchBdstCertYn ?? "D",
|
||||||
rainyObj: {
|
rainyObj: {
|
||||||
...item?.rainyObj,
|
...item?.rainyObj,
|
||||||
pntfColKnd:item.rainyObj?.pntfColKnd??'확인불가',
|
pntfColKnd:item.rainyObj?.pntfColKnd??rainFcltCd[0]?.val??'',
|
||||||
filtrTrtmfctPrs:item.rainyObj?.filtrTrtmfctPrs??'',
|
filtrTrtmfctPrs:item.rainyObj?.filtrTrtmfctPrs??'',
|
||||||
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y",
|
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "D",
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -98,7 +99,7 @@ export default function RainRegForm({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (grntchBdstCertYn === "N") {
|
if (grntchBdstCertYn === "N") {
|
||||||
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
|
form.setValue("reuseBuilding.0.grntchBdstCertYr", '');
|
||||||
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
|
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
|
||||||
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
|
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
|
||||||
} else if (instlCstSprtYn === "N") {
|
} else if (instlCstSprtYn === "N") {
|
||||||
@@ -298,7 +299,10 @@ export default function RainRegForm({
|
|||||||
selectClass={selectClass}
|
selectClass={selectClass}
|
||||||
label={""}
|
label={""}
|
||||||
selectArray={rainFcltCd}
|
selectArray={rainFcltCd}
|
||||||
onSelectValue={readonly ? () => { } : field.onChange}
|
onSelectValue={ (item) =>{
|
||||||
|
if(readonly) return;
|
||||||
|
field.onChange(item)
|
||||||
|
} }
|
||||||
value={field.value ?? rainFcltCd[0]?.val}
|
value={field.value ?? rainFcltCd[0]?.val}
|
||||||
readOnly={readonly}
|
readOnly={readonly}
|
||||||
/>
|
/>
|
||||||
@@ -521,8 +525,7 @@ export default function RainRegForm({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="reuseBuilding.0.grntchBdstCertYr"
|
name="reuseBuilding.0.grntchBdstCertYr"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isUnknown =
|
const isUnknown = field.value === null || field.value === "0000"; // 또는 Number(value) === -1
|
||||||
field.value === null || field.value === "0000"; // 또는 Number(value) === -1
|
|
||||||
return (
|
return (
|
||||||
<FormUi label="인증년도">
|
<FormUi label="인증년도">
|
||||||
<Input
|
<Input
|
||||||
@@ -535,7 +538,7 @@ export default function RainRegForm({
|
|||||||
field.onChange(""); // 초기화
|
field.onChange(""); // 초기화
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
value={isUnknown ? "확인불가" : (field.value ?? "")}
|
value={isUnknown ? "확인불가" : (field.value ?? '9999')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (readonly) return;
|
if (readonly) return;
|
||||||
if (e.target.value === "확인불가") {
|
if (e.target.value === "확인불가") {
|
||||||
|
|||||||
@@ -83,8 +83,10 @@ export default function RswtrRegForm({
|
|||||||
|
|
||||||
const filtrTrtmfctCpct = form.watch('reuseBuilding.0.rswtrObj.filtrTrtmfctCpct')
|
const filtrTrtmfctCpct = form.watch('reuseBuilding.0.rswtrObj.filtrTrtmfctCpct')
|
||||||
const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => {
|
const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => {
|
||||||
|
const buildings =
|
||||||
console.log('raw ', raw)
|
raw?.reuseBuilding && raw.reuseBuilding.length > 0
|
||||||
|
? raw.reuseBuilding
|
||||||
|
: [{}];
|
||||||
return {
|
return {
|
||||||
...raw,
|
...raw,
|
||||||
frstArchPrmsnDt: raw?.frstArchPrmsnDt
|
frstArchPrmsnDt: raw?.frstArchPrmsnDt
|
||||||
@@ -93,14 +95,15 @@ export default function RswtrRegForm({
|
|||||||
archChgPrmsnDt: raw?.archChgPrmsnDt
|
archChgPrmsnDt: raw?.archChgPrmsnDt
|
||||||
? new Date(raw?.archChgPrmsnDt)
|
? new Date(raw?.archChgPrmsnDt)
|
||||||
: null,
|
: null,
|
||||||
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
|
reuseBuilding:buildings.map((item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
instlCmptnDt: item?.instlCmptnDt
|
instlCmptnDt: item?.instlCmptnDt
|
||||||
? new Date(item.instlCmptnDt)
|
? new Date(item.instlCmptnDt)
|
||||||
: new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화
|
: new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화
|
||||||
operMngCd: item?.operMngCd || "", // null → ""
|
operMngCd: item?.operMngCd || "", // null → ""
|
||||||
instlCstSprtYn: item?.instlCstSprtYn || code.yn[0]?.val, // null → "Y"
|
instlCstSprtYn: item?.instlCstSprtYn || 'N', // null → "Y"
|
||||||
grntchBdstCertYn: item?.grntchBdstCertYn || code.yn[0]?.val,
|
grntchBdstCertYn: item?.grntchBdstCertYn ||'N',
|
||||||
|
bdstUsgCd: item?.bdstUsgCd ?? Number("-1"), // null → "-1"
|
||||||
rswtrObj: {
|
rswtrObj: {
|
||||||
...item?.rswtrObj,
|
...item?.rswtrObj,
|
||||||
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn || code.yn[0]?.val,
|
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn || code.yn[0]?.val,
|
||||||
@@ -145,6 +148,9 @@ export default function RswtrRegForm({
|
|||||||
if (rswtZod) form.reset(cleanRswtData(rswtForm));
|
if (rswtZod) form.reset(cleanRswtData(rswtForm));
|
||||||
}, [rswtForm, form]);
|
}, [rswtForm, form]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="!mx-[1.6rem] !p-0r">
|
<div className="!mx-[1.6rem] !p-0r">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
@@ -291,7 +297,7 @@ export default function RswtrRegForm({
|
|||||||
selectClass={selectClass}
|
selectClass={selectClass}
|
||||||
selectArray={code.orgwtKndCd}
|
selectArray={code.orgwtKndCd}
|
||||||
onSelectValue={readonly ? () => { } : field.onChange}
|
onSelectValue={readonly ? () => { } : field.onChange}
|
||||||
value={field.value || code.orgwtKndCd[0]?.val}
|
value={field.value ?? code.orgwtKndCd[0]?.val}
|
||||||
readOnly={readonly}
|
readOnly={readonly}
|
||||||
/>
|
/>
|
||||||
</FormUi>
|
</FormUi>
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ export function BdstReg<T extends MoType>({
|
|||||||
|
|
||||||
const { userSiSggCd } = code;
|
const { userSiSggCd } = code;
|
||||||
|
|
||||||
const [bdstTable, setbdstTable] = useState<BdstTypeMap[T][]>();
|
const [bdstTable, setBdstTable] = useState<BdstTypeMap[T][]>();
|
||||||
const [rainBdstForm, setRainBdstForm] = useState<BdstTypeMap[T]>(
|
const [bdstForm, setBdstForm] = useState<BdstTypeMap[T]>(
|
||||||
{} as BdstTypeMap[T]
|
{} as BdstTypeMap[T]
|
||||||
);
|
);
|
||||||
const [searchTxt, setSearchTxt] = useState<string>();
|
const [searchTxt, setSearchTxt] = useState<string>();
|
||||||
@@ -83,7 +83,7 @@ export function BdstReg<T extends MoType>({
|
|||||||
archArea: archArea,
|
archArea: archArea,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setbdstTable(tableData(bdstData, { code }));
|
setBdstTable(tableData(bdstData, { code }));
|
||||||
}
|
}
|
||||||
}, [bdst]);
|
}, [bdst]);
|
||||||
|
|
||||||
@@ -118,13 +118,13 @@ export function BdstReg<T extends MoType>({
|
|||||||
if (val !== undefined && bdst?.bdstBase.length > 0) {
|
if (val !== undefined && bdst?.bdstBase.length > 0) {
|
||||||
const data: BdstTypeMap[T] = bdst?.bdstBase[val];
|
const data: BdstTypeMap[T] = bdst?.bdstBase[val];
|
||||||
if (data) {
|
if (data) {
|
||||||
setRainBdstForm(data);
|
setBdstForm(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setbdstTable([]); // 초기화 작업
|
setBdstTable([]); // 초기화 작업
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -200,13 +200,13 @@ export function BdstReg<T extends MoType>({
|
|||||||
<div>
|
<div>
|
||||||
{moType === "RNB" ? (
|
{moType === "RNB" ? (
|
||||||
<RainRegForm
|
<RainRegForm
|
||||||
rainForm={rainBdstForm as customRainBdst}
|
rainForm={bdstForm as customRainBdst}
|
||||||
isModal={isModal}
|
isModal={isModal}
|
||||||
setIsModal={setIsModal}
|
setIsModal={setIsModal}
|
||||||
/>
|
/>
|
||||||
) : moType === "RST" ? (
|
) : moType === "RST" ? (
|
||||||
<RswtrRegForm
|
<RswtrRegForm
|
||||||
rswtForm={rainBdstForm as customRswtBdst}
|
rswtForm={bdstForm as customRswtBdst}
|
||||||
isModal={isModal}
|
isModal={isModal}
|
||||||
setIsModal={(open?: boolean) => {
|
setIsModal={(open?: boolean) => {
|
||||||
if (setIsModal && open) {
|
if (setIsModal && open) {
|
||||||
@@ -216,7 +216,7 @@ export function BdstReg<T extends MoType>({
|
|||||||
></RswtrRegForm>
|
></RswtrRegForm>
|
||||||
) : (
|
) : (
|
||||||
<HtdflRegForm
|
<HtdflRegForm
|
||||||
htdflForm={rainBdstForm as unknown as customHtdflBdst}
|
htdflForm={bdstForm as unknown as customHtdflBdst}
|
||||||
isModal={true}
|
isModal={true}
|
||||||
setIsModal={(open?: boolean) => {
|
setIsModal={(open?: boolean) => {
|
||||||
if (setIsModal && open) {
|
if (setIsModal && open) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from "@/components/lib/func/commom";
|
} from "@/components/lib/func/commom";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DatePicker,
|
// DatePicker,
|
||||||
FormField,
|
FormField,
|
||||||
FormUi,
|
FormUi,
|
||||||
SelectBox,
|
SelectBox,
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@common/components/ui";
|
} from "@common/components/ui";
|
||||||
|
import moment from "moment";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
type RuseMngTableProps = {
|
type RuseMngTableProps = {
|
||||||
@@ -49,14 +50,17 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
form,
|
form,
|
||||||
formNm,
|
formNm,
|
||||||
indexBiz,
|
indexBiz,
|
||||||
readonly= false
|
readonly = false,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table key={`${formNm}-table-${indexMng}-${indexBiz}`} variant="secondary"
|
<Table
|
||||||
ref={ref}>
|
key={`${formNm}-table-${indexMng}-${indexBiz}`}
|
||||||
|
variant="secondary"
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
<TableHeader className="font-bold">
|
<TableHeader className="font-bold">
|
||||||
{upHeadList ? (
|
{upHeadList ? (
|
||||||
headMakeprfmnc(headList, upHeadList)
|
headMakeprfmnc(headList, upHeadList)
|
||||||
@@ -76,20 +80,20 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{tableData.map((item, i) => (
|
{tableData.map((item, i) => (
|
||||||
<TableRow key={`${item.id }-${i}` } >
|
<TableRow key={`${item.id}-${i}`}>
|
||||||
{headList.map((head, j) => (
|
{headList.map((head, j) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={`${head.id}-${j}`}
|
key={`${head.id}-${j}`}
|
||||||
className="text-center whitespace-nowrap"
|
className="text-center whitespace-nowrap"
|
||||||
onClick={() => { }}
|
onClick={() => {}}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={
|
name={
|
||||||
typeof indexMng === "number" &&
|
typeof indexMng === "number" &&
|
||||||
indexMng >= 0 &&
|
indexMng >= 0 &&
|
||||||
typeof indexBiz === "number" &&
|
typeof indexBiz === "number" &&
|
||||||
indexBiz >= 0
|
indexBiz >= 0
|
||||||
? `items.${indexMng}.fcBizs.${indexBiz}.${formNm}.${i}.${head.id}`
|
? `items.${indexMng}.fcBizs.${indexBiz}.${formNm}.${i}.${head.id}`
|
||||||
: typeof indexMng === "number" && indexMng >= 0
|
: typeof indexMng === "number" && indexMng >= 0
|
||||||
? `items.${indexMng}.${formNm}.${i}.${head.id}`
|
? `items.${indexMng}.${formNm}.${i}.${head.id}`
|
||||||
@@ -99,7 +103,7 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
<FormUi label="">
|
<FormUi label="">
|
||||||
{head.edit === true ? (
|
{head.edit === true ? (
|
||||||
<Input
|
<Input
|
||||||
className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`}
|
className={`${head.type === "number" ? "text-right" : "text-center"} text-gray-600`}
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(txt) => {
|
onChange={(txt) => {
|
||||||
if (head.type === "number") {
|
if (head.type === "number") {
|
||||||
@@ -113,8 +117,10 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
? formatCommaNumber(field.value)
|
? formatCommaNumber(field.value)
|
||||||
: field.value || ""
|
: field.value || ""
|
||||||
}
|
}
|
||||||
readOnly={readonly|| head.readonly ? true : false}
|
readOnly={
|
||||||
maxLength={head.maxLength?head.maxLength:15}
|
readonly || head.readonly ? true : false
|
||||||
|
}
|
||||||
|
maxLength={head.maxLength ? head.maxLength : 15}
|
||||||
/>
|
/>
|
||||||
) : head.type === "select" ? (
|
) : head.type === "select" ? (
|
||||||
<SelectBox
|
<SelectBox
|
||||||
@@ -134,23 +140,38 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
field.onChange(val);
|
field.onChange(val);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
readOnly={ readonly||head.readonly ? true : false}
|
readOnly={
|
||||||
// Disabled prop removed as SelectBox does not support it
|
readonly || head.readonly ? true : false
|
||||||
|
}
|
||||||
|
// Disabled prop removed as SelectBox does not support it
|
||||||
></SelectBox>
|
></SelectBox>
|
||||||
) : head.type === "date" ? (
|
) : head.type === "date" ? (
|
||||||
<DatePicker
|
// <DatePicker
|
||||||
mode="single"
|
// mode="single"
|
||||||
field={{
|
// field={{
|
||||||
value:
|
// value:
|
||||||
typeof field.value === "string"
|
// typeof field.value === "string"
|
||||||
? new Date(field.value)
|
// ? new Date(field.value)
|
||||||
: field.value,
|
// : field.value,
|
||||||
onChange: (date: Date | undefined) => {
|
// onChange: (date: Date | undefined) => {
|
||||||
console.log("date", date);
|
// console.log("date", date);
|
||||||
field.onChange(date); // readonly 상태가 아닐 때만 값 변경
|
// field.onChange(date); // readonly 상태가 아닐 때만 값 변경
|
||||||
},
|
// },
|
||||||
|
// }}
|
||||||
|
// selectClass={"w-full"}
|
||||||
|
// readOnly={readonly||head.readonly ? true : false}
|
||||||
|
// />
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
onChange={(e) => {
|
||||||
|
// 브라우저에서 선택한 값은 "YYYY-MM-DD"
|
||||||
|
const date = new Date(e.target.value); // e.g. "2025-06-01"
|
||||||
|
field.onChange(date);
|
||||||
}}
|
}}
|
||||||
selectClass={"w-full"}
|
value={
|
||||||
|
field.value? moment(field.value, "YYYYMMDD").format( "YYYY-MM-DD"): ""
|
||||||
|
}
|
||||||
|
className={"w-full text-gray-600"}
|
||||||
readOnly={readonly||head.readonly ? true : false}
|
readOnly={readonly||head.readonly ? true : false}
|
||||||
/>
|
/>
|
||||||
) : head.type === "checkbox" ? (
|
) : head.type === "checkbox" ? (
|
||||||
@@ -158,22 +179,19 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
|
|||||||
className=""
|
className=""
|
||||||
checked={field.value || false}
|
checked={field.value || false}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
if(readonly || head.readonly) {
|
if (readonly || head.readonly) {
|
||||||
return;
|
return;
|
||||||
}else{
|
} else {
|
||||||
field.onChange(checked);
|
field.onChange(checked);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Checkbox>
|
></Checkbox>
|
||||||
) : (
|
) : (
|
||||||
(
|
<span className="text-black">
|
||||||
|
{head.type === "number"
|
||||||
<span className="text-black">{
|
|
||||||
head.type === "number"
|
|
||||||
? formatCommaNumber(field.value)
|
? formatCommaNumber(field.value)
|
||||||
: field.value || ""
|
: field.value || ""}
|
||||||
}</span>
|
</span>
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</FormUi>
|
</FormUi>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
|
|||||||
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||||
const REUSE_API_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL || "";
|
const REUSE_API_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL || "";
|
||||||
const OPERATION_API_URL = process.env.NEXT_PUBLIC_FO_SERVER_API_URL || "";
|
const OPERATION_API_URL = process.env.NEXT_PUBLIC_FO_SERVER_API_URL || "";
|
||||||
|
const BUSINESS_JUSO_API_URL = process.env.NEXT_PUBLIC_FINDADRESS_URL || "";
|
||||||
|
const AdressKey = process.env.NEXT_PUBLIC_WR_ADDRESS_KEY || "";
|
||||||
|
|
||||||
export function middleware(req: NextRequest) {
|
export function middleware(req: NextRequest) {
|
||||||
const pathname = req.nextUrl.pathname;
|
const pathname = req.nextUrl.pathname;
|
||||||
@@ -148,6 +150,24 @@ export function middleware(req: NextRequest) {
|
|||||||
return NextResponse.rewrite(new URL(newUrl));
|
return NextResponse.rewrite(new URL(newUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (pathname.startsWith("/bussiness-juso-service")) {
|
||||||
|
console.log('AdressKey ', AdressKey)
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
currentPage: "1", // 원하는 페이지
|
||||||
|
countPerPage: "5", // 페이지당 결과 수
|
||||||
|
keyword: searchParams.get("keyword") || "", // 검색어(주소)
|
||||||
|
confmKey: AdressKey, // 인증키 인터넷용 패쇄망용은 따로 발급 필요 즉 middleware에서 치환해서 사용해야할듯
|
||||||
|
resultType: "json", // 결과 형식 기본은 xml
|
||||||
|
});
|
||||||
|
const newUrl = `${BUSINESS_JUSO_API_URL}?${params.toString()}`;
|
||||||
|
console.log(`[Middleware] Request to business.juso.go.kr`);
|
||||||
|
console.log(req.url);
|
||||||
|
console.log(`[Middleware] Rewriting from '/business.juso.go.kr' path to: ${newUrl}`);
|
||||||
|
return NextResponse.rewrite(new URL(newUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +181,7 @@ export const config = {
|
|||||||
"/sewerage-service/:path*",
|
"/sewerage-service/:path*",
|
||||||
"/reuse-service/:path*",
|
"/reuse-service/:path*",
|
||||||
"/operation-service/:path*",
|
"/operation-service/:path*",
|
||||||
|
"/bussiness-juso-service/:path*",
|
||||||
"/"
|
"/"
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "./lib/chart";
|
} from "./lib/chart";
|
||||||
import type { ContentType } from "recharts/types/component/DefaultLegendContent";
|
import type { ContentType } from "recharts/types/component/DefaultLegendContent";
|
||||||
import type { set } from "date-fns";
|
import type { set } from "date-fns";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
// 툴크 커스텀 css
|
// 툴크 커스텀 css
|
||||||
/** 차트 기본 스타일 */
|
/** 차트 기본 스타일 */
|
||||||
@@ -126,6 +127,37 @@ export default function LineBarchart({
|
|||||||
monthTicks = getTicks(chartData);
|
monthTicks = getTicks(chartData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CustomAxisText : React.FC<{ viewBox: any, fieldText: string, position:string }> = ({ viewBox, fieldText, position }) => {
|
||||||
|
const { x, y, width, height } = viewBox; // 축 영역
|
||||||
|
const padding = 5; // 차트 바깥으로 띄우기
|
||||||
|
|
||||||
|
if('left' == position) {
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
x={x - padding} // 맨 좌측 바깥
|
||||||
|
y={y + height / 2} // 수직 중앙
|
||||||
|
textAnchor="middle"
|
||||||
|
transform={`rotate(-90, ${x - padding}, ${y + height / 2})`}
|
||||||
|
className="text-[12px] font-bold"
|
||||||
|
>
|
||||||
|
{fieldText}
|
||||||
|
</text>
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
x={x + height / 2 } // 맨 좌측 바깥
|
||||||
|
y={y - width + padding } // 수직 중앙
|
||||||
|
textAnchor="middle"
|
||||||
|
transform={`rotate(90, ${x}, ${y})`}
|
||||||
|
className="text-[12px] font-bold"
|
||||||
|
>
|
||||||
|
{fieldText}
|
||||||
|
</text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={chartClassName}>
|
<div className={chartClassName}>
|
||||||
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
||||||
@@ -133,7 +165,7 @@ export default function LineBarchart({
|
|||||||
{chartTitle}
|
{chartTitle}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="h-[calc(100%-42px)] flex justify-center">
|
<div className="h-[calc(100%-42px)] w-[calc(100%-40px)] ">
|
||||||
<ChartContainer config={chartConfig ?? {}} className="h-full w-full">
|
<ChartContainer config={chartConfig ?? {}} className="h-full w-full">
|
||||||
<ComposedChart
|
<ComposedChart
|
||||||
data={chartData}
|
data={chartData}
|
||||||
@@ -151,18 +183,22 @@ export default function LineBarchart({
|
|||||||
yAxisId="left"
|
yAxisId="left"
|
||||||
className="text-[12px] font-bold"
|
className="text-[12px] font-bold"
|
||||||
tickCount={6}
|
tickCount={6}
|
||||||
orientation="left"
|
tickFormatter={(value) =>
|
||||||
domain={[0, Math.ceil(lineMaxValue * 1)]}
|
typeof value === "number" ? value.toLocaleString() : value
|
||||||
label={{ value: yFieldText, angle: -90, position: 'insideLeft', offset: -8 }}
|
}
|
||||||
|
orientation="left"
|
||||||
|
label={(props) => <CustomAxisText {...props} fieldText={yFieldText} position="left" />}
|
||||||
/>}
|
/>}
|
||||||
{/* 오른쪽 Y축: barVal */}
|
{/* 오른쪽 Y축: barVal */}
|
||||||
{yFieldText2 && <YAxis
|
{yFieldText2 && <YAxis
|
||||||
yAxisId="right"
|
yAxisId="right"
|
||||||
className="text-[12px] font-bold"
|
className="text-[12px] font-bold"
|
||||||
tickCount={6}
|
tickCount={6}
|
||||||
orientation="right"
|
orientation="right"
|
||||||
domain={[0, Math.ceil(barMaxValue * 1)]}
|
tickFormatter={(value) =>
|
||||||
label={{ value: yFieldText2, angle: -90, position: 'insideRight' }}
|
typeof value === "number" ? value.toLocaleString() : value
|
||||||
|
}
|
||||||
|
label={(props) => <CustomAxisText {...props} fieldText={yFieldText} position="right" />}
|
||||||
reversed={true}
|
reversed={true}
|
||||||
/>}
|
/>}
|
||||||
<Tooltip content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />} />
|
<Tooltip content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />} />
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?:
|
|||||||
return (
|
return (
|
||||||
<div className="w-[180px] rounded bg-white p-2.5 shadow-[0px_2px_10px_rgba(0,0,0,0.2)]">
|
<div className="w-[180px] rounded bg-white p-2.5 shadow-[0px_2px_10px_rgba(0,0,0,0.2)]">
|
||||||
<p className="text-[12px] font-bold m-3">{label}</p>
|
<p className="text-[12px] font-bold m-3">{label}</p>
|
||||||
{payload.map((item, index) => (
|
{payload.filter(item => item.value != null && String(item.value) != 'NaN' && item.value != undefined).map((item, index) => (
|
||||||
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
|
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
{/* 색상 박스 추가 (범례 색상과 동일) */}
|
{/* 색상 박스 추가 (범례 색상과 동일) */}
|
||||||
<div style={{ width: "10px", height: "10px", backgroundColor: item.color, borderRadius: "50%" }}></div>
|
<div style={{ width: "10px", height: "10px", backgroundColor: item.color, borderRadius: "50%" }}></div>
|
||||||
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value}`}</span>
|
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value.toString().replace(',','')}`}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -129,6 +129,37 @@ const CustomLegend = (props: LegendProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomAxisText : React.FC<{ viewBox: any, fieldText: string, position:string }> = ({ viewBox, fieldText, position }) => {
|
||||||
|
const { x, y, width, height } = viewBox; // 축 영역
|
||||||
|
const padding = 5; // 차트 바깥으로 띄우기
|
||||||
|
|
||||||
|
if('left' == position) {
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
x={x - padding} // 맨 좌측 바깥
|
||||||
|
y={y + height / 2} // 수직 중앙
|
||||||
|
textAnchor="middle"
|
||||||
|
transform={`rotate(-90, ${x - padding}, ${y + height / 2})`}
|
||||||
|
className="text-[12px] font-bold"
|
||||||
|
>
|
||||||
|
{fieldText}
|
||||||
|
</text>
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
x={x + height / 2 } // 맨 좌측 바깥
|
||||||
|
y={y - width + padding } // 수직 중앙
|
||||||
|
textAnchor="middle"
|
||||||
|
transform={`rotate(90, ${x}, ${y})`}
|
||||||
|
className="text-[12px] font-bold"
|
||||||
|
>
|
||||||
|
{fieldText}
|
||||||
|
</text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type DataType = {label: string; val: string};
|
type DataType = {label: string; val: string};
|
||||||
|
|
||||||
type ChartDataType = {
|
type ChartDataType = {
|
||||||
@@ -220,7 +251,10 @@ export default function Linechart({
|
|||||||
yAxisId="left"
|
yAxisId="left"
|
||||||
className="text-[12px] font-bold"
|
className="text-[12px] font-bold"
|
||||||
orientation="left"
|
orientation="left"
|
||||||
label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} />
|
tickFormatter={(value) =>
|
||||||
|
typeof value === "number" ? value.toLocaleString() : value
|
||||||
|
}
|
||||||
|
label={(props) => <CustomAxisText {...props} fieldText={yFieldText} position="left" />}/>
|
||||||
<Tooltip content={<CustomTooltip />} />
|
<Tooltip content={<CustomTooltip />} />
|
||||||
{showLegend && <Legend content={CustomLegend as ContentType}/>}
|
{showLegend && <Legend content={CustomLegend as ContentType}/>}
|
||||||
{Object.keys(chartData).map((key) => {
|
{Object.keys(chartData).map((key) => {
|
||||||
@@ -233,8 +267,10 @@ export default function Linechart({
|
|||||||
type="linear"
|
type="linear"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
dot={false}
|
dot={false}
|
||||||
|
connectNulls={true}
|
||||||
name={String(config?.label || "X축")}
|
name={String(config?.label || "X축")}
|
||||||
stroke={config?.color}
|
stroke={config?.color?.includes('dash')?config?.color?.replace('dash',''):config?.color}
|
||||||
|
strokeDasharray={config?.color?.includes("dash")?"4 4":undefined}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -30,75 +30,6 @@ export const GisContext = createContext({
|
|||||||
checkedLayerIds: null,
|
checkedLayerIds: null,
|
||||||
});
|
});
|
||||||
export const useGisContext = () => useContext(GisContext);
|
export const useGisContext = () => useContext(GisContext);
|
||||||
export const searchUpperPpls = async (fcltMngNo) => {
|
|
||||||
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
|
||||||
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(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
|
||||||
|
|
||||||
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
|
||||||
const text = await res.text();
|
|
||||||
if (!text || text.trim() === "")
|
|
||||||
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;
|
|
||||||
|
|
||||||
data.forEach((item) => {
|
|
||||||
const pplMngNo = item.ppl_mng_no.trim();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Gis({
|
function Gis({
|
||||||
isOpen, //우측 상세 레이어 팝업
|
isOpen, //우측 상세 레이어 팝업
|
||||||
@@ -121,7 +52,6 @@ function Gis({
|
|||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
setIsPlaying = () => { },
|
setIsPlaying = () => { },
|
||||||
targetLayerTypes = [],
|
targetLayerTypes = [],
|
||||||
orderedLayers = [],
|
|
||||||
checkInterval = 3000,
|
checkInterval = 3000,
|
||||||
height: height = "h-[calc(100vh-5rem)]",
|
height: height = "h-[calc(100vh-5rem)]",
|
||||||
onOpenPopup = (data) => { },
|
onOpenPopup = (data) => { },
|
||||||
@@ -359,7 +289,6 @@ function Gis({
|
|||||||
isPlaying={isPlaying}
|
isPlaying={isPlaying}
|
||||||
setIsPlaying={setIsPlaying}
|
setIsPlaying={setIsPlaying}
|
||||||
targetLayerTypes={targetLayerTypes}
|
targetLayerTypes={targetLayerTypes}
|
||||||
orderedLayers={orderedLayers}
|
|
||||||
checkInterval={checkInterval}
|
checkInterval={checkInterval}
|
||||||
onProgressChange={handleProgressChange}
|
onProgressChange={handleProgressChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default React.memo(function GisTree({
|
|||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
setIsPlaying = () => { },
|
setIsPlaying = () => { },
|
||||||
targetLayerTypes = [],
|
targetLayerTypes = [],
|
||||||
orderedLayers = [],
|
|
||||||
checkInterval = 3000,
|
checkInterval = 3000,
|
||||||
onProgressChange = () => { },
|
onProgressChange = () => { },
|
||||||
}) {
|
}) {
|
||||||
@@ -313,14 +312,15 @@ export default React.memo(function GisTree({
|
|||||||
|
|
||||||
// orderedLayers 순서를 우선 적용하면서, 타입 필터에 맞는 타겟 노드만 선별
|
// orderedLayers 순서를 우선 적용하면서, 타입 필터에 맞는 타겟 노드만 선별
|
||||||
const getTargetNodesInOrder = () => {
|
const getTargetNodesInOrder = () => {
|
||||||
const orderedNodes = [];
|
const target = nodes.filter((n) => n.data?.actlLyrYn === 'N' && layerTypes.includes(n.data?.lyrTyNm));
|
||||||
orderedLayers.forEach((lyrSn) => {
|
|
||||||
const node = nodes.find(
|
// drawOrdr 값을 기준으로 오름차순 정렬
|
||||||
(n) => n.id === lyrSn && layerTypes.includes(n.data?.lyrTyNm)
|
target.sort((a, b) => {
|
||||||
);
|
const aOrder = a.data?.drawOrdr || 0;
|
||||||
if (node) orderedNodes.push(node);
|
const bOrder = b.data?.drawOrdr || 0;
|
||||||
|
return aOrder - bOrder;
|
||||||
});
|
});
|
||||||
return orderedNodes;
|
return target;
|
||||||
};
|
};
|
||||||
|
|
||||||
const targetNodes = getTargetNodesInOrder();
|
const targetNodes = getTargetNodesInOrder();
|
||||||
@@ -510,8 +510,8 @@ export default React.memo(function GisTree({
|
|||||||
{/* 사이드 패널 */}
|
{/* 사이드 패널 */}
|
||||||
<div
|
<div
|
||||||
className={`absolute left-0 top-0 z-40 flex h-full w-[250px] flex-col overflow-hidden border-r border-gray-300 bg-white transition-all duration-300 ${visible
|
className={`absolute left-0 top-0 z-40 flex h-full w-[250px] flex-col overflow-hidden border-r border-gray-300 bg-white transition-all duration-300 ${visible
|
||||||
? "translate-x-0 opacity-100"
|
? "translate-x-0 opacity-100"
|
||||||
: "pointer-events-none -translate-x-full opacity-0"
|
: "pointer-events-none -translate-x-full opacity-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -597,40 +597,63 @@ GisOlLayer.prototype = {
|
|||||||
const tileSize = 256; // 기본 타일 크기
|
const tileSize = 256; // 기본 타일 크기
|
||||||
return Math.ceil(Math.log2(maxResolution / (cameraHeight / tileSize)));
|
return Math.ceil(Math.log2(maxResolution / (cameraHeight / tileSize)));
|
||||||
},
|
},
|
||||||
ensureSearchLayer: function () {
|
ensureSearchLayer: function (option) {
|
||||||
let layer = this.searchLayer;
|
let layer = this.searchLayer;
|
||||||
|
|
||||||
if (!layer) {
|
if (!layer) {
|
||||||
layer = new VectorLayer({
|
let source;
|
||||||
source: new VectorSource(),
|
|
||||||
|
try {
|
||||||
|
if (option.cmncNm == 'bbox') {
|
||||||
|
source = this.getVectorSourceBbox(option);
|
||||||
|
} else {
|
||||||
|
source = this.getVectorSourceAll(option);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
layer = new VectorImageLayer({
|
||||||
|
...option,
|
||||||
|
id: 'search-layer',
|
||||||
|
source: source,
|
||||||
zIndex: 9999
|
zIndex: 9999
|
||||||
});
|
});
|
||||||
|
layer.setStyle(this.gisStyle.getLayerStyleFunction(layer, option));
|
||||||
|
|
||||||
let map = GisApp.Module.core.map;
|
let map = GisApp.Module.core.map;
|
||||||
map.addLayer(layer);
|
map.addLayer(layer);
|
||||||
|
|
||||||
|
this.searchLayer = layer;
|
||||||
}
|
}
|
||||||
return layer;
|
return layer;
|
||||||
},
|
},
|
||||||
clearSearchLayer: function () {
|
clearSearchLayer: function (option) {
|
||||||
const layer = this.ensureSearchLayer();
|
const layer = this.ensureSearchLayer(option);
|
||||||
layer.getSource().clear(true);
|
layer.getSource().clear(true);
|
||||||
|
|
||||||
|
if (this.searchLayer) {
|
||||||
|
GisApp.Module.core.map.removeLayer(this.searchLayer);
|
||||||
|
this.searchLayer = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
showSearchResultsFromGeoJSON: function (lyrTyNm, featureId) {
|
showSearchResultsFromGeoJSON: function (lyrTyNm, featureId) {
|
||||||
const layer = this.ensureSearchLayer();
|
|
||||||
|
|
||||||
// 이전 검색결과는 깨끗이
|
|
||||||
const prev = layer.getSource();
|
|
||||||
if (prev) prev.clear(true);
|
|
||||||
|
|
||||||
//레이어 타입 설정(point, line 등. 이걸 style에서 사용)
|
//레이어 타입 설정(point, line 등. 이걸 style에서 사용)
|
||||||
const layerCode = GisApp.layerCode;
|
const layerCode = GisApp.layerCode;
|
||||||
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
|
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
|
||||||
|
// 이전 검색 레이어 완전히 제거
|
||||||
|
this.clearSearchLayer(matched);
|
||||||
|
// 새로운 검색 레이어 생성
|
||||||
|
const layer = this.ensureSearchLayer(matched);
|
||||||
layer.set("gobjTy", matched.gobjTy);
|
layer.set("gobjTy", matched.gobjTy);
|
||||||
|
|
||||||
// 새 소스 만들기
|
// 새 소스 만들기
|
||||||
const source = this.getFeatureSourceFromGeoserver(lyrTyNm, featureId);
|
const source = this.getFeatureSourceFromGeoserver(lyrTyNm, featureId, matched);
|
||||||
|
|
||||||
// 1) 로드 완료시 extent 맞추기
|
// 1) 로드 완료시 extent 맞추기
|
||||||
const onLoadEnd = () => {
|
const onLoadEnd = () => {
|
||||||
|
if (Object.keys(source.nullGeometryFeatures_).length > 0) alert('해당 시설의 위치 정보가 없습니다.');
|
||||||
|
|
||||||
const feats = source.getFeatures();
|
const feats = source.getFeatures();
|
||||||
if (feats && feats.length > 0) {
|
if (feats && feats.length > 0) {
|
||||||
const select = GisApp.Module.select;
|
const select = GisApp.Module.select;
|
||||||
@@ -665,12 +688,13 @@ GisOlLayer.prototype = {
|
|||||||
* @param {*} featureId
|
* @param {*} featureId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getFeatureSourceFromGeoserver: function (lyrTyNm, featureId) {
|
getFeatureSourceFromGeoserver: function (lyrTyNm, featureId, option) {
|
||||||
|
|
||||||
const typeName = this.storageNm + lyrTyNm;
|
const typeName = this.storageNm + lyrTyNm;
|
||||||
const srsName = GisApp.TO_PROJ;
|
const srsName = GisApp.TO_PROJ;
|
||||||
const baseUrl = GisApp.GEOSERVER_URL;
|
const baseUrl = GisApp.GEOSERVER_URL;
|
||||||
const source = new VectorSource({
|
const source = new VectorSource({
|
||||||
|
...option,
|
||||||
format: new GeoJSON(),
|
format: new GeoJSON(),
|
||||||
url: () => {
|
url: () => {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
@@ -695,13 +719,14 @@ GisOlLayer.prototype = {
|
|||||||
* @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID
|
* @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID
|
||||||
*/
|
*/
|
||||||
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) {
|
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) {
|
||||||
const layer = this.ensureSearchLayer();
|
|
||||||
|
|
||||||
const prev = layer.getSource();
|
|
||||||
if (prev) prev.clear(true);
|
|
||||||
|
|
||||||
const layerCode = GisApp.layerCode;
|
const layerCode = GisApp.layerCode;
|
||||||
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
|
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
|
||||||
|
|
||||||
|
// 이전 검색 레이어 완전히 제거
|
||||||
|
this.clearSearchLayer(matched);
|
||||||
|
// 새로운 검색 레이어 생성
|
||||||
|
const layer = this.ensureSearchLayer(matched);
|
||||||
layer.set("gobjTy", matched.gobjTy);
|
layer.set("gobjTy", matched.gobjTy);
|
||||||
|
|
||||||
const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds);
|
const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds);
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ GisOlSelect.prototype = {
|
|||||||
selectedFeatures.forEach(feature => {
|
selectedFeatures.forEach(feature => {
|
||||||
selectedFeatures.remove(feature);
|
selectedFeatures.remove(feature);
|
||||||
});
|
});
|
||||||
|
selectedFeatures.clear();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* SELECT 이벤트 활성화
|
* SELECT 이벤트 활성화
|
||||||
|
|||||||
@@ -412,10 +412,10 @@ export function SdmRegForm({
|
|||||||
// {...field}
|
// {...field}
|
||||||
// />
|
// />
|
||||||
<SearchInputAddress
|
<SearchInputAddress
|
||||||
initValue={undefined}
|
initValue={updateList?.daddr || ""}
|
||||||
|
readonly={readOnly ??false}
|
||||||
setRtnData={(rtnData: addressType)=> {
|
setRtnData={(rtnData: addressType)=> {
|
||||||
field.onChange(rtnData.roadAddrPart1);
|
field.onChange(rtnData.roadAddrPart1);
|
||||||
console.log("rtnData ", rtnData);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,85 +1,88 @@
|
|||||||
const addresssKey = 'devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM='; //인증키 만료일은 2025-11-23
|
|
||||||
|
// const Key = 'U01TX0FVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA'
|
||||||
|
// const addresssKey = 'devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM='; //인증키 만료일은 2025-11-23
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 주소 API 응답 타입
|
* 주소 API 응답 타입
|
||||||
* https://business.juso.go.kr/addrlink/main.do 해당 사이트 참조
|
* https://business.juso.go.kr/addrlink/main.do 해당 사이트 참조
|
||||||
*/
|
*/
|
||||||
export type AddressApiResponse ={
|
export type AddressApiResponse ={
|
||||||
results: {
|
results: {
|
||||||
common: {
|
common: {
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
countPerPage: string;
|
countPerPage: string;
|
||||||
totalCount: string;
|
totalCount: string;
|
||||||
errorCode: string;
|
errorCode: string;
|
||||||
currentPage: string;
|
currentPage: string;
|
||||||
};
|
|
||||||
juso: addressType[];
|
|
||||||
};
|
};
|
||||||
}
|
juso: addressType[];
|
||||||
|
};
|
||||||
export type addressType ={
|
}
|
||||||
detBdNmList: string;
|
|
||||||
engAddr: string;
|
export type addressType ={
|
||||||
rn: string;
|
detBdNmList: string;
|
||||||
emdNm: string;
|
engAddr: string;
|
||||||
zipNo: string;
|
rn: string;
|
||||||
roadAddrPart2: string;
|
emdNm: string;
|
||||||
emdNo: string;
|
zipNo: string;
|
||||||
sggNm: string;
|
roadAddrPart2: string;
|
||||||
jibunAddr: string;
|
emdNo: string;
|
||||||
siNm: string;
|
sggNm: string;
|
||||||
roadAddrPart1: string;
|
jibunAddr: string;
|
||||||
bdNm: string;
|
siNm: string;
|
||||||
admCd: string;
|
roadAddrPart1: string;
|
||||||
udrtYn: string;
|
bdNm: string;
|
||||||
lnbrMnnm: string;
|
admCd: string;
|
||||||
roadAddr: string;
|
udrtYn: string;
|
||||||
lnbrSlno: string;
|
lnbrMnnm: string;
|
||||||
buldMnnm: string;
|
roadAddr: string;
|
||||||
bdKdcd: string;
|
lnbrSlno: string;
|
||||||
liNm: string;
|
buldMnnm: string;
|
||||||
rnMgtSn: string;
|
bdKdcd: string;
|
||||||
mtYn: string;
|
liNm: string;
|
||||||
bdMgtSn: string;
|
rnMgtSn: string;
|
||||||
buldSlno: string;
|
mtYn: string;
|
||||||
}
|
bdMgtSn: string;
|
||||||
|
buldSlno: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 주소에 따른 검색
|
* 주소에 따른 검색
|
||||||
* @param search : 검색어
|
* @param search : 검색어
|
||||||
* @returns addressType[] 주소 배열
|
* @returns addressType[] 주소 배열
|
||||||
*/
|
*/
|
||||||
export const findAddress = async (search: string) => {
|
export const findAddress = async (search: string) => {
|
||||||
if (!search) {
|
if (!search) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
currentPage: "1", // 원하는 페이지
|
currentPage: "1", // 원하는 페이지
|
||||||
countPerPage: "5", // 페이지당 결과 수
|
countPerPage: "5", // 페이지당 결과 수
|
||||||
keyword: search, // 검색어(주소)
|
keyword: search, // 검색어(주소)
|
||||||
confmKey: addresssKey, // 인증키 인터넷용 패쇄망용은 따로 발급 필요 즉 middleware에서 치환해서 사용해야할듯
|
confmKey: '', // 인증키 인터넷용 패쇄망용은 따로 발급 필요 즉 middleware에서 치환해서 사용해야할듯
|
||||||
resultType: "json", // 결과 형식 기본은 xml
|
resultType: "json", // 결과 형식 기본은 xml
|
||||||
});
|
});
|
||||||
const url = `https://business.juso.go.kr/addrlink/addrLinkApi.do?${params.toString()}`;
|
// const url = `https://business.juso.go.kr/addrlink/addrLinkApi.do?${params.toString()}`;
|
||||||
try {
|
const url = `/bussiness-juso-service/addrlink/addrLinkApi.do?${params.toString()}`;
|
||||||
const response = await fetch(url);
|
try {
|
||||||
if (!response.ok) {
|
const response = await fetch(url);
|
||||||
throw new Error('Network response was not ok');
|
if (!response.ok) {
|
||||||
}
|
throw new Error('Network response was not ok');
|
||||||
const data = await response.json();
|
}
|
||||||
if (data.results && data.results.juso) {
|
const data = await response.json();
|
||||||
return data.results.juso as addressType[];
|
if (data.results && data.results.juso) {
|
||||||
} else {
|
return data.results.juso as addressType[];
|
||||||
return [];
|
} else {
|
||||||
}
|
return [];
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Error fetching address data:', error);
|
} catch (error) {
|
||||||
return [];
|
console.error('Error fetching address data:', error);
|
||||||
}
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type thisProps = {
|
|||||||
* @returns setRtnData => 선택된 주소 데이터 반환
|
* @returns setRtnData => 선택된 주소 데이터 반환
|
||||||
*/
|
*/
|
||||||
export default function SearchInputAddress({
|
export default function SearchInputAddress({
|
||||||
initValue = undefined,
|
initValue ,
|
||||||
setRtnData = () => {},
|
setRtnData = () => {},
|
||||||
readonly=false
|
readonly=false
|
||||||
}: thisProps) {
|
}: thisProps) {
|
||||||
@@ -24,6 +24,13 @@ export default function SearchInputAddress({
|
|||||||
const [searchList, setSearchList] = useState<addressType[]>([]);
|
const [searchList, setSearchList] = useState<addressType[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
|
||||||
|
if(initValue !== textValue && initValue !== undefined && initValue !== null && initValue !== ''){
|
||||||
|
setTextValue(initValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
},[initValue])
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const handleInputChange = useCallback(
|
const handleInputChange = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type UiTableProps = {
|
|||||||
boldArray?: number[];
|
boldArray?: number[];
|
||||||
resetSelection?: boolean;
|
resetSelection?: boolean;
|
||||||
index?: number;
|
index?: number;
|
||||||
|
curPage?: number;
|
||||||
headButton?: React.ReactNode | undefined;
|
headButton?: React.ReactNode | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,9 +55,10 @@ export function UiTable({
|
|||||||
boldArray = [],
|
boldArray = [],
|
||||||
resetSelection = false,
|
resetSelection = false,
|
||||||
index = -1,
|
index = -1,
|
||||||
|
curPage = 1,
|
||||||
headButton = undefined,
|
headButton = undefined,
|
||||||
}: UiTableProps) {
|
}: UiTableProps) {
|
||||||
const [cur, setCur] = useState<number>(1);
|
const [cur, setCur] = useState<number>(curPage);
|
||||||
const [idx, setIdx] = useState<number>(index);
|
const [idx, setIdx] = useState<number>(index);
|
||||||
const [headerHeight, setHeaderHeight] = useState<number>(0);
|
const [headerHeight, setHeaderHeight] = useState<number>(0);
|
||||||
const headerRef = useRef<HTMLTableSectionElement>(null);
|
const headerRef = useRef<HTMLTableSectionElement>(null);
|
||||||
@@ -84,6 +86,10 @@ export function UiTable({
|
|||||||
setIdx(index);
|
setIdx(index);
|
||||||
}, [index])
|
}, [index])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCur(curPage);
|
||||||
|
}, [curPage])
|
||||||
|
|
||||||
const handlePageChange = (page: number) => {
|
const handlePageChange = (page: number) => {
|
||||||
setCur(page);
|
setCur(page);
|
||||||
onPageChange(page);
|
onPageChange(page);
|
||||||
|
|||||||
Reference in New Issue
Block a user