591 lines
22 KiB
JavaScript
591 lines
22 KiB
JavaScript
import { forwardRef, use, useContext, useEffect, useImperativeHandle, useState } from 'react'
|
||
import { useMessage } from '@/hooks/useMessage'
|
||
import { getDegreeInOrientation } from '@/util/canvas-util'
|
||
import { numberCheck } from '@/util/common-utils'
|
||
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
|
||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||
import { roofsState } from '@/store/roofAtom'
|
||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||
import Swal from 'sweetalert2'
|
||
import { normalizeDecimal} from '@/util/input-utils'
|
||
|
||
export const Orientation = forwardRef((props, ref) => {
|
||
const { getMessage } = useMessage()
|
||
const { findCommonCode } = useCommonCode()
|
||
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
||
const basicSetting = useRecoilValue(basicSettingState)
|
||
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //지붕재 선택
|
||
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
||
const [roofTab, setRoofTab] = useState(0) //지붕재 탭
|
||
const [selectedModuleSeries, setSelectedModuleSeries] = useState(null)
|
||
const [moduleSeriesList, setModuleSeriesList] = useState([])
|
||
const [filteredModuleList, setFilteredModuleList] = useState([])
|
||
const {
|
||
roofs,
|
||
setRoofs,
|
||
tabNum,
|
||
setTabNum,
|
||
compasDeg,
|
||
setCompasDeg,
|
||
selectedModules,
|
||
roughnessCodes,
|
||
windSpeedCodes,
|
||
managementState,
|
||
setManagementState,
|
||
moduleList,
|
||
moduleSelectionData,
|
||
setModuleSelectionData,
|
||
setSelectedModules,
|
||
selectedSurfaceType,
|
||
setSelectedSurfaceType,
|
||
installHeight,
|
||
setInstallHeight,
|
||
standardWindSpeed,
|
||
setStandardWindSpeed,
|
||
verticalSnowCover,
|
||
setVerticalSnowCover,
|
||
orientationTrigger,
|
||
nextStep,
|
||
currentCanvasPlan,
|
||
loginUserState,
|
||
updateObjectDataApi,
|
||
} = props
|
||
const [inputCompasDeg, setInputCompasDeg] = useState(compasDeg ?? 0)
|
||
const [inputInstallHeight, setInputInstallHeight] = useState('0')
|
||
const [inputMargin, setInputMargin] = useState('0')
|
||
const [inputVerticalSnowCover, setInputVerticalSnowCover] = useState('0')
|
||
const [inputRoughness, setInputRoughness] = useState(selectedSurfaceType)
|
||
const [inputStandardWindSpeed, setInputStandardWindSpeed] = useState(standardWindSpeed)
|
||
const { restoreModuleInstArea } = useModuleBasicSetting()
|
||
const moduleData = {
|
||
header: [
|
||
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
|
||
{
|
||
name: `${getMessage('height')} (mm)`,
|
||
prop: 'height',
|
||
},
|
||
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
|
||
{ name: `${getMessage('output')} (W)`, prop: 'output' },
|
||
],
|
||
}
|
||
|
||
useEffect(() => {
|
||
if (basicSetting.roofSizeSet == '3') {
|
||
restoreModuleInstArea()
|
||
}
|
||
}, [])
|
||
|
||
useEffect(() => {
|
||
if (moduleSelectionData?.common) {
|
||
setInputMargin(moduleSelectionData?.common?.margin)
|
||
}
|
||
}, [moduleSelectionData])
|
||
|
||
useEffect(() => {
|
||
if (selectedModules) {
|
||
const foundModule = moduleList.find((module) => module.itemId === selectedModules.itemId)
|
||
if (foundModule) {
|
||
setSelectedModules(foundModule)
|
||
|
||
// 선택된 모듈의 시리즈로 업데이트 (시리즈 목록이 있는 경우에만)
|
||
if (moduleSeriesList.length > 0 && foundModule.moduleSerCd) {
|
||
const currentSeries = moduleSeriesList.find(series => series.moduleSerCd === foundModule.moduleSerCd)
|
||
if (currentSeries && (!selectedModuleSeries || selectedModuleSeries.moduleSerCd !== currentSeries.moduleSerCd)) {
|
||
setSelectedModuleSeries(currentSeries)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}, [selectedModules, moduleList, moduleSeriesList])
|
||
|
||
useEffect(() => {
|
||
if (selectedSurfaceType) {
|
||
setInputRoughness(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
|
||
}
|
||
}, [selectedSurfaceType])
|
||
|
||
useEffect(() => {
|
||
if (standardWindSpeed) setInputStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
|
||
}, [standardWindSpeed])
|
||
|
||
useEffect(() => {
|
||
if (managementState?.installHeight && managementState?.installHeight) {
|
||
setSelectedSurfaceType(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
|
||
setInputInstallHeight(managementState?.installHeight)
|
||
setStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
|
||
setInputVerticalSnowCover(managementState?.verticalSnowCover)
|
||
}
|
||
}, [managementState])
|
||
|
||
useImperativeHandle(ref, () => ({
|
||
handleNextStep,
|
||
}))
|
||
|
||
const handleNextStep = () => {
|
||
if (isComplete()) {
|
||
const common = {
|
||
illuminationTp: inputRoughness.clCode,
|
||
illuminationTpNm: inputRoughness.clCodeNm,
|
||
instHt: inputInstallHeight,
|
||
stdWindSpeed: inputStandardWindSpeed?.clCode,
|
||
stdSnowLd: inputVerticalSnowCover,
|
||
saleStoreNorthFlg: managementState?.saleStoreNorthFlg,
|
||
moduleTpCd: selectedModules.itemTp,
|
||
moduleItemId: selectedModules.itemId,
|
||
margin: inputMargin,
|
||
}
|
||
setCompasDeg(inputCompasDeg)
|
||
setInstallHeight(inputInstallHeight)
|
||
setVerticalSnowCover(inputVerticalSnowCover)
|
||
setSelectedSurfaceType(inputRoughness)
|
||
setStandardWindSpeed(inputStandardWindSpeed)
|
||
nextStep(inputCompasDeg)
|
||
setManagementState({
|
||
...managementState,
|
||
installHeight: inputInstallHeight,
|
||
verticalSnowCover: inputVerticalSnowCover,
|
||
standardWindSpeedId: inputStandardWindSpeed?.clCode,
|
||
surfaceType: inputRoughness.clCodeNm,
|
||
surfaceTypeValue: inputRoughness.clCode,
|
||
})
|
||
setModuleSelectionData({
|
||
...moduleSelectionData,
|
||
module: {
|
||
...selectedModules,
|
||
},
|
||
common,
|
||
})
|
||
orientationTrigger({
|
||
compasDeg: inputCompasDeg,
|
||
common: common,
|
||
module: {
|
||
...selectedModules,
|
||
},
|
||
margin: inputMargin,
|
||
})
|
||
updateObjectDataApi({
|
||
objectNo: currentCanvasPlan.objectNo, //오브젝트_no
|
||
standardWindSpeedId: inputStandardWindSpeed?.clCode, //기준풍속코드
|
||
verticalSnowCover: inputVerticalSnowCover, //적설량
|
||
surfaceType: inputRoughness.clCodeNm, //면조도구분
|
||
installHeight: inputInstallHeight, //설치높이
|
||
userId: loginUserState.userId, //작성자아아디
|
||
})
|
||
setTabNum(2)
|
||
} else {
|
||
if (!selectedModules || !selectedModules.itemId) {
|
||
Swal.fire({
|
||
title: getMessage('module.not.found'),
|
||
icon: 'warning',
|
||
})
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
const checkDegree = (e) => {
|
||
if (e === '-0' || e === '-') {
|
||
setInputCompasDeg('-')
|
||
return
|
||
}
|
||
if (e === '0-') {
|
||
setInputCompasDeg('-0')
|
||
return
|
||
}
|
||
const n = Number(normalizeDecimal(e))
|
||
if (n >= -180 && n <= 180) {
|
||
if (numberCheck(n)) {
|
||
setInputCompasDeg(n)
|
||
}
|
||
} else {
|
||
setInputCompasDeg(compasDeg)
|
||
}
|
||
}
|
||
|
||
const isComplete = () => {
|
||
if (!selectedModules || !selectedModules.itemId) return false
|
||
if (basicSetting && basicSetting.roofSizeSet !== '3') {
|
||
if (inputInstallHeight <= 0) {
|
||
return false
|
||
}
|
||
|
||
if (+inputVerticalSnowCover <= 0) {
|
||
return false
|
||
}
|
||
|
||
if (!inputStandardWindSpeed) return false
|
||
if (!inputRoughness) return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
const handleChangeModuleSeries = (e) => {
|
||
resetRoofs()
|
||
setSelectedModuleSeries(e)
|
||
|
||
// 선택된 시리즈에 맞는 모듈 목록 필터링 및 첫 번째 모듈 선택
|
||
if (e && moduleList.length > 0) {
|
||
let filtered
|
||
|
||
if (e.moduleSerCd === 'ALL') {
|
||
// "전체" 선택 시 모든 모듈 표시
|
||
filtered = moduleList
|
||
} else {
|
||
// 특정 시리즈 선택 시 해당 시리즈 모듈만 표시
|
||
filtered = moduleList.filter(module => module.moduleSerCd === e.moduleSerCd)
|
||
}
|
||
|
||
setFilteredModuleList(filtered)
|
||
|
||
// 필터링된 목록의 첫 번째 모듈을 자동 선택
|
||
if (filtered.length > 0) {
|
||
setSelectedModules(filtered[0])
|
||
}
|
||
}
|
||
}
|
||
|
||
const handleChangeModule = (e) => {
|
||
resetRoofs()
|
||
setSelectedModules(e)
|
||
}
|
||
|
||
const handleChangeRoughness = (e) => {
|
||
resetRoofs()
|
||
setInputRoughness(e)
|
||
}
|
||
|
||
const handleChangeInstallHeight = (e) => {
|
||
resetRoofs()
|
||
setInputInstallHeight(e)
|
||
}
|
||
|
||
const handleChangeStandardWindSpeed = (e) => {
|
||
resetRoofs()
|
||
setInputStandardWindSpeed(e)
|
||
}
|
||
|
||
const handleChangeVerticalSnowCover = (e) => {
|
||
resetRoofs()
|
||
setInputVerticalSnowCover(e)
|
||
}
|
||
|
||
const resetRoofs = () => {
|
||
const newRoofs = addedRoofs.map((roof) => {
|
||
return {
|
||
...roof,
|
||
trestle: {
|
||
lengthBase: null,
|
||
trestleMkrCd: null,
|
||
constMthdCd: null,
|
||
constTp: null,
|
||
roofBaseCd: null,
|
||
roofPchBase: null,
|
||
},
|
||
addRoof: {
|
||
...roof.addRoof,
|
||
lengthBase: null,
|
||
eavesMargin: null,
|
||
kerabaMargin: null,
|
||
ridgeMargin: null,
|
||
},
|
||
construction: {
|
||
constTp: null,
|
||
cvrYn: 'N',
|
||
snowGdPossYn: 'N',
|
||
cvrChecked: false,
|
||
snowGdChecked: false,
|
||
},
|
||
}
|
||
})
|
||
// setRoofs(newRoofs)
|
||
// setAddedRoofs(newRoofs)
|
||
setRoofsStore(newRoofs)
|
||
}
|
||
|
||
// 모듈시리즈 목록 생성 및 commonCode와 매핑
|
||
useEffect(() => {
|
||
if (moduleList.length > 0 && moduleSeriesList.length === 0) {
|
||
const moduleSeriesCodes = findCommonCode(207100) || []
|
||
|
||
// moduleList에서 고유한 moduleSerCd 추출
|
||
const uniqueSeriesCd = [...new Set(moduleList.map(module => module.moduleSerCd).filter(Boolean))]
|
||
|
||
if (uniqueSeriesCd.length > 0) {
|
||
// moduleSerCd와 commonCode를 매핑하여 기본 moduleSeriesList 생성
|
||
const mappedSeries = uniqueSeriesCd.map(serCd => {
|
||
const matchedCode = moduleSeriesCodes.find(code => code.clCode === serCd)
|
||
return {
|
||
moduleSerCd: serCd,
|
||
moduleSerNm: matchedCode ? matchedCode.clCodeNm : serCd
|
||
}
|
||
})
|
||
|
||
// "전체" 옵션을 맨 앞에 추가
|
||
const allOption = {
|
||
moduleSerCd: 'ALL',
|
||
moduleSerNm: getMessage("board.sub.total") || 'ALL'
|
||
}
|
||
|
||
const seriesList = [allOption, ...mappedSeries]
|
||
setModuleSeriesList(seriesList)
|
||
|
||
// 현재 선택된 모듈이 있으면 해당 모듈의 시리즈를 찾아서 선택
|
||
if (selectedModules && selectedModules.moduleSerCd) {
|
||
const currentSeries = seriesList.find(series => series.moduleSerCd === selectedModules.moduleSerCd)
|
||
if (currentSeries) {
|
||
setSelectedModuleSeries(currentSeries)
|
||
} else {
|
||
setSelectedModuleSeries(allOption)
|
||
}
|
||
} else {
|
||
// 선택된 모듈이 없으면 "전체"를 기본 선택
|
||
setSelectedModuleSeries(allOption)
|
||
}
|
||
}
|
||
}
|
||
}, [moduleList, selectedModules])
|
||
|
||
// 초기 로딩 시에만 필터링된 모듈 목록 설정
|
||
useEffect(() => {
|
||
if (moduleList.length > 0 && filteredModuleList.length === 0 && selectedModuleSeries) {
|
||
let filtered
|
||
|
||
if (selectedModuleSeries.moduleSerCd === 'ALL') {
|
||
// "전체" 선택 시 모든 모듈 표시
|
||
filtered = moduleList
|
||
} else {
|
||
// 특정 시리즈 선택 시 해당 시리즈 모듈만 표시
|
||
filtered = moduleList.filter(module => module.moduleSerCd === selectedModuleSeries.moduleSerCd)
|
||
}
|
||
|
||
setFilteredModuleList(filtered)
|
||
|
||
if (filtered.length > 0 && !selectedModules) {
|
||
setSelectedModules(filtered[0])
|
||
}
|
||
}
|
||
}, [moduleList, selectedModuleSeries]);
|
||
return (
|
||
<>
|
||
<div className="properties-setting-wrap">
|
||
<div className="outline-wrap">
|
||
<div className="roof-module-inner">
|
||
<div className="compas-wrapper">
|
||
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
|
||
<div className="roof-module-compas">
|
||
<div className="compas-box">
|
||
<div className="compas-box-inner">
|
||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||
<div
|
||
key={index}
|
||
className={`circle ${getDegreeInOrientation(inputCompasDeg) === -15 * index + 180 || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
|
||
onClick={() => {
|
||
if (index === 0) {
|
||
setInputCompasDeg(180)
|
||
return
|
||
}
|
||
setInputCompasDeg(-15 * index + 180)
|
||
}}
|
||
>
|
||
{index === 0 && <i>180°</i>}
|
||
{index === 6 && <i>90°</i>}
|
||
</div>
|
||
))}
|
||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||
<div
|
||
key={index}
|
||
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === -1 * 15 * index ? 'act' : ''}`}
|
||
onClick={() => setInputCompasDeg(15 * index * -1)}
|
||
>
|
||
{index === 0 && <i>0°</i>}
|
||
{index === 6 && <i>-90°</i>}
|
||
</div>
|
||
))}
|
||
<div className="compas">
|
||
<div className="compas-arr" style={{ transform: `rotate(${-1 * getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="center-wrap">
|
||
<div className="outline-form">
|
||
<div className="d-check-box pop mr10">
|
||
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
|
||
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
|
||
</div>
|
||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||
<input
|
||
type="text"
|
||
className="input-origin block"
|
||
value={inputCompasDeg}
|
||
readOnly={!hasAnglePassivity}
|
||
placeholder={0}
|
||
onChange={(e) => checkDegree(e.target.value)}
|
||
/>
|
||
</div>
|
||
<span className="thin">°</span>
|
||
<span className="thin">( -180 〜 180 )</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="compas-table-wrap">
|
||
<div className="compas-table-box mb10">
|
||
<div className="outline-form mb10">
|
||
<span>{getMessage('modal.module.basic.setting.module.series.setting')}</span>
|
||
<div className="grid-select">
|
||
{moduleSeriesList.length > 0 && (
|
||
<QSelectBox
|
||
options={moduleSeriesList}
|
||
value={selectedModuleSeries}
|
||
targetKey={'moduleSerCd'}
|
||
sourceKey={'moduleSerCd'}
|
||
showKey={'moduleSerNm'}
|
||
onChange={(e) => handleChangeModuleSeries(e)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="outline-form mb10">
|
||
<span>{getMessage('modal.module.basic.setting.module.setting2')}</span>
|
||
<div className="grid-select">
|
||
{filteredModuleList && (
|
||
<QSelectBox
|
||
options={filteredModuleList}
|
||
value={selectedModules}
|
||
targetKey={'itemId'}
|
||
sourceKey={'itemId'}
|
||
showKey={'itemNm'}
|
||
onChange={(e) => handleChangeModule(e)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="roof-module-table">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
{moduleData.header.map((header) => {
|
||
return (
|
||
<th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
|
||
{header.name}
|
||
</th>
|
||
)
|
||
})}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{Array.from({ length: 3 }).map((_, index) => {
|
||
return selectedModules && selectedModules?.itemList && selectedModules?.itemList?.length >= index + 1 ? (
|
||
<tr key={index}>
|
||
<td>
|
||
<div className="color-wrap">
|
||
<span
|
||
className="color-box"
|
||
style={{
|
||
backgroundColor: selectedModules.itemList[index].color,
|
||
}}
|
||
></span>
|
||
<span className="name">{selectedModules.itemList[index].itemNm}</span>
|
||
</div>
|
||
</td>
|
||
<td className="al-r">{Number(selectedModules.itemList[index].shortAxis).toFixed(0)}</td>
|
||
<td className="al-r">{Number(selectedModules.itemList[index].longAxis).toFixed(0)}</td>
|
||
<td className="al-r">{Number(selectedModules.itemList[index].wpOut).toFixed(0)}</td>
|
||
</tr>
|
||
) : (
|
||
<tr key={index}>
|
||
<td>
|
||
<div className="color-wrap"></div>
|
||
</td>
|
||
<td className="al-r"></td>
|
||
<td className="al-r"></td>
|
||
<td className="al-r"></td>
|
||
</tr>
|
||
)
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{basicSetting && basicSetting.roofSizeSet == '3' && (
|
||
<div className="outline-form mt15">
|
||
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||
<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />
|
||
</div>
|
||
<span className="thin">m</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{basicSetting && basicSetting.roofSizeSet != '3' && (
|
||
<div className="compas-table-box">
|
||
<div className="compas-grid-table">
|
||
<div className="outline-form">
|
||
<span>{getMessage('modal.module.basic.setting.module.surface.type')}</span>
|
||
<div className="grid-select">
|
||
{roughnessCodes.length > 0 && managementState && (
|
||
<QSelectBox
|
||
options={roughnessCodes}
|
||
value={inputRoughness}
|
||
targetKey={'clCode'}
|
||
sourceKey={'clCode'}
|
||
showKey={'clCodeNm'}
|
||
onChange={(e) => handleChangeRoughness(e)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="outline-form">
|
||
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
||
<div className="input-grid mr10">
|
||
<input
|
||
type="text"
|
||
className="input-origin block"
|
||
value={inputInstallHeight}
|
||
onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}
|
||
/>
|
||
</div>
|
||
<span className="thin">m</span>
|
||
</div>
|
||
<div className="outline-form">
|
||
<span>{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
|
||
<div className="grid-select">
|
||
{windSpeedCodes.length > 0 && managementState && (
|
||
<QSelectBox
|
||
title={''}
|
||
options={windSpeedCodes}
|
||
value={inputStandardWindSpeed}
|
||
targetKey={'clCode'}
|
||
sourceKey={'clCode'}
|
||
showKey={'clCodeNm'}
|
||
onChange={(e) => handleChangeStandardWindSpeed(e)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="outline-form">
|
||
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
||
<div className="input-grid mr10">
|
||
<input
|
||
type="text"
|
||
className="input-origin block"
|
||
value={inputVerticalSnowCover}
|
||
onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}
|
||
/>
|
||
</div>
|
||
<span className="thin">cm</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
})
|