diff --git a/packages/common/business_components/src/ui/chart/DoubleBar.tsx b/packages/common/business_components/src/ui/chart/DoubleBar.tsx index 9627485aa..be930f3af 100644 --- a/packages/common/business_components/src/ui/chart/DoubleBar.tsx +++ b/packages/common/business_components/src/ui/chart/DoubleBar.tsx @@ -83,7 +83,6 @@ export default function DoubleBarChart({chartConfig=undefined, chartData=[], cha .map(key => item[key]) ).flat()); }; - console.log(chartData); const barMaxValue = getMaxValue(chartData, 'barVal'); return ( <> diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 64a7d5bce..d81d6e2c8 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -94,6 +94,7 @@ export default function LineBarchart({ // 데이터의 최대값 계산 const getMaxValue = (data: any[], keyPrefix: string) => { + if (!data || data.length === 0) return 0; return Math.max(...data.map(item => Object.keys(item) .filter(key => key.includes(keyPrefix)) diff --git a/packages/common/business_components/src/ui/chart/LineBarChart2.tsx b/packages/common/business_components/src/ui/chart/LineBarChart2.tsx new file mode 100644 index 000000000..ff8e6b5cb --- /dev/null +++ b/packages/common/business_components/src/ui/chart/LineBarChart2.tsx @@ -0,0 +1,186 @@ +"use client" +import type { CSSProperties } from "react"; +import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from "recharts"; +import { + ChartContainer, + type ChartConfig +} from "./lib/chart"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; + +// 툴크 커스텀 css +/** 차트 기본 스타일 */ +const customStyles = { + legendContainer: { + display: "flex", + justifyContent: "center", + gap: "20px", + fontSize: "9px", + fontWeight : "bold", + marginTop: "10px", + } 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 }: { 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.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`} +
+ ))} +
+ ); + } + return null; +}; + +// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) +const CustomLegend = (props: LegendProps) => { + const { payload } = props; + if (!payload) return null; + + return ( +
+ {payload.map((entry: LegendEntry, index: number) => ( +
+
+ {entry.value} +
+ ))} +
+ ); +}; + +export default function LineBarchart2({ + chartConfig, + chartData, + chartTitle, + chartClassName = 'w-full h-full', + graphSize = 40, + yFieldText, + yFieldText2, + showLegend = true +}: LineBarChartProps) { + if (!chartData || chartData.length === 0) { + return
데이터가 없습니다.
; + } + + // 데이터의 최대값 계산 + const getMaxValue = (data: any[], keyPrefix: string) => { + if (!data || data.length === 0) return 0; + return Math.max(...data.map(item => + Object.keys(item) + .filter(key => key.includes(keyPrefix)) + .map(key => item[key]) + ).flat()); + }; + + const lineMaxValue = getMaxValue(chartData, 'lineVal'); + const barMaxValue = getMaxValue(chartData, 'barVal'); + return ( +
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( +
+ {chartTitle} +
+ ) : null} +
+ + + + + {/* 왼쪽 Y축: lineVal */} + {yFieldText && } + {/* 오른쪽 Y축: barVal */} + {yFieldText2 && } + } /> + {showLegend && } + {/* 바 차트 (오른쪽 Y축 사용) */} + {Object.keys(chartData[0]).map((key, index) => { + const config = chartConfig?.[key]; + if(key.includes("barVal")) { + return ( + + ); + } + return null; + })} + {/* 라인 차트 (왼쪽 Y축 사용) */} + {Object.keys(chartData[0]).map((key, index) => { + const config = chartConfig?.[key]; + if(key.includes("lineVal")) { + return ( + + ); + } + return null; + })} + + +
+
+ ); +} \ 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 6e0010f9a..ee308e378 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -2,6 +2,7 @@ import DoubleBarChart from './DoubleBar'; import DoughnutChart from './DoughnutChart'; import LineBarchart from './LineBarChart'; +import LineBarchart2 from './LineBarChart2'; import LineChart from './LineChart'; /**차트 FORM */ @@ -14,6 +15,8 @@ const CHART_FORM = { DN :'daughnut', /**LINE,BAR차트 */ LINEBAR : 'linebar', + /**LINE,BAR차트 */ + LINEBAR2 : 'linebar2', /**더블바 차트 형태*/ DOUBLEBAR : 'doublebar' }; @@ -72,6 +75,10 @@ function MainChart(props: MainChartProps) { return ( ); + } else if (props.chartForm == CHART_FORM.LINEBAR2){ + return ( + + ); } else if (props.chartForm == CHART_FORM.DOUBLEBAR){ return ( diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 6cb7938fe..9dd0dda76 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -16,6 +16,73 @@ import GisApp from './lib/gis.core'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); +export const searchUpperPpls = async (fcltMngNo) => { + const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ''; + + const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, { + method: "GET", + }); + + let data = null; + if (res.ok) { + try { + // 응답이 비어있는지 확인 + const contentLength = res.headers.get('content-length'); + if (contentLength === '0') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`); + + // 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인 + const text = await res.text(); + if (!text || text.trim() === '') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`); + + // JSON 파싱 시도 + data = JSON.parse(text); + } catch (error) { + console.error('JSON parsing error:', error); + alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`); + } + } + + const select = GisApp.Module.select; + + const gisMap = GisApp.Module.core?.map; + + let foundFeature = null; + + data.forEach(item => { + const pplMngNo = item.ppl_mng_no.trim(); + + gisMap.getLayers().getArray().filter(e => e.get("lyrTyNm") == 'tb_ms_swage_ppl').forEach(layer => { + const source = layer.getSource?.(); + if (source?.getFeatures) { + const features = source.getFeatures(); + for (const f of features) { + if (f.values_.fclt_mng_no.trim() === pplMngNo) { + foundFeature = f; + break; + } + } + } + }); + + if (foundFeature) select.selectMultiFeature(foundFeature, true); + }); + + if (!foundFeature) alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`); + + let geometry = foundFeature?.getGeometry(); + if (geometry) { + let extent = geometry.getExtent(); + + gisMap.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 14, + constrainResolution: true, + nearest: true, + }); + } +}; + function Gis({ isOpen, @@ -94,7 +161,10 @@ function Gis({ featureId = data.bscFctCd; } else if (layerName === '온배수시설') { featureId = data.bdstRuseFcltSn; - } else { + } else if(layerName == '수요처'){ + featureId = data.dmdInfoId.replaceAll('-','') + } + else { console.warn('해당 레이어에 대한 featureId 매핑 없음', layerNm); featureId = data.fid || data.id || data.ftrIdn; } @@ -301,6 +371,7 @@ function Gis({ onFeatureSelect={handleFeatureSelect} /> )} + {/* */} diff --git a/packages/common/business_components/src/ui/map/lib/gis.select.js b/packages/common/business_components/src/ui/map/lib/gis.select.js index 21ab39609..d0209a6ed 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.select.js +++ b/packages/common/business_components/src/ui/map/lib/gis.select.js @@ -429,6 +429,19 @@ GisOlSelect.prototype = { auto: auto, }); }, + /** + * 피처 다중 셀렉트 + */ + selectMultiFeature: function (feature, auto) { + let me = this; + me.selectedFeatures.push(feature); + me.selectInteraction.dispatchEvent({ + type: 'select', + selected: me.selectedFeatures, + deselected: [], + auto: auto, + }); + }, }; export default GisOlSelect; diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 786b07925..09dd10525 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -215,6 +215,8 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun useEffect(() => { + form.setValue('rgtr', '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e'); + if(updateList?.dmdInfoId){ form.reset(updateList) if(updateList?.dmdInfoId.slice(10,14) !== '0000') { @@ -322,7 +324,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun }, { name: "bznmTotNo", - label: "종사업자등록번호", + label: "", component: (field) => ( (
-