Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c72fa450bc | ||
|
|
400d6cf732 | ||
|
|
39e2734a49 | ||
|
|
d92d4d3949 | ||
|
|
b93efa5503 | ||
|
|
0b8559891a | ||
|
|
ffa1781005 | ||
|
|
b47c0dff2b | ||
|
|
83f5d878e7 |
@@ -109,6 +109,41 @@ export default function LineBarchart({
|
||||
const lineMaxValue = getMaxValue(chartData, 'lineVal');
|
||||
const barMaxValue = getMaxValue(chartData, 'barVal');
|
||||
|
||||
/**
|
||||
* 최대값과 구간 수에 따른 범례 만들기
|
||||
* maxValue : 최대값, segments : 구간 수 (구간수가 5면 눈금은 6)
|
||||
*/
|
||||
const makeNiceScale = (maxValue: number, segments = 5) => {
|
||||
// 최대값이 이상할 경우 0, 0으로 반환
|
||||
if (!isFinite(maxValue) || maxValue <= 0) {
|
||||
return { max: 0, ticks: [0] };
|
||||
}
|
||||
|
||||
const exponent = Math.floor(Math.log10(maxValue));
|
||||
const base = Math.pow(10, exponent);
|
||||
const fraction = maxValue / base;
|
||||
|
||||
let niceFraction: number;
|
||||
if (fraction <= 1) niceFraction = 1;
|
||||
else if (fraction <= 2) niceFraction = 2;
|
||||
else if (fraction <= 3) niceFraction = 3;
|
||||
else if (fraction <= 4) niceFraction = 4;
|
||||
else if (fraction <= 5) niceFraction = 5;
|
||||
else if (fraction <= 6) niceFraction = 6;
|
||||
else if (fraction <= 7) niceFraction = 7;
|
||||
else if (fraction <= 8) niceFraction = 8;
|
||||
else if (fraction <= 9) niceFraction = 9;
|
||||
else niceFraction = 10;
|
||||
|
||||
const niceMax = niceFraction * base; // round up to 1/2/5 * 10^k
|
||||
const step = niceMax / segments;
|
||||
const ticks = Array.from({ length: segments + 1 }, (_, i) => Number((i * step).toFixed(10)));
|
||||
return { max: niceMax, ticks };
|
||||
};
|
||||
|
||||
const leftScale = makeNiceScale(lineMaxValue, 5);
|
||||
const rightScale = makeNiceScale(barMaxValue, 5);
|
||||
|
||||
const getTicks = (data : any []) => {
|
||||
const monthSet = new Set<string>();
|
||||
const rtnData : string[] = [];
|
||||
@@ -135,7 +170,7 @@ export default function LineBarchart({
|
||||
return (
|
||||
<text
|
||||
x={x - padding} // 맨 좌측 바깥
|
||||
y={y + height / 2} // 수직 중앙
|
||||
y={y + height / 2 - 12} // 수직 중앙
|
||||
textAnchor="middle"
|
||||
transform={`rotate(-90, ${x - padding}, ${y + height / 2})`}
|
||||
className="text-[12px] font-bold"
|
||||
@@ -166,26 +201,27 @@ export default function LineBarchart({
|
||||
</div>
|
||||
) : null}
|
||||
<div className="h-[calc(100%-42px)] w-[calc(100%-40px)] ">
|
||||
<ChartContainer config={chartConfig ?? {}} className="h-full w-full">
|
||||
<ChartContainer config={chartConfig ?? {}} className="h-full w-[100%] ml-5">
|
||||
<ComposedChart
|
||||
data={chartData}
|
||||
height={graphSize * 7.5}
|
||||
width={graphSize * 12.5}
|
||||
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
|
||||
margin={{ top: 5, right: 20, left: 30, bottom: 5 }}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
{setXAxisYearTick?
|
||||
<XAxis dataKey="label" ticks={monthTicks} tickFormatter={(value) => value.slice(0,7)} fontSize="8px" fontWeight="bold"/> :
|
||||
<XAxis dataKey="label" fontSize="8px" fontWeight="bold"/>
|
||||
<XAxis dataKey="label" ticks={monthTicks} tickFormatter={(value) => value.slice(0,7)} className="text-[12px] font-bold" /> :
|
||||
<XAxis dataKey="label" className="text-[12px] font-bold" />
|
||||
}
|
||||
{/* 왼쪽 Y축: lineVal */}
|
||||
{yFieldText && <YAxis
|
||||
yAxisId="left"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
className="text-[12px] font-bold"
|
||||
tickFormatter={(value) =>
|
||||
typeof value === "number" ? value.toLocaleString() : value
|
||||
}
|
||||
domain={[0, leftScale.max]}
|
||||
ticks={leftScale.ticks}
|
||||
orientation="left"
|
||||
label={(props) => <CustomAxisText {...props} fieldText={yFieldText} position="left" />}
|
||||
/>}
|
||||
@@ -193,12 +229,13 @@ export default function LineBarchart({
|
||||
{yFieldText2 && <YAxis
|
||||
yAxisId="right"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
orientation="right"
|
||||
tickFormatter={(value) =>
|
||||
typeof value === "number" ? value.toLocaleString() : value
|
||||
}
|
||||
label={(props) => <CustomAxisText {...props} fieldText={yFieldText} position="right" />}
|
||||
domain={[0, rightScale.max]}
|
||||
ticks={rightScale.ticks}
|
||||
label={(props) => <CustomAxisText {...props} fieldText={yFieldText2} position="right" />}
|
||||
reversed={true}
|
||||
/>}
|
||||
<Tooltip content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />} />
|
||||
|
||||
@@ -235,15 +235,13 @@ export default function Linechart({
|
||||
{setXAxisYearTick ?
|
||||
<XAxis
|
||||
dataKey="label"
|
||||
fontSize="8px"
|
||||
fontWeight="bold"
|
||||
className="text-[12px] font-bold"
|
||||
ticks={monthTicks}
|
||||
tickFormatter={(value) => value.slice(0,7)}
|
||||
/> :
|
||||
<XAxis
|
||||
dataKey="label"
|
||||
fontSize="8px"
|
||||
fontWeight="bold"
|
||||
className="text-[12px] font-bold"
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ type ExcelDownloadProps = {
|
||||
fileName: string;
|
||||
sheetNameArray: string[];
|
||||
headerRange?: string;
|
||||
decimalArray0? : number[];
|
||||
decimalArray1? : number[];
|
||||
decimalArray2? : number[];
|
||||
decimalArray3? : number[];
|
||||
}
|
||||
|
||||
export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => {
|
||||
@@ -15,6 +19,45 @@ export const downloadExcel = ({ data, fileName = new Date().getTime().toString()
|
||||
excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName);
|
||||
};
|
||||
|
||||
export const downloadExcelFormat = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3', decimalArray0=[], decimalArray1=[], decimalArray2=[], decimalArray3=[]}: 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]);
|
||||
console.log('decimalArray0 : ', decimalArray0);
|
||||
const headerEnd = XLSX.utils.decode_range(headerRange).e.r; // 0-based index
|
||||
const startRow = headerEnd + 1;
|
||||
|
||||
// 🔹 전체 범위
|
||||
const range = XLSX.utils.decode_range(worksheet["!ref"] as string);
|
||||
|
||||
const chaneValue = (array: number[], decimal: number) => {
|
||||
for (let row = startRow; row <= range.e.r; row++) {
|
||||
if(array.length>0){
|
||||
for(let idx=0; idx<array.length; idx++){
|
||||
const cellAddr = XLSX.utils.encode_cell({ r: row, c: Number(array[idx]) });
|
||||
const cell = worksheet[cellAddr];
|
||||
if (cell && typeof cell.v === "number") {
|
||||
if(decimal===1){
|
||||
cell.z = "#,##0.0"; // 소수점 3자리
|
||||
}else if(decimal===2){
|
||||
cell.z = "#,##0.00";
|
||||
}else if(decimal===3){
|
||||
cell.z = "#,##0.000";
|
||||
}else{
|
||||
cell.z = "#,##0"; // 기본 천단위 콤마
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
chaneValue(decimalArray0, 0);
|
||||
chaneValue(decimalArray1, 1);
|
||||
chaneValue(decimalArray2, 2);
|
||||
chaneValue(decimalArray3, 3);
|
||||
excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName);
|
||||
};
|
||||
|
||||
export type SheetParameter = {
|
||||
data: any[][];
|
||||
headerNum: number;
|
||||
|
||||
@@ -331,11 +331,7 @@ function Gis({
|
||||
)}
|
||||
{/* SearchView가 존재할 때만 렌더링 */}
|
||||
{SearchView && (
|
||||
<SearchView
|
||||
open={isSearchModalOpen}
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
<SearchView />
|
||||
)}
|
||||
{/* 시도/시군구 조회 영역 */}
|
||||
{SearchBar && (
|
||||
|
||||
@@ -165,7 +165,10 @@ export const SearchBar = ({
|
||||
let list: SelectDataType[] = [];
|
||||
|
||||
if (type === "tb_il_swage_prcs_fclt") {
|
||||
const cqlFilter = `ctpv_nm='${sidoNm}' and sgg_nm='${sggNm}' and swtrmt_fclt_nm like '%${fcltyNm}%'`;
|
||||
// 하수처리시설
|
||||
let cqlFilter = `swtrmt_fclt_nm like '%${fcltyNm}%'`;
|
||||
if (sggNm !== "시군구" && sggNm) cqlFilter = `sgg_nm='${sggNm}' and ` + cqlFilter;
|
||||
if (sidoNm !== "시도" && sidoNm) cqlFilter = `ctpv_nm='${sidoNm}' and ` + cqlFilter;
|
||||
url += encodeURIComponent(cqlFilter);
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
@@ -8,7 +8,8 @@ interface TableUpperProps {
|
||||
row? : number;
|
||||
rowSpan?: number;
|
||||
colSpan?: number;
|
||||
visible?:boolean;
|
||||
visible?:boolean;
|
||||
widthPercent?: number;
|
||||
}
|
||||
|
||||
const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => {
|
||||
@@ -52,7 +53,7 @@ const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => {
|
||||
colSpan={head.colSpan || 1}
|
||||
rowSpan={head.rowSpan || 1}
|
||||
style={{ whiteSpace: 'pre-line' }}
|
||||
className="bg-gray-50"
|
||||
className="bg-gray-50"
|
||||
>
|
||||
{head.title}
|
||||
</TableHead>
|
||||
|
||||
@@ -32,6 +32,7 @@ type UiTableProps = {
|
||||
index?: number;
|
||||
curPage?: number;
|
||||
headButton?: React.ReactNode | undefined;
|
||||
widthFixed?: boolean;
|
||||
};
|
||||
|
||||
type UiHeadProps = {
|
||||
@@ -39,6 +40,9 @@ type UiHeadProps = {
|
||||
title: string;
|
||||
align?: string;
|
||||
visible?: boolean;
|
||||
row?: number;
|
||||
widthPercent?: number;
|
||||
rowSpan?: number;
|
||||
};
|
||||
|
||||
export function UiTable({
|
||||
@@ -57,6 +61,7 @@ export function UiTable({
|
||||
index = -1,
|
||||
curPage = 1,
|
||||
headButton = undefined,
|
||||
widthFixed = false
|
||||
}: UiTableProps) {
|
||||
const [cur, setCur] = useState<number>(curPage);
|
||||
const [idx, setIdx] = useState<number>(index);
|
||||
@@ -104,6 +109,15 @@ export function UiTable({
|
||||
}
|
||||
};
|
||||
|
||||
const getLeafCols = (list: UiHeadProps[]) => {
|
||||
const maxRow = list?.reduce((max, head) => Math.max(max, head.row || 0), 0);
|
||||
return list
|
||||
.filter(h => h.visible !== false)
|
||||
.filter(h => ((h.row ?? 0) + (h.rowSpan ?? 1) - 1) >= maxRow)
|
||||
;
|
||||
};
|
||||
|
||||
const leafCols = getLeafCols(headlist);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -122,7 +136,13 @@ export function UiTable({
|
||||
<></>
|
||||
)}
|
||||
<div className={`card !p-0r !m-[1.2rem] ${className}`}>
|
||||
<Table variant="secondary">
|
||||
<Table variant="secondary" className={widthFixed?"table-fixed":""}>
|
||||
{widthFixed?
|
||||
<colgroup>
|
||||
{leafCols.map(col => (
|
||||
<col key={col.id} style={{ width: col.widthPercent ? `${col.widthPercent}%` : undefined }} />
|
||||
))}
|
||||
</colgroup>:""}
|
||||
<TableHeader ref={headerRef} className="font-bold">
|
||||
{children ? children : <HeadMakeColSpan upHeadList={headlist} />}
|
||||
</TableHeader>
|
||||
|
||||
Reference in New Issue
Block a user