From ed8a9816313c8441e9cc43db319a56011922b06f Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Thu, 7 Nov 2024 17:40:57 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=9C=EC=A0=84=EC=8B=9C=EB=AE=AC=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=9E=84=EC=8B=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/app/floor-plan/simulator/[mid]/page.jsx | 9 + src/components/floor-plan/CanvasMenu.jsx | 3 + src/components/simulator/Simulator.jsx | 398 ++++++++++++++++++++ yarn.lock | 17 + 5 files changed, 429 insertions(+) create mode 100644 src/app/floor-plan/simulator/[mid]/page.jsx create mode 100644 src/components/simulator/Simulator.jsx diff --git a/package.json b/package.json index 6c94a282..f338fbe4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@nextui-org/react": "^2.4.2", "ag-grid-react": "^32.0.2", "axios": "^1.7.3", + "chart.js": "^4.4.6", "fabric": "^5.3.0", "framer-motion": "^11.2.13", "fs": "^0.0.1-security", @@ -22,6 +23,7 @@ "next": "14.2.3", "next-international": "^1.2.4", "react": "^18", + "react-chartjs-2": "^5.2.0", "react-colorful": "^5.6.1", "react-datepicker": "^7.3.0", "react-dom": "^18", diff --git a/src/app/floor-plan/simulator/[mid]/page.jsx b/src/app/floor-plan/simulator/[mid]/page.jsx new file mode 100644 index 00000000..939e1cc4 --- /dev/null +++ b/src/app/floor-plan/simulator/[mid]/page.jsx @@ -0,0 +1,9 @@ +import Simulator from '@/components/simulator/Simulator' + +export default function SimulatorPage() { + return ( + <> + + + ) +} diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 326bc4f4..613db2bd 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -81,6 +81,9 @@ export default function CanvasMenu(props) { case 4: setType('module') break + case 6: + router.push(`/floor-plan/simulator/${menu.index}`) + break } if (pathname !== '/floor-plan') router.push('/floor-plan') diff --git a/src/components/simulator/Simulator.jsx b/src/components/simulator/Simulator.jsx new file mode 100644 index 00000000..9e7768a1 --- /dev/null +++ b/src/components/simulator/Simulator.jsx @@ -0,0 +1,398 @@ +'use client' + +import 'chart.js/auto' +import { Bar } from 'react-chartjs-2' +import dayjs from 'dayjs' + +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' +import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' + +import { useAxios } from '@/hooks/useAxios' +import { useMessage } from '@/hooks/useMessage' +import { usePlan } from '@/hooks/usePlan' +import { useCommonCode } from '@/hooks/common/useCommonCode' +import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' + +import { convertNumberToPriceDecimal, isEmptyArray } from '@/util/common-utils' + +export default function Simulator() { + const { plans } = usePlan() + const plan = plans.find((plan) => plan.isCurrent === true) + + // recoil 물건번호 + const objectRecoil = useRecoilValue(floorPlanObjectState) + const [objectNo, setObjectNo] = useState('') + + useEffect(() => { + setObjectNo(objectRecoil.floorPlanObjectNo) + }, [objectRecoil]) + + // 캔버스 메뉴 넘버 셋팅 + const { setMenuNumber } = useCanvasMenu() + + useEffect(() => { + setMenuNumber(6) + }, []) + + const { get } = useAxios() + const { getMessage } = useMessage() + + // 공통코드 조회 + const { commonCode, findCommonCode } = useCommonCode() + const [prefCodeList, setPrefCodeList] = useState([]) // 도도부현 코드 + const [areaIdList, setAreaIdList] = useState([]) // 시뮬레이션 코드 + const [windSpeedList, setWindSpeedList] = useState([]) // 기준 풍속 공통코드 + + useEffect(() => { + const code1 = findCommonCode(202000) // 기준풍속 + if (code1 != null) { + setWindSpeedList(code1) + } + }, [commonCode]) + + const chartData = [ + Math.floor(Math.random() * 1000), + Math.floor(Math.random() * 2000), + Math.floor(Math.random() * 3000), + Math.floor(Math.random() * 4000), + Math.floor(Math.random() * 5000), + Math.floor(Math.random() * 6000), + Math.floor(Math.random() * 6000), + Math.floor(Math.random() * 5000), + Math.floor(Math.random() * 4000), + Math.floor(Math.random() * 3000), + Math.floor(Math.random() * 2000), + Math.floor(Math.random() * 1000), + ] + + // 차트 관련 + const data = { + labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + datasets: [ + { + label: 'kWh', + data: chartData, + + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)', + 'rgba(0, 99, 132, 0.2)', + 'rgba(0, 162, 235, 0.2)', + 'rgba(0, 206, 86, 0.2)', + 'rgba(0, 192, 192, 0.2)', + 'rgba(0, 102, 255, 0.2)', + 'rgba(0, 159, 64, 0.2)', + ], + borderColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)', + 'rgba(0, 99, 132, 0.2)', + 'rgba(0, 162, 235, 0.2)', + 'rgba(0, 206, 86, 0.2)', + 'rgba(0, 192, 192, 0.2)', + 'rgba(0, 102, 255, 0.2)', + 'rgba(0, 159, 64, 0.2)', + ], + borderWidth: 1, + }, + ], + } + + const options = { + plugins: { + legend: { + position: 'top', + }, + }, + scales: { + x: { + grid: { + display: false, + }, + }, + y: { + beginAtZero: true, + grid: { + display: true, + }, + }, + }, + } + + useEffect(() => { + if (objectNo) { + fetchObjectDetail(objectNo) + } + fetchSimulatorNotice() + }, [objectNo]) + + // 물건 상세 정보 조회 + const [objectDetail, setObjectDetail] = useState({}) + + const fetchObjectDetail = async (objectNo) => { + const apiUrl = `/api/object/${objectNo}/detail` + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.prefId) { + get({ url: '/api/object/prefecture/list' }).then((res) => { + if (!isEmptyArray(res)) { + setPrefCodeList(res) + } + }) + get({ url: `/api/object/prefecture/${resultData.prefId}/list` }).then((res) => { + if (!isEmptyArray(res)) { + setAreaIdList(res) + } + }) + } + setObjectDetail(resultData) + } + } + + // 모듈배치정보 조회 + const [moduleInfoList, setModuleInfoList] = useState([]) + + // 파워컨디셔너 조회 + const [pcsInfoList, setPcsInfoList] = useState([]) + + // 시뮬레이션 안내사항 조회 + const [content, setContent] = useState('') + + const fetchSimulatorNotice = async () => { + const resultData = '1234123\r\n12\r\n3\r\n123\r\n12\r\n312\r\n312\r\n3123123123123\r\n\r\nㅅㄷㄴㅅ\r\nㅁㅅㄷㄴㅁㅅㄴㅁㅅㄴㅅ' + if (resultData) { + setContent(resultData.replaceAll('\n', '
')) + } else { + setContent(getMessage('common.message.no.data')) + } + } + + return ( +
+
+
+
+
+ {/* 물건번호 */} +
+
{getMessage('simulator.title.sub1')}
+
+ {objectNo} (Plan No: {plan?.id}) +
+
+ {/* 작성일 */} +
+
{getMessage('simulator.title.sub2')}
+
{`${dayjs(objectDetail.lastEditDatetime).format('YYYY.MM.DD')}`}
+
+ {/* 시스템용량 */} +
+
{getMessage('simulator.title.sub3')}
+
??? kW
+
+ {/* 연간예측발전량 */} +
+
{getMessage('simulator.title.sub4')}
+
???
+
+
+
+ {/* 도도부현 */} +
+
{getMessage('simulator.title.sub5')}
+
+ {prefCodeList.map((prefCode) => (prefCode.prefId === objectDetail.prefId ? prefCode.prefName : ''))} + {` `} + {objectDetail.address} +
+
+ {/* 일사량 관측지점 */} +
+
{getMessage('simulator.title.sub6')}
+
+ {areaIdList.map((areaCode) => (areaCode.areaId === objectDetail.areaId ? areaCode.prefName : ''))} +
+
+ {/* 적설조건 */} +
+
{getMessage('simulator.title.sub7')}
+
+ {objectDetail.verticalSnowCover} + {objectDetail.verticalSnowCover && 'cm'} +
+
+ {/* 풍속조건 */} +
+
{getMessage('simulator.title.sub8')}
+
+ {windSpeedList.map((windSpeed) => (windSpeed.clCode === objectDetail.standardWindSpeedId ? windSpeed.clCodeNm : ''))} +
+
+
+
+
+
+
+
+
+
+ +
+
+
+

{getMessage('simulator.table.sub9')}

+
+
+ {/* 예측발전량 */} +
+ + + + + + + + + + + + + + + + + + + + + {chartData.map((data) => ( + + ))} + + + +
1月2月3月4月5月6月7月8月9月10月11月12月{getMessage('simulator.table.sub6')}
{convertNumberToPriceDecimal(data)}{convertNumberToPriceDecimal(chartData.reduce((a, b) => a + b))}
+
+
+
+
+
+
+ + + + + + + + + + + + {moduleInfoList.length > 0 ? ( + moduleInfoList.map((moduleInfo) => { + return ( + <> + + {/* 지붕면 */} + + {/* 경사각 */} + + {/* 방위각(도) */} + + {/* 태양전지모듈 */} + + {/* 매수 */} + + + + ) + }) + ) : ( + + + + )} + +
{getMessage('simulator.table.sub1')}{getMessage('simulator.table.sub2')}{getMessage('simulator.table.sub3')}{getMessage('simulator.table.sub4')}{getMessage('simulator.table.sub5')}
西南西10.3寸81 +
Re.RISE-G2 415
+
20
{getMessage('common.message.no.data')}
+ {moduleInfoList.length > 0 && ( +
+
{getMessage('simulator.table.sub6')}
+
0
+
+ )} +
+
+
+
+ + + + + + + + + {pcsInfoList.length > 0 ? ( + pcsInfoList.map((pcsInfo) => { + return ( + <> + + {/* 파워컨디셔너 */} + + {/* 대 */} + + + + ) + }) + ) : ( + + + + )} + +
{getMessage('simulator.table.sub7')}{getMessage('simulator.table.sub8')}
+
HQJP-MA55-3
+
2
{getMessage('common.message.no.data')}
+
+
+
+
+
+
+
+
+
+ + {getMessage('simulator.notice.sub1')} +
+ {getMessage('simulator.notice.sub2')} +
+
+ {/* 시뮬레이션 안내사항 */} +
+
+
+
+
+
+ ) +} diff --git a/yarn.lock b/yarn.lock index a167fa9d..ed2cceda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -527,6 +527,11 @@ resolved "https://registry.npmjs.org/@js-joda/core/-/core-5.6.3.tgz" integrity sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA== +"@kurkle/color@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" + integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== + "@mapbox/node-pre-gyp@^1.0.0": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" @@ -4337,6 +4342,13 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chart.js@^4.4.6: + version "4.4.6" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.6.tgz#da39b84ca752298270d4c0519675c7659936abec" + integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA== + dependencies: + "@kurkle/color" "^0.3.0" + "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" @@ -5832,6 +5844,11 @@ rbush@^3.0.1: dependencies: quickselect "^2.0.0" +react-chartjs-2@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz#43c1e3549071c00a1a083ecbd26c1ad34d385f5d" + integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA== + react-color-palette@^7.2.2: version "7.2.2" resolved "https://registry.npmjs.org/react-color-palette/-/react-color-palette-7.2.2.tgz"