Merge branch 'iipbs/wr/dev'

This commit is contained in:
2026-06-18 10:47:12 +09:00
35 changed files with 476 additions and 190 deletions
+2 -2
View File
@@ -18,8 +18,8 @@ NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:800
NEXT_PUBLIC_NGII_URL = http://172.29.143.87/poiact
NEXT_PUBLIC_NGII_PARAM = BDE2BEF39DAFEC293AB6730150F222AC&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={y}&tilerow={x}
NEXT_PUBLIC_GATEWAY_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
CONTENTS_INTRO = 31
CONTENTS_EXAMPLE = 32
CONTENTS_INTRO = 24
CONTENTS_EXAMPLE = 25
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
@@ -365,7 +365,7 @@ export default function SimilarCaseModal({
</tr>
<tr>
<th rowSpan={2} className={thBase}>
총대장균군수<br />(개/100ml)
총대장균군수<br />(개/100mL)
</th>
<th rowSpan={2} className={thBase}>
용도별 결합 잔류염소<br />(mg/L)
@@ -428,7 +428,7 @@ export default function DemandRegisterPage() {
</FormRow>
<Divider />
<FormRow label="요구수량(m²/d)" required>
<FormRow label="요구수량(㎥/d)" required>
<TextInput value={reqQty} onChange={setReqQty} />
</FormRow>
<Divider />
@@ -438,7 +438,7 @@ export default function DemandRegisterPage() {
</FormRow>
<Divider />
<FormRow label="희망단가(원)" required>
<FormRow label="희망단가(원/㎥)" required>
<TextInput value={hopePrice} onChange={setHopePrice} />
</FormRow>
</div>
@@ -464,7 +464,7 @@ export default function DemandRegisterPage() {
<div className="border border-neutral-200 bg-white">
<QualityRow
leftLabel="총대장균군수(개/mg)"
leftLabel="총대장균군수(개/100㎖)"
leftRequired
leftControl={
<div className="flex items-center gap-4">
@@ -148,7 +148,7 @@ export default function DemandStatusPage() {
{ label: "요구시기", value: selected.detail.reqDate, required: true },
{ label: "요구수량(㎡/d)", value: selected.detail.reqQty },
{ label: "사용용도", value: selected.detail.usePurpose },
{ label: "희망단가(원)", value: selected.detail.price },
{ label: "희망단가(원/㎥)", value: selected.detail.price },
];
}, [selected]);
@@ -157,7 +157,7 @@ export default function DemandStatusPage() {
if (!selected) return [];
const w = selected.detail.water;
return [
{ label: "총대장균군수(개/100mℓ)", value: w.coliform },
{ label: "총대장균군수(개/100㎖)", value: w.coliform },
{ label: "결합잔류염소(mg/L)", value: "-" },
{ label: "탁도(NTU)", value: w.turbidity },
@@ -44,17 +44,17 @@ export default function DemandRegisterPage() {
const [coliform, setColiform] = useState<string>(""); // 총대장균군수
const [noDetect, setNoDetect] = useState<boolean>(false);
const [tn, setTn] = useState<string>("");
const [residualCl, setResidualCl] = useState<string>("");
const [tp, setTp] = useState<string>("");
const [turbidity, setTurbidity] = useState<string>("");
const [ph, setPh] = useState<PHRange>({ min: "", max: "" });
const [bod, setBod] = useState<string>("");
const [chloride, setChloride] = useState<string>("");
const [odor, setOdor] = useState<string>("불쾌하지 않을 것");
const [ec, setEc] = useState<string>("");
const [color, setColor] = useState<string>("");
const [remark, setRemark] = useState<string>("");
const [tn, setTn] = useState<string>(""); // 총질소(T-N)
const [residualCl, setResidualCl] = useState<string>(""); // 결합잔류염소
const [tp, setTp] = useState<string>(""); // 총인(T-P)
const [turbidity, setTurbidity] = useState<string>(""); // 탁도
const [ph, setPh] = useState<PHRange>({ min: "", max: "" }); // 수소이온농도(pH)
const [bod, setBod] = useState<string>(""); // 생물학적산소요구량(BOD)
const [chloride, setChloride] = useState<string>(""); // 염화물
const [odor, setOdor] = useState<string>("불쾌하지 않을 것"); // 냄새
const [ec, setEc] = useState<string>(""); // 전기전도도
const [color, setColor] = useState<string>(""); // 색도
const [remark, setRemark] = useState<string>(""); // 비고
// 공급처 후보지 선택(필수아님)
const [cand1, setCand1] = useState("");
@@ -373,7 +373,8 @@ export default function DemandRegisterPage() {
switch (value) {
// 청소화장실용도
case '01':
setTp('0.2');
setNoDetect(true); // 총대장균군수 불검출
setResidualCl('0.2'); // 잔류염소
setTurbidity('2');
setPh({ min: '5.8', max: '8.5' });
setBod('5');
@@ -381,23 +382,35 @@ export default function DemandRegisterPage() {
break;
// 세척살수용도
case '02':
setTp('0.2');
setTurbidity('2');
setColiform('1000');
setTurbidity('2'); // 탁도
setPh({ min: '5.8', max: '8.5' });
setBod('5');
break;
// 조경용도
case '03':
setTp('0.2');
setColiform('1000');
setTurbidity('2');
setPh({ min: '5.8', max: '8.5' });
setBod('5');
setChloride('250');
setChloride('250'); // 염화물
break;
// 지하수
// 친수용도
case '04':
setNoDetect(true); // 총대장균군수 불검출
setResidualCl('0.1'); // 잔류염소
setTurbidity('2');
setBod('3');
setColor('10');
setTn('10'); // 총질소
setTp('0.5'); // 총인
setPh({ min: '5.8', max: '8.5' });
setChloride('0.5');
break;
// 지하수충전
case '05':
break;
// 농업용도
// 농업용수
case '06':
setTurbidity('5');
setPh({ min: '5.8', max: '8.5' });
@@ -405,16 +418,18 @@ export default function DemandRegisterPage() {
setEc('2000');
setFarmText("이하 (** 간접:2000,직접:700 )");
break;
// 하천 등 유지보수
// 하천 등 유지용수
case '07':
setColiform('1000');
setTn('10');
setBod('5');
setTn('20');
setTp('0.5');
setPh({ min: '5.8', max: '8.5' });
setBod('5');
setChloride('0.5');
break;
// 공업
// 공업용수
case '08':
setBod('20');
setPh({ min: '5.8', max: '8.5' });
break;
default:
@@ -467,7 +482,7 @@ export default function DemandRegisterPage() {
<Divider />
<FormRow label="요구수량(m²/d)" required>
<FormRow label="요구수량(㎥/d)" required>
<TextInput value={reqQty} onChange={setReqQty} placeholder="" />
</FormRow>
@@ -490,7 +505,7 @@ export default function DemandRegisterPage() {
<Divider />
<FormRow label="희망단가(원)" required>
<FormRow label="희망단가(원/㎥)" required>
<TextInput value={hopePrice} onChange={setHopePrice} />
</FormRow>
</div>
@@ -519,7 +534,7 @@ export default function DemandRegisterPage() {
<div className="border border-neutral-200 bg-white">
{/* 1행 */}
<QualityRow
leftLabel="총대장균군수(개/mg)"
leftLabel="총대장균군수(개/100㎖)"
leftControl={
<div className="flex items-center gap-4">
@@ -30,6 +30,19 @@ const formatValue = (value: string | number | null | undefined) => {
return String(value);
};
const formatNumberWithComma = (value: string | number | null | undefined) => {
if (value === null || value === undefined || value === "") return "-";
const normalized =
typeof value === "string" ? Number(value.replace(/,/g, "")) : Number(value);
if (Number.isNaN(normalized)) {
return String(value);
}
return normalized.toLocaleString("ko-KR");
};
const formatBelow = (value: string | number | null | undefined) => {
if (value === null || value === undefined || value === "") return "-";
if (Number(value) === -1) return "불검출";
@@ -234,9 +247,9 @@ export default function DemandStatusPage() {
return [
{ label: "요구시기", key: "dmndTmng", value: formatDate(selectedSdmInfoStatus.dmndTmng), required: true },
{ label: "요구수량(㎥/d)", key: "dmndQty", value: formatValue(selectedSdmInfoStatus.dmndQty) },
{ label: "요구수량(㎥/d)", key: "dmndQty", value: formatNumberWithComma(selectedSdmInfoStatus.dmndQty) },
{ label: "사용용도", key: "useUsgCd", value: formatValue(selectedSdmInfoStatus.useUsgCd) },
{ label: "희망단가(원/㎥)", key: "wntUntprc", value: formatValue(selectedSdmInfoStatus.wntUntprc) },
{ label: "희망단가(원/㎥)", key: "wntUntprc", value: formatNumberWithComma(selectedSdmInfoStatus.wntUntprc) },
];
}, [selectedSdmInfoStatus]);
@@ -244,7 +257,7 @@ export default function DemandStatusPage() {
if (!selectedSdmInfoStatus) return [];
return [
{ label: "총대장균군수(개/100mL)", key: "grssClfg", value: formatBelow(selectedSdmInfoStatus.grssClfg) },
{ label: "총대장균군수(개/100㎖)", key: "grssClfg", value: formatBelow(selectedSdmInfoStatus.grssClfg) },
{ label: "결합잔류염소(mg/L)", key:"combResdch", value: formatBelow(selectedSdmInfoStatus.combResdch) },
{ label: "탁도(NTU)", key:"trbt", value: formatBelow(selectedSdmInfoStatus.trbt) },
@@ -520,7 +533,7 @@ export default function DemandStatusPage() {
{!selectedSdmInfoStatus ? (
<div className="mt-3 border border-neutral-200 px-6 py-10 text-center text-sm text-slate-400">
상세 정보를 표시할 신청건이 없습니다.
조회된 결과가 없습니다.
</div>
) : (
<div className="mt-3 border border-neutral-200">
+1 -1
View File
@@ -770,7 +770,7 @@ export const SdmReducer = (set: any): SdmActions => {
"/cms-service/api/v1/lwpo-cds/?lwpoCdNm=&sort=regDt,desc&upCd=WR018&page=0&size=100"
);
const comUseTypes = upCd.content.filter(el=>el.lwpoCdNm != '친수용수').map((el: any) => ({ text: el.lwpoCdNm, val: el.lwpoCd }));
const comUseTypes = upCd.content.map((el: any) => ({ text: el.lwpoCdNm, val: el.lwpoCd }));
set((state: SdmType) => ({
Sdm: {
@@ -282,7 +282,7 @@
// },
// {
// name: "money",
// label: "희망단가(원)",
// label: "희망단가(원/㎥)",
// formIndex: 0,
// required: true,
// component: (field) => (
@@ -375,7 +375,7 @@
// render={({ field }) =>
// name === "money" ? (
// <div>
// <Label className="text-black">희망단가(원)*</Label>
// <Label className="text-black">희망단가(원/㎥)*</Label>
// {component(field)}
// </div>
// ) : (
@@ -411,7 +411,7 @@
// return (
// <FormUi
// label={
// !isDisabled ? `총대장균군수(개/mg)*` : "총대장균군수(개/mg)"
// !isDisabled ? `총대장균군수(개/100㎖)*` : "총대장균군수(개/100㎖)"
// }
// >
// <div className="flex items-center gap-2">
+1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -14,6 +14,7 @@ import {
type DmdCoReqType,
type DmdMatTableType,
} from "@/app/store/matDmd/coMatRvw/type";
import { DEFAULT_LIST_SIZE } from "@/src/shared/config";
import { formH1Class } from "@/components/lib/class/comClass";
import {
formatPhoneNumber,
@@ -46,6 +47,50 @@ import { useEffect, useRef, useState } from "react";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
const formatNumberWithComma = (value: string | number | null | undefined) => {
if (value === null || value === undefined || value === "") return "";
const normalized =
typeof value === "string" ? Number(value.replace(/,/g, "")) : Number(value);
if (Number.isNaN(normalized)) {
return String(value);
}
return normalized.toLocaleString("ko-KR");
};
const EMPTY_DETAIL_FORM: Partial<DmdCoReqType> = {
dmdInfoSn: 0,
dmdInfoId: "",
dmndQty: undefined,
wntUntprc: undefined,
useUsgCd: "",
dmndTmng: undefined,
grssClfg: undefined,
combResdch: undefined,
trbt: undefined,
blgcOxygDmndQnt: undefined,
smll: undefined,
crmty: undefined,
tn: undefined,
tph: undefined,
ph: undefined,
clIonQnt: undefined,
cdtnv: undefined,
rmrk: undefined,
ruseUtztnFcltNm: "",
splyCndtList: [],
totalConstCost: 0,
demandCst: {
usgPrcsProcs: "",
cstrnCst: 0,
fcltSbsdCstrnCst: 0,
prcsFcltCpctCstrnCst: 0,
},
demandMatching: undefined,
demandCo: undefined,
};
export default function CoMatRvw() {
const readOnlyInputClass = "bg-slate-50 text-gray-700";
@@ -75,6 +120,8 @@ export default function CoMatRvw() {
const [toDate, setToDate] = useState<string>(
String(new Date().getFullYear())
);
const [selectedRowIndex, setSelectedRowIndex] = useState<number>(-1);
const [resetSelection, setResetSelection] = useState(false);
const [selectData, setSelectData] = useState<DmdCoReqType | null>(null); // selectData를 null로 초기화
const [selectionVersion, setSelectionVersion] = useState(0);
const bottomRef = useRef<HTMLDivElement | null>(null);
@@ -100,14 +147,14 @@ export default function CoMatRvw() {
if (dmdCoNm.length > 0) {
coMatRvwSearch(
pageNo,
13,
DEFAULT_LIST_SIZE,
String(startYear),
String(endYear),
sggCd,
dmdCoNm
);
} else {
coMatRvwInqua(pageNo, 13, String(startYear), String(endYear), sggCd);
coMatRvwInqua(pageNo, DEFAULT_LIST_SIZE, String(startYear), String(endYear), sggCd);
}
};
@@ -119,11 +166,22 @@ export default function CoMatRvw() {
const TableCellClick = (index: number) => {
if (coMatRvwRes && coMatRvwRes[index]) {
setSelectedRowIndex(index);
setResetSelection(false);
setSelectData(coMatRvwRes[index]);
setSelectionVersion((v) => v + 1); // 행을 클릭할 때마다 버전을 1씩 증가
bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // 화면 스크롤 처리
}
};
const resetDetailSections = () => {
setSelectedRowIndex(-1);
setResetSelection(true);
setSelectData(null);
form.reset(EMPTY_DETAIL_FORM);
setSelectionVersion((v) => v + 1);
};
const useUsgCdWatch = useWatch({ control: form.control, name: "useUsgCd" });
const useUsgCdText =
code.sewageOutFclttUse?.find((item) => item.val === useUsgCdWatch)?.text ??
@@ -166,6 +224,9 @@ export default function CoMatRvw() {
};
});
setDmdMtaData(tableData(data, { code }));
} else {
setDmdMtaData([]);
setSelectData(null);
}
}, [coMatRvwRes]);
@@ -185,7 +246,7 @@ export default function CoMatRvw() {
useEffect(() => {
if (!initRef.current) {
coMatRvwInit(1, 13, fromDate, toDate);
coMatRvwInit(1, DEFAULT_LIST_SIZE, fromDate, toDate);
//setMounted(true);
initRef.current = true;
}
@@ -295,6 +356,7 @@ export default function CoMatRvw() {
);
} else {
const con = getCurrentRegionCode();
resetDetailSections();
//setPageNatiion(1);
getSearch(1, fromNum, toNum, con, dmdCoNm);
}
@@ -321,8 +383,10 @@ export default function CoMatRvw() {
tableData={dmdMatData || []}
headlist={coRvwHeadList}
widthFixed={true}
pageSize={13}
pageSize={DEFAULT_LIST_SIZE}
total={coMatRvwCount}
resetSelection={resetSelection}
index={selectedRowIndex}
cellClick={(index: number | undefined) => {
if (index !== undefined && index >= 0) {
TableCellClick(index);
@@ -330,6 +394,7 @@ export default function CoMatRvw() {
}}
onPageChange={(val) => {
const con = getCurrentRegionCode();
resetDetailSections();
//setPageNatiion(val);
getSearch(val, Number(fromDate), Number(toDate), con, dmdCoNm);
}}
@@ -377,12 +442,11 @@ export default function CoMatRvw() {
<FormUi label="요구수량(m³/일)">
<Input
placeholder=""
type="number"
step="0.1"
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
className={readOnlyInputClass}
/>
@@ -416,15 +480,14 @@ export default function CoMatRvw() {
control={form.control}
name="wntUntprc"
render={({ field }) => (
<FormUi label="희망단가(원/m3)">
<FormUi label="희망단가(원/m³)">
<Input
placeholder=""
type="number"
step="0.1"
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
className={readOnlyInputClass}
/>
@@ -440,7 +503,7 @@ export default function CoMatRvw() {
control={form.control}
name="grssClfg"
render={({ field }) => (
<FormUi label={"총대장균군수(개/mg)"}>
<FormUi label={"총대장균군수(개/100㎖)"}>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
@@ -448,9 +511,12 @@ export default function CoMatRvw() {
className={`w-full pr-16 border py-2 text-right rounded-md ${readOnlyInputClass}`}
placeholder=""
value={
field.value != -1 ||field.value != undefined
? `${field.value}`
: "불검출"
field.value === null ||
field.value === undefined
? ""
: field.value === -1
? "불검출"
: formatNumberWithComma(field.value)
}
readOnly
/>
@@ -480,7 +546,7 @@ export default function CoMatRvw() {
type="text"
className={`w-full pr-16 border py-2 text-right rounded-md ${readOnlyInputClass}`}
placeholder=""
value={field.value ?? ""}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -504,7 +570,7 @@ export default function CoMatRvw() {
placeholder=""
className={`w-full pr-16 border py-2 text-right rounded-md ${readOnlyInputClass}`}
type="text"
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className=" text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -532,7 +598,7 @@ export default function CoMatRvw() {
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? ""}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -574,7 +640,7 @@ export default function CoMatRvw() {
className={`w-full pr-16 border py-2 text-right rounded-md ${readOnlyInputClass}`}
type="text"
step="0.1"
value={field.value ?? ""}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -601,7 +667,7 @@ export default function CoMatRvw() {
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -628,7 +694,7 @@ export default function CoMatRvw() {
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -655,7 +721,7 @@ export default function CoMatRvw() {
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
</div>
@@ -679,7 +745,7 @@ export default function CoMatRvw() {
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -701,12 +767,12 @@ export default function CoMatRvw() {
<div className="relative flex-1 min-w-0">
<Input
placeholder=""
className="w-full pr-16 border py-2 text-right rounded-md"
className={`w-full pr-16 border py-2 text-right rounded-md ${readOnlyInputClass}`}
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={formatNumberWithComma(field.value)}
readOnly
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
@@ -462,6 +462,12 @@ export default function PublicReuseFaciltyInsert() {
void (async () => {
const message = await pbnstInsert(data);
alert(message);
if (searchTxt && searchTxt.trim().length > 0) {
await pubHasuSearch(pageNation, laeSeCd, searchTxt);
} else {
await pubHasuInit(pageNation, laeSeCd);
}
})();
setIsRuseReg({ isModal: false });
}
@@ -63,7 +63,7 @@ export default function PbnstSttn() {
const [pbnRuseNm, setPbnRuseNm] = useState<string>("");
const { t } = useTranslation("base");
const alert = useAlertStore((state) => state.setMessage);
const isAllAreaUser = laeSeCd === "0"; // 모든 지자체 권한이 있는 경우
const isAllAreaUser = laeSeCd?.length != 5; // 모든 지자체 권한이 있는 경우
const fixedSidoValue =
!isAllAreaUser && laeSeCd?.length ? laeSeCd.slice(0, 2) : "";
const fixedSigunValue =
@@ -210,11 +210,9 @@ export default function PbnstSttn() {
: code.cityCd
}
selectSigunData={
!isAllAreaUser && laeSeCd?.length === 5
laeSeCd?.length === 5
? code.sggCd.filter((item) => item.val === laeSeCd)
: !isAllAreaUser && laeSeCd?.length === 2
? code.sggCd.filter((item) => item.val.slice(0, 2) === laeSeCd)
: code.sggCd
: code.sggCd
}
selectRegionData={code.rvbsnCd}
selectUnitWaterShedData={code.lgbsnCd}
@@ -60,6 +60,11 @@ export default function RainSttn() {
}>({ data: undefined, isOpen: false });
const [fcltNm, setFcltNm] = useState<string>("");
const [appliedSearch, setAppliedSearch] = useState({
conValue: "0",
fcltNm: "",
});
const [searchTrigger, setSearchTrigger] = useState(0);
const { t } = useTranslation("base");
const alert = useAlertStore((state) => state.setMessage);
const skipNextSearchRef = useRef(true);
@@ -142,16 +147,27 @@ export default function RainSttn() {
return;
}
const initialConValue =
laeSeCd === undefined || laeSeCd === "0"
? "0"
: laeSeCd.length === 5
? laeSeCd
: laeSeCd.slice(0, 2);
skipNextSearchRef.current = true;
setAppliedSearch({
conValue: initialConValue,
fcltNm: "",
});
rainSttnInit(
false,
1,
rainSttnSize,
getFixedConValue(),
fcltNm,
initialConValue,
"",
code as unknown as Record<string, SelectDataType[]>
);
}, [rainSttn.isInit, rainSttnInit, fcltNm, code, getFixedConValue, isLaeSeCdReady]);
}, [rainSttn.isInit, rainSttnInit, code, isLaeSeCdReady, laeSeCd]);
useEffect(() => {
if (!isLaeSeCdReady) {
@@ -163,16 +179,28 @@ export default function RainSttn() {
return;
}
if (searchTrigger === 0) {
return;
}
rainSttnSearch(
false,
pageNation,
rainSttnSize,
String(getFixedConValue()),
fcltNm,
appliedSearch.conValue,
appliedSearch.fcltNm,
code as unknown as Record<string, SelectDataType[]>
);
isResetPage(false);
}, [pageNation, rainSttnSearch, fcltNm, code, getFixedConValue, isLaeSeCdReady]);
}, [
pageNation,
searchTrigger,
rainSttnSearch,
code,
appliedSearch.conValue,
appliedSearch.fcltNm,
isLaeSeCdReady,
]);
useEffect(() => {
if (!isLaeSeCdReady) {
@@ -212,19 +240,31 @@ export default function RainSttn() {
true,
1,
rainSttnResCount,
String(getFixedConValue()),
fcltNm,
appliedSearch.conValue,
appliedSearch.fcltNm,
code as unknown as Record<string, SelectDataType[]>
);
}, [
rainSttnSearch,
fcltNm,
rainSttnResCount,
code,
getFixedConValue,
appliedSearch.conValue,
appliedSearch.fcltNm,
isLaeSeCdReady,
]);
const handleSearch = useCallback(() => {
const nextSearch = {
conValue: String(getFixedConValue()),
fcltNm,
};
setAppliedSearch(nextSearch);
isResetPage(true);
setPageNation(1);
setSearchTrigger((prev) => prev + 1);
}, [fcltNm, getFixedConValue]);
useEffect(() => {
const data = tableData(rainSttnRes, { code });
const chData = data
@@ -318,17 +358,7 @@ export default function RainSttn() {
<div className="flex flex-row space-x-2 items-center">
<Button
className={btnClass}
onClick={() => {
rainSttnSearch(
false,
1,
rainSttnSize,
String(getFixedConValue()),
fcltNm,
code as unknown as Record<string, SelectDataType[]>
);
isResetPage(true);
}}
onClick={handleSearch}
>
조회
</Button>
@@ -55,6 +55,11 @@ export default function RswtSttn() {
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false })
const [fcltNm, setFcltNm] = useState<string>("");
const [appliedSearch, setAppliedSearch] = useState({
conValue: "0",
fcltNm: "",
});
const [searchTrigger, setSearchTrigger] = useState(0);
const { t } = useTranslation('base');
const alert = useAlertStore((state) => state.setMessage);
const isAllAreaUser = laeSeCd === "0";
@@ -137,16 +142,27 @@ export default function RswtSttn() {
return;
}
const initialConValue =
laeSeCd === undefined || laeSeCd === "0"
? "0"
: laeSeCd.length === 5
? laeSeCd
: laeSeCd.slice(0, 2);
skipNextSearchRef.current = true;
setAppliedSearch({
conValue: initialConValue,
fcltNm: "",
});
rswtSttnInit(
false,
1,
rswtSttnSize,
getFixedConValue(),
fcltNm,
initialConValue,
"",
code as unknown as Record<string, SelectDataType[]>
);
}, [rswtSttn.isInit, rswtSttnInit, fcltNm, code, getFixedConValue, isLaeSeCdReady]);
}, [rswtSttn.isInit, rswtSttnInit, code, isLaeSeCdReady, laeSeCd]);
useEffect(() => {
const data = tableData(rswtSttnRes, { code });
@@ -208,8 +224,8 @@ export default function RswtSttn() {
}
isDownloadTriggered.current = true;
rswtSttnSearch(true, 1, rswtSttnResCount, String(getFixedConValue()), fcltNm, code as unknown as Record<string, SelectDataType[]>);
}, [rswtSttnSearch, fcltNm, rswtSttnResCount, code, getFixedConValue, isLaeSeCdReady]);
rswtSttnSearch(true, 1, rswtSttnResCount, appliedSearch.conValue, appliedSearch.fcltNm, code as unknown as Record<string, SelectDataType[]>);
}, [rswtSttnSearch, rswtSttnResCount, code, appliedSearch.conValue, appliedSearch.fcltNm, isLaeSeCdReady]);
useEffect(() => {
if (!isLaeSeCdReady) {
@@ -221,9 +237,29 @@ export default function RswtSttn() {
return;
}
rswtSttnSearch(false, pageNation, rswtSttnSize, String(getFixedConValue()), fcltNm, code as unknown as Record<string, SelectDataType[]>);
if (searchTrigger === 0) {
return;
}
rswtSttnSearch(false, pageNation, rswtSttnSize, appliedSearch.conValue, appliedSearch.fcltNm, code as unknown as Record<string, SelectDataType[]>);
isResetPage(false)
}, [pageNation, rswtSttnSearch, fcltNm, code, getFixedConValue, isLaeSeCdReady]);
}, [pageNation, searchTrigger, rswtSttnSearch, code, appliedSearch.conValue, appliedSearch.fcltNm, isLaeSeCdReady]);
const handleSearch = useCallback(() => {
if (!isLaeSeCdReady) {
return;
}
const nextSearch = {
conValue: String(getFixedConValue()),
fcltNm,
};
setAppliedSearch(nextSearch);
isResetPage(true);
setPageNation(1);
setSearchTrigger((prev) => prev + 1);
}, [fcltNm, getFixedConValue, isLaeSeCdReady]);
return (
<>
@@ -279,13 +315,7 @@ export default function RswtSttn() {
<div className="flex flex-row space-x-2 items-center">
<Button
className={btnClass}
onClick={() => {
if (!isLaeSeCdReady) {
return;
}
rswtSttnSearch(false, 1, rswtSttnSize, String(getFixedConValue()), fcltNm, code as unknown as Record<string, SelectDataType[]>);
isResetPage(true)
}}
onClick={handleSearch}
>
조회
</Button>
@@ -1,5 +1,8 @@
"use client";
/**
* 재이용실적 현황 > 지도점검 현황
*/
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import { rainPageSize } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
+4 -4
View File
@@ -52,10 +52,10 @@ export const authActionsExport: (set: any, get: any) => authActions = (
auth: {
...state.auth,
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].inqAuthrtYn,
regAuthrtYn: response[0].regAuthrtYn,
mdfcnAuthrtYn: response[0].mdfcnAuthrtYn,
delAuthrtYn: response[0].delAuthrtYn,
},
};
});
+3 -1
View File
@@ -1,4 +1,5 @@
import { fetchType, getFetchWithCount, putFectch } from "@/components/lib/api";
import { DEFAULT_LIST_SIZE } from "@/src/shared/config";
import type { CommonTableProps } from "../../common/type/header";
import {
constCostHeadListExport,
@@ -12,6 +13,7 @@ const coRvwHeadList = coRvwHeadListExport;
const coRvwNodeList = coRvwNodeListExport;
const rateFeeHeadList = rateFeeHeadListExport;
const constCostHeadList = constCostHeadListExport;
export type coRvwState = {
coRvw: {
isInit: boolean;
@@ -118,7 +120,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
const coMatRvwRes = await coMatInit(
1,
10,
DEFAULT_LIST_SIZE,
new Date().getFullYear().toString(),
new Date().getFullYear().toString()
);
+2 -2
View File
@@ -75,8 +75,8 @@ export const DmdCoReqSchema = z.object({
dmndTmng: z.coerce.date().nullish(), // 요구시기
dmndQty: z.number().nullish(), // 요구수량(m³/일)
blgcOxygDmndQnt: z.number().nullish(), // 생물학적 산소요구량(BOD)(mg/L)
wntUntprc: z.number().nullish(), // 희망단가(원/m3)
grssClfg: z.number().nullish(), // 총대장균군수(개/mg)
wntUntprc: z.number().nullish(), // 희망단가(원/m³)
grssClfg: z.number().nullish(), // 총대장균군수(개/100㎖)
combResdch: z.number().nullish(), // 결합잔류염소(mg/L)
trbt: z.number().nullish(), // 탁도(NTU)
smll: z.string().nullish(), // 냄새
@@ -124,7 +124,7 @@ export const rainMainHeadNmExport: chHeadType[] = [
export const rainRuseHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "ruseWater", text: "연간대체수 이용량(m³)", length: 13 },
{ headNm: "ruseWater", text: "연간빗물이용량(m³)", length: 13 },
{ headNm: "detailQulity", text: "분기", length: 11 },
];
@@ -133,7 +133,7 @@ export const rainSplHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "detailQulity", text: "분기", length: 11 },
{ headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 },
{ headNm: "splWater", text: "연간대체수이용량(m³)", length: 13 },
];
export const rainChkgdnHeadListExport: TableUpperProps[] = [
@@ -152,7 +152,7 @@ export const rainChkgdnHeadListExport: TableUpperProps[] = [
{ id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)", widthPercent: 7 },
{ id: "ph", title: "수소이온농도(pH)", upName: "useQuilty", upSequnce: 1, type: "number", edit: true, widthPercent: 5 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 2, type: "number", edit: true, widthPercent: 4 },
{ id: "grssClfg", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 3, type: "number", edit: true, widthPercent: 5 },
{ id: "grssClfg", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 3, type: "number", edit: true, widthPercent: 5 },
{ id: "vltnDt", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1, type: "text", edit: true, widthPercent: 5 },
{ id: "vltnMttr", title: "위반사항", upName: "feeHist", upSequnce: 2, type: "text", edit: true, widthPercent: 6 },
{ id: "levyAmt", title: "부과금액(천원)", upName: "feeHist", upSequnce: 3, type: "text", edit: true, widthPercent: 5 },
@@ -149,7 +149,7 @@ export const rswtChkHeadListExport: TableUpperProps[] = [
{ id: "utztnUsgCdText", title: "이용용도", widthPercent: 7 },
{ id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number", widthPercent: 5 },
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number", widthPercent: 5 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 1 ,type: "number", widthPercent: 6 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 1 ,type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mh/L)", upName: "useQuilty", upSequnce: 2 ,type: "number", widthPercent: 6 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3 ,type: "number", widthPercent: 4 },
{ id: "bod", title: "BOD\n(mg/L)", upName: "useQuilty", upSequnce: 4 ,type: "number", widthPercent: 4 },
@@ -15,7 +15,7 @@ export const rainChkgdnHeadSttnExport: TableUpperProps[] = [
{ id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)", widthPercent: 7 },
{ id: "ph", title: "수소이온농도(pH)", upName: "useQuilty", upSequnce: 1, type: "number", widthPercent: 5 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 2, type: "number", widthPercent: 4 },
{ id: "grssClfg", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 3, type: "number", widthPercent: 5 },
{ id: "grssClfg", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 3, type: "number", widthPercent: 5 },
{ id: "vltnDt", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1, type: "text", widthPercent: 5 },
{ id: "vltnMttr", title: "위반사항", upName: "feeHist", upSequnce: 2, type: "text", widthPercent: 6 },
@@ -14,7 +14,7 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [
{ id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number", widthPercent: 5 },
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number", widthPercent: 5 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 1, type: "number", widthPercent: 6 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 1, type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mh/L)", upName: "useQuilty", upSequnce: 2, type: "number", widthPercent: 6 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3, type: "number", widthPercent: 4 },
{ id: "bod", title: "BOD (mg/L)", upName: "useQuilty", upSequnce: 4, type: "number", widthPercent: 4 },
@@ -151,7 +151,7 @@ export const businessSttnInfo: formDetailType[] = [
export const ruseWaterQulityList: formDetailType[] = [
{ id: "bod", lable: "BOD(mg/L)", type: "text", maxLenght:numSize },
{ id: "grssClfg", lable: "총대장균군수(개/mg)", type: "text" , maxLenght:numSize},
{ id: "grssClfg", lable: "총대장균군수(개/100㎖)", type: "text" , maxLenght:numSize},
{ id: "combResdch", lable: "결합잔류염소(mh/L)", type: "text" , maxLenght:numSize},
{ id: "trbt", lable: "탁도(NTU)", type: "text" , maxLenght:numSize},
{ id: "smll", lable: "냄새", type: "text", maxLenght:numSize },
@@ -261,11 +261,14 @@ export const pubFacActionsExport: (set: any, get: any) => pubFacActions = (set:
const newPbnstRes = await initFetch(
pbnstRes.pbnstRuseFcltId.slice(0, 11)
);
const wrRegList = await getFetch("wr", `/reuseFc/pubFc`);
const fclts = Array.isArray(wrRegList) ? wrRegList : [];
set((state: pubFacState) => ({
...state,
pbnst: {
...state.pbnst,
pbnstRes: newPbnstRes,
fclts,
},
}));
return successMessage;
@@ -102,7 +102,7 @@ export const rusePplHeadListExport: TableUpperProps[] = [
export const ruseGuranteeHeadListExport: TableUpperProps[] = [
{ id: "cycl", title: "차수", visible: true },
{ id: "bod", title: "BOD(mg/L)", visible: true },
{ id: "grssClfg", title: "총대장균군수(개/mg)", visible: true },
{ id: "grssClfg", title: "총대장균군수(개/100㎖)", visible: true },
{ id: "combResdch", title: "결합잔류염소(mh/L)", visible: true },
{ id: "trbt", title: "탁도(NTU)", visible: true },
{ id: "smll", title: "냄새", visible: true },
@@ -149,6 +149,7 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
rainSttn: {
...state.rainSttn,
excelList: rainExcel,
rainSttnResCount: rainSttnRes.count,
},
};
});
@@ -159,6 +160,7 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
rainSttn: {
...state.rainSttn,
rainSttnRes: rainSttnRes.data,
rainSttnResCount: rainSttnRes.count,
isInit: false,
},
};
+2 -2
View File
@@ -29,8 +29,8 @@ export const rainChkTableData = (chkData: rainChkZodType[]) => {
totalRuseFcltUtztnQnt: 0,
};
acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt;
acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt;
acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt; // 연간대체수이용량(m³)
acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt; // 연간빗물이용량(m³)
return acc;
}, {});
@@ -28,6 +28,19 @@ import PopoverUi from "@common/business_components/ui/popover_ui";
import { Button, Checkbox, Input, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import { useForm, type UseFormReturn } from "react-hook-form";
const formatNumberWithComma = (value: string | number | null | undefined) => {
if (value === null || value === undefined || value === "") return "";
const normalized =
typeof value === "string" ? Number(value.replace(/,/g, "")) : Number(value);
if (Number.isNaN(normalized)) {
return String(value);
}
return normalized.toLocaleString("ko-KR");
};
/**
* ConCostForm
* 호출: (with-layout)/matMng/coMat/coMatRvw
@@ -456,12 +469,16 @@ export default function ConCostForm({
<FormUi label="요구수량(m³/일)">
<Input
placeholder=""
type="number"
step="0.1"
type={isSim ? "number" : "text"}
step={isSim ? "0.1" : undefined}
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
value={field.value ?? 0}
value={
isSim
? field.value ?? 0
: formatNumberWithComma(field.value)
}
readOnly={!isSim}
className={!isSim ? readOnlyInputClass : ""}
/>
@@ -524,14 +541,13 @@ export default function ConCostForm({
<FormUi label="처리시설용량(m³/일)">
<Input
placeholder=""
type="number"
step="0.001"
type="text"
onChange={(e) => {
console.log("dmndQty ", e);
//field.onChange(Number(e.target.value) * 365 );
}}
// value={Number(field.value) * 365 || 0}
value={Number(field.value) || 0}
value={formatNumberWithComma(Number(field.value) || 0)}
readOnly
className={readOnlyInputClass}
/>
@@ -151,7 +151,7 @@ export function BuseWaterQulity({
name={`items.${indexMng}.fcBizs.${indexBiz}.grssClfg`}
key={`items.${indexMng}.fcBizs.${indexBiz}.grssClfg-key`}
render={({ field }) => (
<FormUi label="총대장균군수(개/mg)">
<FormUi label="총대장균군수(개/100㎖)">
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
@@ -63,8 +63,8 @@ export default function RainChkgdTable({
const handleCellClick = useCallback((index: number, headId: string) => {
if (
headId === "totalSplyUswtrUtztnQnt" ||
headId === "totalRuseFcltUtztnQnt"
headId === "totalSplyUswtrUtztnQnt" || // 연간대체수이용량(m³)
headId === "totalRuseFcltUtztnQnt" // 연간빗물이용량(m³)
) {
handleClick(makeTableData[index])
+1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+56
View File
@@ -21,6 +21,62 @@
.card-header {
@apply border-b-[#3f4042];
}
.gis-layer-panel {
background: #111214;
border-right-color: #2f3136;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.03);
}
.gis-layer-panel-title {
color: #f3f4f6;
}
.gis-layer-panel hr {
border-color: #2f3136;
}
.gis-layer-row {
color: #e5e7eb;
}
.gis-layer-row:hover {
background: rgba(255, 255, 255, 0.08);
}
.gis-layer-row span,
.gis-layer-row label {
color: #e5e7eb !important;
}
.gis-layer-row input[type="checkbox"] {
accent-color: #c026d3;
}
.gis-layer-action-button {
background: #1a1c20;
border-color: #3b82f6;
color: #cfe3ff;
}
.gis-layer-action-button:hover {
background: #23262b;
}
.gis-layer-setting {
background: #111214;
border-top: 1px solid #2f3136;
}
.gis-layer-toggle {
background: #111214;
border-color: #2f3136;
color: #f3f4f6;
}
.gis-layer-toggle:hover {
background: #1a1c20;
}
}
input[type="file"] {
@@ -518,7 +518,7 @@ export default React.memo(function GisTree({
<>
{/* 사이드 패널 */}
<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={`gis-layer-panel 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"
: "pointer-events-none -translate-x-full opacity-0"
}`}
@@ -527,7 +527,7 @@ export default React.memo(function GisTree({
className={`grow overflow-y-auto border-none p-2 pt-0 shadow-none ${activeTab === "layers" ? "block" : "hidden"
}`}
>
<h3 className="mx-2 my-4 text-3xl font-bold text-gray-600 text-center">시설물</h3>
<h3 className="gis-layer-panel-title mx-2 my-4 text-3xl font-bold text-gray-600 text-center">시설물</h3>
<hr className="mb-4" />
{/* DnD Tree */}
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
@@ -550,7 +550,7 @@ export default React.memo(function GisTree({
return (
<div style={{ display: isVisible ? "block" : "none" }}>
<div
className="flex items-center gap-2 rounded px-2 py-1 hover:bg-gray-100"
className="gis-layer-row flex items-center gap-2 rounded px-2 py-1 hover:bg-gray-100"
style={{
marginInlineStart: depth * 16,
}}
@@ -578,7 +578,7 @@ export default React.memo(function GisTree({
{showLayerActionButton && (
<button
type="button"
className="ml-1 shrink-0 rounded-sm border border-[#9cc6f0] bg-white px-2 py-[1px] text-[1.1rem] font-medium leading-5 text-[#2c6fb7] transition-colors hover:bg-[#eef6ff]"
className="gis-layer-action-button ml-1 shrink-0 rounded-sm border border-[#9cc6f0] bg-white px-2 py-[1px] text-[1.1rem] font-medium leading-5 text-[#2c6fb7] transition-colors hover:bg-[#eef6ff]"
onClick={() =>
handleLayerImgClick(
node.data.lyrTyNm,
@@ -627,13 +627,13 @@ export default React.memo(function GisTree({
</DndProvider>
</div>
{LayerSettingView && (
<div className="shrink-0 bg-white px-2 pb-3">
<div className="gis-layer-setting shrink-0 bg-white px-2 pb-3">
<LayerSettingView />
</div>
)}
<button
onClick={() => setVisible(false)}
className={`absolute right-[-3px] z-50 transform rounded-l-none rounded-r border border-l border-gray-300 bg-white px-1 py-2 shadow-sm ${toggleButtonPositionClass}`}
className={`gis-layer-toggle absolute right-[-3px] z-50 transform rounded-l-none rounded-r border border-l border-gray-300 bg-white px-1 py-2 shadow-sm ${toggleButtonPositionClass}`}
>
<ChevronLeft size={16} />
</button>
@@ -641,7 +641,7 @@ export default React.memo(function GisTree({
{!visible && (
<button
onClick={() => setVisible(true)}
className={`absolute left-[-3px] z-50 transform rounded-r border border-r border-gray-300 bg-white px-1 py-2 shadow-sm ${toggleButtonPositionClass}`}
className={`gis-layer-toggle absolute left-[-3px] z-50 transform rounded-r border border-r border-gray-300 bg-white px-1 py-2 shadow-sm ${toggleButtonPositionClass}`}
>
<ChevronRight size={16} />
</button>
@@ -4,7 +4,7 @@ import { Button } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import SearchInputText from "./SearchInputText";
import GisApp from "../lib/gis.core";
import { moveMapToCenter, sidoCenter, sigunguCenter } from "./utils";
import { getSigunguCenter, moveMapToCenter, sidoCenter } from "./utils";
const FACILITY_TYPE_URL_MAP: Record<string, string> = {
tb_il_swage_prcs_fclt:
@@ -112,9 +112,9 @@ export const SearchBar = ({
.map((item: any) => ({
val: item.lwpoCd,
text: item.lwpoCdNm,
center: sigunguCenter[item.lwpoCd?.trim()],
}))
.sort((a: any, b: any) => a.text.localeCompare(b.text, 'ko'));
center: getSigunguCenter(item.lwpoCd),
}));
//.sort((a: any, b: any) => a.text.localeCompare(b.text, 'ko'));
setSidoData([{ text: "시도", val: "0" }, ...sidoList]);
setSigunData(sigunList);
};
@@ -168,7 +168,10 @@ export const SearchBar = ({
const selectedSido = sidoData.find((item) => item.val === sidoValue);
if (!selectedSido?.center) return;
// console.log("시도 : " +sidoValue);
// console.log("시군구 : " + sigunValue);
if (sigunValue !== "0") {
console.log(sigunData.filter((item) => item.val == sigunValue));
const selectedSigun =
filteredSigunData.find((item) => item.val === sigunValue) ??
sigunData.find((item) => item.val === sigunValue);
@@ -2,6 +2,11 @@ import type { RefObject } from "react";
import { transform } from "ol/proj";
import GisApp from "../lib/gis.core";
/**
*
* @param geometry
* @returns
*/
export const getGeometryCenter = (geometry: any): [number, number] | null => {
if (!geometry?.coordinates) return null;
@@ -42,22 +47,34 @@ export const getGeometryCenter = (geometry: any): [number, number] | null => {
return [(minX + maxX) / 2, (minY + maxY) / 2];
};
/**
* 이미 5179좌표계이면 변환하지 않음.
* @param centerCoord
* @returns
*/
export const getMapCenterCoordinate = (centerCoord: [number, number]): [number, number] => {
const [x, y] = centerCoord;
const looksLikeLonLat = Math.abs(x) <= 180 && Math.abs(y) <= 90;
const looksLikeLatLonSwapped = Math.abs(x) <= 90 && Math.abs(y) <= 180;
const looksLikeLonLat = Math.abs(x) <= 180 && Math.abs(y) <= 90; // 경위도
const looksLikeLatLonSwapped = Math.abs(x) <= 90 && Math.abs(y) <= 180; // 위경도
if (looksLikeLonLat) {
return transform(centerCoord, GisApp.FROM_PROJ, GisApp.TO_PROJ) as [number, number];
if (looksLikeLonLat) {
return transform(centerCoord, GisApp.FROM_PROJ, GisApp.TO_PROJ) as [number, number]; // 4326 -> 5179
}
if (looksLikeLatLonSwapped) {
return transform([y, x], GisApp.FROM_PROJ, GisApp.TO_PROJ) as [number, number];
return transform([y, x], GisApp.FROM_PROJ, GisApp.TO_PROJ) as [number, number]; // 4326 -> 5179
}
return centerCoord;
};
/**
*
* @param gisMaps
* @param centerCoord
* @param zoom
* @returns
*/
export const moveMapToCenter = (
gisMaps: RefObject<any>,
centerCoord?: [number, number],
@@ -69,7 +86,7 @@ export const moveMapToCenter = (
if (!map?.getView) return;
const view = map.getView();
const center = getMapCenterCoordinate(centerCoord);
const center = getMapCenterCoordinate(centerCoord); // 4326 -> 5179
if (!Number.isFinite(center[0]) || !Number.isFinite(center[1])) return;
const projection = view.getProjection?.();
@@ -89,7 +106,28 @@ export const moveMapToCenter = (
});
};
/**
* 시군구 코드에 해당하는 중심 좌표를 반환한다.
* 먼저 전체 코드로 정확히 조회하고, 값이 없으면 앞 4자리가 같은 다른 시군구 코드의 좌표를 대체로 사용한다.
*
* @param lwpoCd 행정구역 코드. 예: "41110"
* @returns 시군구 중심 좌표. 조회 결과가 없으면 undefined
*/
export const getSigunguCenter = (lwpoCd?: string | null): [number, number] | undefined => {
const code = lwpoCd?.trim();
if (!code) return undefined;
const exactCenter = sigunguCenter[code];
if (exactCenter) return exactCenter;
const prefix = code.slice(0, 4);
if (prefix.length < 4) return undefined;
const fallbackCode = Object.keys(sigunguCenter).find((key) => key.startsWith(prefix));
return fallbackCode ? sigunguCenter[fallbackCode] : undefined;
};
// EPSG:5179 기준
export const sidoCenter: Record<string, [number, number]> = {
"11": [953551.4119216551, 1951826.357686395], // 서울특별시
"26": [1139616.39795744, 1683535.70569214], // 부산광역시
@@ -98,23 +136,25 @@ export const sidoCenter: Record<string, [number, number]> = {
"29": [939294.465052485, 1684630.0528689548], // 광주광역시
"30": [991301.069516585, 1816039.953510165], // 대전광역시
"31": [1155621.6870651902, 1727062.69578941], // 울산광역시
"36": [979381.304388835, 1841430.83666584], // 세종특별자치시
// "36": [979381.304388835, 1841430.83666584], // 세종특별자치시
"41": [965621.8990488851, 1954345.5683645], // 경기도
"51": [1065114.938094025, 1980661.8453088552], // 강원특별자치도
"42": [1065114.938094025, 1980661.8453088552], // 강원특별자치도
"43": [1041183.7558293699, 1848671.9260201901], // 충청북도
"44": [918689.7963483699, 1836216.9375309348], // 충청남도
"52": [949568.330647045, 1748060.215177075], // 전북특별자치도
"45": [949568.330647045, 1748060.215177075], // 전북특별자치도
"46": [906934.38266853, 1637218.959990745], // 전라남도
"47": [1207219.543276005, 1843195.136606685], // 경상북도
"48": [1081504.77257488, 1690185.644486565], // 경상남도
"50": [912295.3285630699, 1508716.724020285], // 제주특별자치도
"49": [909678.884, 1485988.314], // [912295.3285630699, 1508716.724020285], // 제주특별자치도
"50": [979381.304388835, 1841430.83666584], // 세종특별자치시
};
// EPSG:5179 기준, 구코드 기준임. 즉, 하수도정보시스템 기준, tb_ms_sgg는 신코드 기준임.
export const sigunguCenter: Record<string, [number, number]> = {
"41820": [994886.197272545, 1979052.53325589], // 가평군
"11680": [961673.31210415, 1944170.63969123], // 강남구
"11740": [968769.7482373801, 1950018.61637764], // 강동구
"51150": [1117008.10469879, 1968660.393087245], // 강릉시
"42150": [1117008.10469879, 1968660.393087245], // 강릉시
"11305": [957184.89551525, 1960948.17897649], // 강북구
"11500": [940169.1006571399, 1952038.261591505], // 강서구
"26440": [1125722.34144724, 1680630.4026719849], // 강서구
@@ -128,11 +168,11 @@ export const sigunguCenter: Record<string, [number, number]> = {
"28245": [932663.7165225251, 1951314.751646345], // 계양구
"47830": [1074241.6005863552, 1748726.26930297], // 고령군
"48820": [1073144.623548525, 1668111.92215727], // 고성군
"51820": [1079906.976889925, 2044841.96337935], // 고성군
"42820": [1079906.976889925, 2044841.96337935], // 고성군
"41281": [945034.57886003, 1962467.607140625], // 고양시 덕양구
"41285": [937759.4023643399, 1964877.5403761598], // 고양시 일산동구
"41287": [931767.42700348, 1964473.38259711], // 고양시 일산서구
"52790": [918393.62804468, 1716132.826828825], // 고창군
"45790": [918393.62804468, 1716132.826828825], // 고창군
"46770": [982955.40283695, 1619845.06015117], // 고흥군
"46720": [976956.4637679299, 1689787.68075054], // 곡성군
"44150": [962642.898486655, 1831283.240475945], // 공주시
@@ -148,14 +188,14 @@ export const sigunguCenter: Record<string, [number, number]> = {
"11530": [943255.85156361, 1944224.7496726601], // 구로구
"41310": [967836.346287095, 1955821.725581055], // 구리시
"47190": [1076297.5737083051, 1801900.2253747801], // 구미시
"52130": [904674.42544751, 1767715.183037305], // 군산시
"45130": [904674.42544751, 1767715.183037305], // 군산시
"27720": [1103849.611897755, 1797204.1944948], // 군위군
"41410": [948411.5776788951, 1927135.757437655], // 군포시
"44710": [998110.87580872, 1792026.97835628], // 금산군
"26410": [1144696.85330015, 1697028.92074261], // 금정구
"11545": [946955.492372005, 1940271.753176735], // 금천구
"26710": [1155182.296211175, 1700204.308554315], // 기장군
"52210": [935224.890489175, 1755205.56953502], // 김제시
"45210": [935224.890489175, 1755205.56953502], // 김제시
"47150": [1052988.6733146599, 1783129.650228145], // 김천시
"41570": [925936.0119416551, 1965389.1203246699], // 김포시
"48250": [1123054.361467805, 1698359.11561172], // 김해시
@@ -166,7 +206,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"31140": [1164388.02782853, 1725008.616189565], // 남구
"28200": [931330.60305263, 1937119.742216935], // 남동구
"41360": [976250.43203106, 1960416.01281481], // 남양주시
"52190": [993534.846345315, 1715101.9621615452], // 남원시
"45190": [993534.846345315, 1715101.9621615452], // 남원시
"48840": [1042145.28987352, 1636788.3540021451], // 남해군
"11350": [962686.53614943, 1961831.238915105], // 노원구
"44230": [969310.08745166, 1800569.86826501], // 논산시
@@ -187,20 +227,20 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41250": [963148.54470934, 1991284.1699954001], // 동두천시
"26260": [1143854.4548997101, 1691107.4825817398], // 동래구
"11590": [950875.3949149849, 1944274.8505625948], // 동작구
"51170": [1137915.778823135, 1947378.462972425], // 동해시
"42170": [1137915.778823135, 1947378.462972425], // 동해시
"11440": [947783.55349907, 1951598.7669664049], // 마포구
"46110": [897040.76889998, 1645038.46904772], // 목포시
"46840": [899587.661355305, 1664175.9893810651], // 무안군
"52730": [1019648.0651991949, 1770211.22681309], // 무주군
"45730": [1019648.0651991949, 1770211.22681309], // 무주군
"47280": [1055620.11152262, 1855147.380883595], // 문경시
"28177": [926226.3594373399, 1940093.5607894901], // 미추홀구
"28170": [926226.3594373399, 1940093.5607894901], // 미추홀구
"48270": [1117632.749546145, 1722736.830743895], // 밀양시
"44180": [896412.984839845, 1817364.412693855], // 보령시
"46780": [974939.082074685, 1646364.3864357], // 보성군
"43720": [1018843.696119875, 1832681.50774474], // 보은군
"47920": [1125627.975921425, 1881676.019098885], // 봉화군
"26230": [1140857.032737495, 1686905.10585393], // 부산진구
"52800": [906904.98605649, 1743711.597374285], // 부안군
"45800": [906904.98605649, 1743711.597374285], // 부안군
"44760": [943835.889385045, 1803731.764881715], // 부여군
"41194": [937043.513148805, 1941917.36078241], // 부천시 소사구
"41196": [937651.62357278, 1948088.650724335], // 부천시 오정구
@@ -214,14 +254,14 @@ export const sigunguCenter: Record<string, [number, number]> = {
"48240": [1048464.90586407, 1669923.18795966], // 사천시
"26380": [1134089.12930976, 1669378.70600097], // 사하구
"48860": [1036496.214687915, 1711373.1111203502], // 산청군
"51230": [1142435.56217199, 1918978.17539709], // 삼척시
"42230": [1142435.56217199, 1918978.17539709], // 삼척시
"47250": [1050861.110185015, 1827830.141259105], // 상주시
"26140": [1137931.5983933099, 1678744.3981297952], // 서구
"27170": [1094695.438967345, 1764784.74637237], // 서구
"28260": [922809.601799805, 1950975.3525035651], // 서구
"29140": [941246.057711575, 1682370.2693891348], // 서구
"30170": [985795.240808525, 1808929.24401602], // 서구
"50130": [912245.46105533, 1478972.2801259952], // 서귀포시
"49130": [912245.46105533, 1478972.2801259952], // 서귀포시
"11410": [950162.9458121, 1953864.48715979], // 서대문구
"44210": [909519.454637895, 1868915.7895147852], // 서산시
"44770": [925279.862903135, 1788678.070235445], // 서천군
@@ -233,7 +273,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"11290": [957924.54027983, 1956512.99273417], // 성북구
"47840": [1065227.20912238, 1769071.96163128], // 성주군
"36110": [979381.304388835, 1841430.83666584], // 세종특별자치시
"51210": [1089137.1194603802, 2019900.279034885], // 속초시
"42210": [1089137.1194603802, 2019900.279034885], // 속초시
"11710": [965928.83189988, 1945132.495701395], // 송파구
"27260": [1104712.551021275, 1760363.086093105], // 수성구
"26500": [1146933.8448469099, 1686288.07716536], // 수영구
@@ -241,7 +281,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41117": [961096.623048695, 1919787.236461525], // 수원시 영통구
"41111": [955699.294199125, 1924459.3724800851], // 수원시 장안구
"41115": [956709.41575029, 1919672.72074137], // 수원시 팔달구
"52770": [962532.77503817, 1714757.02288094], // 순창군
"45770": [962532.77503817, 1714757.02288094], // 순창군
"46150": [989441.17839757, 1668074.09581569], // 순천시
"41390": [934025.6249972349, 1932609.450570995], // 시흥시
"46910": [837068.838454425, 1628081.7615658198], // 신안군
@@ -252,14 +292,14 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41550": [983047.68986298, 1891835.77152603], // 안성시
"41173": [951774.092625505, 1933701.863194715], // 안양시 동안구
"41171": [947757.35105343, 1934091.7197804102], // 안양시 만안구
"51800": [1044799.8868826301, 2018930.475621245], // 양구군
"42800": [1044799.8868826301, 2018930.475621245], // 양구군
"48330": [1140452.0009971252, 1713003.89300298], // 양산시
"51830": [1097181.1822994999, 2001875.93591869], // 양양군
"41630": [957059.65493005, 1978556.8707086649], // 양주시
"42830": [1097181.1822994999, 2001875.93591869], // 양양군
"41710": [957059.65493005, 1978556.8707086649], // 양주시
"11470": [943115.85871501, 1947726.50098438], // 양천구
"41830": [1006799.81121338, 1946640.3454016], // 양평군
"46130": [1005710.6789662449, 1606473.622557845], // 여수시
"41670": [1007389.488139985, 1920986.5590416798], // 여주시
"41730": [1007389.488139985, 1920986.5590416798], // 여주시
"28185": [924301.1973107599, 1932716.5874987599], // 연수구
"26470": [1143967.4104618952, 1688206.29479672], // 연제구
"41800": [955142.1788103101, 2011772.731874695], // 연천군
@@ -270,7 +310,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"11560": [948221.9355719599, 1946989.433783595], // 영등포구
"46830": [918568.42419672, 1645644.03685685], // 영암군
"47760": [1147157.8720550952, 1855483.55416634], // 영양군
"51750": [1089601.7085692, 1913792.65877573], // 영월군
"42750": [1089601.7085692, 1913792.65877573], // 영월군
"47210": [1096747.798418135, 1876468.75553605], // 영주시
"47230": [1128168.955619915, 1779896.3669964802], // 영천시
"44810": [935536.49511435, 1852175.0178411552], // 예산군
@@ -279,7 +319,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"43730": [1016925.1630849349, 1812306.15335931], // 옥천군
"28720": [831017.06725137, 1941763.7671776651], // 옹진군
"46890": [934681.072970785, 1580260.96434381], // 완도군
"52710": [971714.974980355, 1764297.860421155], // 완주군
"45710": [971714.974980355, 1764297.860421155], // 완주군
"11170": [954152.265892775, 1948105.11473188], // 용산구
"41463": [966362.167328945, 1919086.059435395], // 용인시 기흥구
"41465": [962751.309054115, 1926067.760215145], // 용인시 수지구
@@ -287,7 +327,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"47940": [1339459.86503077, 1939972.251018405], // 울릉군
"31710": [1152103.383022245, 1727062.6953479801], // 울주군
"47930": [1158508.6557213152, 1879083.142978875], // 울진군
"51130": [1042675.604280725, 1925192.470561375], // 원주시
"42130": [1042675.604280725, 1925192.470561375], // 원주시
"30200": [985050.9636310451, 1820539.200680445], // 유성구
"11380": [949045.72456328, 1957730.2124330248], // 은평구
"43770": [1010863.03644352, 1888460.954926385], // 음성군
@@ -296,17 +336,18 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41430": [954677.940289845, 1928890.756176705], // 의왕시
"41150": [962517.29720437, 1970466.861837395], // 의정부시
"41500": [998578.52141335, 1911408.9081171248], // 이천시
"52140": [954517.27659099, 1780482.64843965], // 익산시
"51810": [1065461.7186508449, 2010839.9914423148], // 인제군
"52750": [978229.1338949, 1735882.14962747], // 임실군
"45140": [954517.27659099, 1780482.64843965], // 익산시
"42810": [1065461.7186508449, 2010839.9914423148], // 인제군
"45750": [978229.1338949, 1735882.14962747], // 임실군
"46880": [932255.0392670301, 1705063.362106785], // 장성군
"52740": [1002766.6532682499, 1739693.270420835], // 장수군
"45740": [1002766.6532682499, 1739693.270420835], // 장수군
"46800": [946864.73876017, 1628580.0165673052], // 장흥군
"45110": [965336.86328101, 1761811.9640769851], // 전주시
"52113": [965336.86328101, 1761811.9640769851], // 전주시 덕진구
"52111": [966893.605588815, 1754468.0213283598], // 전주시 완산구
"51770": [1110360.32014753, 1931013.538670365], // 정선군
"52180": [947098.239651715, 1735070.37728938], // 정읍시
"50110": [912295.3285630699, 1517557.88079664], // 제주시
"42770": [1110360.32014753, 1931013.538670365], // 정선군
"45180": [947098.239651715, 1735070.37728938], // 정읍시
"49110": [910293.463, 1500596.952], //[912295.3285630699, 1517557.88079664], // 제주시
"43150": [1055671.53255738, 1893115.04197246], // 제천시
"11110": [954639.31671291, 1955652.65017071], // 종로구
"11140": [955326.326625775, 1951065.0090038702], // 중구
@@ -318,7 +359,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"11260": [964157.58830932, 1955182.665424475], // 중랑구
"43745": [1009013.34585424, 1864734.46162539], // 증평군
"46900": [870540.7578711801, 1597916.46618356], // 진도군
"52720": [995028.62264954, 1758625.23076353], // 진안군
"45720": [995028.62264954, 1758625.23076353], // 진안군
"48170": [1057167.869451315, 1689727.04548209], // 진주시
"43750": [994342.7517215649, 1875793.7535851048], // 진천군
"48740": [1090841.266427235, 1726085.44764784], // 창녕군
@@ -329,7 +370,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"48129": [1111617.62626141, 1680996.6599733299], // 창원시 진해구
"44131": [974478.67761805, 1862026.08139811], // 천안시 동남구
"44133": [971707.02854063, 1875704.530427825], // 천안시 서북구
"51780": [997447.306165415, 2024301.05104793], // 철원군
"42780": [997447.306165415, 2024301.05104793], // 철원군
"47820": [1116479.97088183, 1746481.442573125], // 청도군
"47750": [1139213.58132652, 1821067.804006115], // 청송군
"44790": [941752.03871163, 1827462.3607307798], // 청양군
@@ -337,16 +378,16 @@ export const sigunguCenter: Record<string, [number, number]> = {
"43112": [994258.23710022, 1838908.2251619152], // 청주시 서원구
"43114": [997183.756680205, 1857171.4242312452], // 청주시 청원구
"43113": [989161.30366111, 1849501.26145491], // 청주시 흥덕구
"51110": [1023515.451806125, 1986727.7291947599], // 춘천시
"42110": [1023515.451806125, 1986727.7291947599], // 춘천시
"43130": [1034982.8699803599, 1889790.67989893], // 충주시
"47850": [1086736.697665845, 1779277.075473485], // 칠곡군
"51190": [1131634.14282853, 1912631.8063723398], // 태백시
"42190": [1131634.14282853, 1912631.8063723398], // 태백시
"44825": [864678.84145598, 1854929.12571281], // 태안군
"48220": [1085316.421521755, 1640596.827168765], // 통영시
"41480": [941812.744809255, 1983528.9263305948], // 파주시
"51760": [1088652.567431635, 1949959.4658569149], // 평창군
"42760": [1088652.567431635, 1949959.4658569149], // 평창군
"41220": [952557.5035727851, 1891740.1058186102], // 평택시
"41650": [979667.7288193901, 1996457.8400075398], // 포천시
"41810": [979667.7288193901, 1996457.8400075398], // 포천시
"47111": [1173941.15980316, 1776164.6804896551], // 포항시 남구
"47113": [1155413.511896325, 1798939.39325854], // 포항시 북구
"41450": [974619.56368595, 1947933.69704679], // 하남시
@@ -358,10 +399,10 @@ export const sigunguCenter: Record<string, [number, number]> = {
"46820": [907157.24936254, 1615346.232287635], // 해남군
"26350": [1151062.0301465848, 1690808.34175552], // 해운대구
"44800": [919404.43702764, 1841255.1935457601], // 홍성군
"51720": [1049442.09422194, 1972233.65191397], // 홍천군
"42720": [1049442.09422194, 1972233.65191397], // 홍천군
"41590": [941810.22622111, 1906896.59289364], // 화성시
"46790": [955520.512544285, 1669429.020528105], // 화순군
"51790": [1015098.9945956401, 2015706.532260655], // 화천군
"51730": [1046904.40402372, 1943541.6685614302], // 횡성군
"42790": [1015098.9945956401, 2015706.532260655], // 화천군
"42730": [1046904.40402372, 1943541.6685614302], // 횡성군
};