모듈 설치 시 validation 추가
This commit is contained in:
parent
bbd3d1ec4b
commit
b9efc4aa47
@ -5,12 +5,13 @@ import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
|
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
|
||||||
|
import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE, MENU } from '@/common/common'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
|
import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
import { useTrestle } from '@/hooks/module/useTrestle'
|
import { useTrestle } from '@/hooks/module/useTrestle'
|
||||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
@ -29,11 +30,12 @@ const ALLOCATION_TYPE = {
|
|||||||
}
|
}
|
||||||
export default function CircuitTrestleSetting({ id }) {
|
export default function CircuitTrestleSetting({ id }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup, addPopup } = usePopup()
|
||||||
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
|
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { saveEstimate } = useEstimate()
|
const { saveEstimate } = useEstimate()
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
|
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||||
const [tabNum, setTabNum] = useState(1)
|
const [tabNum, setTabNum] = useState(1)
|
||||||
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
|
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
|
||||||
@ -106,6 +108,167 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// 모듈이 설치된 경우 rack설치 여부에 따라 설치 된 모듈 아래에 작은 모듈이 설치되어 있을 경우는 모듈 설치 메뉴 reopen
|
||||||
|
useEffect(() => {
|
||||||
|
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||||
|
if (modules.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 랙 설치 시 모듈 크기 검증
|
||||||
|
* - 남쪽: 아래쪽 모듈이 위쪽 모듈보다 커야 함
|
||||||
|
* - 북쪽: 위쪽 모듈이 아래쪽 모듈보다 커야 함
|
||||||
|
* - 동쪽: 오른쪽 모듈이 왼쪽 모듈보다 커야 함
|
||||||
|
* - 서쪽: 왼쪽 모듈이 오른쪽 모듈보다 커야 함
|
||||||
|
*/
|
||||||
|
const validateModuleSizeForRack = (surface) => {
|
||||||
|
const { modules, direction, trestleDetail } = surface
|
||||||
|
const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail
|
||||||
|
|
||||||
|
if (rackYn === 'N' || !modules || modules.length < 2) {
|
||||||
|
return true // 검증 통과
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모듈 중심점 및 크기 정보 계산
|
||||||
|
const centerPoints = modules.map((module) => {
|
||||||
|
const { x, y } = module.getCenterPoint()
|
||||||
|
const { width, height } = module
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width: Math.floor(width),
|
||||||
|
height: Math.floor(height),
|
||||||
|
area: Math.floor(width) * Math.floor(height),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 방향별 설정
|
||||||
|
const isVertical = direction === 'south' || direction === 'north'
|
||||||
|
const primaryInterval = isVertical ? moduleIntvlVer : moduleIntvlHor
|
||||||
|
const secondaryInterval = isVertical ? moduleIntvlHor : moduleIntvlVer
|
||||||
|
|
||||||
|
// 정렬 함수: 큰 모듈이 있어야 할 위치부터 시작
|
||||||
|
const getSortFn = () => {
|
||||||
|
switch (direction) {
|
||||||
|
case 'south': return (a, b) => b.y - a.y // 아래쪽(y 큼)부터
|
||||||
|
case 'north': return (a, b) => a.y - b.y // 위쪽(y 작음)부터
|
||||||
|
case 'east': return (a, b) => b.x - a.x // 오른쪽(x 큼)부터
|
||||||
|
case 'west': return (a, b) => a.x - b.x // 왼쪽(x 작음)부터
|
||||||
|
default: return () => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 타겟 모듈 찾기 (현재 모듈보다 작아야 할 위치의 모듈)
|
||||||
|
const findTargetModules = (current, margin) => {
|
||||||
|
return centerPoints.filter(cp => {
|
||||||
|
const sameAxis = isVertical
|
||||||
|
? Math.abs(cp.x - current.x) < margin
|
||||||
|
: Math.abs(cp.y - current.y) < margin
|
||||||
|
const targetDirection = direction === 'south' ? cp.y < current.y
|
||||||
|
: direction === 'north' ? cp.y > current.y
|
||||||
|
: direction === 'east' ? cp.x < current.x
|
||||||
|
: cp.x > current.x
|
||||||
|
return sameAxis && targetDirection
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 가장 가까운 타겟 모듈 찾기
|
||||||
|
const getClosestTarget = (filtered) => {
|
||||||
|
if (filtered.length === 0) return null
|
||||||
|
return filtered.reduce((closest, cp) => {
|
||||||
|
switch (direction) {
|
||||||
|
case 'south': return cp.y > closest.y ? cp : closest
|
||||||
|
case 'north': return cp.y < closest.y ? cp : closest
|
||||||
|
case 'east': return cp.x > closest.x ? cp : closest
|
||||||
|
case 'west': return cp.x < closest.x ? cp : closest
|
||||||
|
default: return closest
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 두 모듈 간 간격 계산
|
||||||
|
const getGap = (current, target) => {
|
||||||
|
if (isVertical) {
|
||||||
|
return direction === 'south'
|
||||||
|
? (current.y - current.height / 2) - (target.y + target.height / 2)
|
||||||
|
: (target.y - target.height / 2) - (current.y + current.height / 2)
|
||||||
|
} else {
|
||||||
|
return direction === 'east'
|
||||||
|
? (current.x - current.width / 2) - (target.x + target.width / 2)
|
||||||
|
: (target.x - target.width / 2) - (current.x + current.width / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 인접 모듈 여부 확인
|
||||||
|
const isAdjacent = (current, target) => {
|
||||||
|
const gap = getGap(current, target)
|
||||||
|
return gap >= 0 && gap <= primaryInterval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 정렬된 모듈 순회
|
||||||
|
const sortedPoints = [...centerPoints].sort(getSortFn())
|
||||||
|
|
||||||
|
for (const current of sortedPoints) {
|
||||||
|
// 1. 일반 배치: 같은 라인에서 인접 모듈 검사
|
||||||
|
const directTargets = findTargetModules(current, secondaryInterval)
|
||||||
|
const closestTarget = getClosestTarget(directTargets)
|
||||||
|
|
||||||
|
if (closestTarget && isAdjacent(current, closestTarget) && closestTarget.area > current.area) {
|
||||||
|
return false // 검증 실패
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 물떼새 배치: 반 오프셋 위치의 인접 모듈 검사
|
||||||
|
const size = isVertical ? current.width : current.height
|
||||||
|
const halfOffset = (size + secondaryInterval) / 2
|
||||||
|
|
||||||
|
for (const sign of [-1, 1]) {
|
||||||
|
const offsetValue = sign * halfOffset
|
||||||
|
const findHalfTarget = centerPoints.filter((cp) => {
|
||||||
|
const offsetAxis = isVertical
|
||||||
|
? Math.abs(cp.x - (current.x + offsetValue)) <= primaryInterval
|
||||||
|
: Math.abs(cp.y - (current.y + offsetValue)) <= primaryInterval
|
||||||
|
const targetDirection = direction === 'south' ? cp.y < current.y
|
||||||
|
: direction === 'north' ? cp.y > current.y
|
||||||
|
: direction === 'east' ? cp.x < current.x
|
||||||
|
: cp.x > current.x
|
||||||
|
return offsetAxis && targetDirection
|
||||||
|
})
|
||||||
|
|
||||||
|
const closestHalf = getClosestTarget(findHalfTarget)
|
||||||
|
if (closestHalf && isAdjacent(current, closestHalf) && closestHalf.area > current.area) {
|
||||||
|
return false // 검증 실패
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true // 검증 통과
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 설치면에 대해 검증 수행
|
||||||
|
const moduleSetupSurfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
|
||||||
|
for (const surface of moduleSetupSurfaces) {
|
||||||
|
if (!validateModuleSizeForRack(surface)) {
|
||||||
|
swalFire({
|
||||||
|
text: getMessage('module.size.validation.rack.error'),
|
||||||
|
icon: 'error',
|
||||||
|
confirmFn: () => {
|
||||||
|
// 현재 팝업 닫기
|
||||||
|
closePopup(id)
|
||||||
|
// 메뉴 하이라이트 변경
|
||||||
|
setCurrentMenu(MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING)
|
||||||
|
// 모듈/가대설정 팝업 열기
|
||||||
|
const newPopupId = uuidv4()
|
||||||
|
clearTrestle()
|
||||||
|
setAllModuleSurfaceIsComplete(false)
|
||||||
|
addPopup(newPopupId, 1, <BasicSetting id={newPopupId} />)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const capture = async (type) => {
|
const capture = async (type) => {
|
||||||
beforeCapture(type)
|
beforeCapture(type)
|
||||||
|
|
||||||
|
|||||||
@ -134,6 +134,7 @@
|
|||||||
"modal.module.basic.settting.module.error10": "棟側の配置領域の値を{0} mm以上に変更してください。\n(屋根材: {1})",
|
"modal.module.basic.settting.module.error10": "棟側の配置領域の値を{0} mm以上に変更してください。\n(屋根材: {1})",
|
||||||
"modal.module.basic.settting.module.error11": "ケラバ側の配置領域の値を{0} mm以上に変更してください。\n(屋根材: {1})",
|
"modal.module.basic.settting.module.error11": "ケラバ側の配置領域の値を{0} mm以上に変更してください。\n(屋根材: {1})",
|
||||||
"modal.module.basic.settting.module.error12": "施工方法を選択してください。\n(屋根材: {0})",
|
"modal.module.basic.settting.module.error12": "施工方法を選択してください。\n(屋根材: {0})",
|
||||||
|
"module.size.validation.rack.error": "モジュール配置が不正です。 正しく配置し直してください。",
|
||||||
"modal.module.basic.setting.module.placement": "モジュールの配置",
|
"modal.module.basic.setting.module.placement": "モジュールの配置",
|
||||||
"modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。",
|
"modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。",
|
||||||
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置",
|
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置",
|
||||||
|
|||||||
@ -134,6 +134,7 @@
|
|||||||
"modal.module.basic.settting.module.error10": "용마루쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
|
"modal.module.basic.settting.module.error10": "용마루쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
|
||||||
"modal.module.basic.settting.module.error11": "케라바쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
|
"modal.module.basic.settting.module.error11": "케라바쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
|
||||||
"modal.module.basic.settting.module.error12": "시공법을 선택해주세요.\n(지붕재: {0})",
|
"modal.module.basic.settting.module.error12": "시공법을 선택해주세요.\n(지붕재: {0})",
|
||||||
|
"module.size.validation.rack.error": "모듈 배치가 잘못되었습니다. 올바르게 다시 배치해 주세요.",
|
||||||
"modal.module.basic.setting.module.placement": "모듈 배치",
|
"modal.module.basic.setting.module.placement": "모듈 배치",
|
||||||
"modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.",
|
"modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.",
|
||||||
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치",
|
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user