발전시뮬레이션 임시 추가

This commit is contained in:
leeyongjae 2024-11-07 17:40:57 +09:00
parent 2fea382aca
commit ed8a981631
5 changed files with 429 additions and 0 deletions

View File

@ -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",

View File

@ -0,0 +1,9 @@
import Simulator from '@/components/simulator/Simulator'
export default function SimulatorPage() {
return (
<>
<Simulator />
</>
)
}

View File

@ -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')

View File

@ -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', '<br/>'))
} else {
setContent(getMessage('common.message.no.data'))
}
}
return (
<div className="sub-content estimate">
<div className="sub-content-inner">
<div className="sub-content-box">
<div className="sub-table-box">
<div className="estimate-list-wrap">
{/* 물건번호 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub1')}</div>
<div className="estimate-name">
{objectNo} (Plan No: {plan?.id})
</div>
</div>
{/* 작성일 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub2')}</div>
<div className="estimate-name">{`${dayjs(objectDetail.lastEditDatetime).format('YYYY.MM.DD')}`}</div>
</div>
{/* 시스템용량 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub3')}</div>
<div className="estimate-name">??? kW</div>
</div>
{/* 연간예측발전량 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
<div className="estimate-name">???</div>
</div>
</div>
<div className="estimate-list-wrap">
{/* 도도부현 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub5')}</div>
<div className="estimate-name">
{prefCodeList.map((prefCode) => (prefCode.prefId === objectDetail.prefId ? prefCode.prefName : ''))}
{` `}
{objectDetail.address}
</div>
</div>
{/* 일사량 관측지점 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub6')}</div>
<div className="estimate-name">
{areaIdList.map((areaCode) => (areaCode.areaId === objectDetail.areaId ? areaCode.prefName : ''))}
</div>
</div>
{/* 적설조건 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub7')}</div>
<div className="estimate-name">
{objectDetail.verticalSnowCover}
{objectDetail.verticalSnowCover && 'cm'}
</div>
</div>
{/* 풍속조건 */}
<div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub8')}</div>
<div className="estimate-name">
{windSpeedList.map((windSpeed) => (windSpeed.clCode === objectDetail.standardWindSpeedId ? windSpeed.clCodeNm : ''))}
</div>
</div>
</div>
</div>
</div>
<div className="sub-content-box">
<div className="chart-wrap">
<div className="chart-inner">
<div className="sub-table-box">
<div className="chart-box">
<Bar data={data} options={options} />
</div>
<div className="table-box-title-wrap">
<div className="title-wrap">
<h3>{getMessage('simulator.table.sub9')} </h3>
</div>
</div>
{/* 예측발전량 */}
<div className="chart-month-table">
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
<th>10</th>
<th>11</th>
<th>12</th>
<th>{getMessage('simulator.table.sub6')}</th>
</tr>
</thead>
<tbody>
<tr>
{chartData.map((data) => (
<td>{convertNumberToPriceDecimal(data)}</td>
))}
<td>{convertNumberToPriceDecimal(chartData.reduce((a, b) => a + b))}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div className="chart-table-wrap">
<div className="sub-table-box">
<div className="module-table ">
<table className="small">
<thead>
<tr>
<th>{getMessage('simulator.table.sub1')}</th>
<th>{getMessage('simulator.table.sub2')}</th>
<th>{getMessage('simulator.table.sub3')}</th>
<th>{getMessage('simulator.table.sub4')}</th>
<th>{getMessage('simulator.table.sub5')}</th>
</tr>
</thead>
<tbody>
{moduleInfoList.length > 0 ? (
moduleInfoList.map((moduleInfo) => {
return (
<>
<tr>
{/* 지붕면 */}
<td>西南西1</td>
{/* 경사각 */}
<td>0.3</td>
{/* 방위각(도) */}
<td>81</td>
{/* 태양전지모듈 */}
<td>
<div className="overflow-lab">Re.RISE-G2 415</div>
</td>
{/* 매수 */}
<td>20</td>
</tr>
</>
)
})
) : (
<tr>
<td colSpan={5}>{getMessage('common.message.no.data')}</td>
</tr>
)}
</tbody>
</table>
{moduleInfoList.length > 0 && (
<div className="module-total">
<div className="total-title">{getMessage('simulator.table.sub6')}</div>
<div className="total-num">0</div>
</div>
)}
</div>
</div>
<div className="sub-table-box">
<div className="module-table ">
<table className="big">
<thead>
<tr>
<th>{getMessage('simulator.table.sub7')}</th>
<th>{getMessage('simulator.table.sub8')}</th>
</tr>
</thead>
<tbody>
{pcsInfoList.length > 0 ? (
pcsInfoList.map((pcsInfo) => {
return (
<>
<tr>
{/* 파워컨디셔너 */}
<td className="al-l">
<div className="overflow-lab">HQJP-MA55-3</div>
</td>
{/* 대 */}
<td>2</td>
</tr>
</>
)
})
) : (
<tr>
<td colSpan={2}>{getMessage('common.message.no.data')}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div className="sub-content-box">
<div className="sub-table-box">
<div className="simulation-guide-wrap">
<div className="simulation-tit-wrap">
<span>
{getMessage('simulator.notice.sub1')}
<br />
{getMessage('simulator.notice.sub2')}
</span>
</div>
{/* 시뮬레이션 안내사항 */}
<div
className="simulation-guide-box"
dangerouslySetInnerHTML={{
__html: content,
}}
></div>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -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"