하수재이용실적 등록 팝업 화면 수정, 등록 오류 수정, 테이블 merge 추가, 소계 및 총합계 배경색 추가

This commit is contained in:
jyha00
2025-07-01 16:34:43 +09:00
parent 2a1430da6c
commit d16645d0aa
5 changed files with 208 additions and 131 deletions
@@ -25,7 +25,7 @@ import {
} from "@common/components/ui";
import { useState, useEffect, useCallback } from "react";
import { set } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast";
import { headMakeprfmnc } from "../chkgdnReg/TableHead";
import { InputTableBody } from "@/components/lib/func/TableData";
import {
@@ -51,8 +51,8 @@ export default function prfmncPrfmncReg() {
}>({ isModal: false, data: {} as FcPrfType });
const [inputData, setInputData] = useState<any[]>();
const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState<string>();
const [pageNation , setPageNation] = useState<number>(1);
const [pageNation, setPageNation] = useState<number>(1);
const [cellNum, setCellNum] = useState<number>();
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
@@ -76,7 +76,7 @@ export default function prfmncPrfmncReg() {
);
prfmncHasuInit(String(pageNation), "41");
}
}, [inputData, searchYear, pbnstRuseFcltPk,prfmncHasuInit,pageNation]);
}, [inputData, searchYear, pbnstRuseFcltPk, prfmncHasuInit, pageNation]);
@@ -88,10 +88,16 @@ export default function prfmncPrfmncReg() {
isModal: false,
data: prfmncRes[index] || ({} as FcPrfType),
});
const inUsageData = structuredClone(initValInUsage);
const outUsageData = structuredClone(initValOutUsage);
const inSubtotal = structuredClone(subTotalObj);
const outSubtotal = structuredClone(subTotalObj);
const inUsageData = structuredClone(initValInUsage).map(row => ({
...row,
구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map(row => ({
...row,
구분: "장외용수",
}));
const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" };
const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" };
if (prfmncRes[index]?.prfList) {
prfmncRes[index].prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
@@ -127,7 +133,7 @@ export default function prfmncPrfmncReg() {
});
inUsageData.push(inSubtotal);
outUsageData.push(outSubtotal);
const usageTotalObj: { [key: string]: any } = structuredClone(totalObj);
const usageTotalObj: { [key: string]: any } = { ...structuredClone(totalObj), 구분: "총합계", 용수명: "" };
for (const key in usageTotalObj) {
if (["usage", "gubun", "edit"].includes(key)) continue;
const val =
@@ -162,7 +168,7 @@ export default function prfmncPrfmncReg() {
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"하수재이용 시설 등록"} />
<Title title={"하수재이용 실적 등록"} />
<SearchDiv>
<SearchFormLeft>
<div className="col-span-2 flex flex-row space-x-2">
@@ -197,7 +203,7 @@ export default function prfmncPrfmncReg() {
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="space-y">
<div className="flex flex-row space-x-2">
<Button size={"sm"} onClick={(e) => {}}>
<Button size={"sm"} onClick={(e) => { }}>
조회
</Button>
<Button
@@ -211,7 +217,12 @@ export default function prfmncPrfmncReg() {
<Button
size={"sm"}
onClick={() => {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
if (cellNum === undefined) {
toast.error("하수처리재이용시설을 선택해주세요.")
return;
} else {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
}
}}
>
파일등록
@@ -238,6 +249,7 @@ export default function prfmncPrfmncReg() {
headName={"하수재이용시설 목록"}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index, headId);
setCellNum(index ?? 0);
}}
onPageChange={setPageNation}
children={undefined}
@@ -266,7 +278,7 @@ export default function prfmncPrfmncReg() {
</div>
</ContentsDiv>
<UiModal
bodyClass={""}
bodyClass={"w-[30vw] max-w-none"}
body={<PbnPrfExcelModal data={isExcelOpen.data}></PbnPrfExcelModal>}
open={isExcelOpen.isModal}
onOpenChange={() => {
@@ -4,7 +4,7 @@ import type { TableUpperProps } from "../../common/type/header";
export const pbnPrfRegExcelZod = z.object({
ym: z.string().default(""),
bscFctNm: z.string().default(""),
pbnstRuseFcltFk:z.string(),
pbnstRuseFcltFk: z.string(),
pbnstRuseFcltNm: z.string().default(""),
file: z.instanceof(File).optional(),
});
@@ -18,20 +18,21 @@ export const pbnstPrfmncHeadListExport: TableUpperProps[] = [
];
export const monthHeadListExport: TableUpperProps[] = [
{ id: "구분", title: "구분", mergeCell: true },
{ id: "용수명", title: "용수명" },
{ id: "total", title: "계" },
{ id: "1월", title: "1월" ,edit: true },
{ id: "2월", title: "2월" ,edit: true },
{ id: "3월", title: "3월" ,edit: true },
{ id: "4월", title: "4월" ,edit: true },
{ id: "5월", title: "5월" ,edit: true },
{ id: "6월", title: "6월" ,edit: true },
{ id: "7월", title: "7월" ,edit: true },
{ id: "8월", title: "8월" ,edit: true },
{ id: "9월", title: "9월" ,edit: true },
{ id: "10월", title: "10월" ,edit: true },
{ id: "11월", title: "11월" ,edit: true },
{ id: "12월", title: "12월" ,edit: true },
{ id: "1월", title: "1월", edit: true },
{ id: "2월", title: "2월", edit: true },
{ id: "3월", title: "3월", edit: true },
{ id: "4월", title: "4월", edit: true },
{ id: "5월", title: "5월", edit: true },
{ id: "6월", title: "6월", edit: true },
{ id: "7월", title: "7월", edit: true },
{ id: "8월", title: "8월", edit: true },
{ id: "9월", title: "9월", edit: true },
{ id: "10월", title: "10월", edit: true },
{ id: "11월", title: "11월", edit: true },
{ id: "12월", title: "12월", edit: true },
];
+105 -46
View File
@@ -174,8 +174,6 @@ export const chRowspanTable = (
);
};
export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean) => {
return headList.forEach((head) => {
if (head.edit !== undefined)
@@ -187,63 +185,124 @@ export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean)
});
}
const handleChange = (
rowIndex: number,
field: any,
value: string | number,
setInputData: (data:any) => void
setInputData: (data: any) => void
) => {
if(Number(value) < 0 ||! Number(value)){
value = 0;
}else{
if (Number(value) < 0 || !Number(value)) {
value = 0;
} else {
value = Number(value);
}
setInputData((prevData:any) =>
prevData?.map((row:any, index: number) => {
if (index === rowIndex) {
return {
...row,
[field ]: value,
};
}
return row;
})
);
setInputData((prevData: any) =>
prevData?.map((row: any, index: number) => {
if (index === rowIndex) {
return {
...row,
[field]: value,
};
}
return row;
})
);
};
;
export const InputTableBody = (
chgRes: any[] | [],
headList: TableUpperProps[],
setInputData: (data: any[]) => void
) => {
// mergeCell 처리용
const getRowSpan = (data: any[], key: string, rowIndex: number) => {
if (rowIndex > 0 && data[rowIndex][key] === data[rowIndex - 1][key]) {
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;
};
export const InputTableBody = (chgRes: any[] | [] ,headList:TableUpperProps[], setInputData: (data: any[]) => void) => {
return (
return (
<>
{chgRes.map((data, dataIndex) => (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => (
<TableCell key={head.id + headIndex} className="w-36" >
{head.edit ===true && data.edit !==false?
<Input
className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`}
onChange={(e) =>
handleChange(dataIndex, head.id, e.target.value,setInputData)
}
value={data[head.id as keyof any] ?? ""}
/> : (
<div
className={`
${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
!text-xl w-full h-10
`}
{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) => {
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
data["구분"] === 0 &&
head.id === "구분"
) {
return (
<TableCell
key={head.id + headIndex}
colSpan={2}
className="font-bold text-center bg-[#d5e1eb]"
>
{data[head.id as keyof any] ?? ""}
</div>
)
총합계
</TableCell>
);
}
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
if (
data["구분"] === 0 &&
head.id === "용수명"
) {
return null;
}
// mergeCell 처리
if (head.mergeCell) {
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}`}>
{data[head.id as keyof any] ?? ""}
</TableCell>
);
}
</TableCell>
))}
</TableRow>
))}
// 기본 셀
return 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`}
onChange={(e) =>
handleChange(dataIndex, head.id, e.target.value, setInputData)
}
value={data[head.id as keyof any] ?? ""}
/>
</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
`}
>
{data[head.id as keyof any] ?? ""}
</div>
</TableCell>
);
})}
</TableRow>
);
})}
</>
);
}
};
@@ -52,6 +52,7 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
useEffect(() => {
if (files) {
console.log("이런")
const file = files[0];
if (form.getValues("ym").length < 1) {
ToastFormUi({
@@ -62,11 +63,12 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
return;
}
if (file)
if (file) {
filePicked(file, 0).then((res) => {
if (res !== undefined)
setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"),form.getValues('pbnstRuseFcltFk') ) as PrfType[])
setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"), form.getValues('pbnstRuseFcltFk')) as PrfType[])
});
}
}
}, [files]);
@@ -75,82 +77,85 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<div className="flex justify-between items-center">
<Title title={"하수재이용 실적등록"}></Title>
</div>
<div className="flex justify-between">
<h3 className="text-2xl font-bold text-gray-900 mt-1 ml-7">
{"점검정보 입력"}
</h3>
<Button className="" type="submit">
<Button className="mr-6 mt-10" type="submit">
저장
</Button>
</div>
<h3 className="text-2xl font-bold text-gray-900 my-3 ml-7">
{"점검정보 입력"}
</h3>
<div>
<div className="mx-7">
<FormField
control={form.control}
name="ym"
render={({ field }) => (
<FormUi label="등록년도">
<SelectBox
className="w-1/4 text-3xl"
className="w-1/2 pr-4 text-3xl"
selectClass={""}
label=""
selectArray={yearOptions}
value={field.value||new Date().getFullYear()}
value={field.value || new Date().getFullYear()}
onSelectValue={(val) => field.onChange(val)}
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="bscFctNm"
render={({ field }) => (
<FormUi label="시설명">
<Input
className="w-1/4 text-3xl"
value={field.value||''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="pbnstRuseFcltNm"
render={({ field }) => (
<FormUi label="재이용시설명">
<Input
className="w-1/4 text-3xl"
value={field.value||''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="점검결과 파일">
<FileUpload
files={files || []}
setFiles={(files: File[] | null) => {
if (files) setFiles(files);
}}
fileTypes="xlsx"
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
<div className="flex gap-4 mt-4">
<div className="flex-1">
<FormField
control={form.control}
name="bscFctNm"
render={({ field }) => (
<FormUi label="시설명">
<Input
className="text-3xl"
value={field.value || ''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
</div>
<div className="flex-1">
<FormField
control={form.control}
name="pbnstRuseFcltNm"
render={({ field }) => (
<FormUi label="재이용시설명">
<Input
className="flex-1 text-3xl"
value={field.value || ''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
</div>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="점검결과 파일">
<FileUpload
files={files || []}
setFiles={(files: File[] | null) => {
if (files) setFiles(files);
}}
fileTypes="xlsx"
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
Binary file not shown.