Merge branch 'main' into feature/0626_cw_sentry

This commit is contained in:
2026-06-29 09:41:03 +09:00
242 changed files with 28059 additions and 3866 deletions
@@ -1,5 +1,7 @@
"use client";
import { useState } from "react";
import { useUserStore } from "@/shared/lib";
import { useInstlSncnDetail } from "@/entities/instl-sncn";
import { Skeleton } from "@common/components";
+15 -11
View File
@@ -1,17 +1,29 @@
"use client";
import { useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import {
getCoreRowModel,
type PaginationState,
type Row,
type RowSelectionState,
type Table,
type Row,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable } from "@/shared/lib";
import { GroupApi } from "@/entities/group";
import { GroupUsersApi } from "@/entities/group-user";
import {
type AdminUserListResponse,
UserApi,
type UserSearchParams,
} from "@/entities/user";
import {
Button,
Checkbox,
Input,
Label,
Select,
@@ -19,18 +31,10 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
Checkbox,
} from "@common/components";
import { GroupApi } from "@/entities/group";
import {
UserApi,
type UserSearchParams,
type AdminUserListResponse,
} from "@/entities/user";
import { GroupUsersApi } from "@/entities/group-user";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useAlertStore } from "@/shared/lib";
import { columns } from "../model/table-columns";
import { AddAdminDialog } from "./add-admin-dialog";
function AdminPage() {
+41 -34
View File
@@ -1,20 +1,25 @@
"use client";
import { useEffect, useState, useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { BannerApi } from "../api/banner-service";
import { type Banner } from "../model/banner-interface";
import { createColumns } from "../model/banner-table-columns";
import { type Banner, type BannerParams } from "../model/banner-interface";
import { PreviewBannerDialog } from "./preview-banner-dialog";
type PostingTarget = {
@@ -22,50 +27,52 @@ type PostingTarget = {
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
bnrTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function BannerPage() {
const router = useRouter();
const imageRefs = useRef<Map<number, string>>(new Map());
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const [bnrTtl, setBnrTtl] = useState("");
const [searchParams, setSearchParams] = useState<BannerParams>({
bnrTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [bnrTtl, setBnrTtl] = useState(params.bnrTtl);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, params],
queryFn: () =>
BannerApi.getBannerList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
bnrTtl: params.bnrTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
bnrTtl,
});
setParams({ bnrTtl, page: 0 });
};
const handleReset = () => {
setBnrTtl("");
setSearchParams({
...searchParams,
bnrTtl: "",
});
resetParams();
};
const handleRowClick = (row: Banner) => {
@@ -92,7 +99,7 @@ function BannerPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+42 -38
View File
@@ -2,12 +2,19 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Board, BoardApi } from "@/entities/board";
import { CodeSelect } from "@/entities/code";
import {
Button,
Input,
@@ -18,60 +25,57 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import { CodeSelect } from "@/entities/code";
import { BoardApi, type BoardParams } from "@/entities/board";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { type Board } from "@/entities/board";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
bbsTypeCd: "",
bbsNm: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function BbsPage() {
const router = useRouter();
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [bbsTypeCd, setBbsTypeCd] = useState("");
const [bbsNm, setBbsNm] = useState("");
const [searchParams, setSearchParams] = useState<BoardParams>({
bbsTypeCd: "",
bbsNm: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [bbsTypeCd, setBbsTypeCd] = useState(params.bbsTypeCd);
const [bbsNm, setBbsNm] = useState(params.bbsNm);
const query = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.BBS, searchParams, pagination],
queryKey: [ENDPOINT.BOARD_SERVICE.BBS, params],
queryFn: () =>
BoardApi.getBoardList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
bbsTypeCd: params.bbsTypeCd,
bbsNm: params.bbsNm,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
bbsTypeCd,
bbsNm,
});
setParams({ bbsTypeCd, bbsNm, page: 0 });
};
const handleReset = () => {
setBbsTypeCd("");
setBbsNm("");
setSearchParams({
...searchParams,
bbsTypeCd: "",
bbsNm: "",
});
resetParams();
};
const handleRowClick = (row: Board) => {
@@ -89,7 +93,7 @@ function BbsPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
@@ -1,8 +1,11 @@
import dayjs from "dayjs";
import { useState } from "react";
import type { ColumnDef } from "@tanstack/react-table";
import { Button } from "@common/components";
import dayjs from "dayjs";
import type { UpCode } from "@/entities/code";
import { Button } from "@common/components";
import { UpCodeManagementDialog } from "../ui/upcode-dialog";
export const columns: ColumnDef<UpCode>[] = [
@@ -24,7 +27,7 @@ export const columns: ColumnDef<UpCode>[] = [
header: "상위코드명",
},
{
accessorKey: "cdSe",
accessorKey: "cdSeNm",
header: "코드구분",
},
{
+39 -35
View File
@@ -2,60 +2,64 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import {
ContentsApi,
type Contents,
type ContentsParams,
} from "@/entities/content";
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Contents, ContentsApi } from "@/entities/content";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
contsTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ContentsPage() {
const router = useRouter();
const [contsTtl, setContsTtl] = useState("");
const [searchParams, setSearchParams] = useState<ContentsParams>({
contsTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [contsTtl, setContsTtl] = useState(params.contsTtl);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS, params],
queryFn: () =>
ContentsApi.getContentsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
contsTtl: params.contsTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
contsTtl,
});
setParams({ contsTtl, page: 0 });
};
const handleReset = () => {
setContsTtl("");
setSearchParams({
...searchParams,
contsTtl: "",
});
resetParams();
};
const handleRowClick = (row: Contents) => {
@@ -69,7 +73,7 @@ function ContentsPage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
@@ -354,7 +354,7 @@ function DashboardPage() {
<div className="card-header text-foreground">
<h3 className="text-2xl font-medium">실시간 알림</h3>
</div>
<div className="p-4">
<div className="p-4" style={{ height: "120px", overflowY: "auto" }}>
<div className="space-y-4 text-foreground">
{noticeUserList?.map((item) => (
<div
@@ -373,7 +373,7 @@ function DashboardPage() {
<div className="card-header text-foreground">
<h3 className="text-2xl font-medium">일정 알림</h3>
</div>
<div className="p-4">
<div className="p-4" style={{ height: "120px", overflowY: "auto" }}>
<div className="space-y-4 text-foreground">
{scheduleList && scheduleList.length > 0 ? (
scheduleList.map((schedule, index) => {
+7 -4
View File
@@ -1,13 +1,16 @@
"use client";
import { useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { Button, Input, Label } from "@common/components";
import { AttachmentApi, type AttachmentParams } from "@/entities/attachment";
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
import { AttachmentApi, type AttachmentParams } from "@/entities/attachment";
import { Button, Input, Label } from "@common/components";
import { createColumns } from "../model/table-columns";
function FilePage() {
+31 -13
View File
@@ -23,6 +23,11 @@ import {
Input,
Checkbox,
Label,
Select,
SelectValue,
SelectContent,
SelectItem,
SelectTrigger,
} from "@common/components";
import {
UserApi,
@@ -42,9 +47,11 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
const alert = useAlertStore((state) => state.setMessage);
const [open, setOpen] = useState(false);
const [userName, setUserName] = useState("");
const [keywordType, setKeywordType] = useState("all");
const [keyword, setKeyword] = useState("");
const [searchParam, setSearchParam] = useState<UserSearchParams>({
userName: "",
keywordType: "all",
keyword: "",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
@@ -156,7 +163,7 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
...pagination,
pageIndex: 0,
});
setSearchParam({ userName });
setSearchParam({ keywordType, keyword });
};
const resetSearch = () => {
@@ -164,8 +171,8 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
pageIndex: 0,
pageSize: 10,
});
setUserName("");
setSearchParam({ userName: "" });
setKeyword("");
setSearchParam({ keywordType: "all", keyword: "" });
setUserRowSelection({});
};
@@ -184,15 +191,26 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
</DialogHeader>
<form onSubmit={handleSearch} className="mb-4 flex items-end gap-2">
<div className="flex items-center gap-2">
<Label htmlFor="userName" className="mr-2 text-label">
사용자 이름
</Label>
<Select
value={keywordType}
onValueChange={(value) => setKeywordType(value)}
>
<SelectTrigger className="w-[18rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">전체</SelectItem>
<SelectItem value="userId">아이디</SelectItem>
<SelectItem value="userName">이름</SelectItem>
<SelectItem value="email">이메일</SelectItem>
<SelectItem value="mphn">휴대폰번호</SelectItem>
</SelectContent>
</Select>
<Input
id="userName"
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
value={userName}
onChange={(e) => setUserName(e.target.value)}
placeholder="사용자 이름으로 검색"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className="!text-[1.3rem]"
placeholder="검색어를 입력하세요"
/>
</div>
<Button className="h-[3.2rem]" type="submit">
+37 -32
View File
@@ -2,68 +2,73 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { ImageApi } from "../api/image-service";
import { type Image } from "../model/image-interface";
import { columns } from "../model/image-table-columns";
import { type Image, type ImageParams } from "../model/image-interface";
type PostingTarget = {
pstgTrgtNm: string;
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ImagePage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<ImageParams>({
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, params],
queryFn: () =>
ImageApi.getImageList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
resetParams();
};
const handleRowClick = (row: Image) => {
@@ -81,7 +86,7 @@ function ImagePage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+55 -47
View File
@@ -2,45 +2,76 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import {
AuthorizationsApi,
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import {
type Authorizations,
type AuthorizationsParams,
AuthorizationsApi,
} from "@/entities/authorization";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/authorizations-table-columns";
const DEFAULT_PARAMS = {
authorizationName: "",
httpMethodCode: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ModulePage() {
const router = useRouter();
const [authorizationName, setAuthorizationName] = useState("");
const [httpMethodCode, setHttpMethodCode] = useState("");
const [searchParams, setSearchParams] = useState<AuthorizationsParams>({
authorizationName: "",
httpMethodCode: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [authorizationName, setAuthorizationName] = useState(
params.authorizationName,
);
const [httpMethodCode, setHttpMethodCode] = useState(params.httpMethodCode);
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, params],
queryFn: () =>
AuthorizationsApi.getAuthorizationsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
authorizationName: params.authorizationName,
httpMethodCode: params.httpMethodCode,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ authorizationName, httpMethodCode, page: 0 });
};
const handleReset = () => {
setAuthorizationName("");
setHttpMethodCode("");
resetParams();
};
const handleRowClick = (row: Authorizations) => {
router.push(`/module/register/${row.authorizationNo}`);
};
@@ -52,7 +83,7 @@ function ModulePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
@@ -62,29 +93,6 @@ function ModulePage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
authorizationName,
httpMethodCode,
});
};
const handleReset = () => {
setAuthorizationName("");
setHttpMethodCode("");
setSearchParams({
...searchParams,
authorizationName: "",
httpMethodCode: "",
});
};
return (
<div className="card card-border !mt-6">
<div className="card card-border">
+37 -32
View File
@@ -2,70 +2,75 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { Button, Input, Label } from "@common/components";
import { PopupApi } from "../api/popup-service";
import type { Popup } from "../model/popup-interface";
import { columns } from "../model/table-columns";
import type { Popup, PopupParams } from "../model/popup-interface";
type PostingTarget = {
pstgTrgtNm: string;
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function PopupPage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<PopupParams>({
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, params],
queryFn: () =>
PopupApi.getPopupList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
resetParams();
};
const handleRowClick = (row: Popup) => {
@@ -82,7 +87,7 @@ function PopupPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+98 -82
View File
@@ -1,15 +1,35 @@
"use client";
import dayjs from "dayjs";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
import {
useAlertStore,
useDataTable,
useSearchParamsState,
useSelectPrompt,
} from "@/shared/lib";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { BoardApi } from "@/entities/board";
import { AuthorityButton } from "@/entities/menu-authority";
import {
type DeletePost,
deleteReasonMessages,
PostApi,
type RecentPost,
} from "@/entities/post";
import { SortSelect } from "@/widgets/post";
import {
Button,
Input,
@@ -20,76 +40,72 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import { SortSelect } from "@/widgets/post";
import {
PostApi,
deleteReasonMessages,
type DeletePost,
type RecentPost,
type RecentPostParams,
} from "@/entities/post";
import { BoardApi } from "@/entities/board";
import { AuthorityButton } from "@/entities/menu-authority";
import { ENDPOINT } from "@/shared/config";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { useAlertStore, useSelectPrompt, useDataTable } from "@/shared/lib";
import { columns } from "../model/table-columns";
import { MovePostDialog } from "./move-post-dialog";
type DateRange = {
from?: Date | undefined;
to?: Date | undefined;
from?: Date | null | undefined;
to?: Date | null | undefined;
};
const selectData = Object.entries(deleteReasonMessages).map(
([label, value]) => ({ label, value }),
);
const DEFAULT_PARAMS = {
keywordType: "",
keyword: "",
bbsId: "",
delYn: "N",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function parseDateRange(frDateYmd: string, toDateYmd: string): DateRange {
return {
from: frDateYmd ? dayjs(frDateYmd, "YYYYMMDD").toDate() : undefined,
to: toDateYmd ? dayjs(toDateYmd, "YYYYMMDD").toDate() : undefined,
};
}
function RecentPage() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { prompt } = useSelectPrompt(selectData);
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [tableSort, setTableSort] = useState("regDt,desc");
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [presetDateRange, setPresetDateRange] = useState("");
const [dateRange, setDateRange] = useState<DateRange>({
from: undefined,
to: undefined,
});
const [keywordType, setKeywordType] = useState("");
const [keyword, setKeyword] = useState("");
const [bbsId, setBbsId] = useState<string | null>(null);
const [delYn, setDelYn] = useState("N");
const [searchParams, setSearchParams] = useState<RecentPostParams>({
keywordType: "",
keyword: "",
bbsId: null,
delYn: "N",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
});
const [isDeletePostData, setIsDeletePostData] = useState(false);
const [dateRange, setDateRange] = useState<DateRange>(() =>
parseDateRange(params.frDateYmd, params.toDateYmd),
);
const [keywordType, setKeywordType] = useState(params.keywordType);
const [keyword, setKeyword] = useState(params.keyword);
const [bbsId, setBbsId] = useState(params.bbsId);
const [delYn, setDelYn] = useState(params.delYn);
const query = useQuery({
queryKey: [
ENDPOINT.BOARD_SERVICE.POSTS,
searchParams,
pagination,
tableSort,
],
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, params],
queryFn: () =>
PostApi.getRecentPostList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
sort: tableSort,
keywordType: params.keywordType,
keyword: params.keyword,
bbsId: params.bbsId ? parseInt(params.bbsId) : null,
delYn: params.delYn,
frDateYmd: params.frDateYmd,
toDateYmd: params.toDateYmd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
@@ -99,9 +115,22 @@ function RecentPage() {
queryFn: () => BoardApi.getBoardList(),
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const isDeletePostData = params.delYn === "Y";
const handleRowClick = (row: RecentPost) => {
const from = `${pathname}${searchParams?.toString() ? `?${searchParams.toString()}` : ""}`;
router.push(
`/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`,
`/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}?from=${encodeURIComponent(from)}`,
);
};
@@ -116,7 +145,7 @@ function RecentPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
@@ -127,45 +156,32 @@ function RecentPage() {
const isSelectedRow = table.getFilteredSelectedRowModel().rows.length > 0;
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsDeletePostData(delYn === "Y");
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
setParams({
keywordType,
keyword,
bbsId: bbsId ? parseInt(bbsId) : null,
bbsId,
delYn,
frDateYmd: dateRange?.from
? dayjs(dateRange.from).format("YYYYMMDD")
: "",
toDateYmd: dateRange?.to ? dayjs(dateRange.to).format("YYYYMMDD") : "",
page: 0,
});
};
const handleReset = () => {
setKeywordType("");
setKeyword("");
setBbsId(null);
setBbsId("");
setDelYn("N");
setPresetDateRange("");
setDateRange({
from: undefined,
to: undefined,
});
setSearchParams({
...searchParams,
keywordType: "",
keyword: "",
bbsId: null,
delYn: "N",
frDateYmd: "",
toDateYmd: "",
});
setIsDeletePostData(false);
resetParams();
};
const handlePresetDateRange = (value: string) => {
@@ -273,10 +289,7 @@ function RecentPage() {
setDateRange={handleDateRangeChange}
/>
<Label className="ml-4 mr-2 text-label">게시판</Label>
<Select
value={bbsId?.toString() || ""}
onValueChange={(value) => setBbsId(value ? value : null)}
>
<Select value={bbsId} onValueChange={setBbsId}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
@@ -289,7 +302,7 @@ function RecentPage() {
</SelectContent>
</Select>
<Label className="ml-4 mr-2 text-label">상태</Label>
<Select value={delYn} onValueChange={(value) => setDelYn(value)}>
<Select value={delYn} onValueChange={setDelYn}>
<SelectTrigger className="w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
@@ -376,10 +389,10 @@ function RecentPage() {
</>
)}
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(parseInt(value));
}}
value={params.size.toString()}
onValueChange={(value) =>
setParams({ size: parseInt(value), page: 0 })
}
>
<SelectTrigger className="h-[3.2rem] w-[12rem]">
<SelectValue placeholder="페이지 크기" />
@@ -392,7 +405,10 @@ function RecentPage() {
))}
</SelectContent>
</Select>
<SortSelect value={tableSort} onValueChange={setTableSort} />
<SortSelect
value={params.sort}
onValueChange={(value) => setParams({ sort: value })}
/>
</div>
</div>
</div>
+51 -42
View File
@@ -2,39 +2,68 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { RoleApi, type Role, type RoleParams } from "@/entities/role";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Role, RoleApi } from "@/entities/role";
import { Button, Input, Label } from "@common/components";
import { createColumns } from "../model/table-columns";
const DEFAULT_PARAMS = {
keywordType: "roleName",
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function RolePage() {
const router = useRouter();
const alert = useAlertStore((state) => state.setMessage);
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<RoleParams>({
keywordType: "roleName",
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.ROLES, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.ROLES, params],
queryFn: () =>
RoleApi.getRoles({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keywordType: params.keywordType,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
resetParams();
};
const handleRowClick = (row: Role) => {
router.push(`/role/register/${row.roleId}`);
};
@@ -48,7 +77,7 @@ function RolePage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
},
dataTableOptions: {
onRowClick: handleRowClick,
@@ -56,26 +85,6 @@ function RolePage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
};
return (
<div className="card card-border !mt-6">
<div className="card card-border">
+40 -29
View File
@@ -1,54 +1,65 @@
"use client";
import "@common/assets/styles/grid.css";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { SiteApi, type Site, type SiteParams } from "@/entities/site";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Site, SiteApi } from "@/entities/site";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
siteNm: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function SitePage() {
const router = useRouter();
const [siteNm, setSiteNm] = useState("");
const [searchParams, setSearchParams] = useState<SiteParams>({
siteNm: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [siteNm, setSiteNm] = useState(params.siteNm);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.SITES, params],
queryFn: () =>
SiteApi.getSiteList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
siteNm: params.siteNm,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSearchParams({
...searchParams,
siteNm,
});
setParams({ siteNm, page: 0 });
};
const handleReset = () => {
setSiteNm("");
setSearchParams({
...searchParams,
siteNm: "",
});
resetParams();
};
const handleRowClick = (row: Site) => {
@@ -62,7 +73,7 @@ function SitePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+45 -40
View File
@@ -2,19 +2,23 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { AuthorityButton } from "@/entities/menu-authority";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { SurveyApi } from "../api/survey-service";
import type { Survey, SurveyParams } from "../model/survey-interface";
import type { Survey } from "../model/survey-interface";
import { columns } from "../model/survey-table-columns";
type PostingTarget = {
@@ -22,32 +26,53 @@ type PostingTarget = {
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
srvyTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function SurveyPage() {
const router = useRouter();
const [srvyTtl, setSrvyTtl] = useState("");
const [searchParams, setSearchParams] = useState<SurveyParams>({
srvyTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [srvyTtl, setSrvyTtl] = useState(params.srvyTtl);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, params],
queryFn: () =>
SurveyApi.getSurveyList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
srvyTtl: params.srvyTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ srvyTtl, page: 0 });
};
const handleReset = () => {
setSrvyTtl("");
resetParams();
};
const handleRowClick = (row: Survey) => {
router.push(`/survey/${row.srvySn}`);
};
@@ -62,7 +87,7 @@ function SurveyPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
@@ -71,26 +96,6 @@ function SurveyPage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
srvyTtl,
});
};
const handleReset = () => {
setSrvyTtl("");
setSearchParams({
...searchParams,
srvyTtl: "",
});
};
useEffect(() => {
const postingTargets = table
.getFilteredSelectedRowModel()
+44 -32
View File
@@ -2,58 +2,70 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { TemplateApi } from "../api/template-service";
import type { Template } from "../model/template-interface";
import { columns } from "../model/template-table-columns";
import type { Template, TemplateParams } from "../model/template-interface";
const DEFAULT_PARAMS = {
keyword: "",
tmpltSeCd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function TemplatePage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [tmpltSeCd, setTmpltSeCd] = useState("");
const [searchParams, setSearchParams] = useState<TemplateParams>({
keyword: "",
tmpltSeCd: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [tmpltSeCd, setTmpltSeCd] = useState(params.tmpltSeCd);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE, params],
queryFn: () =>
TemplateApi.getTemplateList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
tmpltSeCd: params.tmpltSeCd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSearchParams({
...searchParams,
keyword,
tmpltSeCd,
});
setParams({ keyword, tmpltSeCd, page: 0 });
};
const handleReset = () => {
setKeyword("");
setTmpltSeCd("");
setSearchParams({
...searchParams,
keyword: "",
tmpltSeCd: "",
});
resetParams();
};
const handleRowClick = (row: Template) => {
@@ -67,7 +79,7 @@ function TemplatePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+44 -37
View File
@@ -2,63 +2,70 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { TermsApi } from "../api/terms-service";
import type { Terms } from "../model/terms-interface";
import { columns } from "../model/terms-table-columns";
import type { TermsParams, Terms } from "../model/terms-interface";
const DEFAULT_PARAMS = {
keyword: "",
trmsSeCd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function TermsPage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [trmsSeCd, setTrmsSeCd] = useState("");
const [searchParams, setSearchParams] = useState<TermsParams>({
keyword: "",
trmsSeCd: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [trmsSeCd, setTrmsSeCd] = useState(params.trmsSeCd);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.TERMS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.TERMS, params],
queryFn: () =>
TermsApi.getTermsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
trmsSeCd: params.trmsSeCd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
trmsSeCd,
});
setParams({ keyword, trmsSeCd, page: 0 });
};
const handleReset = () => {
setKeyword("");
setTrmsSeCd("");
setSearchParams({
...searchParams,
keyword: "",
trmsSeCd: "",
});
resetParams();
};
const handleRowClick = (row: Terms) => {
@@ -72,7 +79,7 @@ function TermsPage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+68 -60
View File
@@ -3,19 +3,19 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { CodeApi } from "@/entities/code";
import {
UserApi,
type UserListResponse,
type UserSearchParams,
} from "@/entities/user";
import { UserApi, type UserListResponse } from "@/entities/user";
import {
Button,
Checkbox,
@@ -35,34 +35,39 @@ type DateRange = {
to?: Date | null;
};
const DEFAULT_PARAMS = {
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function parseDateRange(frDateYmd: string, toDateYmd: string): DateRange {
return {
from: frDateYmd ? dayjs(frDateYmd, "YYYYMMDD").toDate() : undefined,
to: toDateYmd ? dayjs(toDateYmd, "YYYYMMDD").toDate() : undefined,
};
}
function UsersPage() {
const router = useRouter();
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [presetDateRange, setPresetDateRange] = useState("");
const [dateRange, setDateRange] = useState<DateRange>({
from: undefined,
to: undefined,
});
const [keywordType, setKeywordType] = useState("all");
const [keyword, setKeyword] = useState("");
const [userStateCode, setUserStateCode] = useState("");
const [aprvYn, setAprvYn] = useState("");
const [grpId, setGrpId] = useState("");
const [searchParams, setSearchParams] = useState<UserSearchParams>({
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
});
const [dateRange, setDateRange] = useState<DateRange>(() =>
parseDateRange(params.frDateYmd, params.toDateYmd),
);
const [keywordType, setKeywordType] = useState(params.keywordType);
const [keyword, setKeyword] = useState(params.keyword);
const [userStateCode, setUserStateCode] = useState(params.userStateCode);
const [aprvYn, setAprvYn] = useState(params.aprvYn);
const [grpId, setGrpId] = useState(params.grpId);
const { data: userSttsStats } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS, "userStatusStatistics"],
@@ -70,13 +75,21 @@ function UsersPage() {
});
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.USERS, params],
queryFn: () =>
UserApi.findPageUser({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keywordType: params.keywordType,
keyword: params.keyword,
userStateCode: params.userStateCode,
aprvYn: params.aprvYn,
grpId: params.grpId,
frDateYmd: params.frDateYmd,
toDateYmd: params.toDateYmd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const { data: userStateCodes } = useQuery({
@@ -84,6 +97,16 @@ function UsersPage() {
queryFn: () => CodeApi.getCode("USERSTTS"),
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleRowClick = (row: UserListResponse) => {
router.push(`/users/${row.userUnqId}`);
};
@@ -97,7 +120,7 @@ function UsersPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
},
dataTableOptions: {
onRowClick: handleRowClick,
@@ -105,14 +128,9 @@ function UsersPage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
setParams({
keywordType,
keyword,
userStateCode,
@@ -122,6 +140,7 @@ function UsersPage() {
? dayjs(dateRange.from).format("YYYYMMDD")
: "",
toDateYmd: dateRange?.to ? dayjs(dateRange.to).format("YYYYMMDD") : "",
page: 0,
});
};
@@ -131,20 +150,12 @@ function UsersPage() {
setUserStateCode("");
setAprvYn("");
setGrpId("");
setPresetDateRange("");
setDateRange({
from: undefined,
to: undefined,
});
setSearchParams({
...searchParams,
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
});
resetParams();
};
const handlePresetDateRange = (value: string) => {
@@ -351,12 +362,9 @@ function UsersPage() {
<h3 className="text-2xl font-medium text-label">회원 목록</h3>
<div className="flex gap-2">
<Select
defaultValue="regDt,desc"
value={params.sort}
onValueChange={(value) => {
setSearchParams({
...searchParams,
sort: value,
});
setParams({ sort: value });
}}
>
<SelectTrigger>
+4 -3
View File
@@ -1,8 +1,9 @@
export { getCookie, setCookie, removeCookie } from "./cookie";
export { useMutationWrapper } from "./use-mutation-wrapper";
export { getCookie, removeCookie, setCookie } from "./cookie";
export { useAlertStore } from "./use-alert-store";
export { useDataTable } from "./use-data-table";
export { useMenuStore } from "./use-menu-store";
export { useUserStore } from "./use-user-store";
export { useMutationWrapper } from "./use-mutation-wrapper";
export { usePrompt } from "./use-prompt";
export { useSearchParamsState } from "./use-search-params-state";
export { useSelectPrompt } from "./use-select-prompt";
export { useUserStore } from "./use-user-store";
@@ -0,0 +1,90 @@
"use client";
import { useCallback, useMemo, useRef } from "react";
import { useSearchParams } from "next/navigation";
/**
* 조회 조건을 URL searchParams와 동기화하는 훅.
*
* - URL이 단일 소스 오브 트루스(SSOT)이며, 새로고침·뒤로가기 시에도 조건이 유지된다.
* - 기본값과 동일한 값은 URL에서 제거해 쿼리스트링을 깨끗하게 유지한다.
* - `window.history.replaceState` 기반 shallow 갱신으로 서버 라운드트립 없이 동작한다.
* - 숫자 기본값이 주어진 키는 자동으로 `Number` 파싱한다.
*
* @example
* const { params, setParams, resetParams } = useSearchParamsState({
* keyword: "",
* page: 0,
* size: 10,
* });
*/
export function useSearchParamsState<T extends Record<string, ParamValue>>(
defaults: T,
): UseSearchParamsStateReturn<T> {
const searchParams = useSearchParams();
const defaultsRef = useRef(defaults);
const params = useMemo(
() => parseParams(searchParams, defaultsRef.current),
[searchParams],
);
const setParams = useCallback((partial: Partial<T>) => {
const next = new URLSearchParams(window.location.search);
for (const [key, value] of Object.entries(partial)) {
if (value === undefined || value === defaultsRef.current[key]) {
next.delete(key);
} else {
next.set(key, String(value));
}
}
replaceUrl(next);
}, []);
const resetParams = useCallback(() => {
const next = new URLSearchParams(window.location.search);
for (const key of Object.keys(defaultsRef.current)) {
next.delete(key);
}
replaceUrl(next);
}, []);
return { params, setParams, resetParams };
}
function parseParams<T extends Record<string, ParamValue>>(
searchParams: ReturnType<typeof useSearchParams>,
defaults: T,
): T {
const result = { ...defaults };
for (const key of Object.keys(defaults) as (keyof T)[]) {
const raw = searchParams?.get(key as string) ?? null;
if (raw === null) continue;
if (typeof defaults[key] === "number") {
const parsed = Number(raw);
if (!Number.isNaN(parsed)) result[key] = parsed as T[keyof T];
} else {
result[key] = raw as T[keyof T];
}
}
return result;
}
function replaceUrl(searchParams: URLSearchParams) {
const qs = searchParams.toString();
const url = qs
? `${window.location.pathname}?${qs}`
: window.location.pathname;
window.history.replaceState(null, "", url);
}
type ParamValue = string | number | null;
interface UseSearchParamsStateReturn<T> {
params: T;
setParams: (partial: Partial<T>) => void;
resetParams: () => void;
}
+11 -6
View File
@@ -1,11 +1,13 @@
import { toast } from "sonner";
import { useEffect } from "react";
import { useRouter, usePathname } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useClipboard } from "@frontend-opensource/use-react-hooks";
import { Button } from "@common/components";
import { deleteReasonMessages } from "@/entities/post";
import { AuthorityButton } from "@/entities/menu-authority";
import { toast } from "sonner";
import { useMenuStore, useSelectPrompt, useUserStore } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { deleteReasonMessages } from "@/entities/post";
import { Button } from "@common/components";
type PostActionProps = {
bbsId: string;
@@ -21,11 +23,14 @@ const selectData = Object.entries(deleteReasonMessages).map(
function PostAction({ children, handleDelete }: PostActionProps) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { copyText, copied } = useClipboard();
const { prompt } = useSelectPrompt(selectData);
const bbsInfo = useMenuStore((state) => state.bbsInfo);
const userInfo = useUserStore((state) => state.userInfo);
const postPageLink = `/bbs/${bbsInfo?.bbsTypeCd.toLowerCase()}/${bbsInfo?.bbsId}`;
const from = searchParams?.get("from");
const backLink = from ?? postPageLink;
const onDelete = async () => {
const { resolved, value } = await prompt("삭제 사유를 입력해주세요.");
@@ -65,7 +70,7 @@ function PostAction({ children, handleDelete }: PostActionProps) {
size="lg"
color="white"
type="button"
onClick={() => router.push(postPageLink)}
onClick={() => router.push(backLink)}
>
목록
</Button>
+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
@@ -17,7 +17,7 @@ type IntroCardItem = {
const INTRO_CARDS: IntroCardItem[] = [
{
title: "물 재이용 법",
title: "물재이용법",
desc: "물의 재이용 촉진 및 지원에 관한 법률을 시행하고, 일부 법률은 개정되었습니다.",
image: card01,
href: "/intro#law-plan",
+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: {
+8 -8
View File
@@ -83,14 +83,14 @@ const MOCK: Record<TabKey, CaseItem[]> = {
sewage: [
{
id: "sewage-1",
title: "구리시 하수 재이용",
title: "구리시 하수처리수 재이용",
desc: "고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화",
imageSrc: "/images/landing/business/sewage/guri_01.png",
href: "/example#example-sewage-guri",
},
{
id: "sewage-2",
title: "부천시 하수재이용",
title: "부천시 하수처리수 재이용",
desc: "굴포하수처리시설 하수처리수 재이용수 생산시설 설치 운영을 통해 여월천 등 부천 관내 하천의 건천화 방지, 하천 생태계 보존 및 주민들에게 친수 공간 제공",
imageSrc: "/images/landing/business/sewage/bucheon_01.png",
href: "/example#example-sewage-bucheon",
@@ -104,42 +104,42 @@ const MOCK: Record<TabKey, CaseItem[]> = {
},
{
id: "sewage-4",
title: "파주시 하수 재이용 시설",
title: "파주시 하수처리수 재이용",
desc: "운정 및 금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주LCD산업단지 내 3개 업체에 공업용수를 공급(LG디스플레이, 희성전자, 엔비텍)",
imageSrc: "/images/landing/business/sewage/paju_01.png",
href: "/example#example-sewage-paju",
},
{
id: "sewage-5",
title: "포항시 하수 재이용 시설",
title: "포항시 하수처리수 재이용",
desc: "포항시 내 공단 개발 확대로 지속적이고 안정적인 공업용수 공급 추진(포스코, 동국산업 등), 포항하수처리장 하수처리수를 재처리하여 공업용수로 공급",
imageSrc: "/images/landing/business/sewage/pohang_01.png",
href: "/example#example-sewage-pohang",
},
{
id: "sewage-6",
title: "울산광역시 하수 재이용 시설",
title: "울산광역시 하수처리수 재이용",
desc: "18년 갈수기시 낙동강 수질 악화로 울산석유화학단지 용수 공급 부족을 해소하기 위해 미포산단 내 한화케미칼 등 8개사 공업용수 공급",
imageSrc: "/images/landing/business/sewage/ulsan_01.png",
href: "/example#example-sewage-ulsan",
},
{
id: "sewage-7",
title: "여수시 하수 재이용 시설",
title: "여수시 하수처리수 재이용",
desc: "수요자가 원하는 양질의 공업용수를 저렴하게 공급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선을 위한 여수국가산단 내 공업용수 공급(GS칼텍스, YNCC 등)",
imageSrc: "/images/landing/business/sewage/yeosu_01.png",
href: "/example#example-sewage-yeosu",
},
{
id: "sewage-8",
title: "보령시 하수 재이용 시설",
title: "보령시 하수처리수 재이용",
desc: "한국중부발전(주)의 보령 및 신보령화력발전소에 10,000㎥/일의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진, 하수처리수 재이용을 통한 수자원 절약 및 지역 물 부족 해소",
imageSrc: "/images/landing/business/sewage/boryeong_01.png",
href: "/example#example-sewage-boryeong",
},
{
id: "sewage-9",
title: "아산시 하수 재이용 시설",
title: "아산시 하수처리수 재이용",
desc: "신도시 개발에 따른 인구 급증으로 발생하는 하수를 재처리하여 탕정산업단지(삼성디스플레이)에 공업용수를 안정적으로 공급함으로써 지역기반 및 활성화를 도모",
imageSrc: "/images/landing/business/sewage/asan_01.png",
href: "/example#example-sewage-asan",
@@ -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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 60 KiB

+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.
+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.
+2 -1
View File
@@ -4,4 +4,5 @@ NEXT_PUBLIC_PROXY_HOST=http://localhost:3001
SERVER_API_URL=http://192.168.0.102:8000
NEXT_PUBLIC_SITE_ID=ST
NEXT_PUBLIC_SUB_DOMAIN=http://localhost:3000
NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2
NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2
NEXT_PUBLIC_ST_DASHBOARD_BBS_ID=13
+20 -170
View File
@@ -1,54 +1,32 @@
"use client";
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
import { usePathname } from "next/navigation";
import { usePathname, useRouter } from "next/navigation";
import {
getTabStoreState as getGridTabStoreState,
useTabStore as gridTabStore,
} from "@rsone/components";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { ENDPOINT } from "@/shared/config";
import { getCookie, useAlertStore, useMenuStore } from "@/shared/lib";
import { isBbsNoticeMenu } from "@/shared/lib/bbs-notice-menu";
import {
menuMatchesStatisticsTabId,
resolveStatisticsTabId,
} from "@/shared/lib/statistics-menu-url";
import { useStatisticsTabUrlSync } from "@/shared/lib/use-statistics-tab-url-sync";
import { isVerificationSelectionReportMenu } from "@/shared/lib/verification-selection-report";
import { StatisticsPageLayout } from "@/shared/ui/statistics-page-layout";
import { tokenManager } from "@/entities/auth";
import type { MenuItem } from "@/entities/menu";
import { MenuApi } from "@/entities/menu";
import { BaseLayout } from "@common/components";
import { getConfig } from "@common/config";
import { getTabStoreState, useTabStore } from "@/store/tab-store";
import VerificationSelectionReport from "@/components/statistics/VerificationSelectionReport";
import { getTabStoreState, useTabStore } from "@/store/tab-store";
const appConfig = getConfig();
@@ -58,42 +36,23 @@ const appSiteId = getCookie("site-id-key") ?? "";
const isLoggedIn = !!userId;
function WithBaseLayoutInner({
children,
}: {
children: React.ReactNode;
}) {
function WithBaseLayoutInner({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const router = useRouter();
const queryClient = useQueryClient();
/** 탭 추가 없이 메뉴만 눌렀을 때 패널 유지 (다른 메뉴 클릭해도 유지) */
const [isVerificationSelectionReportPanelOpen, setReportPanelOpen] =
useState(false);
const setCurrentMenuInfo = useMenuStore(
(state) => state.setCurrentMenuInfo,
);
const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
const addTab = useTabStore((state) => state.addTab);
const setActiveTab = useTabStore((state) => state.setActiveTab);
@@ -108,16 +67,11 @@ function WithBaseLayoutInner({
const addTabForGrid = gridTabStore((state) => state.addTab);
const setActiveTabForGrid = gridTabStore((state) => state.setActiveTab);
const setMenuListForGrid = gridTabStore((state) => state.setMenuList);
const alert = useAlertStore((state) => state.setMessage);
const { syncTabToUrl } = useStatisticsTabUrlSync({
pathname,
menuList,
@@ -129,41 +83,27 @@ function WithBaseLayoutInner({
addTabForGrid,
setCurrentMenuInfo,
});
const { data: menuListData } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
queryFn: () => MenuApi.getMenuRoles(appSiteId),
enabled: isLoggedIn,
});
useEffect(() => {
if (menuListData) {
setMenuList(menuListData);
setMenuListForGrid(menuListData);
}
}, [menuListData, setMenuList, setMenuListForGrid]);
// 새로고침 후 persist로 앱 탭만 복원된 경우 rsone 그리드 스토어에 탭을 맞춤
useEffect(() => {
if (!menuList) return;
const appTabs = getTabStoreState().tabs;
@@ -172,20 +112,13 @@ function WithBaseLayoutInner({
if (getGridTabStoreState().tabs.length > 0) return;
for (const tab of appTabs) {
getGridTabStoreState().addTab(tab.menuItem);
}
const activeId = getTabStoreState().activeTabId;
if (activeId) {
setActiveTab(activeId);
const activeTab = appTabs.find((t) => t.id === activeId);
@@ -193,155 +126,104 @@ function WithBaseLayoutInner({
const gridActiveId = activeTab?.menuItem.menuEngNm;
if (gridActiveId) {
getGridTabStoreState().setActiveTab(gridActiveId);
}
}
}, [menuList, tabs, setActiveTab]);
// 활성 탭의 메뉴를 찾아서 urlPath를 가져옴. 탭이 전부 닫히면 ''를 넘겨 LNB에서 메뉴 강조 제거
const currentPath = useMemo(() => {
if (tabs.length === 0) return "";
if (activeTabId && menuList) {
// menuList에서 활성 탭의 메뉴 찾기
const findMenuByEngNm = (items: MenuItem[]): MenuItem | null => {
for (const item of items) {
if (item.urlPath === activeTabId) {
if (menuMatchesStatisticsTabId(item, activeTabId)) {
return item;
}
if (item.children) {
const found = findMenuByEngNm(item.children);
if (found) return found;
}
}
return null;
};
const activeMenu = findMenuByEngNm(menuList);
if (activeMenu?.urlPath) {
return activeMenu.urlPath;
}
}
// 탭에서 직접 찾기
if (activeTabId) {
const currentTabs = getTabStoreState().tabs;
const activeTab = currentTabs.find((t) => t.id === activeTabId);
if (activeTab?.menuItem?.urlPath) {
return activeTab.menuItem.urlPath;
}
}
// currentMenuInfo에서 가져오기
if (currentMenuInfo?.urlPath) {
return currentMenuInfo.urlPath;
}
// 기본값으로 pathname 사용
return pathname ?? "";
}, [tabs.length, activeTabId, menuList, currentMenuInfo, pathname]);
const handleMenuClick = useCallback(
(item: MenuItem) => {
if (item.urlPath.includes("/cms/")) return;
setCurrentMenuInfo(item);
if (isVerificationSelectionReportMenu(item)) {
setReportPanelOpen(true);
return;
}
if (isBbsNoticeMenu(item)) {
if (item.urlPath) {
router.push(item.urlPath);
}
return;
}
const addTabResult: boolean = addTab(item);
// packages/rsone의 스토어에도 동기화
if (addTabResult) {
addTabForGrid(item);
if (item.urlPath) {
syncTabToUrl(item.urlPath);
syncTabToUrl(resolveStatisticsTabId(item.urlPath));
}
}
if (!addTabResult) {
alert(`최대 추가 할 수 있는 화면 수(10개)를 \n초과 하였습니다.
기존 화면을 닫은 후 이용해주세요.`);
}
},
[
setCurrentMenuInfo,
addTab,
@@ -354,70 +236,38 @@ function WithBaseLayoutInner({
syncTabToUrl,
router,
],
);
return (
<BaseLayout
queryClient={queryClient}
tokenManager={tokenManager}
currentPath={currentPath}
handleMenuClick={handleMenuClick}
>
<div className="flex flex-1 h-full">
<div className="flex h-full flex-1">
{isVerificationSelectionReportPanelOpen && (
<VerificationSelectionReport
onClose={() => setReportPanelOpen(false)}
/>
)}
<StatisticsPageLayout>{children}</StatisticsPageLayout>
</div>
</BaseLayout>
);
}
export default function WithBaseLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<Suspense fallback={null}>
<WithBaseLayoutInner>{children}</WithBaseLayoutInner>
</Suspense>
);
}
@@ -0,0 +1,63 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import {
AUTH_USER_ID,
CLAIM_NAME,
SITE_ID,
ST_SERVICE_URL,
} from "@/shared/config";
import { SEWERAGE_AGGREGATE_SUMMARY_EXCEL_MIME } from "@/entities/statistics/model/statistics-total/statistics-sewerage-aggregate-summary-interface";
const BACKEND_DOWNLOAD_URL = `${ST_SERVICE_URL}/st-management-service/api/v1/summary-excel/download`;
/** middleware `/server` rewrite는 장시간 blob 응답에서 연결이 끊길 수 있어 전용 프록시 사용 */
export const dynamic = "force-dynamic";
export const maxDuration = 3600;
export async function POST(request: Request) {
const cookieStore = await cookies();
const token = cookieStore.get(CLAIM_NAME)?.value;
const userId = cookieStore.get(AUTH_USER_ID)?.value;
const headers: Record<string, string> = {
"Content-Type": "application/json",
"X-Site-Id": SITE_ID,
Accept: SEWERAGE_AGGREGATE_SUMMARY_EXCEL_MIME,
};
if (token) headers[CLAIM_NAME] = token;
if (userId) headers[AUTH_USER_ID] = userId;
let body: string;
try {
body = await request.text();
} catch {
return NextResponse.json({ message: "요청 본문을 읽을 수 없습니다." }, { status: 400 });
}
let backendRes: Response;
try {
backendRes = await fetch(BACKEND_DOWNLOAD_URL, {
method: "POST",
headers,
body,
cache: "no-store",
});
} catch {
return NextResponse.json(
{ message: "엑셀 서버에 연결할 수 없습니다." },
{ status: 502 },
);
}
const responseHeaders = new Headers();
const contentType = backendRes.headers.get("content-type");
const contentDisposition = backendRes.headers.get("content-disposition");
if (contentType) responseHeaders.set("Content-Type", contentType);
if (contentDisposition) responseHeaders.set("Content-Disposition", contentDisposition);
return new NextResponse(backendRes.body, {
status: backendRes.status,
headers: responseHeaders,
});
}
+1 -1
View File
@@ -6,7 +6,7 @@
}
/* 셀 클래스가 있으면 위 기본색보다 우선 (같은 specificity + 나중 규칙) */
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-readonly {
@apply !bg-[#f9f8f8] !text-[#666];
@apply !bg-[#f0f0f0] !text-[#666];
}
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-verify-error {
@apply !bg-[#ffe6e6] !text-[#c00];
@@ -3,9 +3,27 @@ import { ValueType } from "realgrid"
import RealGrid, { GridFitStyle } from "realgrid"
import { RealGridReact, RGDataColumn } from "realgrid-react"
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, CellStyleCallback } from "../type/DataGrid.type"
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, CellStyleCallback, CellStyleCallbackResult, CellStyleCallbackParams } from "../type/DataGrid.type"
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
function mergeCellStyleCallbackResult(
baseStyleName: string | undefined,
extra: CellStyleCallbackResult | undefined,
): CellStyleCallbackResult {
if (!extra) {
return baseStyleName ? { styleName: baseStyleName } : {};
}
if (typeof extra === "string") {
const styleName = [extra, baseStyleName].filter(Boolean).join(" ");
return styleName ? { styleName } : {};
}
const styleName = [extra.styleName, baseStyleName].filter(Boolean).join(" ");
return {
...extra,
...(styleName ? { styleName } : {}),
};
}
// RealGrid 타입 확장
declare module "realgrid" {
interface GridView {
@@ -40,11 +58,14 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
};
if (col.computed) {
const hasFields = "fields" in col.computed && col.computed.fields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && "denominatorFields" in col.computed && col.computed.denominatorFields.length > 0;
const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
const hasHalfSumOfSums = col.computed.type === "halfSumOfSums" && "firstSumFields" in col.computed && "secondSumFields" in col.computed && col.computed.firstSumFields.length > 0 && col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums = col.computed.type === "sumOfTwoHalfSumOfSums" && "firstHalfSum" in col.computed && "secondHalfSum" in col.computed && col.computed.firstHalfSum.firstSumFields.length > 0 && col.computed.firstHalfSum.secondSumFields.length > 0 && col.computed.secondHalfSum.firstSumFields.length > 0 && col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
const hasSumPerTenThousand = col.computed.type === "sumPerTenThousand" && "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && "denominatorField" in col.computed && Boolean(col.computed.denominatorField);
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasHalfSumOfSums || hasSumOfTwoHalfSumOfSums || hasSumOfMaxGroups || hasSumPerTenThousand) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
@@ -408,18 +429,33 @@ const CompactDataGrid = ({
columns.forEach((column) => {
const baseStyleName = column.styleName;
const gridCol = currentGridView.columnByName(column.name) as {
styleCallback?: (g: unknown, dataCell: { itemIndex?: number; index?: { itemIndex?: number }; dataField?: string; fieldName?: string }) => string;
styleCallback?: (
g: unknown,
dataCell: {
itemIndex?: number;
index?: { itemIndex?: number };
dataField?: string;
fieldName?: string;
},
) => CellStyleCallbackResult;
} | null;
if (gridCol) {
gridCol.styleCallback = (_g, dataCell) => {
gridCol.styleCallback = (grid, dataCell) => {
const itemIndex = dataCell?.index?.itemIndex ?? dataCell?.itemIndex ?? -1;
const fieldName = (dataCell?.dataField ?? dataCell?.fieldName ?? column.fieldName) ?? '';
const fieldName =
(dataCell?.dataField ?? dataCell?.fieldName ?? column.fieldName) ?? "";
const data = rowDataRef.current;
if (itemIndex < 0 || itemIndex >= data.length) return baseStyleName ?? '';
if (itemIndex < 0 || itemIndex >= data.length) {
return mergeCellStyleCallbackResult(baseStyleName, undefined);
}
const row = data[itemIndex] as Record<string, string | number>;
const extra = cellStyleCallbackRef.current?.({ rowIndex: itemIndex, fieldName, row });
if (!extra) return baseStyleName ?? '';
return baseStyleName ? `${extra} ${baseStyleName}` : extra;
const extra = cellStyleCallbackRef.current?.({
rowIndex: itemIndex,
fieldName,
row,
grid: grid as CellStyleCallbackParams["grid"],
});
return mergeCellStyleCallbackResult(baseStyleName, extra);
};
}
});
+238 -84
View File
@@ -11,6 +11,8 @@ import { Button } from "@common/components";
import type {
CellStyleCallback,
CellStyleCallbackParams,
CellStyleCallbackResult,
CellTooltipCallback,
ColumnLayoutItem,
DataGridProps,
@@ -21,19 +23,38 @@ import type {
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks";
import {
getExcelImportColumnFieldNames,
prepareGridColumnsForExcelExport,
restoreGridColumnsAfterExcelExport,
shouldPrepareColumnsForExcelExport,
} from "@/components/util/gridExcelExport";
import { useTabStore } from "@/store/tab-store";
function mergeCellStyleCallbackResult(
baseStyleName: string | undefined,
extra: CellStyleCallbackResult | undefined,
): CellStyleCallbackResult {
if (!extra) {
return baseStyleName ? { styleName: baseStyleName } : {};
}
if (typeof extra === "string") {
const styleName = [extra, baseStyleName].filter(Boolean).join(" ");
return styleName ? { styleName } : {};
}
const styleName = [extra.styleName, baseStyleName].filter(Boolean).join(" ");
return {
...extra,
...(styleName ? { styleName } : {}),
};
}
// RealGrid 타입 확장
declare module "realgrid" {
interface GridView {
setOptions(options: {
footer?: { visible?: boolean };
summaryMode?: "aggregate" | "statistical" | "none";
checkBar?: { visible?: boolean };
checkBar?: { visible?: boolean; exclusive?: boolean };
}): void;
}
}
@@ -457,10 +478,31 @@ function sortRowsGroupPreserving(
}
/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */
function getDecimalPlacesFromDataFormat(dataFormat?: string): number | null {
if (!dataFormat) return null;
const match = dataFormat.match(/\.(0+|#+)/);
const decimalToken = match?.[1];
if (decimalToken) return decimalToken.length;
// #,##0 등 정수 포맷 — 소수부 없음 → 0자리 절삭(내림)
if (/[#0]/.test(dataFormat)) return 0;
return null;
}
function truncateByDecimalPlaces(value: number, decimalPlaces: number): number {
if (!Number.isFinite(value)) return 0;
if (decimalPlaces < 0) return value;
const factor = 10 ** decimalPlaces;
const scaled = value * factor;
// 절삭(truncate): 반올림 없이 소수점 이하를 버린다.
const truncated = scaled >= 0 ? Math.floor(scaled) : Math.ceil(scaled);
return truncated / factor;
}
function buildFieldsFromColumns(
columns: {
fieldName: string;
valueType?: ValueType;
dataFormat?: string;
datetimeFormat?: string;
editor?: { type?: string; datetimeFormat?: string };
computed?: import("../type/DataGrid.type").ComputedFieldConfig;
@@ -499,6 +541,7 @@ function buildFieldsFromColumns(
const hasNumeratorDenominator =
"numeratorFields" in col.computed &&
col.computed.numeratorFields.length > 0 &&
"denominatorFields" in col.computed &&
col.computed.denominatorFields.length > 0;
const hasMaxOfFieldAndSum =
"baseField" in col.computed &&
@@ -508,18 +551,68 @@ function buildFieldsFromColumns(
"minuendFields" in col.computed &&
"subtrahendFields" in col.computed &&
col.computed.minuendFields.length > 0;
const hasHalfSumOfSums =
col.computed.type === "halfSumOfSums" &&
"firstSumFields" in col.computed &&
"secondSumFields" in col.computed &&
col.computed.firstSumFields.length > 0 &&
col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums =
col.computed.type === "sumOfTwoHalfSumOfSums" &&
"firstHalfSum" in col.computed &&
"secondHalfSum" in col.computed &&
col.computed.firstHalfSum.firstSumFields.length > 0 &&
col.computed.firstHalfSum.secondSumFields.length > 0 &&
col.computed.secondHalfSum.firstSumFields.length > 0 &&
col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups =
col.computed.type === "sumOfMaxOfFieldAndSumGroups" &&
"groups" in col.computed &&
Array.isArray(col.computed.groups) &&
col.computed.groups.length > 0;
const hasPumpGrossPpqty =
col.computed.type === "pumpGrossPpqty" &&
"pumpGroups" in col.computed &&
col.computed.pumpGroups.length > 0;
const hasBodPrcsRt =
col.computed.type === "bodPrcsRt" &&
"inflwField" in col.computed &&
"dschgField" in col.computed;
const hasSumPerTenThousand =
col.computed.type === "sumPerTenThousand" &&
"numeratorFields" in col.computed &&
col.computed.numeratorFields.length > 0 &&
"denominatorField" in col.computed &&
Boolean(col.computed.denominatorField);
const hasQuotientRatioPercent =
col.computed.type === "quotientRatioPercent" &&
"numeratorQuotient" in col.computed &&
"denominatorQuotient" in col.computed &&
col.computed.numeratorQuotient.length === 2 &&
col.computed.denominatorQuotient.length === 2;
const hasQuotientTimesMultiplierPercent =
col.computed.type === "quotientTimesMultiplierPercent" &&
"numeratorField" in col.computed &&
"denominatorField" in col.computed &&
"multiplierField" in col.computed &&
Boolean(col.computed.numeratorField) &&
Boolean(col.computed.denominatorField) &&
Boolean(col.computed.multiplierField);
if (
hasFields ||
hasNumeratorDenominator ||
hasMaxOfFieldAndSum ||
hasDifferenceOfSums ||
hasSumOfMaxGroups
hasHalfSumOfSums ||
hasSumOfTwoHalfSumOfSums ||
hasSumOfMaxGroups ||
hasPumpGrossPpqty ||
hasBodPrcsRt ||
hasSumPerTenThousand ||
hasQuotientRatioPercent ||
hasQuotientTimesMultiplierPercent
) {
field.valueCallback = getComputedValueCallback(
const computedCallback = getComputedValueCallback(
col.computed,
) as (
prod: unknown,
@@ -528,6 +621,24 @@ function buildFieldsFromColumns(
fieldNames: string[],
values: unknown[],
) => number;
const decimalPlaces = getDecimalPlacesFromDataFormat(col.dataFormat);
field.valueCallback = (
prod: unknown,
dataRow: number,
fieldName: string,
fieldNames: string[],
values: unknown[],
) => {
const computedValue = computedCallback(
prod,
dataRow,
fieldName,
fieldNames,
values,
);
if (decimalPlaces == null) return computedValue;
return truncateByDecimalPlaces(computedValue, decimalPlaces);
};
}
}
return field;
@@ -586,6 +697,7 @@ const DataGrid = ({
const onItemAllCheckedRef =
useRef<typeof onItemAllChecked>(onItemAllChecked);
const isSyncingChecksFromRowDataRef = useRef(false);
const columnsFieldsSigRef = useRef("");
columnsRef.current = columns;
rowDataRef.current = rowData;
cellStyleCallbackRef.current = cellStyleCallback;
@@ -638,20 +750,6 @@ const DataGrid = ({
.find((s) => s.menuEngNm === activeTabId)?.menuKornNm ||
"export";
// 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)")
const originalHeaders: Map<string, string> = new Map();
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header) {
const originalText = gridCol.header.text || "";
originalHeaders.set(col.name, originalText);
const headerWithFieldName = `${originalText} (${col.fieldName})`;
gridCol.header.text = headerWithFieldName;
}
}
});
// exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가)
const normalizeLayoutForExport = (
layout: ColumnLayoutItem[],
@@ -745,20 +843,6 @@ const DataGrid = ({
excelExportVisibilitySnapshot,
);
}
// 헤더 원복
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (
gridCol &&
gridCol.header &&
originalHeaders.has(col.name)
) {
gridCol.header.text =
originalHeaders.get(col.name) || "";
}
}
});
// columnLayout 원복 (footerUserSpans 유지)
if (normalizedLayout && !layoutRestored) {
layoutRestored = true;
@@ -860,36 +944,11 @@ const DataGrid = ({
: headerStartRow;
const dataStartRow = maxHeaderRowIndex + 1;
// 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑
const headerToFieldNameMap = new Map<string, string>();
headers.forEach((headerText, colIndex) => {
let fieldName: string | null = null;
// 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)")
const fieldNameMatch = headerText.match(
/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/,
);
if (fieldNameMatch && fieldNameMatch[1]) {
const extractedFieldName = fieldNameMatch[1];
if (
columns.find(
(c) => c.fieldName === extractedFieldName,
)
) {
fieldName = extractedFieldName;
}
}
// 매칭 실패 시 원본 헤더 텍스트 사용
if (!fieldName) {
console.warn(
`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`,
);
fieldName = headerText;
}
headerToFieldNameMap.set(headerText, fieldName);
});
// 엑셀 N번째 열 → 그리드 N번째 fieldName (보내기 컬럼 순서 기준)
const excelColumnFieldNames = getExcelImportColumnFieldNames(
columns,
columnLayout,
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -907,12 +966,8 @@ const DataGrid = ({
worksheet[
XLSX.utils.encode_cell({ r: row, c: col })
];
const headerName =
headers[colIndex] || `Column${colIndex + 1}`;
// 헤더 텍스트를 fieldName으로 변환
const fieldName =
headerToFieldNameMap.get(headerName) ||
headerName;
const fieldName = excelColumnFieldNames[colIndex];
if (!fieldName) continue;
let cellValue: string | number = "";
if (
@@ -996,6 +1051,21 @@ const DataGrid = ({
const dataProvider = gridRef.current.dataProvider;
applyDefaultGridEditOptions(gridView as GridViewWithEditOptions);
const columnsFieldsSig = columns.map((c) => c.fieldName).join("\u0001");
if (columnsFieldsSig !== columnsFieldsSigRef.current) {
columnsFieldsSigRef.current = columnsFieldsSig;
const dpForFields = dataProvider as {
setFields?: (f: unknown[]) => void;
setRows?: (r: Record<string, string | number>[]) => void;
};
try {
dpForFields.setFields?.(buildFieldsFromColumns(columns));
dpForFields.setRows?.(rowDataRef.current);
} catch (e) {
console.warn("setFields sync:", e);
}
}
// mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리
const hasMergeCells = columns.some((col) => col.mergeCells === true);
@@ -1046,6 +1116,14 @@ const DataGrid = ({
}
columns.forEach((column) => {
const gv = gridView as GridViewWithSetColumnProperty;
const exists =
typeof (
gridView as { columnByName?: (name: string) => unknown }
).columnByName === "function" &&
(gridView as { columnByName: (name: string) => unknown }).columnByName(
column.name,
) != null;
if (!exists) return;
gv.setColumnProperty?.(
column.name,
"header",
@@ -1054,6 +1132,11 @@ const DataGrid = ({
tooltip: resolveColumnTooltip(column),
}),
);
gv.setColumnProperty?.(
column.name,
"editable",
column.editable !== false,
);
applyColumnListEditorProperties(gv, column);
});
if (columns.some((c) => isDropDownEditorColumn(c))) {
@@ -1101,7 +1184,7 @@ const DataGrid = ({
// 헤더 체크박스 표시 여부 설정
gridView.setOptions({
checkBar: { visible: showHeaderCheckbox },
checkBar: { visible: showHeaderCheckbox, exclusive: false },
rowIndicator: { visible: false },
});
@@ -1181,10 +1264,63 @@ const DataGrid = ({
const hasFooterUserSpans = checkFooterUserSpans(columnLayout);
if (hasFooterUserSpans) {
const validColumnNames = new Set(columns.map((c) => c.name));
const sanitizeColumnLayout = (
layout: unknown[],
): unknown[] => {
const result: unknown[] = [];
for (const item of layout) {
if (typeof item === "string") {
if (validColumnNames.has(item)) {
result.push(item);
}
continue;
}
if (!item || typeof item !== "object") continue;
if ("column" in item) {
const columnName = (item as { column?: unknown }).column;
if (
typeof columnName === "string" &&
validColumnNames.has(columnName)
) {
result.push(item);
}
continue;
}
if ("items" in item && Array.isArray((item as { items?: unknown[] }).items)) {
const nextItems = sanitizeColumnLayout(
(item as { items: unknown[] }).items,
);
if (nextItems.length > 0) {
result.push({
...(item as Record<string, unknown>),
items: nextItems,
});
}
continue;
}
result.push(item);
}
return result;
};
// setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함
setTimeout(() => {
if (gridRef.current?.gridView && columnLayout) {
gridRef.current.gridView.setColumnLayout?.(columnLayout);
const safeLayout = sanitizeColumnLayout(columnLayout as unknown[]);
if (safeLayout.length === 0) return;
try {
(
gridRef.current.gridView.setColumnLayout as
| ((layout: unknown) => void)
| undefined
)?.(safeLayout);
} catch (e) {
console.warn("setColumnLayout skipped due to invalid columns:", e);
}
}
}, 100);
}
@@ -1654,6 +1790,22 @@ const DataGrid = ({
layout={columnLayout}
rows={rowData}
onRGDataColumnsChanged={(grid) => {
const dp = gridRef.current?.dataProvider as
| {
setFields?: (f: unknown[]) => void;
setRows?: (
r: Record<string, string | number>[],
) => void;
}
| undefined;
try {
dp?.setFields?.(
buildFieldsFromColumns(columnsRef.current),
);
dp?.setRows?.(rowDataRef.current);
} catch (e) {
console.warn("onRGDataColumnsChanged setFields:", e);
}
reapplyListEditorsOnGrid(
grid as GridViewWithSetColumnProperty,
columnsRef.current,
@@ -1693,6 +1845,9 @@ const DataGrid = ({
dataProvider.setFields(
buildFieldsFromColumns(columns),
);
columnsFieldsSigRef.current = columns
.map((c) => c.fieldName)
.join("\u0001");
}
if (dataProvider?.setRows)
dataProvider.setRows(rowData);
@@ -1796,7 +1951,7 @@ const DataGrid = ({
dataField?: string;
fieldName?: string;
},
) => string;
) => CellStyleCallbackResult;
renderer?: { showTooltip?: boolean };
} | null;
if (gridCol) {
@@ -1835,7 +1990,7 @@ const DataGrid = ({
{ showTooltip: true },
);
}
gridCol.styleCallback = (_g, dataCell) => {
gridCol.styleCallback = (grid, dataCell) => {
const itemIndex =
dataCell?.index?.itemIndex ??
dataCell?.itemIndex ??
@@ -1848,8 +2003,12 @@ const DataGrid = ({
if (
itemIndex < 0 ||
itemIndex >= data.length
)
return baseStyleName ?? "";
) {
return mergeCellStyleCallbackResult(
baseStyleName,
undefined,
);
}
const row = data[itemIndex] as Record<
string,
string | number
@@ -1859,11 +2018,12 @@ const DataGrid = ({
rowIndex: itemIndex,
fieldName,
row,
grid: grid as CellStyleCallbackParams["grid"],
});
if (!extra) return baseStyleName ?? "";
return baseStyleName
? `${extra} ${baseStyleName}`
: extra;
return mergeCellStyleCallbackResult(
baseStyleName,
extra,
);
};
}
});
@@ -1874,15 +2034,9 @@ const DataGrid = ({
{columns.map((column) => {
const baseStyleName = column.styleName;
const columnReactKey =
column.values?.length &&
isDropDownEditorColumn(column)
? `${column.name}-dropdown-${column.values.length}`
: column.name;
return (
<RGDataColumn
key={columnReactKey}
key={column.name}
name={column.name}
fieldName={column.fieldName}
valueType={
@@ -33,7 +33,7 @@ import DataGrid from "@/components/module/DataGrid";
import { VerificationResultHistoryPagination } from "@/components/module/VerificationResultHistoryPagination";
import type { CellStyleCallbackParams, ColumnInfo } from "@/components/type/DataGrid.type";
/** 통계 입력 현황 > 행 클릭 시 예외처리(prcsYn=3) 내역만 조회 */
/** 통계 입력 현황 > 행 클릭 시 사용자예외처리(prcsYn=2) 내역만 조회 */
export interface ExceptionProcessingHistoryQueryContext {
sewerBzmnCd: string;
ctpvNm: string;
@@ -92,13 +92,15 @@ function exceptionPrcsYnNmCellStyle({ fieldName, row }: CellStyleCallbackParams)
const exceptionHistoryColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYnNm", fieldName: "prcsYnNm", header: { text: "처리여부" }, editable: false },
{ name: "pbcpAprvYn", fieldName: "pbcpAprvYn", header: { text: "공단승인여부" }, editable: false },
{ name: "stVrfRjtComNm", fieldName: "stVrfRjtComNm", header: { text: "반려상태" }, editable: false },
{ name: "prcsOpnnExpln", fieldName: "prcsOpnnExpln", header: { text: "처리의견" }, editable: false },
{ name: "rjctOpnnCn", fieldName: "rjctOpnnCn", header: { text: "반려의견" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{
@@ -114,13 +116,15 @@ const exceptionHistoryColumnInfo: ColumnInfo = {
],
columnLayout: [
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYnNm",
"pbcpAprvYn",
"stVrfRjtComNm",
"prcsOpnnExpln",
"rjctOpnnCn",
"regDt",
"mdfcnDt",
"individualReject",
@@ -190,9 +194,9 @@ export function ExceptionProcessingHistoryDialog({
sggNm: queryContext.sggNm,
statsYr: queryContext.statsYr,
statsCd: queryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: PRCS_YN_LIST,
rejectCodeList: "0,1,2",
page: pageIndex + 1,
size: 30,
};
@@ -308,14 +312,16 @@ export function ExceptionProcessingHistoryDialog({
const mapRows = (entries: VerificationResultReportItem[]): ExceptionHistoryGridRow[] =>
entries.map((item) => ({
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYnNm: item.prcsYnNm ?? "-",
stVrfRjtComNm: item.stVrfRjtComNm ?? "-",
prcsOpnnExpln: item.prcsOpnnExpln ?? "-",
rjctOpnnCn: item.rjctOpnnCn ?? "-",
prcsYn: item.prcsYn ?? "",
pbcpAprvYn: item.pbcpAprvYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
individualReject: "개별 반려",
+17 -15
View File
@@ -41,7 +41,7 @@ const getSggListFetcher = async (
key: [string, string, string, string],
): Promise<ApiResponse<Sgg[]>> => {
const gugunSkipYn = doList.includes(key[3]) ? "N" : "Y";
return statisticsApi.getSggAllList(key[1], {
return statisticsApi.getSggList(key[1], {
instSeCd: key[2],
gugunSkipYn,
});
@@ -137,11 +137,11 @@ const SearchForm = (props: SearchFormProps = {}) => {
prev.sido
? prev
: {
...prev,
sido: first.sido,
gugun: "",
bsnmCd: first.bsnmCd,
},
...prev,
sido: first.sido,
gugun: "",
bsnmCd: first.bsnmCd,
},
);
}, [sidoList, props.defaultValues?.sido, mergedProps.sido]);
@@ -398,16 +398,18 @@ const SearchForm = (props: SearchFormProps = {}) => {
</div>
)}
<div className="ml-auto flex items-center gap-2">
<Button
color="white"
className="h-[3.2rem] w-[96px]"
type="button"
onClick={handleReset}
>
초기화
</Button>
{mergedProps.isReset && (
<Button
color="white"
className="h-[3.2rem] w-[96px]"
type="button"
onClick={handleReset}
>
초기화
</Button>
)}
<Button className="h-[3.2rem] w-[96px]" type="submit">
조회
{props.searchButtonText ? props.searchButtonText : '조회'}
</Button>
</div>
</div>
@@ -116,7 +116,7 @@ const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
const getSggAllListFetcher = async (
key: [string, string],
): Promise<ApiResponse<Sgg[]>> => {
return statisticsApi.getSggAllList(key[1]);
return statisticsApi.getSggList(key[1]);
};
type SidoSggFacilityNameSearchFormProps = {
@@ -292,17 +292,12 @@ const SidoSggFacilityNameSearchForm = ({
return;
}
const ctpvNm = String(row.ctpvNm ?? sido);
const sggNm = String(
row.sggNm ?? (gugun !== "ALL" && gugun ? gugun : ""),
);
const params: SeweragePipeFacilityAdminAreaListRequest = {
statsYr: statsYr.trim(),
swtrmtFcltCd,
sewerBzmnCd: initialSearchValues?.bsnmCd || '',
ctpvNm,
...(doNameList.includes(ctpvNm) ? { sggNm } : {}),
ctpvNm: initialSidoValue || '',
...(doNameList.includes(initialSidoValue) ? { sggNm: initialGugunValue } : {}),
};
try {
+40 -57
View File
@@ -10,13 +10,32 @@ import { useAlertStore } from "@/shared/lib";
import { Tabs, TabsContent,TabsList, TabsTrigger } from "@common/components";
import { Button } from "@common/components";
import type { CellStyleCallback,ColumnConfig, ColumnInfo, ColumnLayoutItem, FooterItemWithCallback, GridViewWithSetColumns, RealGridColumnInfo, TabDataGridProps } from "../type/DataGrid.type"
import type { CellStyleCallback, CellStyleCallbackParams, CellStyleCallbackResult, ColumnConfig, ColumnInfo, ColumnLayoutItem, FooterItemWithCallback, GridViewWithSetColumns, RealGridColumnInfo, TabDataGridProps } from "../type/DataGrid.type"
import GridTopActionButton from "./GridTopActionButton";
function mergeCellStyleCallbackResult(
baseStyleName: string | undefined,
extra: CellStyleCallbackResult | undefined,
): CellStyleCallbackResult {
if (!extra) {
return baseStyleName ? { styleName: baseStyleName } : {};
}
if (typeof extra === "string") {
const styleName = [extra, baseStyleName].filter(Boolean).join(" ");
return styleName ? { styleName } : {};
}
const styleName = [extra.styleName, baseStyleName].filter(Boolean).join(" ");
return {
...extra,
...(styleName ? { styleName } : {}),
};
}
import SplitDataGrid from "./SplitDataGrid";
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
import {
getExcelImportColumnFieldNames,
prepareGridColumnsForExcelExport,
restoreGridColumnsAfterExcelExport,
shouldPrepareColumnsForExcelExport,
@@ -72,10 +91,12 @@ function safeGridViewRefresh(gv: { refresh?: () => void } | null | undefined) {
function safeColumnByName(
gv: { columnByName?: (name: string) => unknown } | null | undefined,
name: string,
): { styleCallback?: (g: unknown, dataCell: Record<string, unknown>) => string } | null {
): { styleCallback?: (g: unknown, dataCell: Record<string, unknown>) => CellStyleCallbackResult } | null {
if (!gv?.columnByName) return null;
try {
const col = gv.columnByName(name) as { styleCallback?: (g: unknown, dataCell: Record<string, unknown>) => string } | null;
const col = gv.columnByName(name) as {
styleCallback?: (g: unknown, dataCell: Record<string, unknown>) => CellStyleCallbackResult;
} | null;
return col ?? null;
} catch {
return null;
@@ -219,11 +240,14 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
};
if (col.computed) {
const hasFields = "fields" in col.computed && col.computed.fields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && "denominatorFields" in col.computed && col.computed.denominatorFields.length > 0;
const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
const hasHalfSumOfSums = col.computed.type === "halfSumOfSums" && "firstSumFields" in col.computed && "secondSumFields" in col.computed && col.computed.firstSumFields.length > 0 && col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums = col.computed.type === "sumOfTwoHalfSumOfSums" && "firstHalfSum" in col.computed && "secondHalfSum" in col.computed && col.computed.firstHalfSum.firstSumFields.length > 0 && col.computed.firstHalfSum.secondSumFields.length > 0 && col.computed.secondHalfSum.firstSumFields.length > 0 && col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
const hasSumPerTenThousand = col.computed.type === "sumPerTenThousand" && "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && "denominatorField" in col.computed && Boolean(col.computed.denominatorField);
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasHalfSumOfSums || hasSumOfTwoHalfSumOfSums || hasSumOfMaxGroups || hasSumPerTenThousand) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
@@ -292,7 +316,7 @@ const TabDataGrid = ({
const gridCol = safeColumnByName(gv, column.name);
if (!gridCol) continue;
const baseStyleName = column.styleName;
gridCol.styleCallback = (_g, dataCell) => {
gridCol.styleCallback = (grid, dataCell) => {
const dc = dataCell as {
itemIndex?: number;
index?: { itemIndex?: number };
@@ -303,16 +327,16 @@ const TabDataGrid = ({
const fieldName = dc?.dataField ?? dc?.fieldName ?? column.fieldName;
const data = rowDataMapRef.current.get(tabKey) || [];
if (itemIndex < 0 || itemIndex >= data.length) {
return baseStyleName ?? "";
return mergeCellStyleCallbackResult(baseStyleName, undefined);
}
const row = data[itemIndex] as Record<string, string | number>;
const extra = cellStyleCallbackRef.current?.({
rowIndex: itemIndex,
fieldName,
row,
grid: grid as CellStyleCallbackParams["grid"],
});
if (!extra) return baseStyleName ?? "";
return baseStyleName ? `${extra} ${baseStyleName}` : extra;
return mergeCellStyleCallbackResult(baseStyleName, extra);
};
attachedCount += 1;
}
@@ -373,20 +397,6 @@ const TabDataGrid = ({
const columns = columnInfoItem.columns || [];
const columnLayout = columnInfoItem.columnLayout || [];
// 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)")
const originalHeaders: Map<string, string> = new Map();
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header) {
const originalText = gridCol.header.text || '';
originalHeaders.set(col.name, originalText);
const headerWithFieldName = `${originalText} (${col.fieldName})`;
gridCol.header.text = headerWithFieldName;
}
}
});
// exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가)
const normalizeLayoutForExport = (layout: ColumnLayoutItem[]): ColumnLayoutItem[] => {
return layout.map(item => {
@@ -452,15 +462,6 @@ const TabDataGrid = ({
if (excelExportVisibilitySnapshot) {
restoreGridColumnsAfterExcelExport(gridView, excelExportVisibilitySnapshot);
}
// 헤더 원복
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header && originalHeaders.has(col.name)) {
gridCol.header.text = originalHeaders.get(col.name) || '';
}
}
});
// columnLayout 원복 (footerUserSpans 유지)
if (normalizedLayout && !layoutRestored) {
layoutRestored = true;
@@ -546,29 +547,12 @@ const TabDataGrid = ({
const maxHeaderRowIndex = headerRowIndexes.length > 0 ? Math.max(...headerRowIndexes) : headerStartRow;
const dataStartRow = maxHeaderRowIndex + 1;
// 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑
const headerToFieldNameMap = new Map<string, string>();
const columns = columnInfoItem.columns || [];
headers.forEach((headerText, colIndex) => {
let fieldName: string | null = null;
// 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)")
const fieldNameMatch = headerText.match(/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/);
if (fieldNameMatch && fieldNameMatch[1]) {
const extractedFieldName = fieldNameMatch[1];
if (columns.find(c => c.fieldName === extractedFieldName)) {
fieldName = extractedFieldName;
}
}
// 매칭 실패 시 원본 헤더 텍스트 사용
if (!fieldName) {
console.warn(`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`);
fieldName = headerText;
}
headerToFieldNameMap.set(headerText, fieldName);
});
const columnLayout = columnInfoItem.columnLayout || [];
const excelColumnFieldNames = getExcelImportColumnFieldNames(
columns,
columnLayout,
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -579,9 +563,8 @@ const TabDataGrid = ({
for (let colIndex = 0; colIndex < headers.length; colIndex++) {
const col = range.s.c + colIndex;
const cell = worksheet[XLSX.utils.encode_cell({ r: row, c: col })];
const headerName = headers[colIndex] || `Column${colIndex + 1}`;
// 헤더 텍스트를 fieldName으로 변환
const fieldName = headerToFieldNameMap.get(headerName) || headerName;
const fieldName = excelColumnFieldNames[colIndex];
if (!fieldName) continue;
let cellValue: string | number = '';
if (cell && cell.v !== undefined && cell.v !== null) {
@@ -92,13 +92,12 @@ const verificationSubmitResultColumnInfo: ColumnInfo = {
{ name: "vrfcMthdNm", fieldName: "vrfcMthdNm", header: { text: "구분" }, editable: false },
{ name: "vrfcMthdCd", fieldName: "vrfcMthdCd", header: { text: "검증유형코드" }, editable: false, visible: false },
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYn", fieldName: "prcsYn", header: { text: "처리여부" }, editable: false },
{ name: "pbcpAprvYn", fieldName: "pbcpAprvYn", header: { text: "공단승인여부" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{
@@ -111,13 +110,12 @@ const verificationSubmitResultColumnInfo: ColumnInfo = {
columnLayout: [
"vrfcMthdNm",
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYn",
"pbcpAprvYn",
"regDt",
"mdfcnDt",
"prcsOpnnExpln",
@@ -202,13 +200,12 @@ export function VerificationResultDialog({
vrfcMthdCd,
vrfcMthdNm: VRFC_MTHD_NAME_MAP[vrfcMthdCd] ?? item.vrfcMthdNm,
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYn: PRCS_YN_LABEL[item.prcsYn] ?? item.prcsYn,
pbcpAprvYn: item.pbcpAprvYn,
prcsYn: item.prcsYnNm ?? PRCS_YN_LABEL[item.prcsYn] ?? item.prcsYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
prcsOpnnExpln: item.prcsOpnnExpln ?? "",
@@ -226,9 +223,9 @@ export function VerificationResultDialog({
sggNm: stableQueryContext.sggNm,
statsYr: stableQueryContext.statsYr,
statsCd: stableQueryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: "0",
rejectCodeList: "0,1,2",
page: page + 1,
size: 10000,
};
@@ -137,12 +137,15 @@ function verificationPrcsYnNmCellStyle({ fieldName, row }: CellStyleCallbackPara
const verificationResultColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYnNm", fieldName: "prcsYnNm", header: { text: "처리여부" }, editable: false },
{ name: "stVrfRjtComNm", fieldName: "stVrfRjtComNm", header: { text: "반려상태" }, editable: false },
{ name: "prcsOpnnExpln", fieldName: "prcsOpnnExpln", header: { text: "처리의견" }, editable: false },
{ name: "rjctOpnnCn", fieldName: "rjctOpnnCn", header: { text: "반려의견" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{ name: "prcsYn", fieldName: "prcsYn", header: { text: "검증결과상태코드" }, editable: false, visible: false },
@@ -150,12 +153,15 @@ const verificationResultColumnInfo: ColumnInfo = {
],
columnLayout: [
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYnNm",
"stVrfRjtComNm",
"prcsOpnnExpln",
"rjctOpnnCn",
"regDt",
"mdfcnDt",
"prcsYn",
@@ -175,6 +181,8 @@ export interface VerificationResultHistoryDialogProps {
} | null;
title?: string;
emptyMessage?: string;
/** 예외 제출 취소 API 성공 후 부모(입력 화면)에서 메인 데이터를 다시 조회할 때 사용 */
onExceptionResetSubmitted?: () => void | Promise<void>;
}
export function VerificationResultHistoryDialog({
@@ -183,6 +191,7 @@ export function VerificationResultHistoryDialog({
queryContext,
title = "검증 결과 내역",
emptyMessage = "표시할 검증 결과가 없습니다.",
onExceptionResetSubmitted,
}: VerificationResultHistoryDialogProps) {
const stableQueryContext = useMemo(() => {
if (!queryContext) return null;
@@ -245,9 +254,9 @@ export function VerificationResultHistoryDialog({
sggNm: stableQueryContext.sggNm,
statsYr: stableQueryContext.statsYr,
statsCd: stableQueryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: "0,2,3",
prcsYnList: "0,2",
rejectCodeList: "0,1,2",
page: pageIndex + 1,
size: PAGE_SIZE,
};
@@ -376,14 +385,16 @@ export function VerificationResultHistoryDialog({
vrfcSn: item.vrfcSn,
__checked: isChecked ? 1 : 0,
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYnNm: item.prcsYnNm ?? "-",
stVrfRjtComNm: item.stVrfRjtComNm ?? "-",
prcsOpnnExpln: item.prcsOpnnExpln ?? "-",
rjctOpnnCn: item.rjctOpnnCn ?? "-",
prcsYn: item.prcsYn ?? "",
pbcpAprvYn: item.pbcpAprvYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
};
@@ -451,12 +462,28 @@ export function VerificationResultHistoryDialog({
alert("예외 제출 취소 처리되었습니다.");
setCheckedVrfcByTab((prev) => ({ ...prev, [tab]: new Map() }));
await loadTabPage(tab, tabPages[tab].pageIndex);
try {
await onExceptionResetSubmitted?.();
} catch {
alert(
"예외 제출 취소는 완료되었으나 화면 데이터를 다시 불러오지 못했습니다. 조회로 새로고침 해주세요.",
);
}
} catch {
alert("예외 제출 취소 처리 중 오류가 발생했습니다.");
} finally {
setResetSubmitting(false);
}
}, [activeTab, alert, checkedVrfcByTab, entriesByTab, loadTabPage, stableQueryContext, tabPages]);
}, [
activeTab,
alert,
checkedVrfcByTab,
entriesByTab,
loadTabPage,
onExceptionResetSubmitted,
stableQueryContext,
tabPages,
]);
const renderActiveTabPanel = (tab: VerificationHistoryTabKey) => {
const entries = entriesByTab[tab];
@@ -34,8 +34,8 @@ export async function hasAnyPostSubmitVerificationResult(
sggNm: ctx.sggNm,
statsYr: ctx.statsYr,
statsCd: ctx.statsCd,
pbcpAprvYn: "N" as const,
prcsYnList: "0",
rejectCodeList: "0,1,2",
page: 1,
size: 1,
};
@@ -1,6 +1,6 @@
"use client";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import {
type InfiniteData,
keepPreviousData,
@@ -9,12 +9,12 @@ import {
} from "@tanstack/react-query";
import { useTabStore as useGridTabStore } from "@rsone/components";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { verificationInputReportApi } from "@/entities/statistics/api/verification-input-report-service";
import type {
VerificationResultListRequest,
VerificationResultListResponse,
VerificationResultReportItem,
} from "@/entities/statistics/model/statistics-interface";
VerificationInputReportItem,
VerificationInputReportResponse,
VerificationInputReportTypeCountingInfo,
} from "@/entities/statistics/model/verification-input-report-interface";
import { useAlertStore, useMenuStore } from "@/shared/lib";
import {
findMenuItemByUrlPath,
@@ -59,8 +59,8 @@ const TYPE_CODE_NAME_MAP: Record<string, string> = {
const PAGE_SIZE = 100;
const SCROLL_LOAD_THRESHOLD_PX = 120;
function getNextVerificationResultPage(
lastPage: VerificationResultListResponse,
function getNextVerificationInputReportPage(
lastPage: VerificationInputReportResponse,
): number | undefined {
const pageInfo = lastPage?.data?.pageInfo;
if (!pageInfo) return undefined;
@@ -69,19 +69,29 @@ function getNextVerificationResultPage(
return undefined;
}
function flattenVerificationResults(
data: InfiniteData<VerificationResultListResponse> | undefined,
): VerificationResultReportItem[] {
function flattenVerificationInputReportResults(
data: InfiniteData<VerificationInputReportResponse> | undefined,
): VerificationInputReportItem[] {
return (
data?.pages.flatMap((page) => page?.data?.verifyResultList ?? []) ?? []
);
}
function getVerificationResultTotalCount(
data: InfiniteData<VerificationResultListResponse> | undefined,
loadedCount: number,
): number {
return data?.pages[0]?.data?.pageInfo?.totalElements ?? loadedCount;
function getVerifyTypeCountingInfo(
data: InfiniteData<VerificationInputReportResponse> | undefined,
): VerificationInputReportTypeCountingInfo | null {
return data?.pages[0]?.data?.verifyTypeCountingInfo ?? null;
}
function formatReportItemTitle(item: VerificationInputReportItem): string {
return item.statsNm?.trim() || item.vrfcTrgtNm?.trim() || item.statsCd;
}
function formatReportItemRegion(item: VerificationInputReportItem): string {
const parts = [item.ctpvNm, item.sggNm].filter(
(v): v is string => Boolean(v?.trim()),
);
return parts.length > 0 ? parts.join(" ") : "-";
}
function useVerificationReportList(
@@ -89,28 +99,31 @@ function useVerificationReportList(
statsCdQueryKey: string,
vrfcMthdCd: VerificationReportTabKey,
statsCd: string,
enabled: boolean,
) {
return useInfiniteQuery({
queryKey: [
"verificationResultList",
"verificationInputReport",
statsYr,
vrfcMthdCd,
statsCdQueryKey,
],
initialPageParam: 1,
queryFn: ({ pageParam }) =>
statisticsApi.getVerificationResultList({
verificationInputReportApi.getVerificationInputReport({
statsYr,
pbcpAprvYn: "N",
vrfcMthdCd,
page: pageParam,
size: PAGE_SIZE,
prcsYnList: "0",
vrfcMthdCd,
...(!(statsCd === "" || statsCd === "ALL") && { statsCd }),
} satisfies VerificationResultListRequest),
getNextPageParam: getNextVerificationResultPage,
}),
getNextPageParam: getNextVerificationInputReportPage,
placeholderData: keepPreviousData,
refetchOnWindowFocus: false,
// 탭 전환만으로는 재조회하지 않음(명시적 새로고침/조건 변경 시만 조회)
staleTime: Infinity,
refetchOnMount: false,
enabled,
});
}
@@ -132,28 +145,46 @@ const VerificationSelectionReport = ({
const alert = useAlertStore((s) => s.setMessage);
const [activeTab, setActiveTab] = useState<VerificationReportTabKey>("NV");
const [statsCd, setStatsCd] = useState<string>("ALL");
const [isFullReloading, setIsFullReloading] = useState(false);
const statsYr = String(dayjs().year() - 1);
const statsCdQueryKey =
statsCd === "" || statsCd === "ALL" ? "" : statsCd;
const handleStatsCdChange = useCallback(
(nextStatsCd: string) => {
setStatsCd(nextStatsCd);
setActiveTab("NV");
setIsFullReloading(true);
// 통계항목 변경 시 모든 탭 캐시 제거 후 NV부터 다시 조회
void queryClient.removeQueries({
queryKey: ["verificationInputReport", statsYr],
});
},
[queryClient, statsYr],
);
const nvQuery = useVerificationReportList(
statsYr,
statsCdQueryKey,
"NV",
statsCd,
activeTab === "NV",
);
const imrQuery = useVerificationReportList(
statsYr,
statsCdQueryKey,
"IMR",
statsCd,
activeTab === "IMR",
);
const hbQuery = useVerificationReportList(
statsYr,
statsCdQueryKey,
"HB",
statsCd,
activeTab === "HB",
);
const queryByTab = {
@@ -162,44 +193,51 @@ const VerificationSelectionReport = ({
HB: hbQuery,
} as const;
const isInitialLoading =
nvQuery.isPending || imrQuery.isPending || hbQuery.isPending;
const isRefreshing =
(nvQuery.isFetching && !nvQuery.isPending) ||
(imrQuery.isFetching && !imrQuery.isPending) ||
(hbQuery.isFetching && !hbQuery.isPending);
const error = nvQuery.error ?? imrQuery.error ?? hbQuery.error;
const activeQuery = queryByTab[activeTab];
const isRefreshing = activeQuery.isFetching && !activeQuery.isPending;
const error = activeQuery.error;
/** 통계항목 변경·최초 진입: 탭 영역 숨기고 카드 전체 로딩만 표시 */
const hideTabsSection =
isFullReloading ||
(activeTab === "NV" && !nvQuery.data && (nvQuery.isPending || nvQuery.isFetching));
useEffect(() => {
if (!isFullReloading) return;
if (nvQuery.isFetching) return;
if (nvQuery.isSuccess || nvQuery.isError) {
setIsFullReloading(false);
}
}, [isFullReloading, nvQuery.isFetching, nvQuery.isSuccess, nvQuery.isError]);
const handleRefresh = useCallback(() => {
const tabCodes: VerificationReportTabKey[] = ["NV", "IMR", "HB"];
void Promise.all(
tabCodes.map((code) =>
queryClient.resetQueries({
queryKey: [
"verificationResultList",
statsYr,
code,
statsCdQueryKey,
],
}),
),
);
}, [queryClient, statsYr, statsCdQueryKey]);
setStatsCd("ALL");
setActiveTab("NV");
setIsFullReloading(true);
void queryClient.removeQueries({
queryKey: ["verificationInputReport", statsYr],
});
}, [queryClient, statsYr]);
const listsByTab: Record<VerificationReportTabKey, VerificationResultReportItem[]> = {
NV: flattenVerificationResults(nvQuery.data),
IMR: flattenVerificationResults(imrQuery.data),
HB: flattenVerificationResults(hbQuery.data),
const listsByTab: Record<VerificationReportTabKey, VerificationInputReportItem[]> = {
NV: flattenVerificationInputReportResults(nvQuery.data),
IMR: flattenVerificationInputReportResults(imrQuery.data),
HB: flattenVerificationInputReportResults(hbQuery.data),
};
const typeCountingInfo =
getVerifyTypeCountingInfo(nvQuery.data) ??
getVerifyTypeCountingInfo(imrQuery.data) ??
getVerifyTypeCountingInfo(hbQuery.data);
const totalCountByTab: Record<VerificationReportTabKey, number> = {
NV: getVerificationResultTotalCount(nvQuery.data, listsByTab.NV.length),
IMR: getVerificationResultTotalCount(imrQuery.data, listsByTab.IMR.length),
HB: getVerificationResultTotalCount(hbQuery.data, listsByTab.HB.length),
NV: typeCountingInfo?.nvCount ?? listsByTab.NV.length,
IMR: typeCountingInfo?.imrCount ?? listsByTab.IMR.length,
HB: typeCountingInfo?.hbCount ?? listsByTab.HB.length,
};
const openInputFromStatsCd = useCallback(
(item: VerificationResultReportItem) => {
(item: VerificationInputReportItem) => {
const nav = resolveInputPageNavigationFromStatsCd(item.statsCd);
if (!nav) {
alert(
@@ -263,9 +301,21 @@ const VerificationSelectionReport = ({
[],
);
const isTabContentLoading = (tab: VerificationReportTabKey) => {
const query = queryByTab[tab];
return (
activeTab === tab &&
!hideTabsSection &&
(query.isPending ||
(query.isFetching && !query.isFetchingNextPage) ||
(!query.data && !query.error))
);
};
const renderResultList = (
typeCode: VerificationReportTabKey,
items: VerificationResultReportItem[],
items: VerificationInputReportItem[],
isTabLoading: boolean,
hasNextPage: boolean,
isFetchingNextPage: boolean,
fetchNextPage: () => void,
@@ -281,7 +331,13 @@ const VerificationSelectionReport = ({
)
}
>
{items.length === 0 && !isFetchingNextPage ? (
{isTabLoading ? (
<div className="flex h-full min-h-[8rem] items-center justify-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
) : items.length === 0 && !isFetchingNextPage ? (
<div className="flex h-full min-h-[8rem] items-center justify-center text-lg text-muted-foreground">
항목이 없습니다.
</div>
@@ -303,7 +359,7 @@ const VerificationSelectionReport = ({
>
<div className="mb-2 flex items-center justify-between">
<span className="font-medium">
{item.statsNm}
{formatReportItemTitle(item)}
</span>
<Badge
color={
@@ -320,7 +376,7 @@ const VerificationSelectionReport = ({
{item.rsnExpln}
</p>
<div className="flex justify-between text-lg text-muted-foreground">
<span>{item.admdstNm}</span>
<span>{formatReportItemRegion(item)}</span>
<span>
현재: {item.nowVlCn ?? "-"} / 비교:{" "}
{item.compVlCn ?? "-"}
@@ -383,25 +439,25 @@ const VerificationSelectionReport = ({
<CodeSelectWithAll
upCd="STITEM"
value={statsCd}
onValueChange={setStatsCd}
onValueChange={handleStatsCdChange}
placeholder="전체"
className="w-full"
/>
</div>
<div className="card-body p-2.5 flex-1 flex flex-col overflow-hidden relative min-h-0">
{isInitialLoading && (
{hideTabsSection && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
{error && (
{!hideTabsSection && error && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
데이터를 불러올 수 없습니다.
</div>
)}
{!isInitialLoading && !error && (
{!hideTabsSection && !error && (
<section className="flex min-h-0 flex-1 flex-col">
<Tabs
value={activeTab}
@@ -428,18 +484,14 @@ const VerificationSelectionReport = ({
value={tab.value}
className="mt-0 flex min-h-0 flex-1 flex-col p-0 data-[state=inactive]:hidden"
>
{activeTab === tab.value
? renderResultList(
tab.value,
listsByTab[tab.value],
queryByTab[tab.value]
.hasNextPage ?? false,
queryByTab[tab.value]
.isFetchingNextPage,
queryByTab[tab.value]
.fetchNextPage,
)
: null}
{renderResultList(
tab.value,
listsByTab[tab.value],
isTabContentLoading(tab.value),
queryByTab[tab.value].hasNextPage ?? false,
queryByTab[tab.value].isFetchingNextPage,
queryByTab[tab.value].fetchNextPage,
)}
</TabsContent>
))}
</Tabs>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState, type Dispatch, type SetStateAction } from 'react';
import { useConfirm } from '@frontend-opensource/use-react-hooks';
@@ -157,7 +157,9 @@ const PublicSewageFacilityInp = () => {
const globalProgressVisible = useGlobalProgressStore((state) => state.visible);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [hasSearched, setHasSearched] = useState(false);
const [isNotApplicable, setIsNotApplicable] = useState(false);
const [isNotApplicableByTab, setIsNotApplicableByTab] = useState<Map<string, boolean>>(
() => new Map(),
);
const [showIsNotApplicable, setShowIsNotApplicable] = useState(false);
const [searchValues, setSearchValues] = useState<SearchFormValues | null>(null);
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(null);
@@ -175,6 +177,19 @@ const PublicSewageFacilityInp = () => {
const [mainFacilityMappingSaveDialogKey, setMainFacilityMappingSaveDialogKey] = useState(0);
const [verificationResultDialogOpen, setVerificationResultDialogOpen] = useState(false);
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
const isNotApplicable = isNotApplicableByTab.get('ST00060001') ?? false;
const setIsNotApplicable: Dispatch<SetStateAction<boolean>> = (next) => {
setIsNotApplicableByTab((prev) => {
const current = prev.get('ST00060001') ?? false;
const resolved =
typeof next === 'function'
? (next as (prevState: boolean) => boolean)(current)
: next;
const nextMap = new Map(prev);
nextMap.set('ST00060001', resolved);
return nextMap;
});
};
const periodEnterableRef = useRef(true);
const searchDoneRef = useRef(false);
@@ -195,7 +210,7 @@ const PublicSewageFacilityInp = () => {
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('제출할 데이터가 없습니다.');
alert('해당사항 없음을 체크해주세요.');
return;
}
const verificationBlocked = getVerificationPeriodActionBlockedMessage(periodCheckList);
@@ -276,15 +291,15 @@ const PublicSewageFacilityInp = () => {
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('제출할 데이터가 없습니다.');
const submitList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && submitList.length === 0) {
alert('해당사항 없음을 체크해주세요.');
return;
}
try {
showGlobalProgress({ label: '서버에 요청 중...', progress: 0 });
const userInfo = await getUserInfo();
const submitList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const statsCdForTab = tabStatsCdMap.get(activeTab) ?? activeTab;
const requestData = {
@@ -515,19 +530,19 @@ const PublicSewageFacilityInp = () => {
if (!(await confirm('저장 하시겠습니까?'))) return;
if (activeTab === 'ST00060001') {
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
const saveList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && saveList.length === 0) {
alert('해당사항 없음을 체크해주세요.');
return;
}
try {
showGlobalProgress({ label: '저장 진행 중...', progress: 0 });
const userInfo = await getUserInfo();
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('저장할 데이터가 없습니다.');
return;
}
const saveList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
const param: PublicSewageFacilitySaveRequest = {
statsYr: lastSearchedValues.year ?? '',
ctpvNm: lastSearchedValues.sido ?? '',
@@ -655,8 +670,6 @@ const PublicSewageFacilityInp = () => {
param: PublicSewageFacilityRequestParams,
searchContext: SearchFormValues | null,
): Promise<boolean> => {
// 서버 데이터로 새로 불러오는 시점이므로 "해당없음" 체크는 항상 초기화
setIsNotApplicable(false);
try {
if (tabKey === 'ST00060001') {
const response = await statisticsInputPublicSewageFacilityApiService.getPublicSewageFacility(param);
@@ -856,6 +869,11 @@ const PublicSewageFacilityInp = () => {
setLastSearchedValues(values);
searchDoneRef.current = true;
setHasSearched(true);
setIsNotApplicableByTab((prev) => {
const next = new Map(prev);
next.set('ST00060001', false);
return next;
});
setIsLoading(true);
const param = buildPublicSewageFacilityParams(values);
@@ -870,6 +888,19 @@ const PublicSewageFacilityInp = () => {
};
const handleActiveTabChange = async (tabKey: string) => {
const prevTabKey = activeTabRef.current;
if (prevTabKey === 'ST00060001') {
const latestRowsInPrevTab =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (latestRowsInPrevTab.length > 0 || rowDataMap.has('ST00060001')) {
setRowDataMap((prev) => {
const next = new Map(prev);
next.set('ST00060001', latestRowsInPrevTab);
return next;
});
}
}
activeTabRef.current = tabKey;
setActiveTab(tabKey);
@@ -885,7 +916,7 @@ const PublicSewageFacilityInp = () => {
const handleReset = () => {
setIsLoading(false);
setHasSearched(false);
setIsNotApplicable(false);
setIsNotApplicableByTab(new Map());
setShowIsNotApplicable(false);
setRowDataMap(new Map());
@@ -1215,7 +1246,7 @@ const PublicSewageFacilityInp = () => {
return (
<TabsContent key={tab.tabKey} value={tab.tabKey}>
<DataGrid
key={(tabEnterableForGrid || isAdminInfo) ? 'enterable' : 'readonly'}
key={tab.tabKey}
columnInfo={gridColumnInfo}
rowStartNum={tab.rowStartNum}
excelExportFileSuffix={tab.tabLabel || tab.tabKey}
@@ -1278,6 +1309,11 @@ const PublicSewageFacilityInp = () => {
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const ctx = lastSearchedValues ?? searchValues;
if (!ctx || !searchParamRef.current) return;
await loadPublicSewageFacilityActiveTabData(activeTab, searchParamRef.current, ctx);
}}
/>
<SubmitDailog
open={showDailog}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -396,6 +396,8 @@ const SewagePipeInp = () => {
values: SearchFormValues;
tabKey: string;
} | null>(null);
/** ST00020002 무데이터 안내/자동이동은 동일 조회조건에서 1회만 수행 */
const factInstallNoDataHandledKeyRef = useRef<string | null>(null);
const [verificationSearchDefaults, setVerificationSearchDefaults] =
useState<SearchFormValues | null>(null);
@@ -549,6 +551,12 @@ const SewagePipeInp = () => {
mergeIntoExistingMap: boolean,
updateLastSearchedValues: boolean,
): Promise<boolean> => {
const searchConditionKey = JSON.stringify({
statsYr: searchParam.statsYr ?? "",
ctpvNm: searchParam.ctpvNm ?? "",
sggNm: searchParam.sggNm ?? "",
sewerBzmnCd: searchParam.sewerBzmnCd ?? "",
});
const applyRowMap = (apply: (m: Map<string, Record<string, string | number>[]>) => void) => {
if (mergeIntoExistingMap) {
setRowDataMap((prev) => {
@@ -595,6 +603,23 @@ const SewagePipeInp = () => {
ctpvNm: commonInfo.ctpvNm,
...d,
}));
if (convertedData.length === 0) {
const alreadyHandledForSameSearch =
factInstallNoDataHandledKeyRef.current === searchConditionKey;
if (!alreadyHandledForSameSearch) {
factInstallNoDataHandledKeyRef.current = searchConditionKey;
alert("시설별 설치 현황에 조회된 데이터가 없습니다.\n시설별 현황 관리에서 설정해주세요");
activeTabRef.current = "ST00020006";
setActiveTab("ST00020006");
return await loadSewagePipeActiveTabData(
"ST00020006",
searchParam,
values,
true,
updateLastSearchedValues,
);
}
}
applyRowMap((m) => m.set('ST00020002', toGridRows(convertedData)));
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
@@ -795,7 +820,39 @@ const SewagePipeInp = () => {
}, [searchFormRemountKey]);
const getCurrentRowsForTab = (tabKey: string): Record<string, string | number>[] => {
switch (tabKey) {
case REGIONAL_FACT_TAB_KEY:
return getGridDataRef.current?.() ?? [];
case "ST00020002":
return getFactInstallGridDataRef.current?.() ?? [];
case "ST00020003":
return getPipeTypeGridDataRef.current?.() ?? [];
case "ST00020004":
return getPipeDiameterGridDataRef.current?.() ?? [];
case "ST00020005":
return getManholeGridDataRef.current?.() ?? [];
case "ST00020006":
return [
...(rowDataMap.get("treatmentPlantConnectionInp") ?? []),
...(rowDataMap.get("adminAreaConnectionInp") ?? []),
];
default:
return rowDataMap.get(tabKey) ?? [];
}
};
const handleActiveTabChange = async (tabKey: string) => {
const prevTabKey = activeTabRef.current;
const latestRowsInPrevTab = getCurrentRowsForTab(prevTabKey);
if (latestRowsInPrevTab.length > 0 || rowDataMap.has(prevTabKey)) {
setRowDataMap((prev) => {
const next = new Map(prev);
next.set(prevTabKey, latestRowsInPrevTab);
return next;
});
}
activeTabRef.current = tabKey;
setActiveTab(tabKey);
setStatsCd(tabStatsCdMap.get(tabKey) ?? "");
@@ -1735,7 +1792,7 @@ const SewagePipeInp = () => {
const maxYy = getManholeLastDataYear() % 100;
const saveList = currentData.map((d) => {
const yearlyStats = [];
const yearlyStats: SeweragePipeInputManholeStatusSaveYearlyStatItem[] = [];
for (let yy = minYy; yy <= maxYy; yy++) {
const year = 2000 + yy;
const wholKey = `yy${yy}WholMnhlCnt`;
@@ -1978,7 +2035,7 @@ const SewagePipeInp = () => {
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
key={(tabEnterableForGrid || isAdminInfo) ? "enterable" : "readonly"}
key={tabKey}
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel || tabKey}
@@ -2062,6 +2119,12 @@ const SewagePipeInp = () => {
}
: null
}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values?.year || !values?.sido || !values?.bsnmCd) return;
const refreshedParam = buildSewagePipeSearchParam(values);
await loadSewagePipeActiveTabData(activeTab, refreshedParam, values, true, false);
}}
/>
<SubmitDailog
open={showDailog}
@@ -80,6 +80,12 @@ const SewagePipeMaintainInpTabList = [
const firstTabKey = RepairStatusInpColumn.tabKey;
const SEWAGE_PIPE_MAINTAIN_COLUMN_GUIDE_TAB_KEYS = Array.from(
new Set(
SewagePipeMaintainInpTabList.map((tab) => tab.tabKey).filter(Boolean),
),
);
const toGridColumnInfo = (tab: (typeof SewagePipeMaintainInpTabList)[number]): ColumnInfo => ({
columns: tab.columns ?? [],
columnLayout: tab.columnLayout ?? [],
@@ -302,6 +308,9 @@ const SewagePipeMaintainInp = () => {
const [rowDataMap, setRowDataMap] = useState<
Map<string, Record<string, string | number>[]>
>(new Map());
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
const searchDoneRef = useRef(false);
const periodEnterableRef = useRef(true);
@@ -328,6 +337,37 @@ const SewagePipeMaintainInp = () => {
}
}, [activeTabId, inputFocusNonce]);
useEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE_MAINTAIN) return;
let cancelled = false;
void (async () => {
const entries = await Promise.all(
SEWAGE_PIPE_MAINTAIN_COLUMN_GUIDE_TAB_KEYS.map(async (tabKey) => {
try {
const res = await statisticsApi.getColumnGuides(tabKey);
if (res.status !== "success" || !Array.isArray(res.data)) {
return [tabKey, {}] as const;
}
const next: Record<string, string> = {};
for (const item of res.data) {
const key = item.engColNm?.trim();
const guide = item.columnGuide?.trim();
if (key && guide) next[key] = guide;
}
return [tabKey, next] as const;
} catch {
return [tabKey, {}] as const;
}
}),
);
if (cancelled) return;
setColumnGuidesByTab(Object.fromEntries(entries));
})();
return () => {
cancelled = true;
};
}, [activeTabId]);
const commitToolbarSnapshotForLoadedTab = (
tabKey: string,
statusInfo: SewerageStatusInfo | null,
@@ -912,6 +952,16 @@ const SewagePipeMaintainInp = () => {
}, [searchFormRemountKey]);
const handleActiveTabChange = async (tabKey: string) => {
const prevTabKey = activeTabRef.current;
const latestRowsInPrevTab = getTabGridRows(prevTabKey);
if (latestRowsInPrevTab.length > 0 || rowDataMap.has(prevTabKey)) {
setRowDataMap((prev) => {
const next = new Map(prev);
next.set(prevTabKey, latestRowsInPrevTab);
return next;
});
}
activeTabRef.current = tabKey;
setActiveTab(tabKey);
if (!hasSearched || !lastSearchedValues || !searchParamRef.current) return;
@@ -941,22 +991,22 @@ const SewagePipeMaintainInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo;
const columns = (tab.columns ?? []).map((col: ColumnConfig) =>
canEdit
? col
: {
...col,
editable: false as const,
styleName: col.styleName ?? "rg-cell-readonly",
},
);
const rawColumns = tab.columns ?? [];
const base = canEdit
? rawColumns
: rawColumns.map((col) => ({ ...col, editable: false as const }));
const columns = base
.map((col) => {
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
return guide ? { ...col, tooltip: guide } : col;
});
map.set(tabKey, {
columns,
columnLayout: tab.columnLayout ?? [],
});
}
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo]);
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
@@ -1110,11 +1160,7 @@ const SewagePipeMaintainInp = () => {
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
key={
tabEnterableForGrid || isAdminInfo
? `${tabKey}-enterable`
: `${tabKey}-readonly`
}
key={`${tabKey}-${tabEnterableForGrid ? "enterable" : "readonly"}`}
columnInfo={
columnInfoByTab.get(tabKey) ??
toGridColumnInfo(info)
@@ -1157,6 +1203,17 @@ const SewagePipeMaintainInp = () => {
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
<ConfirmDialog isOpen={false} />
<SubmitDailog
@@ -1170,6 +1227,17 @@ const SewagePipeMaintainInp = () => {
open={verificationResultDialogOpen}
onOpenChange={setVerificationResultDialogOpen}
queryContext={verificationQueryContext}
onExceptionSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
</>
);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -288,7 +288,8 @@ const SeweragePenetrationRateInp = () => {
} else {
alert(response.message);
}
} catch {
} catch (error) {
console.log(error);
alert('데이터 조회 중 오류가 발생했습니다.');
setRowData([]);
setStatusInfo(null);
@@ -790,11 +791,13 @@ const SeweragePenetrationRateInp = () => {
</div>
</div>
)}
{isLoading && !globalProgressVisible && <div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
{isLoading && !globalProgressVisible &&
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
</div>}
}
<DataGrid
key={(isEnterable || isAdminInfo) ? 'enterable' : 'readonly'}
columnInfo={columnInfo}
@@ -859,6 +862,11 @@ const SeweragePenetrationRateInp = () => {
}
: null
}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values) return;
await handleSearch(values);
}}
/>
<VerificationResultDialog
open={verificationResultDialogOpen}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -196,6 +196,20 @@ const LogHistory = () => {
const handleSearch = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (startDate && endDate) {
const start = dayjs(startDate);
const end = dayjs(endDate);
if (start.isAfter(end, "day")) {
alert("시작일은 종료일보다 늦을 수 없습니다.");
return;
}
if (end.isBefore(start, "day")) {
alert("종료일은 시작일보다 빠를 수 없습니다.");
return;
}
}
await loadLogs(1);
};
@@ -213,6 +213,7 @@ function buildRowsFromStatusList(
statKeys: string[],
): Record<string, string | number>[] {
const rowMap = new Map<string, Record<string, string | number>>();
const rowOrder: string[] = [];
for (const item of statusList) {
const { sido, sgg } = splitSidoSggNm(item.sidoSggNm);
@@ -228,6 +229,7 @@ function buildRowsFromStatusList(
row[key] = "";
}
rowMap.set(rowKey, row);
rowOrder.push(rowKey);
}
const row = rowMap.get(rowKey)!;
@@ -236,11 +238,7 @@ function buildRowsFromStatusList(
}
}
return Array.from(rowMap.values()).sort((a, b) => {
const sidoCmp = String(a[SIDO_FIELD]).localeCompare(String(b[SIDO_FIELD]), "ko");
if (sidoCmp !== 0) return sidoCmp;
return String(a[SGG_FIELD]).localeCompare(String(b[SGG_FIELD]), "ko");
});
return rowOrder.map((key) => rowMap.get(key)!);
}
function buildUnSubmitDataRequest(
@@ -1,10 +1,123 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 분리된 분뇨처리시설용 컬럼 구성 임포트
import { ExcretaTreatmentStatusColumnConfig } from "@/components/util/column/total/SewerExcretaTreatmentStatusColumn";
// 집계표(TOTAL) API 및 인터페이스 명세 호출
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
ExcretaTreatmentMaintainTotalRequest
} from "@/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface";
import CellColorInfo from "./CellColorInfo";
const ExcretaTreatmentFacilityTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// null 타입 컴파일 에러를 방지하기 위해 엄격하게 정의한 데이터 상태
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [isLoading, setIsLoading] = useState(false);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const config = ExcretaTreatmentStatusColumnConfig;
const handleReset = () => {
setRowData([]);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (values: SearchFormValues): Promise<boolean> => {
const param: ExcretaTreatmentMaintainTotalRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
const response = await statisticsTotalStatusApi.getExcretaTreatmentStatusTotalList(param);
if (response && response.status === "success" && response.data) {
// object[]로 변환하여 주입함으로써 상위 컴포넌트 간 null 에러 완벽 차단
setRowData(toGridRows(response.data as object[]));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const gridColumnInfo: ColumnInfo = {
columns: config.columns,
columnLayout: config.columnLayout,
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem] mt-[0.6rem]">
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={config.rowStartNum}
excelExportFileSuffix={config.tabLabel}
rowData={rowData}
setRowData={setRowData} // 필수 프로퍼티 에러 해결
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={config.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</div>
</div>
);
};
@@ -1,10 +1,206 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 분리된 개별 3개 컬럼 스펙 파일 로드
import { PrivateSewageTab1ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column";
import { PrivateSewageTab2ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column";
import { PrivateSewageTab3ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column";
// 통계 API 서비스 및 통합 DTO 명세 임포트
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
PrivateSewageFacilitySummaryRequest
} from "@/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface";
import CellColorInfo from "./CellColorInfo";
const PrivateSewageColumnListTot = [
PrivateSewageTab1ColumnConfig,
PrivateSewageTab2ColumnConfig,
PrivateSewageTab3ColumnConfig
];
const firstTabKey = PrivateSewageColumnListTot[0]?.tabKey ?? "ST00040005_1";
const PrivateSewageFacilityTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// null 컴파일 예러를 사전에 원천 차단한 안전 상태 맵 선언
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = PrivateSewageColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
/** 각 탭별 엔드포인트 비동기 디스패치 및 연동 */
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
const param: PrivateSewageFacilitySummaryRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
// [Tab1] 처리용량별 오수처리시설
if (tabKey === "ST00080001") {
const response = await statisticsTotalStatusApi.getPrivateSewageTab1List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [Tab2] 정화조현황
if (tabKey === "ST00080002") {
const response = await statisticsTotalStatusApi.getPrivateSewageTab2List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [Tab3] 개인하수도 관리지역 지정공고 현황
if (tabKey === "ST00080003") {
const response = await statisticsTotalStatusApi.getPrivatePrivateMgmtAreaList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchedValuesRef.current;
if (!values) return;
const existing = rowDataMap.get(tabKey);
if (existing && existing.length > 0) return; // 캐싱 검증 스킵
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{/* 하수관로 규칙과 100% 싱크 맞춘 중앙 정렬 소형 팝업 로딩 바 고정 */}
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel}
</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
// 단언 연산자(as) 배제 형태 완벽 적용
const rowData = rowDataMap.get(tabKey) ?? [];
const gridColumnInfo: ColumnInfo = {
columns: info.columns,
columnLayout: info.columnLayout,
};
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
@@ -1,10 +1,123 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 분리된 중계펌프장용 컬럼 구성 임포트
import { MediationPumpStatusColumnConfig } from "@/components/util/column/total/SewerMediationPumpStatusColumn";
// 집계표(TOTAL) API 및 인터페이스 명세 호출
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
MediationPumpMaintainTotalRequest
} from "@/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface";
import CellColorInfo from "./CellColorInfo";
const PumpingStationTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// null 타입 컴파일 에러가 발생하지 않도록 엄격하게 정의한 데이터 상태
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [isLoading, setIsLoading] = useState(false);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const config = MediationPumpStatusColumnConfig;
const handleReset = () => {
setRowData([]);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (values: SearchFormValues): Promise<boolean> => {
const param: MediationPumpMaintainTotalRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
const response = await statisticsTotalStatusApi.getMediationPumpStationStatusTotalList(param);
if (response && response.status === "success" && response.data) {
// object[]로 변환하여 주입함으로써 null 에러 전면 방지
setRowData(toGridRows(response.data as object[]));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const gridColumnInfo: ColumnInfo = {
columns: config.columns,
columnLayout: config.columnLayout,
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem] mt-[0.6rem]">
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={config.rowStartNum}
excelExportFileSuffix={config.tabLabel}
rowData={rowData}
setRowData={setRowData}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={config.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</div>
</div>
);
};
@@ -1,10 +1,162 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
import CellColorInfo from "./CellColorInfo";
// 12. 하수 및 분뇨 관리 산업 탭 설정 (경로 확인 필수)
import { PublicSewageStaffTab1ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column";
import { PublicHumanWasteStaffTab2ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column";
import { HumanWasteCollectionTab3ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column";
import { IndividualSewageTab4ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column";
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SewerageIndustry,
PublicSewageStaffTab1Response,
PublicHumanWasteStaffTab2Response,
HumanWasteCollectionTab3Response,
IndividualSewageTab4Response
} from "@/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface";
const SewageExcretaIndustryColumnListTot = [PublicSewageStaffTab1ColumnConfig,PublicHumanWasteStaffTab2ColumnConfig,HumanWasteCollectionTab3ColumnConfig,IndividualSewageTab4ColumnConfig].filter(Boolean);
const firstTabKey = SewageExcretaIndustryColumnListTot[0]?.tabKey ?? "ST00120001";
const SewageExcretaRelatedTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
setIsLoading(true);
const param: SewerageIndustry = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
if (tabKey === "ST00120001") {
const response: PublicSewageStaffTab1Response = await statisticsTotalStatusApi.getPublicSewageStaffTab1List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
else if (tabKey === "ST00120002") {
const response: PublicHumanWasteStaffTab2Response = await statisticsTotalStatusApi.getPublicHumanWasteStaffTab2List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
else if (tabKey === "ST00120003") {
const response: HumanWasteCollectionTab3Response = await statisticsTotalStatusApi.getHumanWasteCollectionTab3List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB4] 개인하수처리관련업
else if (tabKey === "ST00120004") {
const response: IndividualSewageTab4Response = await statisticsTotalStatusApi.getIndividualSewageTab4List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
} finally {
setIsLoading(false);
}
};
const handleSearch = async (values: SearchFormValues) => {
lastSearchedValuesRef.current = values;
await loadGridData(activeTab, values);
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchedValuesRef.current;
if (!values || rowDataMap.has(tabKey)) return;
await loadGridData(tabKey, values);
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{SewageExcretaIndustryColumnListTot.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>{info.tabLabel}</TabsTrigger>
))}
</TabsList>
{SewageExcretaIndustryColumnListTot.map((info) => (
<TabsContent key={info.tabKey} value={info.tabKey}>
<DataGrid
columnInfo={{ columns: info.columns, columnLayout: info.columnLayout }}
rowStartNum={info.rowStartNum}
rowData={rowDataMap.get(info.tabKey) ?? []}
excelExportFileSuffix={info.tabLabel}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(info.tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(info.tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
))}
</Tabs>
</div>
</div>
);
};
@@ -1,10 +1,210 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 4개 탭 컬럼 설정 임포트
import { SludgeProcessingTab1ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column";
import { DigestionTankTab2ColumnConfig } from "@/components/util/column/total/sludgeProcessing/DigestionTankTab2Column";
import { SewageSludgeOperTab3ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column";
import { HumanWasteSludgeTab4ColumnConfig } from "@/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column";
// API 인스턴스 및 인터페이스 임포트 (타입 안전성 확보)
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SludgeSummaryRequest,
SludgeProcessingTab1Response,
DigestionTankOperTab2Response,
SewageSludgeOperTab3Response,
HumanWasteSludgeTab4Response
} from "@/entities/statistics/model/statistics-total/statistics-sludge-total-interface";
import CellColorInfo from "./CellColorInfo";
// 탭 순회 설정
const SludgeColumnListTot = [
SludgeProcessingTab1ColumnConfig,
DigestionTankTab2ColumnConfig,
SewageSludgeOperTab3ColumnConfig,
HumanWasteSludgeTab4ColumnConfig
].filter(Boolean);
const firstTabKey = SludgeColumnListTot[0]?.tabKey ?? "ST00110001";
const SewageExcretaSlurgeTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = SludgeColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
setIsLoading(true);
const param: SludgeSummaryRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
// [TAB1] 하수및분뇨찌꺼기 > 찌꺼기처리시설현황
if (tabKey === "ST00110001") {
const response: SludgeProcessingTab1Response = await statisticsTotalStatusApi.getSludgeProcessingTab1List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB2] 소화조 운영현황
else if (tabKey === "ST00110002") {
const response: DigestionTankOperTab2Response = await statisticsTotalStatusApi.getSludgeTab2List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB3] 하수찌꺼기 처리시설 운영현황
else if (tabKey === "ST00110003") {
const response: SewageSludgeOperTab3Response = await statisticsTotalStatusApi.getSludgeTab3List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB4] 분뇨찌꺼기 처리현황
else if (tabKey === "ST00110004") {
const response: HumanWasteSludgeTab4Response = await statisticsTotalStatusApi.getSludgeTab4List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
alert("데이터 조회 중 오류가 발생했습니다.");
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
} finally {
setIsLoading(false);
}
};
/** 검색 버튼 이벤트: SearchForm의 Values 타입을 기반으로 호출 */
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
// 탭 변경 없이 현재 탭(activeTab)에 대해서만 명시적 타입 호출
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch (error) {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
/** 탭 변경 이벤트: 현재 탭 상태와 SearchForm의 마지막 값을 동기화 */
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
// 마지막 검색 값이 있으면 해당 탭만 새로 로드
const values = lastSearchedValuesRef.current;
if (!values || rowDataMap.has(tabKey)) return;
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch (error) {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>{info.tabLabel}</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
const rowData = rowDataMap.get(tabKey) ?? [];
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={{ columns: info.columns, columnLayout: info.columnLayout }}
rowStartNum={info.rowStartNum}
rowData={rowData}
excelExportFileSuffix={info.tabLabel}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
@@ -15,12 +15,17 @@ import type { SewagePipeMaintainTotalRequest } from "@/entities/statistics/model
const firstTabKey = SewagePipeMaintainColumnListTot[0]?.tabKey ?? "ST00030001";
function buildSewagePipeMaintainTotSearchParam(values: SearchFormValues): SewagePipeMaintainTotalRequest {
const isSummaryEnabled = values.isSummary !== 'ALL';
const summaryYearCnt = isSummaryEnabled ? Number(values.isSummary) : undefined;
return {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
rowDataYn: values.isDetail,
summaryYn: isSummaryEnabled,
...(isSummaryEnabled ? { summaryYearCnt } : {}),
};
}
@@ -128,7 +133,7 @@ const SewagePipeMaintainTot = () => {
return (
<div className="relative">
<SearchForm isDetail={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
@@ -1,10 +1,190 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 분리된 개별 컬럼 구성 임포트 경로 유지
import { ReservoirStatusColumnConfig } from "@/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn";
import { PumpStationStatusColumnConfig } from "@/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn";
// 수정 적용한 API 및 인터페이스 명세 호출 유지
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SewerReservoirMaintainTotalRequest
} from "@/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface";
import CellColorInfo from "./CellColorInfo";
const SewerReservoirColumnListTot = [
ReservoirStatusColumnConfig,
PumpStationStatusColumnConfig
];
const firstTabKey = SewerReservoirColumnListTot[0]?.tabKey ?? "ST00040001";
const SewageReservoirPumpTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// 상태 정의에서 null을 제외하여 DataGrid와 타입을 일치시킵니다.
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = SewerReservoirColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
const param: SewerReservoirMaintainTotalRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
if (tabKey === "ST00040001") {
const response = await statisticsTotalStatusApi.getSewerReservoirStatusTotalList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
if (tabKey === "ST00040002") {
const response = await statisticsTotalStatusApi.getSewerPumpStationStatusTotalList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchedValuesRef.current;
if (!values) return;
const existing = rowDataMap.get(tabKey);
if (existing && existing.length > 0) return;
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel}
</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
const rowData = rowDataMap.get(tabKey) ?? [];
const gridColumnInfo: ColumnInfo = {
columns: info.columns,
columnLayout: info.columnLayout,
};
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n =
typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
@@ -0,0 +1,233 @@
import { useEffect, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore } from "@/shared/lib";
import { statisticsSewerageAggregateSummaryApi } from "@/entities/statistics/api/statistics-sewerage-aggregate-summary-service";
import type { SewerageAggregateSummaryDownloadRequest } from "@/entities/statistics/model/statistics-total/statistics-sewerage-aggregate-summary-interface";
import { LoadingWithAnimation } from "@common/components";
import DataGrid from "@/components/module/DataGrid";
import SearchForm from "@/components/module/SearchForm";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
const columnInfo = {
columns: [
{
name: "statsCd",
fieldName: "statsCd",
header: { text: "통계항목코드" },
editable: false,
visible: false,
},
{ name: "tabNm", fieldName: "tabNm", header: { text: "통계항목" }, editable: false },
],
columnLayout: ["statsCd", "tabNm"],
};
type GridViewWithCheckApi = {
getCheckedRows?: (refresh?: boolean) => number[];
getCheckedItems?: (refresh?: boolean) => number[];
};
const EXCEL_DOWNLOAD_PROGRESS_MAX = 98;
const EXCEL_DOWNLOAD_PROGRESS_STEP = 1;
const EXCEL_DOWNLOAD_PROGRESS_INTERVAL_MS = 500;
const EXCEL_DOWNLOAD_PROGRESS_LABEL =
"엑셀 파일을 생성 중입니다.\n창을 닫거나 이동하지 말고 잠시만 기다려주세요.";
const SewerageAggregateSummary = () => {
const { confirm } = useConfirm();
const { setMessage: alert } = useAlertStore((state) => state);
const showGlobalProgress = useGlobalProgressStore((state) => state.show);
const hideGlobalProgress = useGlobalProgressStore((state) => state.hide);
const setGlobalProgress = useGlobalProgressStore((state) => state.setProgress);
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [isLoading, setIsLoading] = useState(false);
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
const getGridRef = useRef<RealGridReact | null>(null);
const downloadProgressTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
let cancelled = false;
void (async () => {
try {
setIsLoading(true);
const response =
await statisticsSewerageAggregateSummaryApi.getAggregateSummaryTabList();
if (cancelled) return;
if (response.status === "success") {
setRowData(toGridRows(response.data ?? []));
} else {
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
}
} catch {
if (!cancelled) {
alert("데이터 조회 중 오류가 발생했습니다.");
}
} finally {
if (!cancelled) {
setIsLoading(false);
}
}
})();
return () => {
cancelled = true;
};
}, [alert]);
useEffect(() => {
return () => {
if (downloadProgressTimerRef.current !== null) {
clearInterval(downloadProgressTimerRef.current);
}
};
}, []);
const stopDownloadProgressTimer = () => {
if (downloadProgressTimerRef.current !== null) {
clearInterval(downloadProgressTimerRef.current);
downloadProgressTimerRef.current = null;
}
};
const startDownloadProgress = () => {
stopDownloadProgressTimer();
let currentProgress = 0;
showGlobalProgress({
label: EXCEL_DOWNLOAD_PROGRESS_LABEL,
progress: 0,
});
downloadProgressTimerRef.current = setInterval(() => {
currentProgress = Math.min(
currentProgress + EXCEL_DOWNLOAD_PROGRESS_STEP,
EXCEL_DOWNLOAD_PROGRESS_MAX,
);
setGlobalProgress(currentProgress);
if (currentProgress >= EXCEL_DOWNLOAD_PROGRESS_MAX) {
stopDownloadProgressTimer();
}
}, EXCEL_DOWNLOAD_PROGRESS_INTERVAL_MS);
};
const handleDownload = async (values: SearchFormValues) => {
const gridView = getGridRef.current?.gridView as GridViewWithCheckApi | undefined;
const checkedIndexes =
gridView?.getCheckedRows?.(false) ?? gridView?.getCheckedItems?.(false) ?? [];
const allRows = getGridDataRef.current?.() ?? rowData;
const statsCdList = checkedIndexes
.map((idx) => normalizeGridString(allRows[idx]?.statsCd))
.filter((cd) => cd.length > 0);
if (statsCdList.length === 0) {
alert("내려받을 통계항목을 선택해주세요.");
return;
}
if (!values.year) {
alert("통계연도를 선택해주세요.");
return;
}
if (
!(await confirm(
`선택한 데이터 및 사용자 네트워크 환경에 따라 최대 1시간까지 시간이 소요될 수 있습니다.
진행하시겠습니까?`
))
) {
return;
}
const summaryYearCnt =
values.isSummary && values.isSummary !== "ALL"
? Number(values.isSummary)
: 0;
const request: SewerageAggregateSummaryDownloadRequest = {
statsCdList,
statsYr: values.year,
ctpvNm: values.sido === "ALL" ? "" : (values.sido ?? ""),
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
summaryYearCnt: Number.isFinite(summaryYearCnt) ? summaryYearCnt : 0,
};
try {
startDownloadProgress();
const { blob, fileName } =
await statisticsSewerageAggregateSummaryApi.downloadAggregateSummary(request);
stopDownloadProgressTimer();
setGlobalProgress(100);
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName || `하수도통계 집계 요약표_${values.year}.xlsx`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
await new Promise((resolve) => setTimeout(resolve, 400));
alert("저장되었습니다.");
} catch (error) {
const message =
error instanceof Error && error.message
? error.message
: "엑셀 내려받기 중 오류가 발생했습니다.";
alert(message);
} finally {
stopDownloadProgressTimer();
hideGlobalProgress();
}
};
return (
<div className="relative">
<SearchForm
isSummary={true}
sidoSelectText="전체"
searchButtonText="내려받기"
onSubmit={(values) => void handleDownload(values)}
isReset={false}
/>
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<DataGrid
columnInfo={columnInfo}
rowStartNum={1}
isExcelImport={false}
isExcelExport={false}
showHeaderCheckbox
rowData={rowData}
setRowData={setRowData}
getGridDataRef={getGridDataRef}
getGridRef={getGridRef}
/>
</div>
);
};
function normalizeGridString(value: unknown): string {
if (value === null || value === undefined) return "";
return typeof value === "string" ? value : String(value);
}
export default SewerageAggregateSummary;
@@ -1,10 +1,217 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 분리된 4개 개별 탭 컬럼 파일 바인딩
import { SewerageFinancialTab1ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column";
import { SewerageFeeTab2ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column";
import { SewerageUsageFeeTab3ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column";
import { SewerageOrgCauseTab4ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column";
// API 인스턴스 및 인터페이스 호출
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SewerageFinanceSummaryRequest
} from "@/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface";
import CellColorInfo from "./CellColorInfo";
const SewerageFinanceColumnListTot = [
SewerageFinancialTab1ColumnConfig,
SewerageFeeTab2ColumnConfig,
SewerageUsageFeeTab3ColumnConfig,
SewerageOrgCauseTab4ColumnConfig
];
const firstTabKey = SewerageFinanceColumnListTot[0]?.tabKey ?? "ST00040006_1";
const SewerageFinanceTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// 컴파일 에러가 발생하지 않도록 원천 가공 처리형 맵 적용
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = SewerageFinanceColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
/** 개별 조건별 데이터 동적 라우팅 디스패치 */
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
const param: SewerageFinanceSummaryRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
// [Tab1] 하수도재정 결산
if (tabKey === "ST00090001") {
const response = await statisticsTotalStatusApi.getSewerageFinancialList(param); // 엔드포인트 바인딩
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [Tab2] 하수도 요금
if (tabKey === "ST00090002") {
const response = await statisticsTotalStatusApi.getSewerageFeeList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [Tab3] 하수도 사용료
if (tabKey === "ST00090003") {
const response = await statisticsTotalStatusApi.getSewerageUsageList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [Tab4] 원인자 부담금
if (tabKey === "ST00090004") {
const response = await statisticsTotalStatusApi.getSewerageOrgCauseList(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchedValuesRef.current;
if (!values) return;
const existing = rowDataMap.get(tabKey);
if (existing && existing.length > 0) return;
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel}
</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
// 요청하신 타입 단언 제거 형태 유지
const rowData = rowDataMap.get(tabKey) ?? [];
const gridColumnInfo: ColumnInfo = {
columns: info.columns,
columnLayout: info.columnLayout,
};
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
@@ -1,131 +1,99 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { ComplaintInpColumn } from "@/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn";
import { ResidentFriendlyFactTotColumn } from "@/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import CellColorInfo from "./CellColorInfo";
const SewerageServiceLevelInpTabList = [
ComplaintInpColumn,
ResidentFriendlyFactTotColumn,
];
const SUPPORTED_TAB_KEYS = [ComplaintInpColumn.tabKey, ResidentFriendlyFactTotColumn.tabKey] as const;
// 13. 하수도서비스수준 컬럼 설정
import { SewageComplaintTab1ColumnConfig } from "@/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn";
import { ResidentFriendlyFacilityTab2ColumnConfig } from "@/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn";
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SewerageServiceLevelRequest,
SewerageServiceLevelComplaintResponse,
SewerageServiceLevelResidentFriendlyFactResponse
} from "@/entities/statistics/model/statistics-total/statistics-service-level-total-interface";
const SewageServiceLevelColumnListTot = [
SewageComplaintTab1ColumnConfig,
ResidentFriendlyFacilityTab2ColumnConfig
].filter(Boolean);
const firstTabKey = SewageServiceLevelColumnListTot[0]?.tabKey ?? "ST00130001";
const SewerageServiceLevelTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState<boolean>(false);
const [activeTab, setActiveTab] = useState(ComplaintInpColumn.tabKey);
const lastSearchValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = SewerageServiceLevelInpTabList;
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(ComplaintInpColumn.tabKey);
lastSearchValuesRef.current = null;
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadSewerageServiceLevelTotTabData = async (
tabKey: string,
values: SearchFormValues,
mergeIntoExistingMap: boolean,
) => {
const param = {
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
setIsLoading(true);
const param: SewerageServiceLevelRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
const fetcherMap = {
[ComplaintInpColumn.tabKey]: statisticsTotalStatusApi.getSewerageServiceLevelComplaint.bind(
statisticsTotalStatusApi,
),
[ResidentFriendlyFactTotColumn.tabKey]:
statisticsTotalStatusApi.getSewerageServiceLevelResidentFriendlyFact.bind(
statisticsTotalStatusApi,
),
};
const fetcher = fetcherMap[tabKey];
if (!fetcher) {
return false;
}
const response = await fetcher(param);
if (response.status === "success" && response.data) {
const rows = toGridRows(response.data as object[]);
if (mergeIntoExistingMap) {
setRowDataMap((prev) => {
const next = new Map(prev);
next.set(tabKey, rows);
return next;
});
} else {
const next = new Map<string, Record<string, string | number>[]>();
next.set(tabKey, rows);
setRowDataMap(next);
}
return true;
}
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
let ok = false;
if (SUPPORTED_TAB_KEYS.includes(activeTab)) {
ok = await loadSewerageServiceLevelTotTabData(activeTab, values, true);
}
if (ok) {
lastSearchValuesRef.current = values;
} else {
lastSearchValuesRef.current = null;
// [TAB1] 민원
if (tabKey === "ST00130001") {
const response: SewerageServiceLevelComplaintResponse = await statisticsTotalStatusApi.getSewerageServiceLevelComplaint(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB2] 주민친화시설
else if (tabKey === "ST00130002") {
const response: SewerageServiceLevelResidentFriendlyFactResponse = await statisticsTotalStatusApi.getSewerageServiceLevelResidentFriendlyFact(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
return false;
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchValuesRef.current = null;
alert("서버 연결 중 오류가 발생했습니다.");
return false;
} finally {
setIsLoading(false);
}
};
const handleSearch = async (values: SearchFormValues) => {
lastSearchedValuesRef.current = values;
await loadGridData(activeTab, values);
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchValuesRef.current;
if (!values) return;
const existing = rowDataMap.get(tabKey);
const hasData = Array.isArray(existing) && existing.length > 0;
if (hasData) return;
if (SUPPORTED_TAB_KEYS.includes(tabKey)) {
try {
setIsLoading(true);
await loadSewerageServiceLevelTotTabData(tabKey, values, true);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
}
const values = lastSearchedValuesRef.current;
if (!values || rowDataMap.has(tabKey)) return;
await loadGridData(tabKey, values);
};
return (
<div className="relative">
<SearchForm isDetail={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
@@ -133,58 +101,48 @@ const SewerageServiceLevelTot = () => {
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel || info.tabKey}
</TabsTrigger>
{SewageServiceLevelColumnListTot.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>{info.tabLabel}</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
if (!info.columns || !info.columnLayout) {
return null;
}
const rowData = rowDataMap.get(tabKey) ?? [];
const gridColumnInfo: ColumnInfo = {
columns: info.columns,
columnLayout: info.columnLayout,
};
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel || tabKey}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? "");
if (rowType === "DETAIL") return undefined;
if (rowType === "BIZ_SUM") return "rg-cell-rowtype-biz-sum";
return "rg-cell-rowtype-other";
}}
/>
</TabsContent>
);
})}
{SewageServiceLevelColumnListTot.map((info) => (
<TabsContent key={info.tabKey} value={info.tabKey}>
<DataGrid
columnInfo={{ columns: info.columns, columnLayout: info.columnLayout }}
rowStartNum={info.rowStartNum}
rowData={rowDataMap.get(info.tabKey) ?? []}
excelExportFileSuffix={info.tabLabel}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(info.tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(info.tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
))}
</Tabs>
</div>
</div>
@@ -1,10 +1,229 @@
import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "../../module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// [폴더 정밀 정비 완료] 프로젝트 트리 구조 5개 컬럼 스펙 패키지 호출
import { SewerageReuseTab1ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageReuseTab1Column";
import { SewerageGrayWaterTab2ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column";
import { SewerageRainwaterTab3ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column";
import { SewerageWwtRtlztnRegTab4ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column";
import { SewerageWwtRtlztnSncnTab5ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column";
// 집계표(TOTAL) API 서비스 및 인터페이스 명세 임포트
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
WaterReuseSummaryRequest
} from "@/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface";
import CellColorInfo from "./CellColorInfo";
// 5개 멀티 탭 매핑을 위한 설정 취합 배열
const WaterReuseColumnListTot = [
SewerageReuseTab1ColumnConfig,
SewerageGrayWaterTab2ColumnConfig,
SewerageRainwaterTab3ColumnConfig,
SewerageWwtRtlztnRegTab4ColumnConfig,
SewerageWwtRtlztnSncnTab5ColumnConfig // 👈 5번째 탭 풀장착 완료
];
const firstTabKey = WaterReuseColumnListTot[0]?.tabKey ?? "ST00040007_1";
const WaterReuseTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
// Map 데이터 구조를 사용하여 완벽하게 안전한 멀티 탭 데이터 상태 유지
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = WaterReuseColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
const param: WaterReuseSummaryRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
// [TAB1] 공공하수처리수 재이용 현황 조회
if (tabKey === "ST00100001") {
const response = await statisticsTotalStatusApi.getSewerageReuseTab1List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [TAB2] 중수도 현황 조회
if (tabKey === "ST00100002") {
const response = await statisticsTotalStatusApi.getGrayWaterStatusTab2List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [TAB3] 빗물이용시설현황 조회
if (tabKey === "ST00100003") {
const response = await statisticsTotalStatusApi.getRainwaterStatusTab3List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [TAB4] 하·폐수처리수 재이용시설 시공업 등록현황 조회
if (tabKey === "ST00100004") {
const response = await statisticsTotalStatusApi.getWwtRtlztnRegStatusTab4List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
// [TAB5] 하·폐수처리수 재이용시설 시공업 인가현황 조회
if (tabKey === "ST00100005") {
const response = await statisticsTotalStatusApi.getWwtRtlztnSncnStatusTab5List(param);
if (response && response.status === "success" && response.data) {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
alert(response?.message || "데이터 조회 중 오류가 발생했습니다.");
}
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const values = lastSearchedValuesRef.current;
if (!values) return;
// 중복 API 통신 방지 필터 캐싱 디스패치
const existing = rowDataMap.get(tabKey);
if (existing && existing.length > 0) return;
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<>
<SearchForm />
</>
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" />
{/* 정중앙 5rem 크기 고정 격리형 로딩 애니메이션 */}
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel}
</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
const rowData = rowDataMap.get(tabKey) ?? [];
const gridColumnInfo: ColumnInfo = {
columns: info.columns,
columnLayout: info.columnLayout,
};
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={gridColumnInfo}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
+59 -2
View File
@@ -116,12 +116,22 @@ export type DataFieldValueCallback = (
/** 계산 필드 설정 (computedFieldCallbacks.ts 참고) */
export type ComputedFieldConfig =
| { fields: string[]; type: "sum" | "avg" | "max" | "difference" }
| { fields: string[]; type: "sum" | "avg" | "max" | "difference" | "quotient" }
| {
minuendFields: string[];
subtrahendFields: string[];
type: "differenceOfSums";
}
| {
firstSumFields: string[];
secondSumFields: string[];
type: "halfSumOfSums";
}
| {
firstHalfSum: { firstSumFields: string[]; secondSumFields: string[] };
secondHalfSum: { firstSumFields: string[]; secondSumFields: string[] };
type: "sumOfTwoHalfSumOfSums";
}
| {
baseField: string;
sumFields: string[];
@@ -149,6 +159,38 @@ export type ComputedFieldConfig =
denominatorFields: string[];
thirdFields: string[];
type: "swrgInstlrtRate";
}
| {
pumpGroups: Array<{
pumpCntField: string;
rsrvPumpCntField: string;
perPpqtyField: string;
}>;
type: "pumpGrossPpqty";
}
| {
inflwField: string;
dschgField: string;
type: "bodPrcsRt";
}
| {
/** Σ(numeratorFields) / denominatorField × 10,000 (건/10,000명) */
numeratorFields: string[];
denominatorField: string;
type: "sumPerTenThousand";
}
| {
/** (quotient[0]/quotient[1]) / (denominatorQuotient[0]/denominatorQuotient[1]) × 100 */
numeratorQuotient: [string, string];
denominatorQuotient: [string, string];
type: "quotientRatioPercent";
}
| {
/** (numeratorField / denominatorField) × multiplierField × 100 */
numeratorField: string;
denominatorField: string;
multiplierField: string;
type: "quotientTimesMultiplierPercent";
};
export type ColumnConfig = {
@@ -271,15 +313,30 @@ export type ColumnInfoList = {
};
// 개별 셀 스타일을 동적으로 결정하기 위한 콜백 (예: 검증 오류 셀 강조)
export type CellStyleCallbackGrid = {
getValue?: (itemIndex: number, fieldName: string) => unknown;
};
export type CellStyleCallbackParams = {
rowIndex: number;
fieldName: string;
row: Record<string, string | number>;
/** styleCallback 시점의 그리드 값 조회(행 단위 동적 스타일용) */
grid?: CellStyleCallbackGrid;
};
/** RealGrid styleCallback — 문자열(styleName) 또는 editable 등 동적 스타일 객체 */
export type CellStyleCallbackResult =
| string
| {
styleName?: string;
editable?: boolean;
readOnly?: boolean;
};
export type CellStyleCallback = (
params: CellStyleCallbackParams,
) => string | undefined;
) => CellStyleCallbackResult | undefined;
/** 셀 hover 시 툴팁 문구 (반환 시 해당 문구 표시, 검증 오류 메시지 등) */
export type CellTooltipCallbackParams = {
@@ -30,10 +30,12 @@ export type SearchFormProps = {
onReset?: () => void;
onValuesChange?: (values: SearchFormValues) => void;
defaultValues?: SearchFormValues;
searchButtonText?: string;
isReset?: boolean;
};
export const defaultSearchFormProps: Required<
Omit<SearchFormProps, "onSubmit" | "onReset" | "onValuesChange" | "defaultValues" | "sidoSelectText">
Omit<SearchFormProps, "onSubmit" | "onReset" | "onValuesChange" | "defaultValues" | "sidoSelectText" | "searchButtonText">
> = {
sido: true,
gugun: true,
@@ -44,4 +46,5 @@ export const defaultSearchFormProps: Required<
userMenual: false,
isDetail: false,
isSummary: false,
isReset: true,
};
@@ -8,58 +8,83 @@ import { ValueType } from "realgrid";
export const ExcretaTreatFactInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 기본 정보
{ name: 'ecscs_nm', fieldName: '시설명', header: { text: '시설명' } },
{ name: 'locplcs', fieldName: '소재지', header: { text: '소재지' } },
{ name: 'telno', fieldName: '전화번호', header: { text: '전화번호' } },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' } },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' } },
{ name: 'telNo', fieldName: 'telNo', header: { text: '전화번호' } },
// 날짜 정보
{ name: 'comp_dt', fieldName: '준공일 yyyy-mm-dd', header: { text: '준공일 yyyy-mm-dd' } },
{ name: 'rn_dt', fieldName: '가동개시일 yyyy-mm-dd', header: { text: '가동개시일 yyyy-mm-dd' } },
{
name: 'cmptnStrtYmd',
fieldName: 'cmptnStrtYmd',
header: { text: '준공일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
{
name: 'oprtngStrtYmd',
fieldName: 'oprtngStrtYmd',
header: { text: '가동개시일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
// 사업비/용량 정보
{ name: 'wct', fieldName: '사업비(백만원)', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ecscs_cpcty', fieldName: '시설용량(m³/일)', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'process_cpcty', fieldName: '처리량(m³/일)', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'opr_daycnt', fieldName: '가동일수(일/년)', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'wholPjtcoAmt', fieldName: 'wholPjtcoAmt', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'cpct', fieldName: 'cpct', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 처리 공법 및 연계
{ name: 'trt_msrc', fieldName: '처리공법', header: { text: '처리공법' } },
{ name: 'cntc_prcplt', fieldName: '연계처리장명', header: { text: '연계처리장명' } },
{ name: 'assct_trt_cpc', fieldName: '연계처리량(m³/일)', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리공법' } },
{ name: 'linkTmplntCn', fieldName: 'linkTmplntCn', header: { text: '연계처리장명' } },
{ name: 'linkPrcsQnt', fieldName: 'linkPrcsQnt', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 유입수질
{ name: 'infl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'infl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'infl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'inflwBodWqVl', fieldName: 'inflwBodWqVl', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwTocWqVl', fieldName: 'inflwTocWqVl', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwSsWqVl', fieldName: 'inflwSsWqVl', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwTnWqVl', fieldName: 'inflwTnWqVl', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'inflwTpWqVl', fieldName: 'inflwTpWqVl', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'inflwClfgCnt', fieldName: 'inflwClfgCnt', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 방류수질
{ name: 'effl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'effl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'effl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'dschgBodWqVl', fieldName: 'dschgBodWqVl', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgTocWqVl', fieldName: 'dschgTocWqVl', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgSsWqVl', fieldName: 'dschgSsWqVl', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgTnWqVl', fieldName: 'dschgTnWqVl', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'dschgTpWqVl', fieldName: 'dschgTpWqVl', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'dschgClfgCnt', fieldName: 'dschgClfgCnt', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 방류수역
{ name: 'rvbsn', fieldName: '수계', header: { text: '수계' } },
{ name: 'bstrm', fieldName: '지류', header: { text: '지류' } },
{ name: 'lgbsn', fieldName: '중권역', header: { text: '중권역' } },
{ name: 'mdmbsn', fieldName: '소권역', header: { text: '소권역' } },
{ name: 'wtsysNm', fieldName: 'wtsysNm', header: { text: '수계' } },
{ name: 'trbtrNm', fieldName: 'trbtrNm', header: { text: '지류' } },
{ name: 'msirbCn', fieldName: 'msirbCn', header: { text: '중권역' } },
{ name: 'ssirbCn', fieldName: 'ssirbCn', header: { text: '소권역' } },
// 운영 정보
{ name: 'oper_mth', fieldName: '운영주체(자체/공기업/민간위탁)', header: { text: '운영주체(자체/공기업/민간위탁)' } },
{ name: 'cntc_prcplt_cpc', fieldName: '직원총수(명)', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'safe_acdnt_co', fieldName: '안전사고건수(건/인/년)', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'operMthdCdCn', fieldName: 'operMthdCdCn', header: { text: '운영주체(자체/공기업/민간위탁)' } },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden Columns : 시스템 관리를 위한 값
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설일련번호' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
@@ -67,41 +92,51 @@ export const ExcretaTreatFactInpColumn = {
name: 'adminGroup',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'gugun'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'ecscs_nm',
'locplcs',
'telno',
'comp_dt',
'rn_dt',
'wct',
'ecscs_cpcty',
'process_cpcty',
'opr_daycnt',
'trt_msrc',
'cntc_prcplt',
'assct_trt_cpc',
'fcltNm',
'fcltPstnCn',
'telNo',
'cmptnStrtYmd',
'oprtngStrtYmd',
'wholPjtcoAmt',
'prcsFcltCpct',
'cpct',
'oprtngDayCnt',
'prcsMthdExpln',
'linkTmplntCn',
'linkPrcsQnt',
{
name: 'influenceGroup',
hideChildHeaders: false,
header: { text: '유입수질(mg/L, 개/mL)' },
items: ['infl_wqlt_bod', 'infl_wqlt_toc', 'infl_wqlt_ss', 'infl_wqlt_tn', 'infl_wqlt_tp', 'infl_wqlt_clfgrpcnt'],
items: ['inflwBodWqVl', 'inflwTocWqVl', 'inflwSsWqVl', 'inflwTnWqVl', 'inflwTpWqVl', 'inflwClfgCnt'],
},
{
name: 'effluentGroup',
hideChildHeaders: false,
header: { text: '방류수질(mg/L, 개/mL)' },
items: ['effl_wqlt_bod', 'effl_wqlt_toc', 'effl_wqlt_ss', 'effl_wqlt_tn', 'effl_wqlt_tp', 'effl_wqlt_clfgrpcnt'],
items: ['dschgBodWqVl', 'dschgTocWqVl', 'dschgSsWqVl', 'dschgTnWqVl', 'dschgTpWqVl', 'dschgClfgCnt'],
},
{
name: 'dischargeGroup',
hideChildHeaders: false,
header: { text: '방류수역' },
items: ['rvbsn', 'bstrm', 'lgbsn', 'mdmbsn'],
items: ['wtsysNm', 'trbtrNm', 'msirbCn', 'ssirbCn'],
},
'oper_mth',
'cntc_prcplt_cpc',
'safe_acdnt_co',
'rmk',
]
'operMthdCdCn',
'wholNope',
'sftyAcdntEvntcsCnt',
'rmrk',
'admdstCd',
'fcltSn',
'admdstNm'
],
rowStartNum: 2,
fixedColumns: 2,
tabKey: 'ST00070001',
};
@@ -8,7 +8,7 @@ import { footerDecimal1, footerDecimal3,footerInt } from "@/components/util/grid
export const PublicSewageFactColumn = {
columns: [
// 행정구역
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, footer: { text: '합계' }, styleName: 'rg-cell-readonly' },
// 시설명
@@ -18,7 +18,20 @@ export const PublicSewageFactColumn = {
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' }, editable: false, styleName: 'rg-cell-readonly' },
// 시설용량
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{
name: 'prcsFcltCpct',
fieldName: 'prcsFcltCpct',
header: { text: '시설용량(500m³/일 이상/미만)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['prcsFcltPhysCpct', 'prcsFcltBoilCpct', 'prcsFcltAltdCpct'],
type: 'sum' as const,
},
},
// (m³/일)
{ name: 'prcsFcltPhysCpct', fieldName: 'prcsFcltPhysCpct', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
@@ -56,8 +69,22 @@ export const PublicSewageFactColumn = {
{ name: 'dschgGrssClfgVl', fieldName: 'dschgGrssClfgVl', header: { text: '방류총대장균군수' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 처리특성
{ name: 'prcsRt', fieldName: 'prcsRt', header: { text: '처리효율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{ name: 'prcsLodQnt', fieldName: 'prcsLodQnt', header: { text: '처리부하량(kgBOD/D)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{
name: 'prcsRt',
fieldName: 'prcsRt',
header: { text: '처리효율(%)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
inflwField: 'inflwBodVl',
dschgField: 'dschgBodVl',
type: 'bodPrcsRt' as const,
},
},
{ name: 'prcsLodQnt', fieldName: 'prcsLodQnt', header: { text: '처리부하량(kgBOD/D)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
{ name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리방법' }, editable: false, styleName: 'rg-cell-readonly' },
// 연계처리량
@@ -86,7 +113,20 @@ export const PublicSewageFactColumn = {
{ name: 'goalWqNo', fieldName: 'goalWqNo', header: { text: '목표수질(BOD)' }, editable: false, styleName: 'rg-cell-readonly' },
// 에너지 자립률
{ name: 'engySrlneRt', fieldName: 'engySrlneRt', header: { text: '에너지자립률(%)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
{
name: 'engySrlneRt',
fieldName: 'engySrlneRt',
header: { text: '에너지자립률(%)' },
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
numeratorFields: ['engyItslfPrdctnQnt'],
denominatorFields: ['anlEngyWholUseQnt'],
type: 'pblswrgPopltnSuprtRate' as const,
},
},
// 에너지 자체 생산량
{ name: 'engyItslfPrdctnQnt', fieldName: 'engyItslfPrdctnQnt', header: { text: '에너지자체생산량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
@@ -97,7 +137,7 @@ export const PublicSewageFactColumn = {
{ name: 'itslfEngyUseQnt', fieldName: 'itslfEngyUseQnt', header: { text: '자체 에너지사용량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 하수처리량당 CO2배출량
{ name: 'swtrmtEmsnQnt', fieldName: 'swtrmtEmsnQnt', header: { text: '하수처리량당 CO2배출량(kgCO2/m³)' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000', styleName: 'rg-cell-total' },
{ name: 'swtrmtEmsnQnt', fieldName: 'swtrmtEmsnQnt', header: { text: '하수처리량당 CO2배출량(kgCO2/m³)' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000', styleName: 'rg-cell-readonly' },
// 운영주체
{ name: 'operMnbdCn', fieldName: 'operMnbdCn', header: { text: '운영주체(자체/공기업/민간대행)' }, editable: false, styleName: 'rg-cell-readonly' },
@@ -123,7 +163,10 @@ export const PublicSewageFactColumn = {
name: 'administrative',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['ctpvNm', 'sggNm'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'swtrmtFcltNm',
'fcltPstnCn',
@@ -1,6 +1,33 @@
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
/** 펌프장 구분(`pumpSeNm`) */
export const PUMPING_STATION_PUMP_SE_VALUES = ["합류식", "분류식"] as const;
function dropdownColumn(
col: Pick<ColumnConfig, "name" | "fieldName" | "header"> &
Partial<ColumnConfig>,
options: readonly string[],
): ColumnConfig {
const values = [...options];
return {
...col,
values,
labels: values,
lookupDisplay: true,
editor: {
type: "dropdown",
domainOnly: true,
textReadOnly: true,
commitOnSelect: true,
dropDownWhenClick: true,
values,
labels: values,
},
};
}
/**
* 하수도 통계 관리 > 05. 펌프장
* 단일 Tab
@@ -8,69 +35,155 @@ import { ValueType } from "realgrid";
export const PumpingStationInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 기본 정보
{ name: 'treatmentFacilityName', fieldName: '처리장명', header: { text: '처리장명' } },
{ name: 'facilityName', fieldName: '시설명', header: { text: '시설명' } },
{ name: 'classification', fieldName: '구분(합류식 분류식)', header: { text: '구분(합류식 분류식)' } },
{ name: 'location', fieldName: '위치', header: { text: '위치' } },
{ name: 'operationDate', fieldName: '가동 년월일 yyyy-mm-dd', header: { text: '가동 년월일 yyyy-mm-dd' } },
{ name: 'planDrainageArea', fieldName: '계획배수면적(㎢)', header: { text: '계획배수면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '처리장명' } },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' } },
dropdownColumn(
{ name: 'pumpSeNm', fieldName: 'pumpSeNm', header: { text: '구분(합류식 분류식)' } },
PUMPING_STATION_PUMP_SE_VALUES,
),
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '위치' } },
{
name: 'oprtngStrtYmd',
fieldName: 'oprtngStrtYmd',
header: { text: '가동 년월일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
{ name: 'planMltvArea', fieldName: 'planMltvArea', header: { text: '계획배수면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
// 계획시간 대 최대배수량
{ name: 'fairWeatherFlow', fieldName: '청천시(A)', header: { text: '청천시(A)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainyWeatherFlow', fieldName: '우천시(B)', header: { text: '우천시(B)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainwaterFlow', fieldName: '우수량(B-A)', header: { text: '우수량(B-A)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sndyArea', fieldName: 'sndyArea', header: { text: '청천시(A)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainArea', fieldName: 'rainArea', header: { text: '우천시(B)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'exlnQnt',
fieldName: 'exlnQnt',
header: { text: '우수량(B-A)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: { fields: ['rainArea', 'sndyArea'], type: 'difference' as const },
styleName: 'rg-cell-total',
},
// 배수능력 > 총 양수량 (오수)
{ name: 'sewageTotalPumpCapacity', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'drtwPumpGrssPpqty',
fieldName: 'drtwPumpGrssPpqty',
header: { text: '총양수량(m³/min)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'drtwPumpN1Cnt',
rsrvPumpCntField: 'drtwPumpN1RsrvCnt',
perPpqtyField: 'drtwPumpN1Ppqty',
},
{
pumpCntField: 'drtwPumpN2Cnt',
rsrvPumpCntField: 'drtwPumpN2RsrvCnt',
perPpqtyField: 'drtwPumpN2Ppqty',
},
{
pumpCntField: 'drtwPumpN3Cnt',
rsrvPumpCntField: 'drtwPumpN3RsrvCnt',
perPpqtyField: 'drtwPumpN3Ppqty',
},
],
},
},
// 배수능력 > 오수펌프(1)
{ name: 'sewagePump1Diameter', fieldName: '오수펌프(1) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump1Capacity', fieldName: '오수펌프(1) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sewagePump1Count', fieldName: '오수펌프(1) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump1Reserve', fieldName: '오수펌프(1) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN1DmtrArea', fieldName: 'drtwPumpN1DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN1Ppqty', fieldName: 'drtwPumpN1Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'drtwPumpN1Cnt', fieldName: 'drtwPumpN1Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN1RsrvCnt', fieldName: 'drtwPumpN1RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 오수펌프(2)
{ name: 'sewagePump2Diameter', fieldName: '오수펌프(2) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump2Capacity', fieldName: '오수펌프(2) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sewagePump2Count', fieldName: '오수펌프(2) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump2Reserve', fieldName: '오수펌프(2) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN2DmtrArea', fieldName: 'drtwPumpN2DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN2Ppqty', fieldName: 'drtwPumpN2Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'drtwPumpN2Cnt', fieldName: 'drtwPumpN2Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN2RsrvCnt', fieldName: 'drtwPumpN2RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 오수펌프(3)
{ name: 'sewagePump3Diameter', fieldName: '오수펌프(3) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump3Capacity', fieldName: '오수펌프(3) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sewagePump3Count', fieldName: '오수펌프(3) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewagePump3Reserve', fieldName: '오수펌프(3) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN3DmtrArea', fieldName: 'drtwPumpN3DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN3Ppqty', fieldName: 'drtwPumpN3Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'drtwPumpN3Cnt', fieldName: 'drtwPumpN3Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPumpN3RsrvCnt', fieldName: 'drtwPumpN3RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 총 양수량 (우수)
{ name: 'rainwaterTotalPumpCapacity', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'exlnPumpGrssPpqty',
fieldName: 'exlnPumpGrssPpqty',
header: { text: '총양수량(m³/min)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'exlnPumpN1Cnt',
rsrvPumpCntField: 'exlnPumpN1RsrvCnt',
perPpqtyField: 'exlnPumpN1Ppqty',
},
{
pumpCntField: 'exlnPumpN2Cnt',
rsrvPumpCntField: 'exlnPumpN2RsrvCnt',
perPpqtyField: 'exlnPumpN2Ppqty',
},
{
pumpCntField: 'exlnPumpN3Cnt',
rsrvPumpCntField: 'exlnPumpN3RsrvCnt',
perPpqtyField: 'exlnPumpN3Ppqty',
},
],
},
},
// 배수능력 > 우수펌프(1)
{ name: 'rainwaterPump1Diameter', fieldName: '우수펌프(1) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump1Capacity', fieldName: '우수펌프(1) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainwaterPump1Count', fieldName: '우수펌프(1) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump1Reserve', fieldName: '우수펌프(1) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN1DmtrArea', fieldName: 'exlnPumpN1DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN1Ppqty', fieldName: 'exlnPumpN1Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'exlnPumpN1Cnt', fieldName: 'exlnPumpN1Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN1RsrvCnt', fieldName: 'exlnPumpN1RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 우수펌프(2)
{ name: 'rainwaterPump2Diameter', fieldName: '우수펌프(2) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump2Capacity', fieldName: '우수펌프(2) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainwaterPump2Count', fieldName: '우수펌프(2) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump2Reserve', fieldName: '우수펌프(2) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN2DmtrArea', fieldName: 'exlnPumpN2DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN2Ppqty', fieldName: 'exlnPumpN2Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'exlnPumpN2Cnt', fieldName: 'exlnPumpN2Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN2RsrvCnt', fieldName: 'exlnPumpN2RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 우수펌프(3)
{ name: 'rainwaterPump3Diameter', fieldName: '우수펌프(3) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump3Capacity', fieldName: '우수펌프(3) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainwaterPump3Count', fieldName: '우수펌프(3) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rainwaterPump3Reserve', fieldName: '우수펌프(3) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN3DmtrArea', fieldName: 'exlnPumpN3DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN3Ppqty', fieldName: 'exlnPumpN3Ppqty', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'exlnPumpN3Cnt', fieldName: 'exlnPumpN3Cnt', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'exlnPumpN3RsrvCnt', fieldName: 'exlnPumpN3RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 연간전력 사용량
{ name: 'annualPowerUsage', fieldName: '연간전력사용량(kWh/년)', header: { text: '연간전력사용량(kWh/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'anlElpwrUseQnt', fieldName: 'anlElpwrUseQnt', header: { text: '연간전력사용량(kWh/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'remarks', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden Columns : 시스템 관리를 위한 값
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설일련번호' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
@@ -78,66 +191,75 @@ export const PumpingStationInpColumn = {
name: 'administrativeArea',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'sgg'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'treatmentFacilityName',
'facilityName',
'classification',
'location',
'operationDate',
'planDrainageArea',
'swtrmtFcltNm',
'fcltNm',
'pumpSeNm',
'fcltPstnCn',
'oprtngStrtYmd',
'planMltvArea',
{
name: 'planFlowGroup',
hideChildHeaders: false,
header: { text: '계획시간대최대배수량(m³/hr)' },
items: ['fairWeatherFlow', 'rainyWeatherFlow', 'rainwaterFlow'],
items: ['sndyArea', 'rainArea', 'exlnQnt'],
},
{
name: 'drainageCapability',
hideChildHeaders: false,
header: { text: '배수능력' },
items: [
'sewageTotalPumpCapacity',
'drtwPumpGrssPpqty',
{
name: 'sewagePump1Group',
hideChildHeaders: false,
header: { text: '오수펌프(1)' },
items: ['sewagePump1Diameter', 'sewagePump1Capacity', 'sewagePump1Count', 'sewagePump1Reserve'],
items: ['drtwPumpN1DmtrArea', 'drtwPumpN1Ppqty', 'drtwPumpN1Cnt', 'drtwPumpN1RsrvCnt'],
},
{
name: 'sewagePump2Group',
hideChildHeaders: false,
header: { text: '오수펌프(2)' },
items: ['sewagePump2Diameter', 'sewagePump2Capacity', 'sewagePump2Count', 'sewagePump2Reserve'],
items: ['drtwPumpN2DmtrArea', 'drtwPumpN2Ppqty', 'drtwPumpN2Cnt', 'drtwPumpN2RsrvCnt'],
},
{
name: 'sewagePump3Group',
hideChildHeaders: false,
header: { text: '오수펌프(3)' },
items: ['sewagePump3Diameter', 'sewagePump3Capacity', 'sewagePump3Count', 'sewagePump3Reserve'],
items: ['drtwPumpN3DmtrArea', 'drtwPumpN3Ppqty', 'drtwPumpN3Cnt', 'drtwPumpN3RsrvCnt'],
},
'rainwaterTotalPumpCapacity',
'exlnPumpGrssPpqty',
{
name: 'rainwaterPump1Group',
hideChildHeaders: false,
header: { text: '우수펌프(1)' },
items: ['rainwaterPump1Diameter', 'rainwaterPump1Capacity', 'rainwaterPump1Count', 'rainwaterPump1Reserve'],
items: ['exlnPumpN1DmtrArea', 'exlnPumpN1Ppqty', 'exlnPumpN1Cnt', 'exlnPumpN1RsrvCnt'],
},
{
name: 'rainwaterPump2Group',
hideChildHeaders: false,
header: { text: '우수펌프(2)' },
items: ['rainwaterPump2Diameter', 'rainwaterPump2Capacity', 'rainwaterPump2Count', 'rainwaterPump2Reserve'],
items: ['exlnPumpN2DmtrArea', 'exlnPumpN2Ppqty', 'exlnPumpN2Cnt', 'exlnPumpN2RsrvCnt'],
},
{
name: 'rainwaterPump3Group',
hideChildHeaders: false,
header: { text: '우수펌프(3)' },
items: ['rainwaterPump3Diameter', 'rainwaterPump3Capacity', 'rainwaterPump3Count', 'rainwaterPump3Reserve'],
items: ['exlnPumpN3DmtrArea', 'exlnPumpN3Ppqty', 'exlnPumpN3Cnt', 'exlnPumpN3RsrvCnt'],
},
],
},
'annualPowerUsage',
'remarks',
]
'anlElpwrUseQnt',
'rmrk',
'admdstCd',
'fcltSn',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 3,
tabKey: 'ST00050001',
};
@@ -5,66 +5,64 @@ import { ValueType } from "realgrid";
export const PrivateSewageDesigStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 개인하수도관리지역공고현황
{ name: 'MGMT_AR', fieldName: '공고지역(개소)', header: { text: '공고지역(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'APPN_SCOPE', fieldName: '지정범위(㎢)', header: { text: '지정범위(㎢)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'indvSwagePbancRgnCnt', fieldName: 'indvSwagePbancRgnCnt', header: { text: '공고지역(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'indvSwageDsgnScpLen', fieldName: 'indvSwageDsgnScpLen', header: { text: '지정범위(㎢)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 개인하수도관리지역공고현황 > 정화조
{ name: 'STTY_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_CPC', fieldName: '처리용량(인/일)', header: { text: '처리용량(인/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'spctnkGrssFcltCnt', fieldName: 'spctnkGrssFcltCnt', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'spctnkQnt', fieldName: 'spctnkQnt', header: { text: '처리용량(인/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 개인하수도관리지역공고현황 > 오수시설
{ name: 'WRRTN_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'WRRTN_CPC', fieldName: '처리용량(m³/일)', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'drtwPrcsFcltGrssFcltCnt', fieldName: 'drtwPrcsFcltGrssFcltCnt', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPrcsFcltQnt', fieldName: 'drtwPrcsFcltQnt', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// 검증 필드 (hidden)
{ name: 'CHK_MGMT_AR', fieldName: 'CHK_MGMT_AR', header: { text: '' } },
{ name: 'CHK_APPN_SCOPE', fieldName: 'CHK_APPN_SCOPE', header: { text: '' } },
{ name: 'CHK_STTY_CNT', fieldName: 'CHK_STTY_CNT', header: { text: '' } },
{ name: 'CHK_STTY_CPC', fieldName: 'CHK_STTY_CPC', header: { text: '' } },
{ name: 'CHK_WRRTN_CNT', fieldName: 'CHK_WRRTN_CNT', header: { text: '' } },
{ name: 'CHK_WRRTN_CPC', fieldName: 'CHK_WRRTN_CPC', header: { text: '' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_notice',
header: { text: '개인하수도관리지역공고현황 (하수도법제34조의2)' },
items: [
'MGMT_AR',
'APPN_SCOPE',
'indvSwagePbancRgnCnt',
'indvSwageDsgnScpLen',
{
name: 'group_septic',
header: { text: '정화조' },
items: [
'STTY_CNT',
'STTY_CPC'
'spctnkGrssFcltCnt',
'spctnkQnt'
]
},
{
name: 'group_sewage',
header: { text: '오수시설' },
items: [
'WRRTN_CNT',
'WRRTN_CPC'
'drtwPrcsFcltGrssFcltCnt',
'drtwPrcsFcltQnt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
@@ -5,182 +5,316 @@ import { ValueType } from "realgrid";
export const SepticTankStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 총 시설 수
{ name: 'TOT_FCLTY_CNT', fieldName: '총 시설 수 (A+D)', header: { text: '총 시설 수 (A+D)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 총 시설 수 — (B+C)/2 + (E+F)/2 = A + D
{
name: 'grssFcltCnt',
fieldName: 'grssFcltCnt',
header: { text: '총 시설 수 (A+D)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstHalfSum: {
firstSumFields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
secondSumFields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
},
secondHalfSum: {
firstSumFields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
secondSumFields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
},
type: 'sumOfTwoHalfSumOfSums' as const,
},
},
// 하수처리구역 내 > 총계
{ name: 'SPZI_SM', fieldName: '총계 A=(B+C)/2', header: { text: '총계 A=(B+C)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdWhol',
fieldName: 'staaInsdWhol',
header: { text: '총계 A=(B+C)/2' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstSumFields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
secondSumFields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
type: 'halfSumOfSums' as const,
},
},
// 하수처리구역 내 > 처리방식별
{ name: 'SPZI_MTHD_SM', fieldName: '계(B)', header: { text: '계(B)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdPrcsMthByWhol',
fieldName: 'staaInsdPrcsMthByWhol',
header: { text: '계(B)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
type: 'sum' as const,
},
},
// 하수처리구역 내 > 처리방식별 > 부패탱크
{ name: 'SPZI_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdSttkNprsflCnt', fieldName: 'staaInsdSttkNprsflCnt', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step1StaaInsdSttkFrcCnt', fieldName: 'step1StaaInsdSttkFrcCnt', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 내 > 처리방식별
{ name: 'SPZI_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdSprktpFltrtbCnt', fieldName: 'staaInsdSprktpFltrtbCnt', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdAertMthdCnt', fieldName: 'staaInsdAertMthdCnt', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdCntcAertCnt', fieldName: 'staaInsdCntcAertCnt', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdEtcCnt', fieldName: 'staaInsdEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 내 > 처리용량별
{ name: 'SPZI_PROC_CPC_SM', fieldName: '계(C)', header: { text: '계(C)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdPrcsCpctByWhol',
fieldName: 'staaInsdPrcsCpctByWhol',
header: { text: '계(C)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaInsdNope', fieldName: 'step1StaaInsdNope', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaInsdNope', fieldName: 'step2StaaInsdNope', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaInsdNope', fieldName: 'step3StaaInsdNope', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaInsdNope', fieldName: 'step4StaaInsdNope', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaInsdNope', fieldName: 'step5StaaInsdNope', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaInsdNope', fieldName: 'step6StaaInsdNope', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaInsdNope', fieldName: 'step7StaaInsdNope', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step8StaaInsdNope', fieldName: 'step8StaaInsdNope', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 총계
{ name: 'SPZO_SM', fieldName: '총계 D=(E+F)/2', header: { text: '총계 D=(E+F)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpWhol',
fieldName: 'staaExcpWhol',
header: { text: '총계 D=(E+F)/2' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstSumFields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
secondSumFields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
type: 'halfSumOfSums' as const,
},
},
// 하수처리구역 외 > 처리방식별
{ name: 'SPZO_MTHD_SM', fieldName: '계(E)', header: { text: '계(E)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpPrcsMthByWhol',
fieldName: 'staaExcpPrcsMthByWhol',
header: { text: '계(E)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
type: 'sum' as const,
},
},
// 하수처리구역 외 > 처리방식별 > 부패탱크
{ name: 'SPZO_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpSttkNprsflCnt', fieldName: 'staaExcpSttkNprsflCnt', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step1StaaExcpSttkFrcCnt', fieldName: 'step1StaaExcpSttkFrcCnt', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 처리방식별
{ name: 'SPZO_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpSprktpFltrtbCnt', fieldName: 'staaExcpSprktpFltrtbCnt', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpAertMthdCnt', fieldName: 'staaExcpAertMthdCnt', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpCntcAertCnt', fieldName: 'staaExcpCntcAertCnt', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpEtcCnt', fieldName: 'staaExcpEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 처리용량별
{ name: 'SPZO_PROC_CPC_SM', fieldName: '계(F)', header: { text: '계(F)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpPrcsCpctByWhol',
fieldName: 'staaExcpPrcsCpctByWhol',
header: { text: '계(F)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaExcpNope', fieldName: 'step1StaaExcpNope', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaExcpNope', fieldName: 'step2StaaExcpNope', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaExcpNope', fieldName: 'step3StaaExcpNope', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaExcpNope', fieldName: 'step4StaaExcpNope', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaExcpNope', fieldName: 'step5StaaExcpNope', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaExcpNope', fieldName: 'step6StaaExcpNope', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaExcpNope', fieldName: 'step7StaaExcpNope', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step8StaaExcpNope', fieldName: 'step8StaaExcpNope', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// 검증 필드 (검증 여부 표시용 - 빨간색 표시)
{ name: 'CHK_TOT_FCLTY_CNT', fieldName: 'CHK_TOT_FCLTY_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_SM', fieldName: 'CHK_SPZI_SM', header: { text: '' } },
{ name: 'CHK_SPZI_MTHD_SM', fieldName: 'CHK_SPZI_MTHD_SM', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_NPFL_CNT', fieldName: 'CHK_SPZI_ROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_TANK_SM', fieldName: 'CHK_SPZI_ROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_NPFL_CNT', fieldName: 'CHK_SPZI_IMHF_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_TANK_SM', fieldName: 'CHK_SPZI_IMHF_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_NPFL_CNT', fieldName: 'CHK_SPZI_WTROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_TANK_SM', fieldName: 'CHK_SPZI_WTROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_TFM', fieldName: 'CHK_SPZI_TFM', header: { text: '' } },
{ name: 'CHK_SPZI_ARTM', fieldName: 'CHK_SPZI_ARTM', header: { text: '' } },
{ name: 'CHK_SPZI_CTARM', fieldName: 'CHK_SPZI_CTARM', header: { text: '' } },
{ name: 'CHK_SPZI_NPFL', fieldName: 'CHK_SPZI_NPFL', header: { text: '' } },
{ name: 'CHK_SPZI_ENFCT_PUMP', fieldName: 'CHK_SPZI_ENFCT_PUMP', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_SM', fieldName: 'CHK_SPZI_PROC_CPC_SM', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_1', fieldName: 'CHK_SPZI_PROC_CPC_1', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_2', fieldName: 'CHK_SPZI_PROC_CPC_2', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_3', fieldName: 'CHK_SPZI_PROC_CPC_3', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_4', fieldName: 'CHK_SPZI_PROC_CPC_4', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_5', fieldName: 'CHK_SPZI_PROC_CPC_5', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_6', fieldName: 'CHK_SPZI_PROC_CPC_6', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_7', fieldName: 'CHK_SPZI_PROC_CPC_7', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_8', fieldName: 'CHK_SPZI_PROC_CPC_8', header: { text: '' } },
{ name: 'CHK_SPZO_SM', fieldName: 'CHK_SPZO_SM', header: { text: '' } },
{ name: 'CHK_SPZO_MTHD_SM', fieldName: 'CHK_SPZO_MTHD_SM', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_NPFL_CNT', fieldName: 'CHK_SPZO_ROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_TANK_SM', fieldName: 'CHK_SPZO_ROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_NPFL_CNT', fieldName: 'CHK_SPZO_IMHF_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_TANK_SM', fieldName: 'CHK_SPZO_IMHF_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_NPFL_CNT', fieldName: 'CHK_SPZO_WTROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_TANK_SM', fieldName: 'CHK_SPZO_WTROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_TFM', fieldName: 'CHK_SPZO_TFM', header: { text: '' } },
{ name: 'CHK_SPZO_ARTM', fieldName: 'CHK_SPZO_ARTM', header: { text: '' } },
{ name: 'CHK_SPZO_CTARM', fieldName: 'CHK_SPZO_CTARM', header: { text: '' } },
{ name: 'CHK_SPZO_NPFL', fieldName: 'CHK_SPZO_NPFL', header: { text: '' } },
{ name: 'CHK_SPZO_ENFCT_PUMP', fieldName: 'CHK_SPZO_ENFCT_PUMP', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_SM', fieldName: 'CHK_SPZO_PROC_CPC_SM', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_1', fieldName: 'CHK_SPZO_PROC_CPC_1', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_2', fieldName: 'CHK_SPZO_PROC_CPC_2', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_3', fieldName: 'CHK_SPZO_PROC_CPC_3', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_4', fieldName: 'CHK_SPZO_PROC_CPC_4', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_5', fieldName: 'CHK_SPZO_PROC_CPC_5', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_6', fieldName: 'CHK_SPZO_PROC_CPC_6', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_7', fieldName: 'CHK_SPZO_PROC_CPC_7', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_8', fieldName: 'CHK_SPZO_PROC_CPC_8', header: { text: '' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'TOT_FCLTY_CNT',
'grssFcltCnt',
{
name: 'group_spzi',
header: { text: '하수처리구역 내' },
items: [
'SPZI_SM',
'staaInsdWhol',
{
name: 'group_spzi_mthd',
header: { text: '처리방식별' },
items: [
'SPZI_MTHD_SM',
'staaInsdPrcsMthByWhol',
{
name: 'group_spzi_rot',
header: { text: '부패탱크' },
items: [
'SPZI_ROT_NPFL_CNT',
'SPZI_ROT_ENFCT_PUMP_1'
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt'
]
},
'SPZI_WTROT_NPFL_CNT',
'SPZI_TFM',
'SPZI_ARTM',
'SPZI_CTARM'
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt'
]
},
{
name: 'group_spzi_proc',
header: { text: '처리용량별' },
items: [
'SPZI_PROC_CPC_SM',
'SPZI_PROC_CPC_1',
'SPZI_PROC_CPC_2',
'SPZI_PROC_CPC_3',
'SPZI_PROC_CPC_4',
'SPZI_PROC_CPC_5',
'SPZI_PROC_CPC_6',
'SPZI_PROC_CPC_7',
'SPZI_PROC_CPC_8'
'staaInsdPrcsCpctByWhol',
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope'
]
}
]
@@ -189,47 +323,49 @@ export const SepticTankStatusInpColumn = {
name: 'group_spzo',
header: { text: '하수처리구역 외' },
items: [
'SPZO_SM',
'staaExcpWhol',
{
name: 'group_spzo_mthd',
header: { text: '처리방식별' },
items: [
'SPZO_MTHD_SM',
'staaExcpPrcsMthByWhol',
{
name: 'group_spzo_rot',
header: { text: '부패탱크' },
items: [
'SPZO_ROT_NPFL_CNT',
'SPZO_ROT_ENFCT_PUMP_1'
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt'
]
},
'SPZO_WTROT_NPFL_CNT',
'SPZO_TFM',
'SPZO_ARTM',
'SPZO_CTARM'
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt'
]
},
{
name: 'group_spzo_proc',
header: { text: '처리용량별' },
items: [
'SPZO_PROC_CPC_SM',
'SPZO_PROC_CPC_1',
'SPZO_PROC_CPC_2',
'SPZO_PROC_CPC_3',
'SPZO_PROC_CPC_4',
'SPZO_PROC_CPC_5',
'SPZO_PROC_CPC_6',
'SPZO_PROC_CPC_7',
'SPZO_PROC_CPC_8'
'staaExcpPrcsCpctByWhol',
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 4,
fixedColumns: 1,
tabKey: 'septicTankStatusInp',
tabKey: 'ST00080002',
tabLabel: '정화조 현황'
};
@@ -5,65 +5,119 @@ import { ValueType } from "realgrid";
export const SewageTreatFactStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 총계
{ name: 'STTY_SM', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'wholDrtwPrcsFcltCnt',
fieldName: 'wholDrtwPrcsFcltCnt',
header: { text: '총계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt',
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt',
],
type: 'sum' as const,
},
},
// 오수처리시설 > 하수처리구역 내(m³/일)
{ name: 'STTY_ZONE_INNER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'wholStaaCnt',
fieldName: 'wholStaaCnt',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaPrcsQnt', fieldName: 'step1StaaPrcsQnt', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaPrcsQnt', fieldName: 'step2StaaPrcsQnt', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaPrcsQnt', fieldName: 'step3StaaPrcsQnt', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaPrcsQnt', fieldName: 'step4StaaPrcsQnt', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaPrcsQnt', fieldName: 'step5StaaPrcsQnt', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaPrcsQnt', fieldName: 'step6StaaPrcsQnt', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaPrcsQnt', fieldName: 'step7StaaPrcsQnt', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 오수처리시설 > 하수처리구역 외(m³/일)
{ name: 'STTY_ZONE_OUTER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpWhol',
fieldName: 'staaExcpWhol',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaExcpPrcsQnt', fieldName: 'step1StaaExcpPrcsQnt', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaExcpPrcsQnt', fieldName: 'step2StaaExcpPrcsQnt', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaExcpPrcsQnt', fieldName: 'step3StaaExcpPrcsQnt', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaExcpPrcsQnt', fieldName: 'step4StaaExcpPrcsQnt', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaExcpPrcsQnt', fieldName: 'step5StaaExcpPrcsQnt', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaExcpPrcsQnt', fieldName: 'step6StaaExcpPrcsQnt', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaExcpPrcsQnt', fieldName: 'step7StaaExcpPrcsQnt', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// 검증 필드 (검증 여부 표시용, 빨간색 표시)
{ name: 'CHK_STTY_SM', fieldName: 'CHK_STTY_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_SM', fieldName: 'CHK_STTY_ZONE_INNER_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_2', fieldName: 'CHK_STTY_ZONE_INNER_2', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_3', fieldName: 'CHK_STTY_ZONE_INNER_3', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_11', fieldName: 'CHK_STTY_ZONE_INNER_11', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_21', fieldName: 'CHK_STTY_ZONE_INNER_21', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_51', fieldName: 'CHK_STTY_ZONE_INNER_51', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_101', fieldName: 'CHK_STTY_ZONE_INNER_101', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_501', fieldName: 'CHK_STTY_ZONE_INNER_501', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_SM', fieldName: 'CHK_STTY_ZONE_OUTER_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_2', fieldName: 'CHK_STTY_ZONE_OUTER_2', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_3', fieldName: 'CHK_STTY_ZONE_OUTER_3', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_11', fieldName: 'CHK_STTY_ZONE_OUTER_11', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_21', fieldName: 'CHK_STTY_ZONE_OUTER_21', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_51', fieldName: 'CHK_STTY_ZONE_OUTER_51', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_101', fieldName: 'CHK_STTY_ZONE_OUTER_101', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_501', fieldName: 'CHK_STTY_ZONE_OUTER_501', header: { text: '' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'STTY_SM',
'wholDrtwPrcsFcltCnt',
{
name: 'group_stty',
header: { text: '오수처리시설' },
@@ -72,36 +126,38 @@ export const SewageTreatFactStatusInpColumn = {
name: 'group_inner',
header: { text: '하수처리구역 내(m³/일)' },
items: [
'STTY_ZONE_INNER_SM',
'STTY_ZONE_INNER_2',
'STTY_ZONE_INNER_3',
'STTY_ZONE_INNER_11',
'STTY_ZONE_INNER_21',
'STTY_ZONE_INNER_51',
'STTY_ZONE_INNER_101',
'STTY_ZONE_INNER_501'
'wholStaaCnt',
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt'
]
},
{
name: 'group_outer',
header: { text: '하수처리구역 외(m³/일)' },
items: [
'STTY_ZONE_OUTER_SM',
'STTY_ZONE_OUTER_2',
'STTY_ZONE_OUTER_3',
'STTY_ZONE_OUTER_11',
'STTY_ZONE_OUTER_21',
'STTY_ZONE_OUTER_51',
'STTY_ZONE_OUTER_101',
'STTY_ZONE_OUTER_501'
'staaExcpWhol',
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewageTreatFactStatusInp',
tabKey: 'ST00080001',
tabLabel: '처리용량별 오수처리시설 현황'
};
@@ -5,74 +5,75 @@ import { ValueType } from "realgrid";
export const ExcretaRelatedIndustryInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 행정코드 (hidden)
{ name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false },
{ name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false },
{ name: 'ADST_DONG', fieldName: '행정동', header: { text: '행정동' }, visible: false },
{ name: 'RGN_NM', fieldName: '지역명', header: { text: '지역명' }, visible: false },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 업체수(개소)
{ name: 'ECSCS_SM', fieldName: '업체수(개소)', header: { text: '업체수(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'bzentyCnt', fieldName: 'bzentyCnt', header: { text: '업체수(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 종사인원(명)
{ name: 'ECSCS_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'empNope', fieldName: 'empNope', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 시설(차량) 확보현황(대수)
{ name: 'VHCLE_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'THREE_BELOW', fieldName: '3톤이하', header: { text: '3톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'FOUR_BELOW', fieldName: '4.5톤이하', header: { text: '4.5톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'EIGHT_BELOW', fieldName: '8톤이하', header: { text: '8톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'fcltSecrPrstCnt',
fieldName: 'fcltSecrPrstCnt',
header: { text: '계' },
editable: false, footer:
footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: ['n3TonUdrCnt', 'n4TonUdrCnt', 'n8TonUdrCnt', 'etcCnt'],
type: 'sum' as const,
},
},
{ name: 'n3TonUdrCnt', fieldName: 'n3TonUdrCnt', header: { text: '3톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n4TonUdrCnt', fieldName: 'n4TonUdrCnt', header: { text: '4.5톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n8TonUdrCnt', fieldName: 'n8TonUdrCnt', header: { text: '8톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'etcCnt', fieldName: 'etcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 수집·운반 실적(m³/년)
{ name: 'COLCT_TRSP_ACMSLT', fieldName: '수집·운반 실적(m³/년)', header: { text: '수집·운반 실적(m³/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'clctCnvncPrfmncQnt', fieldName: 'clctCnvncPrfmncQnt', header: { text: '수집·운반 실적(m³/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견 필드
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리
{ name: 'CHK_ECSCS_SM', fieldName: 'CHK_ECSCS_SM', header: { text: 'CHK_ECSCS_SM' }, visible: false },
{ name: 'CHK_ECSCS_BSMN', fieldName: 'CHK_ECSCS_BSMN', header: { text: 'CHK_ECSCS_BSMN' }, visible: false },
{ name: 'CHK_VHCLE_SM', fieldName: 'CHK_VHCLE_SM', header: { text: 'CHK_VHCLE_SM' }, visible: false },
{ name: 'CHK_THREE_BELOW', fieldName: 'CHK_THREE_BELOW', header: { text: 'CHK_THREE_BELOW' }, visible: false },
{ name: 'CHK_FOUR_BELOW', fieldName: 'CHK_FOUR_BELOW', header: { text: 'CHK_FOUR_BELOW' }, visible: false },
{ name: 'CHK_EIGHT_BELOW', fieldName: 'CHK_EIGHT_BELOW', header: { text: 'CHK_EIGHT_BELOW' }, visible: false },
{ name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false },
{ name: 'CHK_COLCT_TRSP_ACMSLT', fieldName: 'CHK_COLCT_TRSP_ACMSLT', header: { text: 'CHK_COLCT_TRSP_ACMSLT' }, visible: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'ECSCS_SM',
'ECSCS_BSMN',
'bzentyCnt',
'empNope',
{
name: 'group_vehicle_status',
header: { text: '시설(차량) 확보현황(대수)' },
items: [
'VHCLE_SM',
'THREE_BELOW',
'FOUR_BELOW',
'EIGHT_BELOW',
'ETC'
'fcltSecrPrstCnt',
'n3TonUdrCnt',
'n4TonUdrCnt',
'n8TonUdrCnt',
'etcCnt'
]
},
'COLCT_TRSP_ACMSLT',
'RMK'
'clctCnvncPrfmncQnt',
'rmrk',
'admdstCd',
'admdstNm',
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'excretaRelatedIndustryInp',
fixedColumns: 0,
tabKey: 'ST00120003',
tabLabel: '분뇨수집·운반업'
};
@@ -5,82 +5,59 @@ import { ValueType } from "realgrid";
export const PrivateSewageTreatRelatedIndustryInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 행정코드 (hidden)
{ name: 'ADST_DONG', fieldName: '행정동', header: { text: '행정동' }, visible: false },
{ name: 'RGN_NM', fieldName: '지역명', header: { text: '지역명' }, visible: false },
{ name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 개인하수처리시설 설계·시공업
{ name: 'CNSTRCT_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CNSTRCT_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'designMdsrCnt', fieldName: 'designMdsrCnt', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'designMdsrNope', fieldName: 'designMdsrNope', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 개인하수처리시설 제조업
{ name: 'MNFCTUR_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'MNFCTUR_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mfbizCnt', fieldName: 'mfbizCnt', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mfbizMdsrNope', fieldName: 'mfbizMdsrNope', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 개인하수처리시설 관리업
{ name: 'MGMT_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'MGMT_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mngCnt', fieldName: 'mngCnt', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mngMdsrNope', fieldName: 'mngMdsrNope', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견 필드
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: 추가 필드 (화면에 미노출이, DB 저장)
{ name: 'CNSTRCT_FCT_CNT', fieldName: '설계시공시설개수', header: { text: '설계시공시설개수' }, visible: false },
{ name: 'CNSTRCT_FCT_CPCTY', fieldName: '설계시공시설용량', header: { text: '설계시공시설용량' }, visible: false },
{ name: 'MNFCTUR_STTY_CNT', fieldName: '제조정원', header: { text: '제조정원' }, visible: false },
{ name: 'MNFCTUR_WRRTN_CNT', fieldName: '제조권원', header: { text: '제조권원' }, visible: false },
{ name: 'MGMT_STTY_CNT', fieldName: '관리정원', header: { text: '관리정원' }, visible: false },
{ name: 'MGMT_WRRTN_CNT', fieldName: '관리권원', header: { text: '관리권원' }, visible: false },
// Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리
{ name: 'CHK_CNSTRCT_ENTRPS', fieldName: 'CHK_CNSTRCT_ENTRPS', header: { text: 'CHK_CNSTRCT_ENTRPS' }, visible: false },
{ name: 'CHK_CNSTRCT_BSMN', fieldName: 'CHK_CNSTRCT_BSMN', header: { text: 'CHK_CNSTRCT_BSMN' }, visible: false },
{ name: 'CHK_CNSTRCT_FCT_CNT', fieldName: 'CHK_CNSTRCT_FCT_CNT', header: { text: 'CHK_CNSTRCT_FCT_CNT' }, visible: false },
{ name: 'CHK_CNSTRCT_FCT_CPCTY', fieldName: 'CHK_CNSTRCT_FCT_CPCTY', header: { text: 'CHK_CNSTRCT_FCT_CPCTY' }, visible: false },
{ name: 'CHK_MNFCTUR_ENTRPS', fieldName: 'CHK_MNFCTUR_ENTRPS', header: { text: 'CHK_MNFCTUR_ENTRPS' }, visible: false },
{ name: 'CHK_MNFCTUR_BSMN', fieldName: 'CHK_MNFCTUR_BSMN', header: { text: 'CHK_MNFCTUR_BSMN' }, visible: false },
{ name: 'CHK_MNFCTUR_STTY_CNT', fieldName: 'CHK_MNFCTUR_STTY_CNT', header: { text: 'CHK_MNFCTUR_STTY_CNT' }, visible: false },
{ name: 'CHK_MNFCTUR_WRRTN_CNT', fieldName: 'CHK_MNFCTUR_WRRTN_CNT', header: { text: 'CHK_MNFCTUR_WRRTN_CNT' }, visible: false },
{ name: 'CHK_MGMT_ENTRPS', fieldName: 'CHK_MGMT_ENTRPS', header: { text: 'CHK_MGMT_ENTRPS' }, visible: false },
{ name: 'CHK_MGMT_BSMN', fieldName: 'CHK_MGMT_BSMN', header: { text: 'CHK_MGMT_BSMN' }, visible: false },
{ name: 'CHK_MGMT_STTY_CNT', fieldName: 'CHK_MGMT_STTY_CNT', header: { text: 'CHK_MGMT_STTY_CNT' }, visible: false },
{ name: 'CHK_MGMT_WRRTN_CNT', fieldName: 'CHK_MGMT_WRRTN_CNT', header: { text: 'CHK_MGMT_WRRTN_CNT' }, visible: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_design_construct',
header: { text: '개인하수처리시설 설계·시공업' },
items: ['CNSTRCT_ENTRPS', 'CNSTRCT_BSMN']
items: ['designMdsrCnt', 'designMdsrNope']
},
{
name: 'group_manufacture',
header: { text: '개인하수처리시설 제조업' },
items: ['MNFCTUR_ENTRPS', 'MNFCTUR_BSMN']
items: ['mfbizCnt', 'mfbizMdsrNope']
},
{
name: 'group_management',
header: { text: '개인하수처리시설 관리업' },
items: ['MGMT_ENTRPS', 'MGMT_BSMN']
items: ['mngCnt', 'mngMdsrNope']
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm',
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'privateSewageTreatRelatedIndustryInp',
fixedColumns: 0,
tabKey: 'ST00120004',
tabLabel: '개인하수처리관련업'
};
@@ -5,86 +5,107 @@ import { ValueType } from "realgrid";
export const PublicInstitutionExcretaIndustryInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 행정코드 (hidden)
{ name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 직제
{ name: 'OCGNZ', fieldName: '직 제', header: { text: '직 제' } },
{ name: 'ognzNm', fieldName: 'ognzNm', header: { text: '직 제' } },
// 직원총수
{ name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'empGrssCnt',
fieldName: 'empGrssCnt',
header: { text: '직원총수' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: ['engrkCnt', 'mchnCnt', 'prvtmCnt', 'chmengCnt', 'envCnt', 'enjbEtcCnt', 'etcCnt'],
type: 'sum' as const,
},
},
// 행정직
{ name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'pbadmsNope', fieldName: 'pbadmsNope', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기술직
{ name: 'TCHNLGY_JSSFC_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'TCHNLGY_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기능직 (hidden)
{ name: 'TCNSRV', fieldName: '기능직', header: { text: '기능직' }, visible: false },
{
name: 'techWholNope',
fieldName: 'techWholNope',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: ['engrkCnt', 'mchnCnt', 'prvtmCnt', 'chmengCnt', 'envCnt', 'enjbEtcCnt'],
type: 'sum' as const,
},
},
{ name: 'engrkCnt', fieldName: 'engrkCnt', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mchnCnt', fieldName: 'mchnCnt', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prvtmCnt', fieldName: 'prvtmCnt', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'chmengCnt', fieldName: 'chmengCnt', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'envCnt', fieldName: 'envCnt', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'enjbEtcCnt', fieldName: 'enjbEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기타
{ name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'etcCnt', fieldName: 'etcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견 필드
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리
{ name: 'CHK_OCGNZ', fieldName: 'CHK_OCGNZ', header: { text: 'CHK_OCGNZ' }, visible: false },
{ name: 'CHK_TOT_WTR_QRM', fieldName: 'CHK_TOT_WTR_QRM', header: { text: 'CHK_TOT_WTR_QRM' }, visible: false },
{ name: 'CHK_ADMINIST_JSSFC', fieldName: 'CHK_ADMINIST_JSSFC', header: { text: 'CHK_ADMINIST_JSSFC' }, visible: false },
{ name: 'CHK_TCHNLGY_JSSFC_SM', fieldName: 'CHK_TCHNLGY_JSSFC_SM', header: { text: 'CHK_TCHNLGY_JSSFC_SM' }, visible: false },
{ name: 'CHK_ENGRK_JSSFC', fieldName: 'CHK_ENGRK_JSSFC', header: { text: 'CHK_ENGRK_JSSFC' }, visible: false },
{ name: 'CHK_MCHN_JSSFC', fieldName: 'CHK_MCHN_JSSFC', header: { text: 'CHK_MCHN_JSSFC' }, visible: false },
{ name: 'CHK_ELCTY_JSSFC', fieldName: 'CHK_ELCTY_JSSFC', header: { text: 'CHK_ELCTY_JSSFC' }, visible: false },
{ name: 'CHK_CHMSTRY_JSSFC', fieldName: 'CHK_CHMSTRY_JSSFC', header: { text: 'CHK_CHMSTRY_JSSFC' }, visible: false },
{ name: 'CHK_ENVRN_JSSFC', fieldName: 'CHK_ENVRN_JSSFC', header: { text: 'CHK_ENVRN_JSSFC' }, visible: false },
{ name: 'CHK_TCHNLGY_ETC', fieldName: 'CHK_TCHNLGY_ETC', header: { text: 'CHK_TCHNLGY_ETC' }, visible: false },
{ name: 'CHK_TCNSRV', fieldName: 'CHK_TCNSRV', header: { text: 'CHK_TCNSRV' }, visible: false },
{ name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'ognzSn', fieldName: 'ognzSn', header: { text: '조직일련번호' }, visible: false },
{
name: '__isNewArticle',
fieldName: '__isNewArticle',
header: { text: '신규항목' },
visible: false,
editable: false,
valueType: ValueType.NUMBER,
},
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'OCGNZ',
'TOT_WTR_QRM',
'ADMINIST_JSSFC',
'ognzNm',
'empGrssCnt',
'pbadmsNope',
{
name: 'group_technology',
header: { text: '기술직' },
items: [
'TCHNLGY_JSSFC_SM',
'ENGRK_JSSFC',
'MCHN_JSSFC',
'ELCTY_JSSFC',
'CHMSTRY_JSSFC',
'ENVRN_JSSFC',
'TCHNLGY_ETC'
'techWholNope',
'engrkCnt',
'mchnCnt',
'prvtmCnt',
'chmengCnt',
'envCnt',
'enjbEtcCnt'
]
},
'ETC',
'RMK'
'etcCnt',
'rmrk',
'admdstCd',
'admdstNm',
'ognzSn',
'__isNewArticle',
],
rowStartNum: 2,
fixedColumns: 0,
tabKey: 'publicInstitutionExcretaIndustryInp',
tabKey: 'ST00120002',
tabLabel: '공공기관 분뇨분야'
};
@@ -5,86 +5,107 @@ import { ValueType } from "realgrid";
export const PublicInstitutionSewerIndustryInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 행정코드 (hidden)
{ name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 직제
{ name: 'OCGNZ', fieldName: '직 제', header: { text: '직 제' } },
{ name: 'ognzNm', fieldName: 'ognzNm', header: { text: '직 제' } },
// 직원총수
{ name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'empGrssCnt',
fieldName: 'empGrssCnt',
header: { text: '직원총수' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: ['engrkCnt', 'mchnCnt', 'prvtmCnt', 'chmengCnt', 'envCnt', 'enjbEtcCnt', 'etcCnt'],
type: 'sum' as const,
},
},
// 행정직
{ name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'pbadmsNope', fieldName: 'pbadmsNope', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기술직
{ name: 'TCHNLGY_JSSFC_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'TCHNLGY_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기능직 (hidden)
{ name: 'TCNSRV', fieldName: '기능직', header: { text: '기능직' }, visible: false },
{
name: 'techWholNope',
fieldName: 'techWholNope',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: ['engrkCnt', 'mchnCnt', 'prvtmCnt', 'chmengCnt', 'envCnt', 'enjbEtcCnt'],
type: 'sum' as const,
},
},
{ name: 'engrkCnt', fieldName: 'engrkCnt', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mchnCnt', fieldName: 'mchnCnt', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prvtmCnt', fieldName: 'prvtmCnt', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'chmengCnt', fieldName: 'chmengCnt', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'envCnt', fieldName: 'envCnt', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'enjbEtcCnt', fieldName: 'enjbEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기타
{ name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'etcCnt', fieldName: 'etcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견 필드
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리
{ name: 'CHK_OCGNZ', fieldName: 'CHK_OCGNZ', header: { text: 'CHK_OCGNZ' }, visible: false },
{ name: 'CHK_TOT_WTR_QRM', fieldName: 'CHK_TOT_WTR_QRM', header: { text: 'CHK_TOT_WTR_QRM' }, visible: false },
{ name: 'CHK_ADMINIST_JSSFC', fieldName: 'CHK_ADMINIST_JSSFC', header: { text: 'CHK_ADMINIST_JSSFC' }, visible: false },
{ name: 'CHK_TCHNLGY_JSSFC_SM', fieldName: 'CHK_TCHNLGY_JSSFC_SM', header: { text: 'CHK_TCHNLGY_JSSFC_SM' }, visible: false },
{ name: 'CHK_ENGRK_JSSFC', fieldName: 'CHK_ENGRK_JSSFC', header: { text: 'CHK_ENGRK_JSSFC' }, visible: false },
{ name: 'CHK_MCHN_JSSFC', fieldName: 'CHK_MCHN_JSSFC', header: { text: 'CHK_MCHN_JSSFC' }, visible: false },
{ name: 'CHK_ELCTY_JSSFC', fieldName: 'CHK_ELCTY_JSSFC', header: { text: 'CHK_ELCTY_JSSFC' }, visible: false },
{ name: 'CHK_CHMSTRY_JSSFC', fieldName: 'CHK_CHMSTRY_JSSFC', header: { text: 'CHK_CHMSTRY_JSSFC' }, visible: false },
{ name: 'CHK_ENVRN_JSSFC', fieldName: 'CHK_ENVRN_JSSFC', header: { text: 'CHK_ENVRN_JSSFC' }, visible: false },
{ name: 'CHK_TCHNLGY_ETC', fieldName: 'CHK_TCHNLGY_ETC', header: { text: 'CHK_TCHNLGY_ETC' }, visible: false },
{ name: 'CHK_TCNSRV', fieldName: 'CHK_TCNSRV', header: { text: 'CHK_TCNSRV' }, visible: false },
{ name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'ognzSn', fieldName: 'ognzSn', header: { text: '조직일련번호' }, visible: false },
{
name: '__isNewArticle',
fieldName: '__isNewArticle',
header: { text: '신규항목' },
visible: false,
editable: false,
valueType: ValueType.NUMBER,
},
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'OCGNZ',
'TOT_WTR_QRM',
'ADMINIST_JSSFC',
'ognzNm',
'empGrssCnt',
'pbadmsNope',
{
name: 'group_technology',
header: { text: '기술직' },
items: [
'TCHNLGY_JSSFC_SM',
'ENGRK_JSSFC',
'MCHN_JSSFC',
'ELCTY_JSSFC',
'CHMSTRY_JSSFC',
'ENVRN_JSSFC',
'TCHNLGY_ETC'
'techWholNope',
'engrkCnt',
'mchnCnt',
'prvtmCnt',
'chmengCnt',
'envCnt',
'enjbEtcCnt'
]
},
'ETC',
'RMK'
'etcCnt',
'rmrk',
'admdstCd',
'admdstNm',
'ognzSn',
'__isNewArticle',
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'publicInstitutionSewerIndustryInp',
fixedColumns: 0,
tabKey: 'ST00120001',
tabLabel: '공공기관 하수분야'
};
@@ -1,91 +1,158 @@
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import { footerDecimal1 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
export const DIGESTOR_OPERATING_STATUS_YN_VALUES = ["Y", "N"] as const;
export const DIGESTOR_OPERATING_STATUS_EN_YN_FIELD = "digstnEnYn" as const;
/** 소화조 유/무가 N이면 해당 행의 입력 셀을 잠근다(digstnEnYn 제외). */
export function isDigestorOperatingRowDisabled(
row: Record<string, string | number>,
grid?: { getValue?: (itemIndex: number, fieldName: string) => unknown },
rowIndex?: number,
): boolean {
const live =
grid?.getValue != null && rowIndex != null && rowIndex >= 0
? grid.getValue(rowIndex, DIGESTOR_OPERATING_STATUS_EN_YN_FIELD)
: undefined;
const raw =
live !== undefined && live !== null
? live
: row[DIGESTOR_OPERATING_STATUS_EN_YN_FIELD];
return String(raw ?? "").trim().toUpperCase() === "N";
}
function dropdownColumn(
col: Pick<ColumnConfig, "name" | "fieldName" | "header"> &
Partial<ColumnConfig>,
options: readonly string[],
): ColumnConfig {
const values = [...options];
return {
...col,
values,
labels: values,
lookupDisplay: true,
editor: {
type: "dropdown",
domainOnly: true,
textReadOnly: true,
commitOnSelect: true,
dropDownWhenClick: true,
values,
labels: values,
},
};
}
// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리 > tab 2. 소화조 운영현황
export const DigestorOperatingStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 하수처리장명
{ name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수처리장명' }, editable: false },
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '하수처리장명' }, editable: false },
// 소화조 운영현황
{ name: 'DGSTR_FNNC', fieldName: '소화조 유/무(Y/N)선택', header: { text: '소화조 유/무(Y/N)선택' } },
{ name: 'DGSTR_CPC', fieldName: '설계용량(m³)', header: { text: '설계용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_OPR_CPC', fieldName: '실제운전용량(m³)', header: { text: '실제운전용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_IN', fieldName: '투입량(m³/일)', header: { text: '투입량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_TS', fieldName: '투입 TS 농도(%)', header: { text: '투입 TS 농도(%)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_YRSTP_RN_DYN', fieldName: '가동일수(연간)', header: { text: '가동일수(연간)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_OCCUR_GAS', fieldName: '발생가스량(m³/일)', header: { text: '발생가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
dropdownColumn(
{
name: 'digstnEnYn',
fieldName: 'digstnEnYn',
header: { text: '소화조 유/무(Y/N)선택' },
},
DIGESTOR_OPERATING_STATUS_YN_VALUES,
),
{ name: 'digstnCpct', fieldName: 'digstnCpct', header: { text: '설계용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'actlDrvnCpct', fieldName: 'actlDrvnCpct', header: { text: '실제운전용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnGasInputQnt', fieldName: 'digstnGasInputQnt', header: { text: '투입량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnGasInputSldDnst', fieldName: 'digstnGasInputSldDnst', header: { text: '투입 TS 농도(%)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnTnkOprtngDayCnt', fieldName: 'digstnTnkOprtngDayCnt', header: { text: '가동일수(연간)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ocrnGasQnt', fieldName: 'ocrnGasQnt', header: { text: '발생가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 소화조 운영현황 > 소화가스 이용현황(m³/일)
{ name: 'DGSTR_SM', fieldName: '계', header: { text: '계' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_SLE', fieldName: '판매', header: { text: '판매' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_ELTGNR', fieldName: '발전', header: { text: '발전' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_ONSLF', fieldName: '자체활용', header: { text: '자체활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'DGSTR_ETC', fieldName: '기타(CNG등)', header: { text: '기타(CNG등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'digstnGasUtztnWholPrst',
fieldName: 'digstnGasUtztnWholPrst',
header: { text: '계' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'digstnGasUtztnNtslQnt',
'digstnGasUtztnEtgtQnt',
'digstnGasUtztnItslfQnt',
'digstnGasUtztnEtcQnt',
],
type: 'sum' as const,
},
},
{ name: 'digstnGasUtztnNtslQnt', fieldName: 'digstnGasUtztnNtslQnt', header: { text: '판매' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnGasUtztnEtgtQnt', fieldName: 'digstnGasUtztnEtgtQnt', header: { text: '발전' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnGasUtztnItslfQnt', fieldName: 'digstnGasUtztnItslfQnt', header: { text: '자체활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'digstnGasUtztnEtcQnt', fieldName: 'digstnGasUtztnEtcQnt', header: { text: '기타(CNG등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 소화조 운영현황 > 잉여가스량
{ name: 'DGSTR_GAS', fieldName: '잉여가스량(m³/일)', header: { text: '잉여가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'surplsGasQnt', fieldName: 'surplsGasQnt', header: { text: '잉여가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'DGSTR_RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: Check fields for change detection
{ name: 'CHK_DGSTR_FNNC', fieldName: 'CHK_DGSTR_FNNC', header: { text: 'CHK_DGSTR_FNNC' }, visible: false },
{ name: 'CHK_DGSTR_IN', fieldName: 'CHK_DGSTR_IN', header: { text: 'CHK_DGSTR_IN' }, visible: false },
{ name: 'CHK_DGSTR_TS', fieldName: 'CHK_DGSTR_TS', header: { text: 'CHK_DGSTR_TS' }, visible: false },
{ name: 'CHK_DGSTR_YRSTP_RN_DYN', fieldName: 'CHK_DGSTR_YRSTP_RN_DYN', header: { text: 'CHK_DGSTR_YRSTP_RN_DYN' }, visible: false },
{ name: 'CHK_DGSTR_OCCUR_GAS', fieldName: 'CHK_DGSTR_OCCUR_GAS', header: { text: 'CHK_DGSTR_OCCUR_GAS' }, visible: false },
{ name: 'CHK_DGSTR_CPC', fieldName: 'CHK_DGSTR_CPC', header: { text: 'CHK_DGSTR_CPC' }, visible: false },
{ name: 'CHK_DGSTR_OPR_CPC', fieldName: 'CHK_DGSTR_OPR_CPC', header: { text: 'CHK_DGSTR_OPR_CPC' }, visible: false },
{ name: 'CHK_DGSTR_SM', fieldName: 'CHK_DGSTR_SM', header: { text: 'CHK_DGSTR_SM' }, visible: false },
{ name: 'CHK_DGSTR_SLE', fieldName: 'CHK_DGSTR_SLE', header: { text: 'CHK_DGSTR_SLE' }, visible: false },
{ name: 'CHK_DGSTR_ELTGNR', fieldName: 'CHK_DGSTR_ELTGNR', header: { text: 'CHK_DGSTR_ELTGNR' }, visible: false },
{ name: 'CHK_DGSTR_ONSLF', fieldName: 'CHK_DGSTR_ONSLF', header: { text: 'CHK_DGSTR_ONSLF' }, visible: false },
{ name: 'CHK_DGSTR_ETC', fieldName: 'CHK_DGSTR_ETC', header: { text: 'CHK_DGSTR_ETC' }, visible: false },
{ name: 'CHK_DGSTR_GAS', fieldName: 'CHK_DGSTR_GAS', header: { text: 'CHK_DGSTR_GAS' }, visible: false },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설물일련번호' }, visible: false },
{ name: 'swtrmtFcltCd', fieldName: 'swtrmtFcltCd', header: { text: '처리장코드' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO_CD', 'ADST_CD']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'BSC_FCT_NM',
'swtrmtFcltNm',
{
name: 'group_digestor_operating',
header: { text: '소화조 운영현황' },
items: [
'DGSTR_FNNC',
'DGSTR_CPC',
'DGSTR_OPR_CPC',
'DGSTR_IN',
'DGSTR_TS',
'DGSTR_YRSTP_RN_DYN',
'DGSTR_OCCUR_GAS',
'digstnEnYn',
'digstnCpct',
'actlDrvnCpct',
'digstnGasInputQnt',
'digstnGasInputSldDnst',
'digstnTnkOprtngDayCnt',
'ocrnGasQnt',
{
name: 'group_gas_usage',
header: { text: '소화가스 이용현황(m³/일)' },
items: [
'DGSTR_SM',
'DGSTR_SLE',
'DGSTR_ELTGNR',
'DGSTR_ONSLF',
'DGSTR_ETC'
'digstnGasUtztnWholPrst',
'digstnGasUtztnNtslQnt',
'digstnGasUtztnEtgtQnt',
'digstnGasUtztnItslfQnt',
'digstnGasUtztnEtcQnt'
]
},
'DGSTR_GAS'
'surplsGasQnt'
]
},
'DGSTR_RMK'
'rmrk',
'admdstCd',
'admdstNm',
'fcltSn',
'swtrmtFcltCd'
],
rowStartNum: 3,
fixedColumns: 0,
tabKey: 'digestorOperatingStatusInp',
tabKey: 'ST00110002',
tabLabel: '소화조 운영현황'
};
@@ -5,79 +5,116 @@ import { ValueType } from "realgrid";
export const ExcretaSlurgeProcessingStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 분뇨처리장명
{ name: 'ECSCS_NM', fieldName: '분 뇨 처리장명', header: { text: '분 뇨 처리장명' }, editable: false },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '분뇨 처리장명' }, editable: false },
// 발생량
{ name: 'OCTY', fieldName: '발생량 (A)=(B)+(C)', header: { text: '발생량 (A)=(B)+(C)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'gnam',
fieldName: 'gnam',
header: { text: '발생량 (A)=(B)+(C)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt',
'nondspsQnt'
],
type: 'sum' as const,
},
},
// 처 분 량(톤/년)
{ name: 'PROCESS_CPCTY_SM', fieldName: '계(B)', header: { text: '계(B)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'LAND_RCLM', fieldName: '육상매립', header: { text: '육상매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'INCNR', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'FUEL', fieldName: '연료화', header: { text: '연료화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholDspsQnt',
fieldName: 'wholDspsQnt',
header: { text: '계(B)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt',
],
type: 'sum' as const,
},
},
{ name: 'rcycQnt', fieldName: 'rcycQnt', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'olandLfvlm', fieldName: 'olandLfvlm', header: { text: '육상매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'incnrQnt', fieldName: 'incnrQnt', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'oceanInputQnt', fieldName: 'oceanInputQnt', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fuelQnt', fieldName: 'fuelQnt', header: { text: '연료화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcQnt', fieldName: 'etcQnt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 미처분량(이월량)(C)
{ name: 'CYFD_CPCTY', fieldName: '미처분량(이월량)(C)', header: { text: '미처분량(이월량)(C)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'nondspsQnt', fieldName: 'nondspsQnt', header: { text: '미처분량(이월량)(C)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 타시설연계여부
{ name: 'OUT_BSC_FCT_NM', fieldName: '타시설연계여부', header: { text: '타시설연계여부' } },
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '타시설연계여부' } },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: Check fields for change detection
{ name: 'CHK_ECSCS_NM', fieldName: 'CHK_ECSCS_NM', header: { text: 'CHK_ECSCS_NM' }, visible: false },
{ name: 'CHK_OCTY', fieldName: 'CHK_OCTY', header: { text: 'CHK_OCTY' }, visible: false },
{ name: 'CHK_PROCESS_CPCTY_SM', fieldName: 'CHK_PROCESS_CPCTY_SM', header: { text: 'CHK_PROCESS_CPCTY_SM' }, visible: false },
{ name: 'CHK_RUSE', fieldName: 'CHK_RUSE', header: { text: 'CHK_RUSE' }, visible: false },
{ name: 'CHK_LAND_RCLM', fieldName: 'CHK_LAND_RCLM', header: { text: 'CHK_LAND_RCLM' }, visible: false },
{ name: 'CHK_INCNR', fieldName: 'CHK_INCNR', header: { text: 'CHK_INCNR' }, visible: false },
{ name: 'CHK_OCEAN_DPSL', fieldName: 'CHK_OCEAN_DPSL', header: { text: 'CHK_OCEAN_DPSL' }, visible: false },
{ name: 'CHK_FUEL', fieldName: 'CHK_FUEL', header: { text: 'CHK_FUEL' }, visible: false },
{ name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false },
{ name: 'CHK_CYFD_CPCTY', fieldName: 'CHK_CYFD_CPCTY', header: { text: 'CHK_CYFD_CPCTY' }, visible: false },
{ name: 'CHK_OUT_BSC_FCT_NM', fieldName: 'CHK_OUT_BSC_FCT_NM', header: { text: 'CHK_OUT_BSC_FCT_NM' }, visible: false },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설물일련번호' }, visible: false },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '시설물위치내용' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO_CD', 'ADST_CD']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'ECSCS_NM',
'OCTY',
'fcltNm',
'gnam',
{
name: 'group_process_amount',
header: { text: '처 분 량(톤/년)' },
items: [
'PROCESS_CPCTY_SM',
'RUSE',
'LAND_RCLM',
'INCNR',
'OCEAN_DPSL',
'FUEL',
'ETC'
'wholDspsQnt',
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt'
]
},
'CYFD_CPCTY',
'OUT_BSC_FCT_NM',
'RMK'
'nondspsQnt',
'swtrmtFcltNm',
'rmrk',
'admdstCd',
'admdstNm',
'fcltSn',
'fcltPstnCn'
],
rowStartNum: 2,
fixedColumns: 2,
tabKey: 'excretaSlurgeProcessingStatusInp',
tabKey: 'ST00110004',
tabLabel: '분뇨찌꺼기(슬러지)처리현황'
};
@@ -5,22 +5,37 @@ import { ValueType } from "realgrid";
export const SewageSlurgeProcessingFacStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, styleName: 'rg-cell-readonly' },
// 찌꺼기처리시설명
{ name: 'SLG_FCT_NM', fieldName: '찌꺼기처리시설명', header: { text: '찌꺼기처리시설명' } },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '찌꺼기처리시설명' } },
// 처리시설 운영현황
{ name: 'TRT_METHOD', fieldName: '처리방식', header: { text: '처리방식' } },
{ name: 'SLG_FCT_CPC', fieldName: '시설용량(톤/일)', header: { text: '시설용량(톤/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'OPR_CPC', fieldName: '연간처리량(톤/년)', header: { text: '연간처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ODY_RN_TIME', fieldName: '일평균가동시간(1~24)', header: { text: '일평균가동시간(1~24)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'YRSTP_RN_DYN', fieldName: '연중 가동일(1~330)', header: { text: '연중 가동일(1~330)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'OPR_RT', fieldName: '가동율(%)', header: { text: '가동율(%)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'prcsMthExpln', fieldName: 'prcsMthExpln', header: { text: '처리방식' } },
{ name: 'fcltCpct', fieldName: 'fcltCpct', header: { text: '시설용량(톤/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'anlPrcsQnt', fieldName: 'anlPrcsQnt', header: { text: '연간처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dayAvgOprtngHr', fieldName: 'dayAvgOprtngHr', header: { text: '일평균가동시간(1~24)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'anlOprtngDayCnt', fieldName: 'anlOprtngDayCnt', header: { text: '연중 가동일(1~330)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'oprtngRt',
fieldName: 'oprtngRt',
header: { text: '가동율(%)' },
footer: footerDecimal1,
editable: false,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
numeratorField: 'anlPrcsQnt',
denominatorField: 'fcltCpct',
multiplierField: 'anlOprtngDayCnt',
type: 'quotientTimesMultiplierPercent' as const,
},
},
// 비고
{ name: 'SLG_FCT_RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 기본 정보
{ name: 'STPN_SEQ', fieldName: 'STPN_SEQ', header: { text: 'STPN_SEQ' }, visible: false },
@@ -30,40 +45,42 @@ export const SewageSlurgeProcessingFacStatusInpColumn = {
{ name: 'BSNM_CD', fieldName: 'BSNM_CD', header: { text: 'BSNM_CD' }, visible: false },
{ name: 'BSC_FCT_CD', fieldName: 'BSC_FCT_CD', header: { text: 'BSC_FCT_CD' }, visible: false },
// Hidden: Check fields for change detection
{ name: 'CHK_SLG_FCT_NM', fieldName: 'CHK_SLG_FCT_NM', header: { text: 'CHK_SLG_FCT_NM' }, visible: false },
{ name: 'CHK_TRT_METHOD', fieldName: 'CHK_TRT_METHOD', header: { text: 'CHK_TRT_METHOD' }, visible: false },
{ name: 'CHK_SLG_FCT_CPC', fieldName: 'CHK_SLG_FCT_CPC', header: { text: 'CHK_SLG_FCT_CPC' }, visible: false },
{ name: 'CHK_OPR_CPC', fieldName: 'CHK_OPR_CPC', header: { text: 'CHK_OPR_CPC' }, visible: false },
{ name: 'CHK_ODY_RN_TIME', fieldName: 'CHK_ODY_RN_TIME', header: { text: 'CHK_ODY_RN_TIME' }, visible: false },
{ name: 'CHK_YRSTP_RN_DYN', fieldName: 'CHK_YRSTP_RN_DYN', header: { text: 'CHK_YRSTP_RN_DYN' }, visible: false },
{ name: 'CHK_OPR_RT', fieldName: 'CHK_OPR_RT', header: { text: 'CHK_OPR_RT' }, visible: false },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설물일련번호' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO_CD', 'ADST_CD']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'SLG_FCT_NM',
'fcltNm',
{
name: 'group_processing_fac_operating',
header: { text: '처리시설 운영현황' },
items: [
'TRT_METHOD',
'SLG_FCT_CPC',
'OPR_CPC',
'ODY_RN_TIME',
'YRSTP_RN_DYN',
'OPR_RT'
'prcsMthExpln',
'fcltCpct',
'anlPrcsQnt',
'dayAvgOprtngHr',
'anlOprtngDayCnt',
'oprtngRt'
]
},
'SLG_FCT_RMK'
'rmrk',
'admdstCd',
'admdstNm',
'fcltSn',
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'sewageSlurgeProcessingFacStatusInp',
tabKey: 'ST00110003',
tabLabel: '하수찌꺼기 처리시설 운영현황'
};
@@ -5,212 +5,357 @@ import { ValueType } from "realgrid";
export const SlurgeProcessingFacStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 하수처리장명
{ name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수처리장명' }, },
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '하수처리장명' }, editable: false, styleName: 'rg-cell-readonly' },
// 찌꺼기발생량(톤/년, 탈수기준)
{ name: 'TOT_AMNT', fieldName: '합계', header: { text: '합계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'P_AMNT', fieldName: '자체', header: { text: '자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'OUT_AMNT', fieldName: '외부유입량', header: { text: '외부유입량' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'gnamWhol',
fieldName: 'gnamWhol',
header: { text: '합계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['itslfQnt', 'otsdInflwQnt'],
type: 'sum' as const,
},
},
{ name: 'itslfQnt', fieldName: 'itslfQnt', header: { text: '자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdInflwQnt', fieldName: 'otsdInflwQnt', header: { text: '외부유입량' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 이송처리장
{ name: 'OUT_FCT_NM', fieldName: '이송처리장', header: { text: '이송처리장' } },
{ name: 'transfTmplntNm', fieldName: 'transfTmplntNm', header: { text: '이송처리장' } },
// 이송처리량 - 쿼리에서 못찾음 (임의)
{ name: 'a', fieldName: '이송처리량(m³/년)', header: { text: '이송처리량(m³/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'transfPrcsQnt', fieldName: 'transfPrcsQnt', header: { text: '이송처리량(m³/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 함수율
{ name: 'DSP_MOIRAT', fieldName: '함수율(%, 탈수기준)', header: { text: '함수율(%, 탈수기준)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'srpFncRt', fieldName: 'srpFncRt', header: { text: '함수율(%, 탈수기준)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 찌꺼기처리량합계
{ name: 'DSP_SM', fieldName: '찌꺼기처리량합계(톤/년)', header: { text: '찌꺼기처리량합계(톤/년)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholSrpPrcsQnt',
fieldName: 'wholSrpPrcsQnt',
header: { text: '찌꺼기처리량합계(톤/년)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'itslfTs1RclmQnt',
'itslfTs1IncnrQnt',
'itslfTs1AltdQnt',
'itslfTs1DryQnt',
'itslfTs1FuelQnt',
'itslfTs2CmpstgQnt',
'otsdCnsgnRclmQnt',
'otsdCnsgnIncnrQnt',
'otsdCnsgnFuelPwplQnt',
'otsdCnsgnPrdctRmtrlQnt',
'otsdCnsgnFmlyrQnt',
'otsdCnsgnCmpstgQnt',
'cparTs1Whol',
],
type: 'sum' as const,
}
},
// 자체 찌꺼기 처리량 > 자체찌꺼기처리량소계
{ name: 'DSP_SELF_SM', fieldName: '자체찌꺼기처리량소계', header: { text: '자체찌꺼기처리량소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'itslfTs1Whol',
fieldName: 'itslfTs1Whol',
header: { text: '자체찌꺼기처리량소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'itslfTs1RclmQnt',
'itslfTs1IncnrQnt',
'itslfTs1AltdQnt',
'itslfTs1DryQnt',
'itslfTs1FuelQnt',
'itslfTs2CmpstgQnt',
],
type: 'sum' as const,
}
},
// 자체 찌꺼기 처리량 > 매립
{ name: 'O_DSP_RCLM', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1RclmQnt', fieldName: 'itslfTs1RclmQnt', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 소각(1차)
{ name: 'O_DSP_INCNR', fieldName: '소각(1차)', header: { text: '소각(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1IncnrQnt', fieldName: 'itslfTs1IncnrQnt', header: { text: '소각(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 소각 후 처리(2차) - 쿼리에 없음 (임의 생성)
{ name: 'b1', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'b2', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'b3', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'b4', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'b5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } },
{
name: 'incnrPrcsWhol',
fieldName: 'incnrPrcsWhol',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'incnrPrcsLfvlm',
'incnrPrcsPrdctRmtrlQnt',
'incnrPrcsFmlyrQnt',
],
type: 'sum' as const,
}
},
{ name: 'incnrPrcsLfvlm', fieldName: 'incnrPrcsLfvlm', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'incnrPrcsPrdctRmtrlQnt', fieldName: 'incnrPrcsPrdctRmtrlQnt', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'incnrPrcsFmlyrQnt', fieldName: 'incnrPrcsFmlyrQnt', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'incnrPrcsFcltNm', fieldName: 'incnrPrcsFcltNm', header: { text: '시설(업체명)' } },
// 자체 찌꺼기 처리량 > 고화(1차) - 쿼리에 없음 (임의 생성)
{ name: 'c1', fieldName: '고화(1차)', header: { text: '고화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1AltdQnt', fieldName: 'itslfTs1AltdQnt', header: { text: '고화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 고화 후 처리(2차) - 쿼리에 없음 (임의 생성)
{ name: 'c2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'c3', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'c4', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'c5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } },
{
name: 'stbznPrcsWhol',
fieldName: 'stbznPrcsWhol',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'stbznPrcsLfvlm',
'stbznPrcsFmlyrQnt',
],
type: 'sum' as const,
}
},
{ name: 'stbznPrcsLfvlm', fieldName: 'stbznPrcsLfvlm', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'stbznPrcsFmlyrQnt', fieldName: 'stbznPrcsFmlyrQnt', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'stbznPrcsFcltNm', fieldName: 'stbznPrcsFcltNm', header: { text: '시설(업체명)' } },
// 자체 찌꺼기 처리량 > 건조(1차)
{ name: 'DSP_DESIC', fieldName: '건조(1차)', header: { text: '건조(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1DryQnt', fieldName: 'itslfTs1DryQnt', header: { text: '건조(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 건조 후 처리(2차) - 쿼리에 없음
{ name: 'd1', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd2', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd3', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd4', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd5', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd6', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd7', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'd8', fieldName: '시설(업체명)', header: { text: '시설(업체명)' }, editable: true },
{
name: 'dryPrcsWhol',
fieldName: 'dryPrcsWhol',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'dryPrcsLfvlm',
'dryPrcsIncnrQnt',
'dryPrcsFuelQnt',
'dryPrcsPrdctRmtrlQnt',
'dryPrcsFmlyrQnt',
'dryPrcsCmpstgQnt',
],
type: 'sum' as const,
}
},
{ name: 'dryPrcsLfvlm', fieldName: 'dryPrcsLfvlm', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsIncnrQnt', fieldName: 'dryPrcsIncnrQnt', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsFuelQnt', fieldName: 'dryPrcsFuelQnt', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsPrdctRmtrlQnt', fieldName: 'dryPrcsPrdctRmtrlQnt', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsFmlyrQnt', fieldName: 'dryPrcsFmlyrQnt', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsCmpstgQnt', fieldName: 'dryPrcsCmpstgQnt', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dryPrcsFcltNm', fieldName: 'dryPrcsFcltNm', header: { text: '시설(업체명)' }, editable: true },
// 자체 찌꺼기 처리량 > 탄화(1차) - 쿼리에 없음 (임의 생성)
{ name: 'e1', fieldName: '탄화(1차)', header: { text: '탄화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1FuelQnt', fieldName: 'itslfTs1FuelQnt', header: { text: '탄화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 탄화 후 처리(2차) - 쿼리에 없음 (임의 생성)
{ name: 'e2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'e3', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'e4', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'e5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } },
{
name: 'fuelPrcsWhol',
fieldName: 'fuelPrcsWhol',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['fuelPrcsIncnrQnt', 'fuelPrcsFuelQnt'],
type: 'sum' as const,
}
},
{ name: 'fuelPrcsIncnrQnt', fieldName: 'fuelPrcsIncnrQnt', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fuelPrcsFuelQnt', fieldName: 'fuelPrcsFuelQnt', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fuelPrcsFcltNm', fieldName: 'fuelPrcsFcltNm', header: { text: '시설(업체명)' } },
// 자체 찌꺼기 처리량 > 퇴비화 - 쿼리에 없음 (임의 생성)
{ name: 'f1', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs2CmpstgQnt', fieldName: 'itslfTs2CmpstgQnt', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 퇴비화 > 시설(업체명) - 쿼리에 없음 (임의 생성)
{ name: 'f2', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } },
{ name: 'cmpsPrcsFcltBzentyNm', fieldName: 'cmpsPrcsFcltBzentyNm', header: { text: '시설(업체명)' } },
// 외부위탁 처리량(톤/년)
{ name: 'DSP_CONSIGNMENT', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g1', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g2', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g3', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g4', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g5', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g6', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'g7', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } },
{
name: 'otsdCnsgnWhol',
fieldName: 'otsdCnsgnWhol',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'otsdCnsgnRclmQnt',
'otsdCnsgnIncnrQnt',
'otsdCnsgnFuelPwplQnt',
'otsdCnsgnPrdctRmtrlQnt',
'otsdCnsgnFmlyrQnt',
'otsdCnsgnCmpstgQnt',
],
type: 'sum' as const,
}
},
{ name: 'otsdCnsgnRclmQnt', fieldName: 'otsdCnsgnRclmQnt', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnIncnrQnt', fieldName: 'otsdCnsgnIncnrQnt', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnFuelPwplQnt', fieldName: 'otsdCnsgnFuelPwplQnt', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnPrdctRmtrlQnt', fieldName: 'otsdCnsgnPrdctRmtrlQnt', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnFmlyrQnt', fieldName: 'otsdCnsgnFmlyrQnt', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnCmpstgQnt', fieldName: 'otsdCnsgnCmpstgQnt', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'otsdCnsgnFcltNm', fieldName: 'otsdCnsgnFcltNm', header: { text: '시설(업체명)' } },
// 수도권광역위탁처리량
{ name: 'DSP_WIDE', fieldName: '수도권광역위탁처리량(톤/년)', header: { text: '수도권광역위탁처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'cparTs1Whol', fieldName: 'cparTs1Whol', header: { text: '수도권광역위탁처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: Check fields
{ name: 'CHK_TOT_AMNT', fieldName: 'CHK_TOT_AMNT', header: { text: 'CHK_TOT_AMNT' }, visible: false },
{ name: 'CHK_P_AMNT', fieldName: 'CHK_P_AMNT', header: { text: 'CHK_P_AMNT' }, visible: false },
{ name: 'CHK_OUT_AMNT', fieldName: 'CHK_OUT_AMNT', header: { text: 'CHK_OUT_AMNT' }, visible: false },
{ name: 'CHK_DSP_MOIRAT', fieldName: 'CHK_DSP_MOIRAT', header: { text: 'CHK_DSP_MOIRAT' }, visible: false },
{ name: 'CHK_DSP_SM', fieldName: 'CHK_DSP_SM', header: { text: 'CHK_DSP_SM' }, visible: false },
{ name: 'CHK_DSP_SELF_SM', fieldName: 'CHK_DSP_SELF_SM', header: { text: 'CHK_DSP_SELF_SM' }, visible: false },
{ name: 'CHK_O_DSP_RCLM', fieldName: 'CHK_O_DSP_RCLM', header: { text: 'CHK_O_DSP_RCLM' }, visible: false },
{ name: 'CHK_O_DSP_INCNR', fieldName: 'CHK_O_DSP_INCNR', header: { text: 'CHK_O_DSP_INCNR' }, visible: false },
{ name: 'CHK_DSP_DESIC', fieldName: 'CHK_DSP_DESIC', header: { text: 'CHK_DSP_DESIC' }, visible: false },
{ name: 'CHK_DSP_CONSIGNMENT', fieldName: 'CHK_DSP_CONSIGNMENT', header: { text: 'CHK_DSP_CONSIGNMENT' }, visible: false },
{ name: 'CHK_DSP_WIDE', fieldName: 'CHK_DSP_WIDE', header: { text: 'CHK_DSP_WIDE' }, visible: false },
{ name: 'CHK_OUT_FCT_NM', fieldName: 'CHK_OUT_FCT_NM', header: { text: 'CHK_OUT_FCT_NM' }, visible: false },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설물일련번호' }, visible: false },
{ name: 'swtrmtFcltCd', fieldName: 'swtrmtFcltCd', header: { text: '처리장코드' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO_CD', 'ADST_CD']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'BSC_FCT_NM',
'swtrmtFcltNm',
{
name: 'group_occur_amount',
header: { text: '찌꺼기발생량(톤/년, 탈수기준)' },
items: [
'TOT_AMNT',
'P_AMNT',
'OUT_AMNT'
'gnamWhol',
'itslfQnt',
'otsdInflwQnt'
]
},
'OUT_FCT_NM',
'a',
'DSP_MOIRAT',
'DSP_SM',
'transfTmplntNm',
'transfPrcsQnt',
'srpFncRt',
'wholSrpPrcsQnt',
{
name: 'group_self_treatment',
header: { text: '자체 찌꺼기 처리량(톤/년)' },
items: [
'DSP_SELF_SM',
'O_DSP_RCLM',
'O_DSP_INCNR',
'itslfTs1Whol',
'itslfTs1RclmQnt',
'itslfTs1IncnrQnt',
{
name: 'group_incinerate_2nd',
header: { text: '소각 후 처리(2차)' },
items: [
'b1',
'b2',
'b3',
'b4',
'b5'
'incnrPrcsWhol',
'incnrPrcsLfvlm',
'incnrPrcsPrdctRmtrlQnt',
'incnrPrcsFmlyrQnt',
'incnrPrcsFcltNm'
]
},
'c1',
'itslfTs1AltdQnt',
{
name: 'group_solidify_2nd',
header: { text: '고화 후 처리(2차)' },
items: [
'c2',
'c3',
'c4',
'c5'
'stbznPrcsWhol',
'stbznPrcsLfvlm',
'stbznPrcsFmlyrQnt',
'stbznPrcsFcltNm'
]
},
'DSP_DESIC',
'itslfTs1DryQnt',
{
name: 'group_drying_2nd',
header: { text: '건조 후 처리(2차)' },
items: [
'd1',
'd2',
'd3',
'd4',
'd5',
'd6',
'd7',
'd8'
'dryPrcsWhol',
'dryPrcsLfvlm',
'dryPrcsIncnrQnt',
'dryPrcsFuelQnt',
'dryPrcsPrdctRmtrlQnt',
'dryPrcsFmlyrQnt',
'dryPrcsCmpstgQnt',
'dryPrcsFcltNm'
]
},
'e1',
'itslfTs1FuelQnt',
{
name: 'group_carbonize_2nd',
header: { text: '탄화 후 처리(2차)' },
items: [
'e2',
'e3',
'e4',
'e5'
'fuelPrcsWhol',
'fuelPrcsIncnrQnt',
'fuelPrcsFuelQnt',
'fuelPrcsFcltNm'
]
},
'f1',
'f2'
'itslfTs2CmpstgQnt',
'cmpsPrcsFcltBzentyNm'
]
},
{
name: 'group_external_treatment',
header: { text: '외부위탁 처리량(톤/년)' },
items: [
'DSP_CONSIGNMENT',
'g1',
'g2',
'g3',
'g4',
'g5',
'g6',
'g7'
'otsdCnsgnWhol',
'otsdCnsgnRclmQnt',
'otsdCnsgnIncnrQnt',
'otsdCnsgnFuelPwplQnt',
'otsdCnsgnPrdctRmtrlQnt',
'otsdCnsgnFmlyrQnt',
'otsdCnsgnCmpstgQnt',
'otsdCnsgnFcltNm'
]
},
'DSP_WIDE',
'RMK'
'cparTs1Whol',
'rmrk',
'admdstCd',
'admdstNm',
'fcltSn',
'swtrmtFcltCd'
],
rowStartNum: 3,
fixedColumns: 2,
tabKey: 'slurgeProcessingFacStatusInp',
tabKey: 'ST00110001',
tabLabel: '찌꺼기(슬러지)처리시설현황'
};
@@ -10,7 +10,7 @@ export const AdminAreaConnectionInpColumn = {
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false },
// 연결여부
{ name: 'isConnection', fieldName: 'isConnection', header: { text: '연결여부' }, editable: false },
{ name: 'isAreaConnection', fieldName: 'isAreaConnection', header: { text: '연결여부' }, editable: false },
// 연결하수처리장
{ name: 'connectionBscFctNmList', fieldName: 'connectionBscFctNmList', header: { text: '연결하수처리장' }, editable: false },
{
@@ -28,7 +28,7 @@ export const AdminAreaConnectionInpColumn = {
header: { text: '행정구역' },
items: ['ctpvNm', 'sggNm', 'admdstNm'],
},
'isConnection',
'isAreaConnection',
'connectionBscFctNmList',
'admdstCd'
],
@@ -1,6 +1,33 @@
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import { footerInt, footerDecimal1, footerDecimal2, footerDecimal3 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
const RAINFALL_PUMP_FCLTY_KIND_VALUES = ["해당없음", "제1종", "제2종", "제3종"] as const;
const RAINFALL_PUMP_YN_VALUES = ["유", "무"] as const;
function dropdownColumn(
col: Pick<ColumnConfig, "name" | "fieldName" | "header"> &
Partial<ColumnConfig>,
options: readonly string[],
): ColumnConfig {
const values = [...options];
return {
...col,
values,
labels: values,
lookupDisplay: true,
editor: {
type: "dropdown",
domainOnly: true,
textReadOnly: true,
commitOnSelect: true,
dropDownWhenClick: true,
values,
labels: values,
},
};
}
// 하수도 통계 관리 > 04.하수저류시설 및 빗물펌프장 > tab 2: 빗물펌프장
export const RainfallPumpStationInpColumn = {
columns: [
@@ -10,7 +37,10 @@ export const RainfallPumpStationInpColumn = {
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' } },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '위치' } },
{ name: 'fcltyKndCn', fieldName: 'fcltyKndCn', header: { text: '시설물종류' } },
dropdownColumn(
{ name: 'fcltyKndCn', fieldName: 'fcltyKndCn', header: { text: '시설물종류' } },
RAINFALL_PUMP_FCLTY_KIND_VALUES,
),
{
name: 'oprtngStrtYmd',
@@ -29,14 +59,46 @@ export const RainfallPumpStationInpColumn = {
{ name: 'wtrStrgZoneArea', fieldName: 'wtrStrgZoneArea', header: { text: '집수구역면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
// 유수지
{ name: 'rsvrYn', fieldName: 'rsvrYn', header: { text: '유수지유무(유,무)' } },
dropdownColumn(
{ name: 'rsvrYn', fieldName: 'rsvrYn', header: { text: '유수지유무(유,무)' } },
RAINFALL_PUMP_YN_VALUES,
),
{ name: 'rsvrImpndQnt', fieldName: 'rsvrImpndQnt', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 흡수정
{ name: 'abstnRgnImpdQnt', fieldName: 'abstnRgnImpdQnt', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 펌프시설 > 총양수량(m³/min)
{ name: 'grssPpqty', fieldName: 'grssPpqty', header: { text: '총양수량(m³/min)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'grssPpqty',
fieldName: 'grssPpqty',
header: { text: '총양수량(m³/min)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'pumpFcltN1PumpCnt',
rsrvPumpCntField: 'pumpFcltN1RsrvPumpCnt',
perPpqtyField: 'pumpFcltN1PerPpqty',
},
{
pumpCntField: 'pumpFcltN2PumpCnt',
rsrvPumpCntField: 'pumpFcltN2RsrvPumpCnt',
perPpqtyField: 'pumpFcltN2PerPpqty',
},
{
pumpCntField: 'pumpFcltN3PumpCnt',
rsrvPumpCntField: 'pumpFcltN3RsrvPumpCnt',
perPpqtyField: 'pumpFcltN3PerPpqty',
},
],
},
},
// 펌프시설1
{ name: 'pumpFcltN1DmtrExpln', fieldName: 'pumpFcltN1DmtrExpln', header: { text: '구경(mm)' } },
@@ -57,9 +119,18 @@ export const RainfallPumpStationInpColumn = {
{ name: 'pumpFcltN3RsrvPumpCnt', fieldName: 'pumpFcltN3RsrvPumpCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 부대시설
{ name: 'prcsgFcltYn', fieldName: 'prcsgFcltYn', header: { text: '전처리시설유무(유/무)' } },
{ name: 'ddrztnEqpmYn', fieldName: 'ddrztnEqpmYn', header: { text: '탈취설비유무(유/무)' } },
{ name: 'oslfEtgtYn', fieldName: 'oslfEtgtYn', header: { text: '자가발전유무(유/무)' } },
dropdownColumn(
{ name: 'prcsgFcltYn', fieldName: 'prcsgFcltYn', header: { text: '전처리시설유무(유/무)' } },
RAINFALL_PUMP_YN_VALUES,
),
dropdownColumn(
{ name: 'ddrztnEqpmYn', fieldName: 'ddrztnEqpmYn', header: { text: '탈취설비유무(유/무)' } },
RAINFALL_PUMP_YN_VALUES,
),
dropdownColumn(
{ name: 'oslfEtgtYn', fieldName: 'oslfEtgtYn', header: { text: '자가발전유무(유/무)' } },
RAINFALL_PUMP_YN_VALUES,
),
// 방류수역
{ name: 'dschgWtrsExpln', fieldName: 'dschgWtrsExpln', header: { text: '방류수역' } },
@@ -11,6 +11,10 @@ export const SEWAGE_RESERVOIR_FCLT_STRCT_VALUES = [
"지하터널식",
"기타",
] as const;
export const SEWAGE_RESERVOIR_YN_VALUES = ["유", "무"] as const;
export const SEWAGE_RESERVOIR_MLTV_MTH_VALUES = ["자연배수", "강제배수"] as const;
export const SEWAGE_RESERVOIR_IMPD_MTH_VALUES = ["직렬", "병렬"] as const;
export const SEWAGE_RESERVOIR_IMPD_PRCS_MTHD_VALUES = ["자체처리", "연계처리"] as const;
function dropdownColumn(
col: Pick<ColumnConfig, "name" | "fieldName" | "header"> &
@@ -99,7 +103,36 @@ export const SewageReservoirInpColumn = {
{ name: 'impdFcltCpct', fieldName: 'impdFcltCpct', header: { text: '시설용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 펌프시설 > 총양수량
{ name: 'grssPpqty', fieldName: 'grssPpqty', header: { text: '총양수량(m³/min)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'grssPpqty',
fieldName: 'grssPpqty',
header: { text: '총양수량(m³/min)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'pumpFcltN1PumpCnt',
rsrvPumpCntField: 'pumpFcltN1RsrvPumpCnt',
perPpqtyField: 'pumpFcltN1PerPpqty',
},
{
pumpCntField: 'pumpFcltN2PumpCnt',
rsrvPumpCntField: 'pumpFcltN2RsrvPumpCnt',
perPpqtyField: 'pumpFcltN2PerPpqty',
},
{
pumpCntField: 'pumpFcltN3PumpCnt',
rsrvPumpCntField: 'pumpFcltN3RsrvPumpCnt',
perPpqtyField: 'pumpFcltN3PerPpqty',
},
],
},
},
// 펌프시설 1
{ name: 'pumpFcltN1DmtrExpln', fieldName: 'pumpFcltN1DmtrExpln', header: { text: '구경(mm)' } },
@@ -120,18 +153,60 @@ export const SewageReservoirInpColumn = {
{ name: 'pumpFcltN3RsrvPumpCnt', fieldName: 'pumpFcltN3RsrvPumpCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 부대시설
{ name: 'prcsgFcltYn', fieldName: 'prcsgFcltYn', header: { text: '전처리시설유무(유/무)' } },
{ name: 'ddrztnEqpmYn', fieldName: 'ddrztnEqpmYn', header: { text: '탈취설비유무(유/무)' } },
{ name: 'oslfEtgtYn', fieldName: 'oslfEtgtYn', header: { text: '자가발전유무(유/무)' } },
dropdownColumn(
{
name: "prcsgFcltYn",
fieldName: "prcsgFcltYn",
header: { text: "전처리시설유무(유/무)" },
},
SEWAGE_RESERVOIR_YN_VALUES,
),
dropdownColumn(
{
name: "ddrztnEqpmYn",
fieldName: "ddrztnEqpmYn",
header: { text: "탈취설비유무(유/무)" },
},
SEWAGE_RESERVOIR_YN_VALUES,
),
dropdownColumn(
{
name: "oslfEtgtYn",
fieldName: "oslfEtgtYn",
header: { text: "자가발전유무(유/무)" },
},
SEWAGE_RESERVOIR_YN_VALUES,
),
// 배수 방식
{ name: 'mltvMthCn', fieldName: 'mltvMthCn', header: { text: '배수방식(자연배수,강제배수)' } },
dropdownColumn(
{
name: "mltvMthCn",
fieldName: "mltvMthCn",
header: { text: "배수방식(자연배수,강제배수)" },
},
SEWAGE_RESERVOIR_MLTV_MTH_VALUES,
),
// 저류 방식
{ name: 'impdMthCn', fieldName: 'impdMthCn', header: { text: '저류방식(직렬,병렬)' } },
dropdownColumn(
{
name: "impdMthCn",
fieldName: "impdMthCn",
header: { text: "저류방식(직렬,병렬)" },
},
SEWAGE_RESERVOIR_IMPD_MTH_VALUES,
),
// 저류수 처리방법
{ name: 'impdPrcsMthdCn', fieldName: 'impdPrcsMthdCn', header: { text: '저류수처리방법(자체처리,연계처리)' } },
dropdownColumn(
{
name: "impdPrcsMthdCn",
fieldName: "impdPrcsMthdCn",
header: { text: "저류수처리방법(자체처리,연계처리)" },
},
SEWAGE_RESERVOIR_IMPD_PRCS_MTHD_VALUES,
),
// 방류 수역
{ name: 'dschgWtrsExpln', fieldName: 'dschgWtrsExpln', header: { text: '방류수역(또는 연계처리장명)' } },
@@ -5,46 +5,72 @@ import { ValueType } from "realgrid";
export const CauserBurdenInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 부과액
{ name: 'LEVY_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'LEVY_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'levyEvntcsCnt', fieldName: 'levyEvntcsCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'levyAmt', fieldName: 'levyAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 대상별 징수현황 > 계
{ name: 'TRGET_LEV_STTUS_CO', fieldName: '건수', header: { text: '건수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'TRGET_LEV_STTUS_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'trgtByWholClctnEvntcsCnt',
fieldName: 'trgtByWholClctnEvntcsCnt',
header: { text: '건수' },
editable: false,
footer: footerInt,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
computed: {
fields: ['mltvEqpmEvntcsCnt', 'othrCstrnOthrActrCnt'],
type: 'sum' as const,
},
},
{
name: 'trgtByWholClctnAmt',
fieldName: 'trgtByWholClctnAmt',
header: { text: '금액(백만원)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['mltvEqpmAmt', 'othrCstrnOthrActrAmt'],
type: 'sum' as const,
},
},
// 대상별 징수현황 > 배수설비설치자
{ name: 'DRNG_EQP_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'DRNG_EQP_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'mltvEqpmEvntcsCnt', fieldName: 'mltvEqpmEvntcsCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mltvEqpmAmt', fieldName: 'mltvEqpmAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 대상별 징수현황 > 타공사·타행위자
{ name: 'CNTRWK_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CNTRWK_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'othrCstrnOthrActrCnt', fieldName: 'othrCstrnOthrActrCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'othrCstrnOthrActrAmt', fieldName: 'othrCstrnOthrActrAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
// 참조 필드
{ name: 'AMNTBRNCSR', fieldName: 'AMNTBRNCSR', header: { text: 'AMNTBRNCSR' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_assess',
header: { text: '부과액' },
items: ['LEVY_CO', 'LEVY_AMOUNT']
items: ['levyEvntcsCnt', 'levyAmt']
},
{
name: 'group_collection',
@@ -53,25 +79,27 @@ export const CauserBurdenInpColumn = {
{
name: 'coll_group_total',
header: { text: '계' },
items: ['TRGET_LEV_STTUS_CO', 'TRGET_LEV_STTUS_AMOUNT']
items: ['trgtByWholClctnEvntcsCnt', 'trgtByWholClctnAmt']
},
{
name: 'coll_group_inst',
header: { text: '배수설비설치자(하수도법 제61조 제1항)' },
items: ['DRNG_EQP_CO', 'DRNG_EQP_AMOUNT']
items: ['mltvEqpmEvntcsCnt', 'mltvEqpmAmt']
},
{
name: 'coll_group_otr',
header: { text: '타공사·타행위자(하수도법 제61조 제2항)' },
items: ['CNTRWK_CO', 'CNTRWK_AMOUNT']
items: ['othrCstrnOthrActrCnt', 'othrCstrnOthrActrAmt']
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'causerBurdenInp',
tabKey: 'ST00090004',
tabLabel: '원인자 부담금'
};
@@ -5,45 +5,86 @@ import { ValueType } from "realgrid";
export const SewerageChargeInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 요금
{ name: 'FYER_LEVY_QY', fieldName: '연간조정량(m³)(A)', header: { text: '연간조정량(m³)(A)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'LEVY_AMOUNT', fieldName: '하수도사용료수익(원)(B)', header: { text: '하수도사용료수익(원)(B)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'AVRG_UNTPC', fieldName: 'm³당요금(원/m³) C=(B/A)', header: { text: 'm³당요금(원/m³) C=(B/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'anlLevyQnt', fieldName: 'anlLevyQnt', header: { text: '연간조정량(m³)(A)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'levyAmt', fieldName: 'levyAmt', header: { text: '하수도사용료수익(원)(B)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'avgUntprcAmt',
fieldName: 'avgUntprcAmt',
header: { text: 'm³당요금(원/m³) C=(B/A)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['levyAmt', 'anlLevyQnt'],
type: 'quotient' as const,
},
},
{ name: 'SMRIZE_PRMPC', fieldName: '총괄원가(원)(D)', header: { text: '총괄원가(원)(D)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SMRIZE_UNIT_PRMPC', fieldName: 'm³당원가(원/m³) E=(D/A)', header: { text: 'm³당원가(원/m³) E=(D/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'RLTY_RT', fieldName: '요금현실화율(%) F=(C/E*100)', header: { text: '요금현실화율(%) F=(C/E*100)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'grssPrmcoAmt', fieldName: 'grssPrmcoAmt', header: { text: '총괄원가(원)(D)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'grssUnitPrmcoAmt',
fieldName: 'grssUnitPrmcoAmt',
header: { text: 'm³당원가(원/m³) E=(D/A)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['grssPrmcoAmt', 'anlLevyQnt'],
type: 'quotient' as const,
},
},
{
name: 'rlztnRt',
fieldName: 'rlztnRt',
header: { text: '요금현실화율(%) F=(C/E*100)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
numeratorQuotient: ['levyAmt', 'anlLevyQnt'] as [string, string],
denominatorQuotient: ['grssPrmcoAmt', 'anlLevyQnt'] as [string, string],
type: 'quotientRatioPercent' as const,
},
},
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 참조 필드
{ name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'FYER_LEVY_QY',
'LEVY_AMOUNT',
'AVRG_UNTPC',
'SMRIZE_PRMPC',
'SMRIZE_UNIT_PRMPC',
'RLTY_RT',
'RMK'
'anlLevyQnt',
'levyAmt',
'avgUntprcAmt',
'grssPrmcoAmt',
'grssUnitPrmcoAmt',
'rlztnRt',
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'sewerageChargeInp',
tabKey: 'ST00090002',
tabLabel: '하수도 요금'
};
@@ -5,146 +5,238 @@ import { ValueType } from "realgrid";
export const SewerageFinanceSettleInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 재정별 사업비
{ name: 'FNANC_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'NTNXPN', fieldName: '중앙정부지원액', header: { text: '중앙정부지원액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'fnnByWholPjtcoAmt',
fieldName: 'fnnByWholPjtcoAmt',
header: { text: '계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'cnterGovSprtAmt',
'mctpvLcexpAmt',
'wareaSggLclAmt',
'sewerUseAmt',
'orgprBrdnAmt',
'etcIncmAmt',
'crfwAmt',
],
type: 'sum' as const,
},
},
{ name: 'cnterGovSprtAmt', fieldName: 'cnterGovSprtAmt', header: { text: '중앙정부지원액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 재정별 사업비 > 지방비
{ name: 'LCLEXPNS', fieldName: '특·광역시, 도', header: { text: '특·광역시, 도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'LCLTY_CT', fieldName: '시,군,구 지자체', header: { text: '시,군,구 지자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'mctpvLcexpAmt', fieldName: 'mctpvLcexpAmt', header: { text: '특·광역시, 도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'wareaSggLclAmt', fieldName: 'wareaSggLclAmt', header: { text: '시,군,구 지자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 재정별 사업비
{ name: 'SEWER_RNTFEE', fieldName: '하수도사용료', header: { text: '하수도사용료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'AMNTBRNCSR', fieldName: '원인자부담금', header: { text: '원인자부담금' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_INCME', fieldName: '기타잡수입', header: { text: '기타잡수입' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'CYFD_AMOUNT', fieldName: '이월액', header: { text: '이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sewerUseAmt', fieldName: 'sewerUseAmt', header: { text: '하수도사용료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'orgprBrdnAmt', fieldName: 'orgprBrdnAmt', header: { text: '원인자부담금' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcIncmAmt', fieldName: 'etcIncmAmt', header: { text: '기타잡수입' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'crfwAmt', fieldName: 'crfwAmt', header: { text: '이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 부채액
{ name: 'DEBT_AMOUNT', fieldName: '부채액(백만원)', header: { text: '부채액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'lbltAmt', fieldName: 'lbltAmt', header: { text: '부채액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 계
{ name: 'PRPOS_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'usgByWholPjtcoAmt',
fieldName: 'usgByWholPjtcoAmt',
header: { text: '계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
},
// 용도별사업비 > 처리장
{ name: 'PRCPLT_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'tmplntWholAmt',
fieldName: 'tmplntWholAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['tmplntFcltAmt', 'tmplntMendngAmt', 'tmplntOperKeepAmt', 'tmplntEtcAmt'],
type: 'sum' as const,
},
},
{ name: 'tmplntFcltAmt', fieldName: 'tmplntFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntMendngAmt', fieldName: 'tmplntMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntOperKeepAmt', fieldName: 'tmplntOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntEtcAmt', fieldName: 'tmplntEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 하수관로
{ name: 'SEWAGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholSwagePplAmt',
fieldName: 'wholSwagePplAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'swagePplFcltAmt',
'swagePplMendngAmt',
'swagePplOperKeepAmt',
'swagePplEtcAmt',
],
type: 'sum' as const,
},
},
{ name: 'swagePplFcltAmt', fieldName: 'swagePplFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplMendngAmt', fieldName: 'swagePplMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplOperKeepAmt', fieldName: 'swagePplOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplEtcAmt', fieldName: 'swagePplEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 하수저류시설
{ name: 'SWGE_STRGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholSwageImpdFcltAmt',
fieldName: 'wholSwageImpdFcltAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'swageImpdFcltAmt',
'swageImpdMendngAmt',
'swageImpdOperKeepAmt',
'swageImpdFcltEtcAmt',
],
type: 'sum' as const,
},
},
{ name: 'swageImpdFcltAmt', fieldName: 'swageImpdFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdMendngAmt', fieldName: 'swageImpdMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdOperKeepAmt', fieldName: 'swageImpdOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdFcltEtcAmt', fieldName: 'swageImpdFcltEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 기타
{ name: 'ETC_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ACDNT_CYFD_AMOUNT', fieldName: '사고이월액', header: { text: '사고이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'UNRCIV_AMOUNT', fieldName: '미수납액', header: { text: '미수납액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_AMOUNT', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'etcWholAmt',
fieldName: 'etcWholAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['etcAcdntCrfwAmt', 'etcNrcpAmt', 'etcAmt'],
type: 'sum' as const,
},
},
{ name: 'etcAcdntCrfwAmt', fieldName: 'etcAcdntCrfwAmt', header: { text: '사고이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcNrcpAmt', fieldName: 'etcNrcpAmt', header: { text: '미수납액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcAmt', fieldName: 'etcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 참조 필드
{ name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'D_LEVY_AMOUNT', fieldName: 'D_LEVY_AMOUNT', header: { text: 'D_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_finance',
header: { text: '재정별 사업비(백만원)' },
items: [
'FNANC_WCT_SM',
'NTNXPN',
'fnnByWholPjtcoAmt',
'cnterGovSprtAmt',
{
name: 'group_local_finance',
header: { text: '지방비' },
items: ['LCLEXPNS', 'LCLTY_CT']
items: ['mctpvLcexpAmt', 'wareaSggLclAmt']
},
'SEWER_RNTFEE',
'AMNTBRNCSR',
'ETC_INCME',
'CYFD_AMOUNT'
'sewerUseAmt',
'orgprBrdnAmt',
'etcIncmAmt',
'crfwAmt'
]
},
'DEBT_AMOUNT',
'lbltAmt',
{
name: 'group_purpose',
header: { text: '용도별사업비(백만원)' },
items: [
'PRPOS_WCT_SM',
'usgByWholPjtcoAmt',
{
name: 'group_treatment',
header: { text: '처리장' },
items: [
'PRCPLT_SM',
'PRCPLT_FCLTY',
'PRCPLT_RPAIR',
'PRCPLT_ORPNS',
'PRCPLT_ETC'
'tmplntWholAmt',
'tmplntFcltAmt',
'tmplntMendngAmt',
'tmplntOperKeepAmt',
'tmplntEtcAmt'
]
},
{
name: 'group_pipe',
header: { text: '하수관로' },
items: [
'SEWAGE_SM',
'SEWAGE_FCLTY',
'SEWAGE_RPAIR',
'SEWAGE_ORPNS',
'SEWAGE_ETC'
'wholSwagePplAmt',
'swagePplFcltAmt',
'swagePplMendngAmt',
'swagePplOperKeepAmt',
'swagePplEtcAmt'
]
},
{
name: 'group_storage',
header: { text: '하수저류시설' },
items: [
'SWGE_STRGE_SM',
'SWGE_STRGE_FCLTY',
'SWGE_STRGE_RPAIR',
'SWGE_STRGE_ORPNS',
'SWGE_STRGE_ETC'
'wholSwageImpdFcltAmt',
'swageImpdFcltAmt',
'swageImpdMendngAmt',
'swageImpdOperKeepAmt',
'swageImpdFcltEtcAmt'
]
},
{
name: 'group_etc_purpose',
header: { text: '기타' },
items: [
'ETC_SM',
'ACDNT_CYFD_AMOUNT',
'UNRCIV_AMOUNT',
'ETC_AMOUNT'
'etcWholAmt',
'etcAcdntCrfwAmt',
'etcNrcpAmt',
'etcAmt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewerageFinanceSettleInp',
tabKey: 'ST00090001',
tabLabel: '하수도재정 결산'
};
@@ -5,67 +5,108 @@ import { ValueType } from "realgrid";
export const SewerageUsageFeeInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 총계 > 부과액
{ name: 'LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'levyAmt',
fieldName: 'levyAmt',
header: { text: '(백만원)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: [
'hshusLevyAmt',
'taskFrLevyAmt',
'fbuLevyAmt',
'bthhsFrSpcs1LevyAmt',
'bthhsFrSpcs2LevyAmt',
'induLevyAmt',
'etcLevyAmt',
],
type: 'sum' as const,
},
},
// 총계 > 부과량
{ name: 'LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'levyQnt',
fieldName: 'levyQnt',
header: { text: '(천톤)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: [
'hshusLevyQnt',
'taskFrLevyQnt',
'fbuLevyQnt',
'bthhsFrSpcs1LevyQnt',
'bthhsFrSpcs2LevyQnt',
'induLevyQnt',
'etcLevyQnt',
],
type: 'sum' as const,
},
},
// 가정용
{ name: 'HOME_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'HOME_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'HOME_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusLevyAmt', fieldName: 'hshusLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusLevyQnt', fieldName: 'hshusLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusUntprcAmt', fieldName: 'hshusUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 업무용
{ name: 'JOB_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'JOB_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'JOB_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrLevyAmt', fieldName: 'taskFrLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrLevyQnt', fieldName: 'taskFrLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrUntprcAmt', fieldName: 'taskFrUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 영업용(일반용)
{ name: 'BSN_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BSN_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BSN_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuLevyAmt', fieldName: 'fbuLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuLevyQnt', fieldName: 'fbuLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuUntprcAmt', fieldName: 'fbuUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 욕탕1종(대중탕용)
{ name: 'BATHS1_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BATHS1_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BATHS1_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1LevyAmt', fieldName: 'bthhsFrSpcs1LevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1LevyQnt', fieldName: 'bthhsFrSpcs1LevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1UntprcAmt', fieldName: 'bthhsFrSpcs1UntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 욕탕2종
{ name: 'PBBATH_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PBBATH_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PBBATH_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2LevyAmt', fieldName: 'bthhsFrSpcs2LevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2LevyQnt', fieldName: 'bthhsFrSpcs2LevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2UntprcAmt', fieldName: 'bthhsFrSpcs2UntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 산업 및 공업용
{ name: 'INDUST_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'INDUST_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'INDUST_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induLevyAmt', fieldName: 'induLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induLevyQnt', fieldName: 'induLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induUntprcAmt', fieldName: 'induUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 기타
{ name: 'ETC_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyAmt', fieldName: 'etcLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyQnt', fieldName: 'etcLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyUntprcAmt', fieldName: 'etcLevyUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
// 참조 필드
{ name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' } },
{ name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_total',
@@ -74,12 +115,12 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'total_amount',
header: { text: '부과액' },
items: ['LEVY_AMOUNT']
items: ['levyAmt']
},
{
name: 'total_qty',
header: { text: '부과량' },
items: ['LEVY_QY']
items: ['levyQnt']
}
]
},
@@ -90,17 +131,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'home_amount',
header: { text: '부과액' },
items: ['HOME_LEVY_AMOUNT']
items: ['hshusLevyAmt']
},
{
name: 'home_qty',
header: { text: '부과량' },
items: ['HOME_LEVY_QY']
items: ['hshusLevyQnt']
},
{
name: 'home_price',
header: { text: '단가' },
items: ['HOME_UNTPC']
items: ['hshusUntprcAmt']
}
]
},
@@ -111,17 +152,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'job_amount',
header: { text: '부과액' },
items: ['JOB_LEVY_AMOUNT']
items: ['taskFrLevyAmt']
},
{
name: 'job_qty',
header: { text: '부과량' },
items: ['JOB_LEVY_QY']
items: ['taskFrLevyQnt']
},
{
name: 'job_price',
header: { text: '단가' },
items: ['JOB_UNTPC']
items: ['taskFrUntprcAmt']
}
]
},
@@ -132,17 +173,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'bsn_amount',
header: { text: '부과액' },
items: ['BSN_LEVY_AMOUNT']
items: ['fbuLevyAmt']
},
{
name: 'bsn_qty',
header: { text: '부과량' },
items: ['BSN_LEVY_QY']
items: ['fbuLevyQnt']
},
{
name: 'bsn_price',
header: { text: '단가' },
items: ['BSN_UNTPC']
items: ['fbuUntprcAmt']
}
]
},
@@ -153,17 +194,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'baths1_amount',
header: { text: '부과액' },
items: ['BATHS1_LEVY_AMOUNT']
items: ['bthhsFrSpcs1LevyAmt']
},
{
name: 'baths1_qty',
header: { text: '부과량' },
items: ['BATHS1_LEVY_QY']
items: ['bthhsFrSpcs1LevyQnt']
},
{
name: 'baths1_price',
header: { text: '단가' },
items: ['BATHS1_UNTPC']
items: ['bthhsFrSpcs1UntprcAmt']
}
]
},
@@ -174,17 +215,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'pbbath_amount',
header: { text: '부과액' },
items: ['PBBATH_LEVY_AMOUNT']
items: ['bthhsFrSpcs2LevyAmt']
},
{
name: 'pbbath_qty',
header: { text: '부과량' },
items: ['PBBATH_LEVY_QY']
items: ['bthhsFrSpcs2LevyQnt']
},
{
name: 'pbbath_price',
header: { text: '단가' },
items: ['PBBATH_UNTPC']
items: ['bthhsFrSpcs2UntprcAmt']
}
]
},
@@ -195,17 +236,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'indust_amount',
header: { text: '부과액' },
items: ['INDUST_LEVY_AMOUNT']
items: ['induLevyAmt']
},
{
name: 'indust_qty',
header: { text: '부과량' },
items: ['INDUST_LEVY_QY']
items: ['induLevyQnt']
},
{
name: 'indust_price',
header: { text: '단가' },
items: ['INDUST_UNTPC']
items: ['induUntprcAmt']
}
]
},
@@ -216,25 +257,27 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'etc_amount',
header: { text: '부과액' },
items: ['ETC_LEVY_AMOUNT']
items: ['etcLevyAmt']
},
{
name: 'etc_qty',
header: { text: '부과량' },
items: ['ETC_LEVY_QY']
items: ['etcLevyQnt']
},
{
name: 'etc_price',
header: { text: '단가' },
items: ['ETC_UNTPC']
items: ['etcLevyUntprcAmt']
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewerageUsageFeeInp',
tabKey: 'ST00090003',
tabLabel: '하수도 사용료'
};
@@ -1,80 +1,105 @@
import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
const COMPLAINT_TYPE_COUNT_FIELDS = [
'dschgWqCnt',
'odorCnt',
'ldsCnt',
'plltnAcdntCnt',
'swagePplCnt',
'sewerCrgAmt',
'etcCnt',
] as const;
// 하수도 통계 관리 > 13. 하수도 서비스 수준 > tab 1: 민원
export const ComplaintInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 행정코드 (hidden)
{ name: 'ADST_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false },
{ name: 'ADST_DONG', fieldName: '행정구역명', header: { text: '행정구역명' }, visible: false },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 하수처리인구(명)
{ name: 'SWGE_PROC_POPLTN', fieldName: '하수처리인구(명)', header: { text: '하수처리인구(명)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'swtrmtPpltnCnt', fieldName: 'swtrmtPpltnCnt', header: { text: '하수처리인구(명)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0', styleName: 'rg-cell-readonly' },
// 민원유형(건)
{ name: 'CVPL_CM', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_BSML', fieldName: '악취', header: { text: '악취' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_DCWTR_QLTWTR', fieldName: '하수관로', header: { text: '하수관로' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_FIVW', fieldName: '배수설비', header: { text: '배수설비' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_POLLUTN_ACDNT', fieldName: '맨홀', header: { text: '맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_SSOS', fieldName: '정화조', header: { text: '정화조' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_CHRGE', fieldName: '하수도요금', header: { text: '하수도요금' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CVPL_UNKD', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'whol',
fieldName: 'whol',
header: { text: '총계' },
editable: false,
styleName: 'rg-cell-total',
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
computed: {
fields: [...COMPLAINT_TYPE_COUNT_FIELDS],
type: 'sum' as const,
},
},
{ name: 'dschgWqCnt', fieldName: 'dschgWqCnt', header: { text: '악취' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'odorCnt', fieldName: 'odorCnt', header: { text: '하수관로' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ldsCnt', fieldName: 'ldsCnt', header: { text: '배수설비' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'plltnAcdntCnt', fieldName: 'plltnAcdntCnt', header: { text: '맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'swagePplCnt', fieldName: 'swagePplCnt', header: { text: '정화조' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sewerCrgAmt', fieldName: 'sewerCrgAmt', header: { text: '하수도요금' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'etcCnt', fieldName: 'etcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 기준인구당 민원건수(건/10,000명)
{ name: 'STDR_POPLTN_PER_CVPL', fieldName: '기준인구당민원건수(건/10,000명)', header: { text: '기준인구당민원건수(건/10,000명)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 기준인구당 민원건수(건/10,000명) = 민원유형 합계 / 하수처리인구 × 10,000
{
name: 'crtrPpltnCvlcptEvntcsCnt',
fieldName: 'crtrPpltnCvlcptEvntcsCnt',
header: { text: '기준인구당민원건수(건/10,000명)' },
editable: false,
styleName: 'rg-cell-total',
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
numeratorFields: [...COMPLAINT_TYPE_COUNT_FIELDS],
denominatorField: 'swtrmtPpltnCnt',
type: 'sumPerTenThousand' as const,
},
},
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden: 의견 필드
{ name: 'WRTER_OPINION', fieldName: '작성자의견', header: { text: '작성자의견' }, visible: false },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false },
// Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리
{ name: 'CHK_SWGE_PROC_POPLTN', fieldName: 'CHK_SWGE_PROC_POPLTN', header: { text: 'CHK_SWGE_PROC_POPLTN' }, visible: false },
{ name: 'CHK_CVPL_CM', fieldName: 'CHK_CVPL_CM', header: { text: 'CHK_CVPL_CM' }, visible: false },
{ name: 'CHK_CVPL_BSML', fieldName: 'CHK_CVPL_BSML', header: { text: 'CHK_CVPL_BSML' }, visible: false },
{ name: 'CHK_CVPL_DCWTR_QLTWTR', fieldName: 'CHK_CVPL_DCWTR_QLTWTR', header: { text: 'CHK_CVPL_DCWTR_QLTWTR' }, visible: false },
{ name: 'CHK_CVPL_FIVW', fieldName: 'CHK_CVPL_FIVW', header: { text: 'CHK_CVPL_FIVW' }, visible: false },
{ name: 'CHK_CVPL_POLLUTN_ACDNT', fieldName: 'CHK_CVPL_POLLUTN_ACDNT', header: { text: 'CHK_CVPL_POLLUTN_ACDNT' }, visible: false },
{ name: 'CHK_CVPL_SSOS', fieldName: 'CHK_CVPL_SSOS', header: { text: 'CHK_CVPL_SSOS' }, visible: false },
{ name: 'CHK_CVPL_CHRGE', fieldName: 'CHK_CVPL_CHRGE', header: { text: 'CHK_CVPL_CHRGE' }, visible: false },
{ name: 'CHK_CVPL_UNKD', fieldName: 'CHK_CVPL_UNKD', header: { text: 'CHK_CVPL_UNKD' }, visible: false },
{ name: 'CHK_STDR_POPLTN_PER_CVPL', fieldName: 'CHK_STDR_POPLTN_PER_CVPL', header: { text: 'CHK_STDR_POPLTN_PER_CVPL' }, visible: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'SWGE_PROC_POPLTN',
'swtrmtPpltnCnt',
{
name: 'group_complaint_type',
header: { text: '민원유형(건)' },
items: [
'CVPL_CM',
'CVPL_BSML',
'CVPL_DCWTR_QLTWTR',
'CVPL_FIVW',
'CVPL_POLLUTN_ACDNT',
'CVPL_SSOS',
'CVPL_CHRGE',
'CVPL_UNKD'
'whol',
'dschgWqCnt',
'odorCnt',
'ldsCnt',
'plltnAcdntCnt',
'swagePplCnt',
'sewerCrgAmt',
'etcCnt'
]
},
'STDR_POPLTN_PER_CVPL',
'RMK'
'crtrPpltnCvlcptEvntcsCnt',
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'complaintInp',
tabKey: 'ST00130001',
tabLabel: '민원'
};

Some files were not shown because too many files have changed in this diff Show More