st: refactor: DataGrid 설정 수정 중

This commit is contained in:
2025-12-30 17:55:05 +09:00
parent a542cdd87d
commit 32ad548de2
15 changed files with 227 additions and 75 deletions
@@ -0,0 +1,16 @@
import { Button } from "@common/components";
interface OperateDataButtonProps {
getOperateDataFunc: () => void;
}
const OperateDataButton = ({ getOperateDataFunc }: OperateDataButtonProps) => {
return (
<Button color="emerald" className="px-6" onClick={getOperateDataFunc}>
<i className="diveicon di-arrow-left-right" />
<span>운영 데이터 가져오기</span>
</Button>
);
};
export default OperateDataButton;
+51 -40
View File
@@ -1,4 +1,4 @@
import { useRef, useEffect, useState } from "react"
import { useRef, useEffect } from "react"
import RealGrid, { GridFitStyle, type DisplayOptions } from "realgrid"
import { RealGridReact, RGDataColumn } from "realgrid-react"
@@ -13,9 +13,10 @@ import { useTabStore } from "@/store/tab-store"
// RealGrid 타입 확장
declare module "realgrid" {
interface GridView {
setOptions(options: {
setOptions(options: {
footer?: { visible?: boolean };
summaryMode?: 'aggregate' | 'statistical' | 'none';
checkBar?: { visible?: boolean };
}): void;
}
}
@@ -34,7 +35,7 @@ if (typeof window !== "undefined") {
RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || '');
const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fixedColumns }: DataGridProps) => {
const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, fixedColumns, onRowsChange, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const gridRef = useRef<RealGridReact>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -42,29 +43,6 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
const activeTabId = useTabStore((state) => state.activeTabId);
const menuList = useTabStore((state) => state.menuList);
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
// footer가 있는 컬럼의 데이터를 숫자로 변환
const convertNumericData = (data: Record<string, string>[]): Record<string, string | number>[] => {
const footerColumns = columns.filter(col => col.footer);
const footerFieldNames = new Set(footerColumns.map(col => col.fieldName));
return data.map(row => {
const convertedRow: Record<string, string | number> = { ...row };
footerFieldNames.forEach(fieldName => {
const value = row[fieldName];
if (value !== undefined && value !== null && value !== '') {
// 쉼표 제거 후 숫자로 변환
const numericValue = Number(String(value).replace(/,/g, ''));
if (!isNaN(numericValue)) {
convertedRow[fieldName] = numericValue;
}
}
});
return convertedRow;
});
};
const exportExcel = () => {
if (rowData.length === 0) {
alert('데이터가 없습니다.');
@@ -157,20 +135,34 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
const dataStartRow = maxHeaderRowIndex + 1;
// 데이터 행 읽기
const dataRows: Record<string, string>[] = [];
const dataRows: Record<string, string | number>[] = [];
for (let row = dataStartRow; row <= range.e.r; row++) {
const rowData: Record<string, string> = {};
const rowData: Record<string, string | number> = {};
let hasData = false;
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}`;
const cellValue = cell && cell.v !== undefined && cell.v !== null
? String(cell.v)
: '';
if (cellValue !== '') {
let cellValue: string | number = '';
if (cell && cell.v !== undefined && cell.v !== null) {
// 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환
if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) {
cellValue = cell.v as number;
} else {
const stringValue = String(cell.v);
// 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환
const numericValue = Number(stringValue.replace(/,/g, ''));
if (!isNaN(numericValue) && stringValue.trim() !== '') {
cellValue = numericValue;
} else {
cellValue = stringValue;
}
}
}
if (cellValue !== '' && cellValue !== null && cellValue !== undefined) {
hasData = true;
}
@@ -182,9 +174,7 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
dataRows.push(rowData);
}
}
const convertedDataRows = convertNumericData(dataRows);
setRowData(convertedDataRows);
setRowData(dataRows);
} catch (error) {
console.error('엑셀 파일 파싱 오류:', error);
alert('엑셀 파일 파싱 중 오류가 발생했습니다.');
@@ -220,6 +210,12 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
(gridView.displayOptions as DisplayOptions).fitStyle = GridFitStyle.EVEN;
gridView.header.height = 60;
// 헤더 체크박스 표시 여부 설정
gridView.setOptions({
checkBar: { visible: showHeaderCheckbox },
rowIndicator: { visible: false },
});
// footer가 있는 컬럼이 있으면 footer 높이 설정
const hasFooter = columns.some(col => col.footer);
if (hasFooter) {
@@ -247,12 +243,26 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
}
});
if (rows.length > 0) {
const convertedRows = convertNumericData(rows);
setRowData(convertedRows);
}
}, [rows, columns, columnLayout, fixedColumns, rowData]);
// mergeCells가 true인 컬럼에 대해 2개씩 병합하는 mergeRule 설정
columns.forEach(col => {
if (col.mergeCells === true) {
try {
const column = gridView.columnByName(col.name);
if (column) {
// mergeRule: 2개 행씩 병합 (row div 2)
(column as { mergeRule?: { criteria: string; breakMergeOnEmpty?: boolean } }).mergeRule = {
criteria: 'row div 2',
breakMergeOnEmpty: false
};
}
} catch (error) {
console.warn(`Failed to set mergeRule for column ${col.name}:`, error);
}
}
});
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]);
return (
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
@@ -288,6 +298,7 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi
name={column.name}
fieldName={column.fieldName}
header={column.header}
editable={column.editable}
footer={column.footer ? {
text: column.footer.text,
expression: column.footer.expression,
+88 -11
View File
@@ -1,6 +1,6 @@
import { useRef, useEffect, useState } from "react"
import RealGrid, { GridFitStyle } from "realgrid"
import RealGrid, { GridFitStyle, type DisplayOptions } from "realgrid"
import { RealGridReact, RGDataColumn } from "realgrid-react"
import type { TabDataGridProps } from "../type/DataGrid.type"
import JSZip from "jszip"
@@ -11,6 +11,17 @@ import { useTabStore } from "@/store/tab-store"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components";
import { Button } from "@common/components";
// RealGrid 타입 확장
declare module "realgrid" {
interface GridView {
setOptions(options: {
footer?: { visible?: boolean };
summaryMode?: 'aggregate' | 'statistical' | 'none';
checkBar?: { visible?: boolean };
}): void;
}
}
// Window 인터페이스 확장
declare global {
interface Window {
@@ -25,7 +36,7 @@ if (typeof window !== "undefined") {
RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || '');
const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGridProps) => {
const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [], showHeaderCheckbox = false }: TabDataGridProps) => {
// 각 탭별로 그리드 ref와 데이터를 관리
const gridRefs = useRef<Map<string, RealGridReact>>(new Map());
const fileInputRefs = useRef<Map<string, HTMLInputElement>>(new Map());
@@ -34,7 +45,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGri
const menuList = useTabStore((state) => state.menuList);
// 각 탭별로 데이터를 관리 (key: tabKey)
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string>[]>>(new Map());
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [activeTab, setActiveTab] = useState<string>(() => {
// 초기 활성 탭을 첫 번째 tabKey로 설정
const firstTab = columnInfo[0];
@@ -143,20 +154,34 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGri
const dataStartRow = maxHeaderRowIndex + 1;
// 데이터 행 읽기
const dataRows: Record<string, string>[] = [];
const dataRows: Record<string, string | number>[] = [];
for (let row = dataStartRow; row <= range.e.r; row++) {
const rowData: Record<string, string> = {};
const rowData: Record<string, string | number> = {};
let hasData = false;
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}`;
const cellValue = cell && cell.v !== undefined && cell.v !== null
? String(cell.v)
: '';
let cellValue: string | number = '';
if (cell && cell.v !== undefined && cell.v !== null) {
// 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환
if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) {
cellValue = cell.v as number;
} else {
const stringValue = String(cell.v);
// 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환
const numericValue = Number(stringValue.replace(/,/g, ''));
if (!isNaN(numericValue) && stringValue.trim() !== '') {
cellValue = numericValue;
} else {
cellValue = stringValue;
}
}
}
if (cellValue !== '') {
if (cellValue !== '' && cellValue !== null && cellValue !== undefined) {
hasData = true;
}
@@ -308,14 +333,60 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGri
const gridView = el.gridView;
if (!gridView) return;
const currentInfo = columnInfo.find(info => info.tabKey === tabKey);
if (!currentInfo) return;
gridView.setDisplayOptions({
fitStyle: GridFitStyle.EVEN
});
gridView.header.height = 60;
// 헤더 체크박스 표시 여부 설정
gridView.setOptions({
checkBar: { visible: showHeaderCheckbox },
rowIndicator: { visible: false },
});
// footer가 있는 컬럼이 있으면 footer 높이 설정
const hasFooter = currentInfo.columns.some(col => col.footer);
if (hasFooter) {
gridView.footer.height = 40;
}
gridView.setOptions({
summaryMode: "aggregate"
});
// footer의 valueCallback 설정
currentInfo.columns.forEach(col => {
if (col.footer && col.footer.valueCallback) {
const column = gridView.columnByName(col.name);
if (column && column.footer) {
(column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback;
}
}
});
// mergeCells가 true인 컬럼에 대해 2개씩 병합하는 mergeRule 설정
currentInfo.columns.forEach(col => {
if (col.mergeCells === true) {
try {
const column = gridView.columnByName(col.name);
if (column) {
// mergeRule: 2개 행씩 병합 (row div 2)
(column as { mergeRule?: { criteria: string; breakMergeOnEmpty?: boolean } }).mergeRule = {
criteria: 'row div 2',
breakMergeOnEmpty: false
};
}
} catch (error) {
console.warn(`Failed to set mergeRule for column ${col.name}:`, error);
}
}
});
// 고정 컬럼 설정
const currentInfo = columnInfo.find(info => info.tabKey === tabKey);
if (currentInfo?.fixedColumns !== undefined && currentInfo.fixedColumns > 0) {
if (currentInfo.fixedColumns !== undefined && currentInfo.fixedColumns > 0) {
gridView.setFixedOptions({
colCount: currentInfo.fixedColumns,
});
@@ -336,6 +407,12 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGri
name={column.name}
fieldName={column.fieldName}
header={column.header}
editable={column.editable}
footer={column.footer ? {
text: column.footer.text,
expression: column.footer.expression,
numberFormat: column.footer.numberFormat,
} : undefined}
/>
))}
</RealGridReact>
@@ -4,6 +4,7 @@ import SubmitButton from "@/components/button/SumitButton";
import SaveButton from "@/components/button/SaveButton";
import LastYearDataButton from "@/components/button/LastYearDataButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import { useState } from "react";
// 하수도 통계 관리 > 07. 분뇨처리시설
const ExcretaTreatmentFacilityMng = () => {
@@ -126,6 +127,8 @@ const ExcretaTreatmentFacilityMng = () => {
// 제출 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -137,7 +140,7 @@ const ExcretaTreatmentFacilityMng = () => {
<SaveButton saveFunc={saveFunc} />
<SubmitButton submitFunc={submitFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} fixedColumns={2} isExcelImport={true} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} fixedColumns={2} isExcelImport={true} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -2,8 +2,9 @@ import SearchForm from '@/components/module/SearchForm';
import DataGrid from '@/components/module/DataGrid';
import SubmitButton from '@/components/button/SumitButton';
import SaveButton from '@/components/button/SaveButton';
import LastYearDataButton from '@/components/button/LastYearDataButton';
import SubmitExceptionButton from '@/components/button/SubmitExceptionButton';
import OperateDataButton from '@/components/button/OperateDataButton';
import { useState } from 'react';
// 하수도 통계 관리 > 06. 공공하수처리시설
const PublicSewageFacilityMng = () => {
@@ -212,9 +213,9 @@ const PublicSewageFacilityMng = () => {
// 제출 로직: 데이터 검증 후 제출 팝업 호출
};
const getLastYearDataFunc = () => {
console.log('get last year data');
// 지난연도 데이터 가져오기 로직
const getOperateDataFunc = () => {
console.log('get operate data');
// 운영 데이터 가져오기
};
const SubmitExceptionFunc = () => {
@@ -222,16 +223,18 @@ const PublicSewageFacilityMng = () => {
// 예외제출 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<OperateDataButton getOperateDataFunc={getOperateDataFunc} />
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
<SubmitButton submitFunc={submitFunc} />
<SaveButton saveFunc={saveFunc} />
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={3} fixedColumns={2} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={3} fixedColumns={2} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -4,6 +4,7 @@ import SubmitButton from "@/components/button/SumitButton";
import SaveButton from "@/components/button/SaveButton";
import LastYearDataButton from "@/components/button/LastYearDataButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import { useState } from "react";
// 하수도 통계 관리 > 05. 펌프장
const PumpingStationMng = () => {
@@ -159,6 +160,8 @@ const PumpingStationMng = () => {
// 예외 제출 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -168,7 +171,7 @@ const PumpingStationMng = () => {
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
</div>
<DataGrid columnInfo={{columns, columnLayout}} rowStartNum={3} isExcelImport={true} fixedColumns={3} />
<DataGrid columnInfo={{columns, columnLayout}} rowStartNum={3} isExcelImport={true} fixedColumns={3} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -6,6 +6,7 @@ import LastYearDataButton from "@/components/button/LastYearDataButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import type { GridView, DataColumn } from "@/components/type/DataGrid.type";
import { getFooterSummary } from "@/components/util/grid/footerSummary";
import { useState } from "react";
// 하수도 통계 관리 > 01. 하수도 보급률
const SeweragePenetrationRateMng = () => {
@@ -143,6 +144,8 @@ const SeweragePenetrationRateMng = () => {
// 예외 제출 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -152,7 +155,7 @@ const SeweragePenetrationRateMng = () => {
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={3} isExcelImport={true} fixedColumns={1} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={3} isExcelImport={true} fixedColumns={1} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -4,6 +4,7 @@ import ApproveButton from "@/components/button/ApproveButton";
import RejectButton from "@/components/button/RejectButton";
import ReturnButton from "@/components/button/ReturnButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import { useState } from "react";
// 하수도 통계 관리 > 00. 통계 입력 현황
const StatsInputSatusMng = () => {
@@ -55,6 +56,8 @@ const StatsInputSatusMng = () => {
// 검토 의견 작성 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm />
@@ -66,7 +69,7 @@ const StatsInputSatusMng = () => {
<RejectButton rejectFunc={rejectFunc} />
<ApproveButton approveFunc={approveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} fixedColumns={3} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} fixedColumns={3} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -4,6 +4,7 @@ import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import ReturnButton from "@/components/button/ReturnButton";
import { useState } from "react";
const ExcretaTreatmentFacilityRvw = () => {
@@ -126,6 +127,8 @@ const ExcretaTreatmentFacilityRvw = () => {
// 반려
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} fctName={true} />
@@ -135,7 +138,7 @@ const ExcretaTreatmentFacilityRvw = () => {
<ReturnButton returnFunc={returnFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={2} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={2} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -4,6 +4,7 @@ import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import ReturnButton from "@/components/button/ReturnButton";
import { useState } from "react";
const PublicSewageFacilityRvw = () => {
const columns = [
@@ -187,6 +188,8 @@ const PublicSewageFacilityRvw = () => {
// 반려
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} fctName={true} />
@@ -196,7 +199,7 @@ const PublicSewageFacilityRvw = () => {
<ReturnButton returnFunc={returnFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={3} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={3} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -5,6 +5,7 @@ import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import ReturnButton from "@/components/button/ReturnButton";
import { useState } from "react";
const PumpingStationRvw = () => {
const columns = [
@@ -146,6 +147,8 @@ const PumpingStationRvw = () => {
// 반려
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} fctName={true} />
@@ -155,7 +158,7 @@ const PumpingStationRvw = () => {
<ReturnButton returnFunc={returnFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={3} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={3} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -5,12 +5,16 @@ import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import ReturnButton from "@/components/button/ReturnButton";
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import { useState } from "react";
const SeweragePenetrationRateRvw = () => {
const columns = [
{ name: 'sido', fieldName: '시도', header: { text: '시도' } },
{ name: 'sgg', fieldName: '구군', header: { text: '구군' } },
{ name: '구분', fieldName: '구분', header: { text: '구분' } },
const columns: ColumnConfig[] = [
{ name: 'sn', fieldName: '순번', header: { text: '순번' }, mergeCells: true, editable: false },
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, mergeCells: true, editable: false },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, mergeCells: true, editable: false },
{ name: '구분', fieldName: '구분', header: { text: '구분' }, editable: false },
{ name: '수계', fieldName: '수계', header: { text: '수계' } },
{ name: 'bstrm', fieldName: '지류', header: { text: '지류' } },
{ name: 'mdbsn', fieldName: '세부단위유역', header: { text: '세부단위유역' } },
@@ -49,6 +53,7 @@ const SeweragePenetrationRateRvw = () => {
];
const columnLayout = [
'sn',
{
name: 'groupA',
hideChildHeaders: false,
@@ -121,6 +126,8 @@ const SeweragePenetrationRateRvw = () => {
// 반려
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -130,7 +137,7 @@ const SeweragePenetrationRateRvw = () => {
<ReturnButton returnFunc={returnFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={2} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={3} isExcelImport={true} fixedColumns={3} rowData={rowData} setRowData={setRowData} />
</>
);
};
@@ -4,6 +4,7 @@ import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
import ReturnButton from "@/components/button/ReturnButton";
import { useState } from "react";
const StatsInputSatusRvw = () => {
const columns = [
@@ -197,6 +198,8 @@ const StatsInputSatusRvw = () => {
// 반려
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -206,7 +209,7 @@ const StatsInputSatusRvw = () => {
<ReturnButton returnFunc={returnFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={2} />
<DataGrid columnInfo={{ columns, columnLayout }} rowStartNum={2} isExcelImport={false} fixedColumns={2} rowData={rowData} setRowData={setRowData} />
</>
);
}
@@ -4,6 +4,7 @@ import SubmitButton from "@/components/button/SumitButton";
import SaveButton from "@/components/button/SaveButton";
import LastYearDataButton from "@/components/button/LastYearDataButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import { useState } from "react";
const SeweragePenetrationRateTot = () => {
const columns = [
@@ -122,6 +123,8 @@ const SeweragePenetrationRateTot = () => {
// 예외 제출 로직
};
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
return (
<>
<SearchForm userMenual={true} />
@@ -131,7 +134,7 @@ const SeweragePenetrationRateTot = () => {
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
</div>
<DataGrid columnInfo={{columns, columnLayout}} rowStartNum={2} isExcelImport={true} fixedColumns={1} />
<DataGrid columnInfo={{columns, columnLayout}} rowStartNum={2} isExcelImport={true} fixedColumns={1} rowData={rowData} setRowData={setRowData} />
</>
);
};
+14 -3
View File
@@ -1,12 +1,18 @@
import React from "react";
// RealGrid 타입 정의
export type GridView = {
getSummary(field: string, summary: 'sum' | 'avg' | 'count' | 'max' | 'min'): number | undefined;
getDataSource(): {
getRowCount(): number;
getValue(rowIndex: number, field: string): number;
getValue(rowIndex: number, field: string): number | string;
getDataRowId(dataRow: number): number | string;
};
getDataRow(itemIndex: number): number;
};
export type MergeCallback = (grid: GridView, column: DataColumn, itemIndex: number, value: number | string | null | undefined) => number | null;
export type DataColumn = {
name: string;
fieldName: string;
@@ -22,6 +28,8 @@ export type ColumnConfig = {
name: string;
fieldName: string;
header: { text: string };
editable?: boolean; // 셀 편집 가능 여부
mergeCells?: boolean; // 같은 값이 연속될 때 셀 병합 여부
footer?: {
text?: string;
expression?: 'sum' | 'avg' | 'count' | 'max' | 'min';
@@ -53,11 +61,13 @@ export type ColumnInfoList = {
export type DataGridProps = {
columnInfo: ColumnInfo;
rows?: Record<string, string>[];
rowStartNum?: number; // 헤더 행 개수 (1부터 시작, 예: 2면 1행과 2행까지 헤더로 간주)
isExcelImport?: boolean; // 엑셀 가져오기 유무
onRowsChange?: (rows: Record<string, string>[]) => void;
onRowsChange?: (rows: Record<string, string | number>[]) => void;
fixedColumns?: number; // 고정할 컬럼 개수 (좌측부터)
showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true)
rowData: Record<string, string | number>[];
setRowData: React.Dispatch<React.SetStateAction<Record<string, string | number>[]>>;
};
export type TabDataGridProps = {
@@ -65,4 +75,5 @@ export type TabDataGridProps = {
rows?: Record<string, string>[];
isExcelImport?: boolean; // 엑셀 가져오기 유무
onRowsChange?: (rows: Record<string, string>[]) => void;
showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true)
};