diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index a338556dc..e7aae9fc5 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -44,6 +44,7 @@ "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", "xlsx": "^0.18.5", + "xlsx-populate": "^1.21.0", "zod": "^3.24.2" }, "devDependencies": { @@ -59,6 +60,7 @@ "@storybook/test": "^8.5.3", "@storybook/theming": "^8.5.5", "@vitejs/plugin-react": "^4.3.4", + "@xlsx/xlsx-populate": "^0.0.8", "autoprefixer": "^10.4.20", "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.0.0", diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 82499306c..b75bf373c 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -1,42 +1,127 @@ "use client" -import type { CSSProperties } from "react"; -import { - Card, - CardContent -} from "./lib/card"; +import { Bar, BarChart, CartesianGrid, Legend, XAxis, YAxis } from "recharts"; import { ChartContainer, ChartTooltip, - ChartTooltipContent, type ChartConfig } from "./lib/chart"; -import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; +import type { CSSProperties } from "react"; -export default function Chart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle: string}) { +const customStyles = { + legendContainer: { + display: "flex", + justifyContent: "center", + gap: "20px", + fontSize: "9px", + fontWeight : "bold", + marginTop: "10px", + } as CSSProperties, +}; + +type BarChartProps = { + chartConfig: ChartConfig | undefined; + chartData: any[] | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string; + showLegend?: boolean; +} + +// 커스텀 툴팁 +const CustomTooltip = ({ + active, + payload, + label +}: { + active?: boolean; + payload?: Array<{ + name: string; + value: number; + color: string; + }>; + label?: string; +}) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+ {payload.map((item, index) => ( +
+ {/* 색상 박스 추가 (범례 색상과 동일) */} +
+ {` ${item.name} : ${item.value}`} +
+ ))} +
+ ); + } + return null; +}; + +// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) +const CustomLegend = (props: any) => { + const { payload } = props; return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( -
- {props.chartTitle} -
): null} -
- - - - value.slice(0, 3)} - /> - }/> - - - +
+ {payload.map((entry, index: number) => ( +
+
+ {entry.value}
- - + ))} +
+ ); +}; + +export default function Chart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) { + const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"]; + + return ( + <> + {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( +
+ {chartTitle} +
): null} +
+ + + + + + }/> + {chartData && chartData.length > 0 && ( + Object.keys(chartData[0]) + .filter(key => key !== "label") + .map((key, idx) => ( + + )) + )} + {showLegend && } + + +
+ ) } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 5f2ae4715..6db4c3b0a 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -1,37 +1,15 @@ "use client" import type { CSSProperties } from "react"; import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from "recharts"; -import { - Card, - CardContent -} from "./lib/card"; import { ChartContainer, type ChartConfig } from "./lib/chart"; -import React from "react"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; // 툴팁 커스텀 css /** 차트 기본 스타일 */ -const styles = { - tooltipDiv: { - borderRadius: "5px", - backgroundColor: "white", - padding: "10px", - boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", - width: "180px", // 적절한 크기로 설정 - } as React.CSSProperties, - tooltipLabel: { - fontWeight: "bold", - fontSize: "12px", - marginBottom: "5px", - borderBottom: "1px solid #ddd", - paddingBottom: "3px", - } as React.CSSProperties, - tooltipText: { - fontSize: "11px", - fontWeight:"bold", - } as React.CSSProperties, +const customStyles = { legendContainer: { display: "flex", justifyContent: "center", @@ -42,17 +20,39 @@ const styles = { } as CSSProperties, }; +type LineBarChartProps = { + chartConfig: ChartConfig | undefined; + chartData: any[] | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string | undefined; + yFieldText2?: string | undefined; + showLegend?: boolean; +} + +type LegendEntry = { + value: string; + color: string; + type?: string; + payload?: unknown; +}; + +type LegendProps = { + payload?: LegendEntry[]; +}; + // 커스텀 툴팁 -const CustomTooltip = ({ active, payload, label }) => { +const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => { if (active && payload && payload.length) { return ( -
-

{label}

+
+

{label}

{payload.map((item, index) => (
{/* 색상 박스 추가 (범례 색상과 동일) */}
- {`${item.name}: ${item.value}`} + {` ${item.name} : ${item.value}`}
))}
@@ -62,47 +62,83 @@ const CustomTooltip = ({ active, payload, label }) => { }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: any) => { +const CustomLegend = (props: LegendProps) => { const { payload } = props; + if (!payload) return null; + return ( -
- {payload.map((entry, index) => ( -
-
- {entry.value} +
+ {payload.map((entry: LegendEntry, index: number) => ( +
+
+ {entry.value}
))}
); }; -export default function LineBarchart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle:string }) { +export default function LineBarchart({ + chartConfig, + chartData, + chartTitle, + chartClassName = 'w-full h-full', + graphSize = 40, + yFieldText, + yFieldText2, + showLegend = true +}: LineBarChartProps) { + if (!chartData || chartData.length === 0) { + return
데이터가 없습니다.
; + } + + const barConfig = chartConfig?.barVal; + const lineConfig = chartConfig?.lineVal; + return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
- {props.chartTitle} -
): null} -
- - - - - {/* 왼쪽 Y축: lineVal (정상) */} - - {/* 오른쪽 Y축: barVal (위에서 아래로 반전) */} - - } /> - - {/* 바 차트 (오른쪽 Y축 사용, 위에서 아래로) */} - - {/* 라인 차트 (왼쪽 Y축 사용) */} - - - + {chartTitle}
- - - ) + ) : null} +
+ + + + + {/* 왼쪽 Y축: lineVal */} + {yFieldText && } + {/* 오른쪽 Y축: barVal */} + {yFieldText2 && } + } /> + {showLegend && } + {/* 바 차트 (오른쪽 Y축 사용) */} + + {/* 라인 차트 (왼쪽 Y축 사용) */} + + + +
+
+ ); } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index 136034e00..b2a147ee0 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -1,37 +1,24 @@ "use client" import type { CSSProperties } from "react"; -import { - Card, - CardContent -} from "./lib/card"; +import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; import { ChartContainer, - ChartTooltip, - ChartTooltipContent, type ChartConfig } from "./lib/chart"; -import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; + +type LineChartProps = { + chartConfig: ChartConfig | undefined; + chartData: ChartDataType | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string; + showLegend?: boolean; +} /** 차트 기본 스타일 */ -const styles = { - tooltipDiv: { - borderRadius: "5px", - backgroundColor: "white", - padding: "10px", - boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", - width: "180px", // 적절한 크기로 설정 - } as React.CSSProperties, - tooltipLabel: { - fontWeight: "bold", - fontSize: "12px", - marginBottom: "5px", - borderBottom: "1px solid #ddd", - paddingBottom: "3px", - } as React.CSSProperties, - tooltipText: { - fontSize: "11px", - fontWeight:"bold", - } as React.CSSProperties, +const customStyles = { legendContainer: { display: "flex", justifyContent: "center", @@ -42,17 +29,28 @@ const styles = { } as CSSProperties, }; +type LegendEntry = { + value: string; + color: string; + type?: string; + payload?: unknown; +}; + +type LegendProps = { + payload?: LegendEntry[]; +}; + // 커스텀 툴팁 -const CustomTooltip = ({ active, payload, label }) => { +const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => { if (active && payload && payload.length) { return ( -
-

{label}

+
+

{label}

{payload.map((item, index) => (
{/* 색상 박스 추가 (범례 색상과 동일) */}
- {`${item.name}: ${item.value}`} + {` ${item.name} : ${item.value}`}
))}
@@ -62,60 +60,90 @@ const CustomTooltip = ({ active, payload, label }) => { }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: any) => { +const CustomLegend = (props: LegendProps) => { const { payload } = props; + if (!payload) return null; + return ( -
- {payload.map((entry, index) => ( -
-
- {entry.value} +
+ {payload.map((entry: LegendEntry, index: number) => ( +
+
+ {entry.value}
))}
); }; -type DataType = {label : string; val:number}; +type DataType = {label: string; val: string}; + type ChartDataType = { - chartData1: DataType[]; - chartData2: DataType[]; + [key: string]: DataType[]; }; -export default function Linechart(props: { chartConfig: ChartConfig; chartData: ChartDataType; chartTitle:string}) { - const mergedData = Object.values(props.chartData).reduce((acc, dataset) => { +export default function Linechart({ + chartConfig, + chartData, + chartTitle, + chartClassName = 'w-full h-full', + graphSize = 40, + yFieldText, + showLegend = true +}: LineChartProps) { + if(!chartData || Object.keys(chartData).length === 0){ + return
데이터가 없습니다.
+ } + + // 데이터 병합 및 변환 + const mergedData = Object.entries(chartData).reduce>((acc, [key, dataset]) => { dataset.forEach((entry, index) => { if (!acc[index]) { - acc[index] = { label: entry.label }; // label 추가 + acc[index] = { label: entry.label }; } - const key = Object.keys(props.chartData).find(k => props.chartData[k] === dataset); - if (key) acc[index][key] = entry.val; + acc[index][key] = parseFloat(entry.val); }); return acc; - }, [] as any[]); + }, []); return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
- {props.chartTitle} -
): null} -
- - - - - - } /> - - {Object.keys(props.chartData).map((key, index) => ( - - ))} - - + {chartTitle}
- - - ) + ) : null} +
+ + + + + + } /> + {showLegend && } + {Object.keys(chartData).map((key) => { + const config = chartConfig?.[key]; + return ( + + ); + })} + + +
+
+ ); } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx index fc5747f3a..b30eef3c3 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -18,15 +18,41 @@ const CHART_FORM = { LINEBAR : 'linebar' }; -function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; chartTitle:string; }) { +/** + * 차트 컴포넌트 + * @param chartForm 차트 형태 + * @param chartConfig 차트 설정 + * @param chartData 차트 데이터 + * @param chartTitle 차트 제목 + * @param chartClassName 차트 클래스명 + * @param graphSize 차트 크기 + * @param yFieldText 차트 Y축 텍스트 + * @param yFieldText2 차트 Y축 텍스트2(라인바 차트때 사용) + * @param showLegend 차트 범례 표시 여부 + * @returns + */ + +type MainChartProps = { + chartForm: string; + chartConfig: any; + chartData: any; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText? : string | undefined; + yFieldText2? : string | undefined; + showLegend? : boolean; +} + +function MainChart(props: MainChartProps) { if( props.chartForm == CHART_FORM.BAR ){ return ( - + ); } else if( props.chartForm == CHART_FORM.LINE ){ return ( - + ); } else if( props.chartForm == CHART_FORM.DN ){ @@ -36,7 +62,7 @@ function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; ); } else if (props.chartForm == CHART_FORM.LINEBAR){ return ( - + ); } diff --git a/packages/common/business_components/src/ui/excel/excelStyle.tsx b/packages/common/business_components/src/ui/excel/excelStyle.tsx new file mode 100644 index 000000000..adc21880b --- /dev/null +++ b/packages/common/business_components/src/ui/excel/excelStyle.tsx @@ -0,0 +1,54 @@ +import type { sheetParameter } from '@/index'; +import * as XlsxPopulate from '@xlsx/xlsx-populate'; + + + +const makeCellStyle =(styleQ: XlsxPopulate.StyleQuery )=>{ + styleQ.fill({ type: "solid", color: {rgb:"9bc1e7"} }); + styleQ.borderEach( { style: "thin", color: { rgb: "FFFFFF" } } ); + styleQ.wrapText(true); + // styleQ.fontColor({rgb:"FFFFFF"}); + styleQ.fontSize(13); + styleQ.horizontalAlignment("center"); + styleQ.verticalAlignment("center"); + styleQ.bold(true); +} + +const makeExcelFile = async(workbook: XlsxPopulate.Workbook,fileNm:string)=>{ + const outputData = await workbook.output("array-buffer"); + const blob = new Blob([outputData], { type: "application/octet-stream" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = fileNm+".xlsx"; + link.click(); + +} + + +export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fileNm:string) => { + const workbook = await XlsxPopulate.Workbook.fromData(data); + const rangeCells = workbook.activeSheet(0)?.sheet(0)?.range(range); + console.log(rangeCells); + rangeCells?.style((styleQ)=>{ + makeCellStyle(styleQ) + }); + makeExcelFile(workbook, fileNm) + + +} + + +export const excelArrayStyle = async (data: ArrayBuffer, range: [typeof sheetParameter], fileNm:string) => { + const workbook = await XlsxPopulate.Workbook.fromData(data); + if(range){ + range.forEach((item , index)=>{ + const rangeCells = workbook.activeSheet(index)?.sheet(index)?.range(item.headerRange); + console.log(rangeCells); + rangeCells?.style((styleQ)=>{ + makeCellStyle(styleQ) + }); + } ) + } + makeExcelFile(workbook, fileNm) + +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx new file mode 100644 index 000000000..e33b0c88d --- /dev/null +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -0,0 +1,156 @@ +import * as XLSX from 'xlsx'; +import { excelArrayStyle, excelStyle } from './excelStyle'; + +type ExcelDownloadProps = { + data: JSON | JSON[]; + fileName: string; + sheetNameArray: string[]; + headerRange: string; +} + +export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { + const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); + excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); +}; + + + +export const filePicked=(oFile: File, headerNum:number=0):Promise =>{ + // Create A File Reader HTML5 + const reader = new FileReader(); + return new Promise((resolve, reject) => { + // Ready The Event For When A File Gets Selected + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + resolve(undefined); + return; + } + //셀데이터에서 데이트 형식으로 입력하면 yyyymmdd 형식으로 변환 + const wb = XLSX.read(data, { type: 'binary', cellDates: true, dateNF: "yyyymmdd" }); + let jsonXls: JSON | undefined; + // Loop Over Each Sheet + wb.SheetNames.forEach(function (sheetName,index ) { + // Obtain The Current Row As CSV + // 일단 첫번째 시트만 읽을 수 있도록 수정 + if(index ===0){ + const sheet = wb.Sheets[sheetName]; + + if (sheet) { + jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw:false }) as unknown as JSON; + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + resolve(jsonXls); + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(undefined); + }; + + // Tell JS To Start Reading The File.. You could delay this if desired + reader.readAsBinaryString(oFile); + }); +} + + + +export const filePickedArray=(oFile: File, headerNum:number=0):Promise =>{ + // Create A File Reader HTML5 + const reader = new FileReader(); + return new Promise((resolve, reject) => { + // Ready The Event For When A File Gets Selected + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + resolve(undefined); + return; + } + //셀데이터에서 데이트 형식으로 입력하면 yyyymmdd 형식으로 변환 + const wb = XLSX.read(data, { type: 'binary', cellDates: true, dateNF: "yyyymmdd" }); + const arrayXls: JSON[] = [] + let jsonXls: JSON | undefined; + // Loop Over Each Sheet + wb.SheetNames.forEach(function (sheetName,index ) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + console.log(sheet) + jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw:false }) as unknown as JSON; + arrayXls.push(jsonXls) + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + }); + resolve(arrayXls); + + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(undefined); + }; + + // Tell JS To Start Reading The File.. You could delay this if desired + reader.readAsBinaryString(oFile); + }); +} + + + +export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3'):Promise =>{ // Create A File Reader HTML5 + return new Promise(() => { + // Ready The Event For When A File Gets Selected + const wb = XLSX.read(oFile, { type: 'binary' }); + // Loop Over Each Sheet + wb.SheetNames.forEach(async function (sheetName, index) { + // Obtain The Current Row As CSV + if(index==0){ + const sheet = wb.Sheets[sheetName]; + if (sheet) { + //오리진 확인 test 필요 + // const sheet = XLSX.utils.json_to_sheet(data); + XLSX.utils.sheet_add_aoa(sheet, data, { origin: 'A'+headerNum }); + // XLSX.utils.book_append_sheet(wb, sheet, 'test'); + await excelStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), headerRange, fileNm) + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + }); + } + +export const sheetParameter ={ + data:[[]], headerNum:0, headerRange:'A1:V3' + } + export const filePickedBlobArray= (oFile: ArrayBuffer,fileNm:string, sheetArray: [typeof sheetParameter]):Promise =>{ // Create A File Reader HTML5 + return new Promise (async () => { + // Ready The Event For When A File Gets Selected + const wb = XLSX.read(oFile, { type: 'binary' }); + // Loop Over Each Sheet + wb.SheetNames.forEach( function (sheetName, index) { + // Obtain The Current Row As CSV + if(sheetArray[index]){ + const sheet = wb.Sheets[sheetName]; + if (sheet) { + //오리진 확인 test 필요 + // const sheet = XLSX.utils.json_to_sheet(data); + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: 'A'+sheetArray[index].headerNum }); + // XLSX.utils.book_append_sheet(wb, sheet, 'test'); + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + + + await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + }); + } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/excel_download/index.tsx b/packages/common/business_components/src/ui/excel_download/index.tsx deleted file mode 100644 index 58ecf8a52..000000000 --- a/packages/common/business_components/src/ui/excel_download/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as XLSX from 'xlsx'; - -type ExcelDownloadProps = { - data: JSON | JSON[]; - fileName: string; - sheetNameArray: string[]; -} - -export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => { - const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); - const workbook = XLSX.utils.book_new(); - XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); - //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); - //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); - XLSX.writeFile(workbook, fileName + '.xlsx'); -}; - - - -export const filePicked=(oFile: File, headerNum:number=0):Promise =>{ - // Create A File Reader HTML5 - const reader = new FileReader(); - return new Promise((resolve, reject) => { - // Ready The Event For When A File Gets Selected - reader.onload = function (e) { - const data = e.target?.result; - if (!data) { - console.error("Failed to read file: e.target.result is null"); - resolve(undefined); - return; - } - const wb = XLSX.read(data, { type: 'binary' }); - let jsonXls: JSON | undefined; - - //TODO 시트가 2개이상일 경우 마지막 시트의 데이터만 표출됨 - // Loop Over Each Sheet - wb.SheetNames.forEach(function (sheetName) { - // Obtain The Current Row As CSV - const sheet = wb.Sheets[sheetName]; - - if (sheet) { - jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum }) as unknown as JSON; - } else { - console.error(`Sheet "${sheetName}" is undefined.`); - } - }); - resolve(jsonXls); - }; - - reader.onerror = function () { - console.error("Error reading file"); - reject(undefined); - }; - - // Tell JS To Start Reading The File.. You could delay this if desired - reader.readAsBinaryString(oFile); - }); -} - - - -export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0):Promise =>{ - // Create A File Reader HTML5 - return new Promise(() => { - // Ready The Event For When A File Gets Selected - const wb = XLSX.read(oFile, { type: 'binary' }); - // Loop Over Each Sheet - wb.SheetNames.forEach(function (sheetName, index) { - // Obtain The Current Row As CSV - if(index==0){ - const sheet = wb.Sheets[sheetName]; - if (sheet) { - //오리진 확인 test 필요 - // const sheet = XLSX.utils.json_to_sheet(data); - XLSX.utils.sheet_add_aoa(sheet, data, { origin: 'A'+headerNum }); - // XLSX.utils.book_append_sheet(wb, sheet, 'test'); - XLSX.writeFile(wb, `${fileNm}.xlsx`); - } else { - console.error(`Sheet "${sheetName}" is undefined.`); - } - } - }); - }); - } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index e591be5b5..90bf029c0 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -1,7 +1,7 @@ export * from "./chart"; export * from "./date_picker"; export * from "./dialog_ui"; -export * from "./excel_download"; +export * from "./excel"; export * from "./file-upload"; export * from "./form"; export * from "./form_ui"; @@ -19,3 +19,4 @@ export * from "./tab"; export * from "./table"; export * from "./table_div"; export * from "./title"; + diff --git a/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx index dc859a55d..ebd611c1d 100644 --- a/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx +++ b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx @@ -8,16 +8,20 @@ interface TableUpperProps { row? : number; rowSpan?: number; colSpan?: number; + visible?:boolean; } const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => { - const maxRow = upHeadList.reduce((max, head) => Math.max(max, head.row || 0), 0); + const maxRow = upHeadList?.reduce((max, head) => Math.max(max, head.row || 0), 0); + //upHeadList.forEach((item) => item.visible ?item.visible:true) if(maxRow == 0){ return ( <> - {upHeadList.map((head: TableUpperProps) => { + {upHeadList + .filter((item) => item.visible != false) + .map((head: TableUpperProps) => { return ( { <> {Array.from({ length: maxRow + 1 }, (_, rowIndex) => ( - {upHeadList.map((head: TableUpperProps) => { + {upHeadList + .filter((item) => item.visible != false) + .map((head: TableUpperProps) => { if (head.row === rowIndex) { return ( (1); const [idx, setIdx] = useState(-1); @@ -58,8 +60,20 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr {tableData.length > 0 ? tableData?.map((el: { [key: string]: any}, index) => ( - {headlist?.map((head) => ( + {headlist + .filter((item) => item.visible != false) + .map((head) => ( head.id && head.id.length > 1 && ( + boldArray.includes(index) ? handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')} + :