2025-06-11 18:29:54 +09:00

763 lines
25 KiB
JavaScript

import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState, useEffect, useContext, useRef } from 'react'
import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect'
import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
import { useMasterController } from '@/hooks/common/useMasterController'
import { correntObjectNoState } from '@/store/settingAtom'
import { useRecoilValue } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useRecoilState } from 'recoil'
import { makersState, modelsState, modelState, pcsCheckState, selectedMakerState, selectedModelsState, seriesState } from '@/store/circuitTrestleAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
import { useTrestle } from '@/hooks/module/useTrestle'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { v4 as uuidv4 } from 'uuid'
import { useEstimate } from '@/hooks/useEstimate'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { useImgLoader } from '@/hooks/floorPlan/useImgLoader'
import { usePlan } from '@/hooks/usePlan'
import { QcastContext } from '@/app/QcastProvider'
import { fabric } from 'fabric'
import { fontSelector } from '@/store/fontAtom'
const ALLOCATION_TYPE = {
AUTO: 'auto',
PASSIVITY: 'passivity',
}
export default function CircuitTrestleSetting({ id }) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
const { swalFire } = useSwal()
const { saveEstimate } = useEstimate()
const canvas = useRecoilValue(canvasState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [tabNum, setTabNum] = useState(1)
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
const { managementState, setManagementState } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState)
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
const flowText = useRecoilValue(fontSelector('flowText'))
const lengthText = useRecoilValue(fontSelector('lengthText'))
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
// 회로할당(승합설정)에서 선택된 값들을 저장할 상태 추가
const [selectedStepUpValues, setSelectedStepUpValues] = useState({})
const [getStepUpSelections, setGetStepUpSelections] = useState(null)
// const { trigger: setCircuitData } = useCanvasPopupStatusController(4)
// const [stepUpListData, setStepUpListData] = useRecoilState(stepUpListDataState)
const [stepUpListData, setStepUpListData] = useState([])
const [seletedMainOption, setSeletedMainOption] = useState(null)
const [seletedSubOption, setSeletedSubOption] = useState(null)
const { setModuleStatisticsData } = useCircuitTrestle()
const { handleCanvasToPng } = useImgLoader()
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const passivityCircuitAllocationRef = useRef()
const { setIsGlobalLoading } = useContext(QcastContext)
const {
makers,
setMakers,
selectedMaker,
setSelectedMaker,
series,
setSeries,
models,
setModels,
selectedModels,
setSelectedModels,
pcsCheck,
setPcsCheck,
getOptYn,
getPcsItemList,
getSelectedPcsItemList,
getUseModuleItemList,
getRoofSurfaceList,
getModuleList,
removeNotAllocationModules,
resetCircuits,
} = useCircuitTrestle()
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
useEffect(() => {
if (!managementState) {
}
// setCircuitData({
// makers,
// selectedMaker,
// series,
// models,
// selectedModels,
// pcsCheck,
// })
return () => {
const moduleSetupSurfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
if (moduleSetupSurfaces.some((surface) => !surface.isComplete)) {
resetCircuits()
}
}
}, [])
// 캡쳐 전 처리
const beforeCapture = () => {
// setCanvasZoom(100)
const x = canvas.width / 2
const y = canvas.height / 2
canvas.zoomToPoint(new fabric.Point(x, y), 0.5)
changeFontSize('lengthText', '28')
changeFontSize('circuitNumber', '28')
changeFontSize('flowText', '28')
canvas.renderAll()
}
// 캡쳐 후 처리
const afterCapture = () => {
setCanvasZoom(100)
canvas.set({ zoom: 1 })
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
changeFontSize('lengthText', lengthText.fontSize.value)
changeFontSize('circuitNumber', circuitNumberText.fontSize.value)
changeFontSize('flowText', flowText.fontSize.value)
canvas.renderAll()
}
// 수동할당 시 모듈 삭제
// 시리즈중 자동으로 추천 PCS 정보 조회
const onAutoRecommend = () => {
if (series.filter((s) => s.selected).length === 0) {
swalFire({
title: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error02'),
type: 'alert',
})
return
}
const params = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList(),
pcsItemList: getPcsItemList(),
}
// 파워컨디셔너 추천 목록 조회
if (selectedModels.length === 0) {
// 시리즈중 자동으로 추천 PCS 정보 조회
getPcsAutoRecommendList(params).then((res) => {
if (res.data?.pcsItemList) {
let pcsItemList = []
let pcsObj = {}
models.forEach((model) => {
pcsObj[model.itemId] = model
})
res.data?.pcsItemList.forEach((item) => {
if (pcsObj[item.itemId]) {
pcsItemList.push({
...pcsObj[item.itemId],
isUsed: false,
id: uuidv4(),
})
}
})
// 회로 구성 가능 여부 체크 요청 파라미터
const pcsVoltageChkParams = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList(),
pcsItemList: pcsItemList,
}
// 추천 목록 선택
setSelectedModels(pcsItemList)
// 회로 구성 가능 여부 체크
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
if (res.resultCode === 'S') {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({
title: res.resultMsg,
type: 'alert',
})
}
})
} else {
// 데이터가 없는 경우 오류 메시지 확인 필요
if (res.result.resultCode === 'E') {
swalFire({
title: res.result.resultMsg,
type: 'alert',
})
} else {
swalFire({
title: '파워컨디셔너를 추가해 주세요.',
type: 'alert',
})
}
}
})
} else {
// 회로 구성 가능 여부 체크
getPcsVoltageChk({ ...params, pcsItemList: getSelectedPcsItemList() }).then((res) => {
if (res.resultCode === 'S') {
// 회로 구성 가능 여부 체크 통과 시 승압설정 정보 조회
getPcsVoltageStepUpList({
...params,
pcsItemList: getSelectedPcsItemList(),
}).then((res) => {
if (res?.result.resultCode === 'S' && res?.data) {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({ text: getMessage('common.message.send.error') })
}
})
} else {
swalFire({
title: res.resultMsg,
type: 'alert',
})
}
})
}
}
// 자동할당 버튼 클릭 시
const onAutoAllocation = () => {
let moduleStdQty = 0
let moduleMaxQty = 0
const selectedModels = models.filter((m) => m.selected)
// 시리즈중 자동으로 추천 PCS 정보 조회
if (selectedModels.length === 0) {
onAutoRecommend()
} else {
// 모듈 최소 매수
moduleStdQty = selectedModels.reduce((acc, model) => {
return acc + parseInt(model.moduleStdQty)
}, 0)
moduleMaxQty = selectedModels.reduce((acc, model) => {
return acc + parseInt(model.moduleMaxQty)
}, 0)
}
// const target = pcsCheck.max ? moduleMaxQty : moduleStdQty
// const placementModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
// if (placementModules.length > target) {
// swalFire({
// title: '배치가능 매수를 초과합니다. 파워컨디셔너를 다시 선택해 주세요.',
// type: 'alert',
// })
// return
// }
// setAllocationType(ALLOCATION_TYPE.AUTO)
// setTabNum(2)
}
// 수동할당 버튼 클릭 시
const onPassivityAllocation = () => {
if (selectedModels.length === 0) {
const params = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList().map((surface) => {
return {
...surface,
moduleList: surface.moduleList.map((module) => {
return {
itemId: module.itemId,
uniqueId: module.uniqueId,
}
}),
}
}),
pcsItemList: getPcsItemList(),
}
// 파워컨디셔너 추천 목록 조회
getPcsAutoRecommendList(params).then((res) => {
if (res.data?.pcsItemList) {
let pcsItemList = []
let pcsObj = {}
models.forEach((model) => {
pcsObj[model.itemId] = model
})
res.data?.pcsItemList.forEach((item) => {
if (pcsObj[item.itemId]) {
pcsItemList.push({
...pcsObj[item.itemId],
isUsed: false,
id: uuidv4(),
})
}
})
const pcsVoltageChkParams = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList(),
pcsItemList: pcsItemList.map((item) => {
return {
itemId: item.itemId,
pcsMkrCd: item.pcsMkrCd,
pcsSerCd: item.pcsSerCd,
}
}),
}
setSelectedModels(pcsItemList)
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
setAllModuleSurfaceIsComplete(false)
clearTrestle()
})
} else {
swalFire({
title: res.result.resultMsg,
type: 'alert',
confirmFn: () => {
return
},
})
return
}
})
} else {
const moduleStdQty = selectedModels.reduce((acc, model) => {
return acc + parseInt(model.moduleStdQty)
}, 0)
const moduleMaxQty = selectedModels.reduce((acc, model) => {
return acc + parseInt(model.moduleMaxQty)
}, 0)
const target = pcsCheck.max ? moduleMaxQty : moduleStdQty
const placementModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
let moduleAmount = placementModules.reduce((acc, module) => {
if (moduleSelectionData.module.itemList.length === 1 || module.moduleInfo.itemId === moduleSelectionData.module.itemList[0].itemId) {
return acc + 1
} else {
return acc + 0.66
}
}, 0)
if (moduleAmount > target) {
swalFire({
title: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error01'),
type: 'alert',
})
return
}
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
clearTrestle()
}
}
// StepUp에서 선택된 값들을 처리하는 함수 수정
const handleStepUpValuesSelected = (selectionData) => {
const { gooodsNo } = selectionData
setSelectedStepUpValues((prev) => ({
...prev,
[gooodsNo]: selectionData,
}))
}
// StepUp 컴포넌트 초기화 핸들러
const handleStepUpInitialize = (getCurrentSelections) => {
setGetStepUpSelections(() => getCurrentSelections)
}
// 회로할당(승압설정) 저장 버튼 클릭 시
const onApply = async () => {
setAllModuleSurfaceIsComplete(false)
setIsGlobalLoading(true)
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
.map((obj) => {
obj.pcses = getStepUpListData()
})
beforeCapture()
handleCanvasToPng(1)
afterCapture()
//회로할당 저장 시 result=null인 경우에도 회로번호 텍스트 표시 유지 처리
// 회로할당 저장 클릭 시
// 가대 및 지지금구 설치
// apply()
// apply() 결과를 체크하여 null이면 함수 종료
const applyResult = apply()
if (!applyResult) {
setIsGlobalLoading(false)
return
}
const result = await getEstimateData()
if (result) {
beforeCapture()
handleCanvasToPng(2)
afterCapture()
// 견적서 저장
await saveEstimate(result)
} else {
setIsGlobalLoading(false)
}
// removeNotAllocationModules()
}
const changeFontSize = (name, size) => {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === name)
textObjs.forEach((obj) => {
obj.set({
fontSize: size,
})
})
canvas.renderAll()
}
// 이전 버튼 클릭 시
const onClickPrev = () => {
// setAllocationType(ALLOCATION_TYPE.AUTO)
swalFire({
text: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset.info'),
type: 'alert',
icon: 'warning',
confirmFn: () => {
const circuitModules = canvas.getObjects().filter((obj) => obj.name === 'module')
canvas.remove(...circuitModules.map((module) => module.circuit))
circuitModules.forEach((obj) => {
obj.circuit = null
obj.pcsItemId = null
obj.circuitNumber = null
})
setSelectedModels(
JSON.parse(JSON.stringify(selectedModels)).map((model) => {
model.isUsed = false
return model
}),
)
if (allocationType === ALLOCATION_TYPE.PASSIVITY) {
setAllocationType(ALLOCATION_TYPE.AUTO)
setTabNum(1)
} else {
setTabNum(1)
}
clearTrestle()
canvas.renderAll()
setModuleStatisticsData()
},
})
}
// 파워컨디셔너 컴포넌트 속성
const powerConditionalSelectProps = {
tabNum,
setTabNum,
makers,
setMakers,
selectedMaker,
setSelectedMaker,
series,
setSeries,
models,
setModels,
selectedModels,
setSelectedModels,
managementState,
getUseModuleItemList,
}
// 수동할당 컴포넌트 속성
const passivityProps = {
tabNum,
setTabNum,
pcsCheck,
selectedModels,
setSelectedModels,
getOptYn,
getUseModuleItemList,
getRoofSurfaceList,
}
// 승압설정 컴포넌트 속성
const stepUpProps = {
tabNum,
setTabNum,
models,
setModels,
allocationType,
circuitAllocationType,
setCircuitAllocationType,
selectedMaker,
selectedModels,
setSelectedModels,
getSelectedPcsItemList,
getOptYn, // 옵션 Y/N
getUseModuleItemList, // 사용된 모듈아이템 List
getRoofSurfaceList, // 지붕면 목록
getPcsItemList, // PCS 아이템 목록
onValuesSelected: handleStepUpValuesSelected, // 선택된 값들을 처리하는 함수
stepUpListData,
setStepUpListData,
seletedMainOption,
setSeletedMainOption,
seletedSubOption,
setSeletedSubOption,
getModuleList,
setModuleStatisticsData,
}
// 승압설정 목록 조회
const getStepUpListData = () => {
const pcs = []
console.log(stepUpListData)
stepUpListData[0].pcsItemList.map((item, index) => {
return item.serQtyList
.filter((serQty) => serQty.selected && serQty.paralQty > 0)
.forEach((serQty) => {
pcs.push({
pcsMkrCd: item.pcsMkrCd,
pcsSerCd: item.pcsSerCd,
pcsItemId: item.itemId,
pscOptCd: getPcsOptCd(index),
paralQty: serQty.paralQty,
connections: item.connList?.length
? [
{
connItemId: item.connList[0].itemId,
},
]
: [],
})
// return {
// pcsMkrCd: item.pcsMkrCd,
// pcsSerCd: item.pcsSerCd,
// pcsItemId: item.itemId,
// pscOptCd: getPcsOptCd(index),
// paralQty: serQty.paralQty,
// connections: item.connList?.length
// ? [
// {
// connItemId: item.connList[0].itemId,
// },
// ]
// : [],
// }
})
})
return pcs
}
const getPcsOptCd = (index) => {
console.log(selectedModels)
console.log(seletedMainOption)
console.log(seletedSubOption)
if (selectedModels.some((model) => model.pcsSerParallelYn === 'Y')) {
if (selectedModels.length > 1) {
if (index === 0) {
return seletedMainOption.code
} else {
return seletedSubOption.code
}
} else {
return seletedMainOption.code
}
} else {
return seletedMainOption.code
}
}
const handleStepUp = () => {
handlePassivityAllocationCkeck()
// const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit)
// if (notAllocationModules.length > 0) {
// swalFire({
// title: getMessage('not.allocation.exist.module'),
// type: 'alert',
// })
// return
// } else {
// passivityCircuitAllocationRef.current.onApply()
// setTabNum(2)
// }
}
// 닫기 버튼 클릭 시 처리하는 함수 추가
const handleClose = () => {
// // 회로 번호 텍스트 제거
if (
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
.some((surface) => !surface.isComplete)
) {
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'circuitNumber'))
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.forEach((obj) => {
obj.circuit = null
obj.pcsItemId = null
obj.circuitNumber = null
})
canvas.renderAll()
}
closePopup(id)
}
const handlePassivityAllocationCkeck = () => {
let pcsCount = {}
let result = {}
const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit)
if (notAllocationModules.length > 0) {
swalFire({
text: getMessage('not.allocation.exist.module'),
type: 'alert',
icon: 'warning',
})
return
}
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.forEach((module) => {
const circuitNumber = module.circuitNumber.replace(/[()]/g, '')
pcsCount[circuitNumber] = (pcsCount[circuitNumber] || 0) + 1
})
for (const key in pcsCount) {
const firstPart = key.split('-')[0] // '-' 기준으로 첫 번째 부분을 추출
const value = pcsCount[key]
// 그룹이 없으면 초기화
if (!result[firstPart]) {
result[firstPart] = { maxValue: value, count: 1 }
} else {
// 이미 그룹이 있으면 큰 값으로 갱신, count는 증가
result[firstPart].maxValue = Math.max(result[firstPart].maxValue, value)
result[firstPart].count += 1
}
}
const params = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList(),
pcsItemList: selectedModels.map((model, index) => {
return {
pcsMkrCd: model.pcsMkrCd,
pcsSerCd: model.pcsSerCd,
itemId: model.itemId,
itemNm: model.itemNm,
goodsNo: model.goodsNo,
serQtyList: [
{
serQty: result[(index + 1).toString()] ? result[(index + 1).toString()].maxValue : 0,
paralQty: result[(index + 1).toString()] ? result[(index + 1).toString()].count : 0,
rmdYn: 'Y',
usePossYn: 'Y',
roofSurfaceList: canvas
.getObjects()
.filter((obj) => POLYGON_TYPE.MODULE_SETUP_SURFACE === obj.name && obj?.modules.length > 0)
.map((surface) => {
return {
roofSurfaceId: surface.id,
roofSurface: surface.direction,
roofSurfaceIncl: +canvas.getObjects().filter((obj) => obj.id === surface.parentId)[0].pitch,
moduleList: surface.modules.map((module) => {
return {
itemId: module.moduleInfo.itemId,
circuit: module.circuitNumber,
pcsItemId: module.pcsItemId,
}
}),
}
}),
},
],
}
}),
}
getPcsManualConfChk(params).then((res) => {
if (res?.resultCode === 'E') {
swalFire({
text: res.resultMsg,
type: 'alert',
icon: 'warning',
})
return
} else {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
}
})
}
return (
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }} className="l-2">
<WithDraggable.Header title={getMessage('modal.circuit.trestle.setting')} onClose={() => handleClose()} />
<WithDraggable.Body>
<div className="roof-module-tab">
<div className={`module-tab-bx act`}>{getMessage('modal.circuit.trestle.setting.power.conditional.select')}</div>
<span className={`tab-arr ${tabNum === 2 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 2 ? 'act' : ''}`}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')})
</div>
</div>
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && <PowerConditionalSelect {...powerConditionalSelectProps} />}
{tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && (
<PassivityCircuitAllocation {...passivityProps} ref={passivityCircuitAllocationRef} />
)}
{tabNum === 2 && <StepUp {...stepUpProps} onInitialize={handleStepUpInitialize} />}
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && (
<div className="grid-btn-wrap">
<button className="btn-frame modal mr5 act" onClick={() => onAutoRecommend()}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation.auto')}
</button>
<button className="btn-frame modal act" onClick={() => onPassivityAllocation()}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity')}
</button>
</div>
)}
{tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && (
<div className="grid-btn-wrap">
<button className="btn-frame modal mr5 " onClick={() => onClickPrev()}>
{getMessage('modal.common.prev')}
</button>
<button className="btn-frame modal act" onClick={() => handleStepUp()}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')})
</button>
</div>
)}
{tabNum === 2 && (
<div className="grid-btn-wrap">
<button className="btn-frame modal mr5" onClick={() => onClickPrev()}>
{getMessage('modal.common.prev')}
</button>
{/* <button className="btn-frame modal act" onClick={() => apply()}> */}
<button className="btn-frame modal act" onClick={onApply}>
{getMessage('modal.common.save')}({getMessage('modal.circuit.trestle.setting.circuit.allocation')})
</button>
</div>
)}
</WithDraggable.Body>
</WithDraggable>
)
}