diff --git a/src/common/common.js b/src/common/common.js
index 4220e5f5..60a57c76 100644
--- a/src/common/common.js
+++ b/src/common/common.js
@@ -219,7 +219,8 @@ export const SAVE_KEY = [
'originWidth',
'originHeight',
'skeletonLines',
- 'skeleton'
+ 'skeleton',
+ 'viewportTransform',
]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype]
diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx
index 4b718c05..63dc523a 100644
--- a/src/components/floor-plan/CanvasFrame.jsx
+++ b/src/components/floor-plan/CanvasFrame.jsx
@@ -2,7 +2,7 @@
import { useContext, useEffect, useRef } from 'react'
-import { useRecoilValue, useResetRecoilState } from 'recoil'
+import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import QContextMenu from '@/components/common/context-menu/QContextMenu'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
@@ -11,7 +11,7 @@ import { useCanvas } from '@/hooks/useCanvas'
import { usePlan } from '@/hooks/usePlan'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
-import { currentMenuState } from '@/store/canvasAtom'
+import { canvasZoomState, currentMenuState } from '@/store/canvasAtom'
import { totalDisplaySelector } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
@@ -50,6 +50,7 @@ export default function CanvasFrame() {
const resetSeriesState = useResetRecoilState(seriesState)
const resetModelsState = useResetRecoilState(modelsState)
const resetCompasDeg = useResetRecoilState(compasDegAtom)
+ const [zoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const resetSelectedModelsState = useResetRecoilState(selectedModelsState)
const resetPcsCheckState = useResetRecoilState(pcsCheckState)
const { handleModuleSelectionTotal } = useCanvasPopupStatusController()
@@ -67,6 +68,13 @@ export default function CanvasFrame() {
canvasLoadInit() //config된 상태로 캔버스 객체를 그린다
canvas?.renderAll() // 캔버스를 다시 그립니다.
+ if (canvas.viewportTransform) {
+ if (canvas.viewportTransform[0] !== 1) {
+ setCanvasZoom(Number((canvas.viewportTransform[0] * 100).toFixed(0)))
+ }
+ }
+ canvas.originViewPortTransform = canvas.viewportTransform
+
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
setTimeout(() => {
setSelectedMenu('module')
diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx
index 6ddd94c7..97d878a0 100644
--- a/src/components/floor-plan/CanvasMenu.jsx
+++ b/src/components/floor-plan/CanvasMenu.jsx
@@ -2,7 +2,7 @@
import { useContext, useEffect, useState } from 'react'
-import { usePathname, useRouter, useSearchParams } from 'next/navigation'
+import { usePathname, useRouter } from 'next/navigation'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
@@ -25,17 +25,18 @@ import { useCommonUtils } from '@/hooks/common/useCommonUtils'
import useMenu from '@/hooks/common/useMenu'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { useAxios } from '@/hooks/useAxios'
-import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState, currentCanvasPlanState } from '@/store/canvasAtom'
+import {
+ canvasSettingState,
+ canvasState,
+ canvasZoomState,
+ currentCanvasPlanState,
+ currentMenuState,
+ verticalHorizontalModeState,
+} from '@/store/canvasAtom'
import { sessionStore } from '@/store/commonAtom'
import { outerLinePointsState } from '@/store/outerLineAtom'
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
-import {
- addedRoofsState,
- basicSettingState,
- corridorDimensionSelector,
- selectedRoofMaterialSelector,
- settingModalFirstOptionsState,
-} from '@/store/settingAtom'
+import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
import { commonUtilsState } from '@/store/commonUtilsAtom'
import { menusState } from '@/store/menuAtom'
@@ -51,6 +52,7 @@ import { QcastContext } from '@/app/QcastProvider'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePolygon } from '@/hooks/usePolygon'
import { useTrestle } from '@/hooks/module/useTrestle'
+
export default function CanvasMenu(props) {
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const { selectedMenu, setSelectedMenu } = props
@@ -515,7 +517,10 @@ export default function CanvasMenu(props) {
if (createUser === 'T01' && sessionState.storeId !== 'T01') {
setAllButtonStyles('none')
} else {
- setEstimateContextState({ tempFlg: estimateRecoilState.tempFlg, lockFlg: estimateRecoilState.lockFlg })
+ setEstimateContextState({
+ tempFlg: estimateRecoilState.tempFlg,
+ lockFlg: estimateRecoilState.lockFlg,
+ })
handleButtonStyles(estimateRecoilState.tempFlg, estimateRecoilState.lockFlg, estimateContextState.docNo)
}
}
diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
index 3827e116..f86a7ead 100644
--- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
+++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
@@ -227,11 +227,33 @@ export default function CircuitTrestleSetting({ id }) {
return
}
+ const isMultiModule = selectedModules.itemList.length > 1
+
+ let isAllIndfcs = false
+
+ if (isMultiModule) {
+ //INDFCS 실내집중, OUTDMULTI 옥외멀티
+ // 1. 모듈이 혼합형일 경우 선택한 pcs가 실내집중인 경우 alert
+ if (selectedModels.length > 0) {
+ isAllIndfcs = selectedModels.every((model) => model.pcsTpCd === 'INDFCS')
+ } else {
+ isAllIndfcs = models.every((model) => model.pcsTpCd === 'INDFCS')
+ }
+ }
+
+ if (isAllIndfcs) {
+ swalFire({
+ title: getMessage('module.circuit.indoor.focused.error'),
+ type: 'alert',
+ })
+ return
+ }
+
const params = {
...getOptYn(),
useModuleItemList: getUseModuleItemList(),
roofSurfaceList: getRoofSurfaceList(),
- pcsItemList: getPcsItemList(),
+ pcsItemList: getPcsItemList(isMultiModule),
}
// 파워컨디셔너 추천 목록 조회
@@ -292,12 +314,12 @@ export default function CircuitTrestleSetting({ id }) {
})
} else {
// 회로 구성 가능 여부 체크
- getPcsVoltageChk({ ...params, pcsItemList: getSelectedPcsItemList() }).then((res) => {
+ getPcsVoltageChk({ ...params, pcsItemList: getSelectedPcsItemList(isMultiModule) }).then((res) => {
if (res.resultCode === 'S') {
// 회로 구성 가능 여부 체크 통과 시 승압설정 정보 조회
getPcsVoltageStepUpList({
...params,
- pcsItemList: getSelectedPcsItemList(),
+ pcsItemList: getSelectedPcsItemList(isMultiModule),
}).then((res) => {
if (res?.result.resultCode === 'S' && res?.data) {
setTabNum(2)
@@ -523,6 +545,7 @@ export default function CircuitTrestleSetting({ id }) {
obj.circuit = null
obj.pcsItemId = null
obj.circuitNumber = null
+ obj.pcs = null
})
setSelectedModels(
JSON.parse(JSON.stringify(selectedModels)).map((model) => {
diff --git a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx
index b0222aa3..5b956cc2 100644
--- a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx
+++ b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx
@@ -649,7 +649,13 @@ export default function StepUp(props) {
style={{ cursor: allocationType === 'auto' ? 'pointer' : 'default' }}
>
{item.serQty} |
- {item.paralQty} |
+
+ {/* 2025.12.04 select 추가 */}
+
+ |
+ {/* {item.paralQty} | */}
)
})}
diff --git a/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx
index 049efe10..22b0dd16 100644
--- a/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx
+++ b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx
@@ -9,7 +9,7 @@ import { moduleStatisticsState } from '@/store/circuitTrestleAtom'
import { fontSelector } from '@/store/fontAtom'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { circuitNumDisplaySelector } from '@/store/settingAtom'
-import { useContext, useEffect, useState } from 'react'
+import { useContext, useEffect, useRef, useState } from 'react'
import { useRecoilValue } from 'recoil'
import { normalizeDigits } from '@/util/input-utils'
@@ -32,6 +32,7 @@ export default function PassivityCircuitAllocation(props) {
const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
const [circuitNumber, setCircuitNumber] = useState(1)
const [targetModules, setTargetModules] = useState([])
+ const targetModulesRef = useRef([])
const { getPcsManualConfChk } = useMasterController()
const isDisplayCircuitNumber = useRecoilValue(circuitNumDisplaySelector)
const { setModuleStatisticsData } = useCircuitTrestle()
@@ -59,6 +60,10 @@ export default function PassivityCircuitAllocation(props) {
}
}, [])
+ useEffect(() => {
+ targetModulesRef.current = targetModules
+ }, [targetModules])
+
const handleTargetModules = (obj) => {
if (!Array.isArray(targetModules)) {
setTargetModules([])
@@ -79,6 +84,7 @@ export default function PassivityCircuitAllocation(props) {
}
const handleCircuitNumberFix = () => {
+ const pcsTpCd = selectedPcs.pcsTpCd // 실내집중형, 옥외멀티형
let uniqueCircuitNumbers = [
...new Set(
canvas
@@ -91,13 +97,13 @@ export default function PassivityCircuitAllocation(props) {
const surfaceList = targetModules.map((module) => {
return canvas.getObjects().filter((obj) => obj.id === canvas.getObjects().filter((obj) => obj.id === module)[0].surfaceId)[0]
})
+ let surfaceType = {}
+
+ surfaceList.forEach((surface) => {
+ surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
+ })
if (surfaceList.length > 1) {
- let surfaceType = {}
-
- surfaceList.forEach((surface) => {
- surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
- })
if (Object.keys(surfaceType).length > 1) {
swalFire({
text: getMessage('module.circuit.fix.not.same.roof.error'),
@@ -107,6 +113,7 @@ export default function PassivityCircuitAllocation(props) {
return
}
}
+
if (!circuitNumber || circuitNumber === 0) {
swalFire({
text: getMessage('module.circuit.minimun.error'),
@@ -114,31 +121,65 @@ export default function PassivityCircuitAllocation(props) {
icon: 'warning',
})
return
- } else if (targetModules.length === 0) {
+ }
+
+ if (targetModules.length === 0) {
swalFire({
text: getMessage('module.not.found'),
type: 'alert',
icon: 'warning',
})
return
- } else if (selectedModels.length > 1) {
- let result = false
+ }
- uniqueCircuitNumbers.forEach((number) => {
- if (
- number.split('-')[1] === circuitNumber + ')' &&
- number.split('-')[0] !== '(' + (selectedModels.findIndex((model) => model.id === selectedPcs.id) + 1)
- ) {
- result = true
- }
- })
- if (result) {
- swalFire({
- text: getMessage('module.already.exist.error'),
- type: 'alert',
- icon: 'warning',
+ switch (pcsTpCd) {
+ case 'INDFCS': {
+ const originHaveThisPcsModules = canvas
+ .getObjects()
+ .filter((obj) => obj.name === POLYGON_TYPE.MODULE && obj.pcs && obj.pcs.id === selectedPcs.id)
+ // 이미 해당 pcs로 설치된 모듈의 surface의 방향을 구한다.
+ const originSurfaceList = canvas
+ .getObjects()
+ .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && originHaveThisPcsModules.map((obj) => obj.surfaceId).includes(obj.id))
+
+ originSurfaceList.concat(originSurfaceList).forEach((surface) => {
+ surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
})
- return
+
+ if (surfaceList.length > 1) {
+ if (Object.keys(surfaceType).length > 1) {
+ swalFire({
+ text: getMessage('module.circuit.fix.not.same.roof.error'),
+ type: 'alert',
+ icon: 'warning',
+ })
+ return
+ }
+ }
+
+ break
+ }
+ case 'OUTDMULTI': {
+ if (selectedModels.length > 1) {
+ let result = false
+
+ uniqueCircuitNumbers.forEach((number) => {
+ if (
+ number.split('-')[1] === circuitNumber + ')' &&
+ number.split('-')[0] !== '(' + (selectedModels.findIndex((model) => model.id === selectedPcs.id) + 1)
+ ) {
+ result = true
+ }
+ })
+ if (result) {
+ swalFire({
+ text: getMessage('module.already.exist.error'),
+ type: 'alert',
+ icon: 'warning',
+ })
+ return
+ }
+ }
}
}
@@ -189,6 +230,7 @@ export default function PassivityCircuitAllocation(props) {
roofSurfaceId: surface.id,
roofSurface: surface.direction,
roofSurfaceIncl: +canvas.getObjects().filter((obj) => obj.id === surface.parentId)[0].pitch,
+ roofSurfaceNorthYn: surface.direction === 'north' ? 'Y' : 'N',
moduleList: surface.modules.map((module) => {
return {
itemId: module.moduleInfo.itemId,
@@ -270,6 +312,12 @@ export default function PassivityCircuitAllocation(props) {
return
}
+ targetModules.forEach((module) => {
+ const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
+ const targetModule = modules.find((obj) => obj.id === module)
+ targetModule.pcs = selectedPcs
+ })
+
setTargetModules([])
setCircuitNumber(+circuitNumber + 1)
setModuleStatisticsData()
diff --git a/src/components/floor-plan/modal/setting01/FirstOption.jsx b/src/components/floor-plan/modal/setting01/FirstOption.jsx
index 51a52cdb..ef2d8e0b 100644
--- a/src/components/floor-plan/modal/setting01/FirstOption.jsx
+++ b/src/components/floor-plan/modal/setting01/FirstOption.jsx
@@ -1,8 +1,10 @@
-import React, { useEffect, useState } from 'react'
+import React, { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE } from '@/common/common'
import { useEvent } from '@/hooks/useEvent'
import { useRoofFn } from '@/hooks/common/useRoofFn'
+import { outlineDisplaySelector } from '@/store/settingAtom'
+import { useRecoilValue } from 'recoil'
export default function FirstOption(props) {
const { getMessage } = useMessage()
@@ -11,6 +13,7 @@ export default function FirstOption(props) {
const { option1, option2, dimensionDisplay } = settingModalFirstOptions
const { initEvent } = useEvent()
const { setSurfaceShapePattern } = useRoofFn()
+ const outlineDisplay = useRecoilValue(outlineDisplaySelector)
// 데이터를 최초 한 번만 조회
useEffect(() => {
@@ -18,6 +21,13 @@ export default function FirstOption(props) {
setSettingsDataSave({ ...settingsData })
}, [])
+ useEffect(() => {
+ const outline = canvas.getObjects().filter((obj) => obj.name === 'originRoofOuterLine')
+ outline.forEach((obj) => {
+ obj.visible = outlineDisplay
+ })
+ }, [outlineDisplay])
+
const onClickOption = async (item) => {
let dimensionDisplay = settingModalFirstOptions?.dimensionDisplay
let option1 = settingModalFirstOptions?.option1
@@ -58,7 +68,12 @@ export default function FirstOption(props) {
// setSettingModalFirstOptions({ ...settingModalFirstOptions, option1: [...options] })
}
- setSettingsData({ ...settingsData, option1: [...option1], option2: [...option2], dimensionDisplay: [...dimensionDisplay] })
+ setSettingsData({
+ ...settingsData,
+ option1: [...option1],
+ option2: [...option2],
+ dimensionDisplay: [...dimensionDisplay],
+ })
}
// useEffect(() => {
diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js
index 7c53f98c..9fe5a221 100644
--- a/src/hooks/common/useCommonUtils.js
+++ b/src/hooks/common/useCommonUtils.js
@@ -31,8 +31,11 @@ export function useCommonUtils() {
useEffect(() => {
commonTextMode()
if (commonUtils.dimension) {
+ generateTempGrid()
commonDimensionMode()
return
+ } else {
+ removeTempGrid()
}
if (commonUtils.distance) {
commonDistanceMode()
@@ -645,6 +648,7 @@ export function useCommonUtils() {
lockMovementY: true,
name: obj.name,
editable: false,
+ selectable: true, // 복사된 객체 선택 가능하도록 설정
id: uuidv4(), //복사된 객체라 새로 따준다
})
@@ -653,19 +657,25 @@ export function useCommonUtils() {
//배치면일 경우
if (obj.name === 'roof') {
- clonedObj.setCoords()
- clonedObj.fire('modified')
- clonedObj.fire('polygonMoved')
+ clonedObj.canvas = canvas // canvas 참조 설정
clonedObj.set({
direction: obj.direction,
directionText: obj.directionText,
roofMaterial: obj.roofMaterial,
+ stroke: 'black', // 복사된 객체는 선택 해제 상태의 색상으로 설정
+ selectable: true, // 선택 가능하도록 설정
+ evented: true, // 마우스 이벤트를 받을 수 있도록 설정
+ isFixed: false, // containsPoint에서 특별 처리 방지
})
obj.lines.forEach((line, index) => {
clonedObj.lines[index].set({ attributes: line.attributes })
})
+ clonedObj.fire('polygonMoved') // 내부 좌표 재계산 (points, pathOffset)
+ clonedObj.fire('modified')
+ clonedObj.setCoords() // 모든 속성 설정 후 좌표 업데이트
+ canvas.setActiveObject(clonedObj)
canvas.renderAll()
addLengthText(clonedObj) //수치 추가
drawDirectionArrow(clonedObj) //방향 화살표 추가
@@ -905,6 +915,45 @@ export function useCommonUtils() {
}
}
+ const generateTempGrid = () => {
+ if (!canvas) return
+
+ const objects = canvas.getObjects().filter((obj) => ['QPolygon'].includes(obj.type))
+ const gridLines = []
+
+ objects.forEach((obj) => {
+ const lines = obj.lines
+
+ lines.forEach((line) => {
+ const gridLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
+ stroke: 'gray',
+ strokeWidth: 1,
+ selectable: false,
+ evented: false,
+ opacity: 0.5,
+ name: 'tempGrid',
+ direction: line.x1 === line.x2 ? 'vertical' : 'horizontal',
+ visible: false,
+ })
+ gridLines.push(gridLine)
+ })
+ })
+
+ gridLines.forEach((line) => {
+ canvas.add(line)
+ })
+
+ canvas.renderAll()
+ }
+
+ const removeTempGrid = () => {
+ if (!canvas) return
+
+ const tempGrids = canvas.getObjects().filter((obj) => obj.name === 'tempGrid' && !obj.visible)
+ tempGrids.forEach((grid) => canvas.remove(grid))
+ canvas.renderAll()
+ }
+
return {
commonFunctions,
dimensionSettings,
@@ -916,5 +965,7 @@ export function useCommonUtils() {
editText,
changeDimensionExtendLine,
deleteOuterLineObject,
+ generateTempGrid,
+ removeTempGrid,
}
}
diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js
index 811d6fd6..3ea1894c 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -9,6 +9,7 @@ import {
basicSettingState,
correntObjectNoState,
corridorDimensionSelector,
+ outlineDisplaySelector,
roofDisplaySelector,
roofMaterialsSelector,
selectedRoofMaterialSelector,
@@ -61,9 +62,11 @@ export function useRoofAllocationSetting(id) {
const { saveCanvas } = usePlan()
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
+ const outerLinePoints = useRecoilValue(outerLinePointsState)
const resetPoints = useResetRecoilState(outerLinePointsState)
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
const { changeCorridorDimensionText } = useText()
+ const outlineDisplay = useRecoilValue(outlineDisplaySelector)
useEffect(() => {
/** 배치면 초기설정에서 선택한 지붕재 배열 설정 */
@@ -305,11 +308,53 @@ export function useRoofAllocationSetting(id) {
addPopup(popupId, 1, )
} else {
apply()
+ //기존 지붕 선은 남겨둔다.
+ drawOriginRoofLine()
resetPoints()
+
basicSettingSave()
}
}
+ const drawOriginRoofLine = () => {
+ // outerLinePoints 배열을 이용하여 빨간색 Line 객체들 생성
+ if (outerLinePoints && outerLinePoints.length > 1) {
+ // 연속된 점들을 연결하여 라인 생성
+ for (let i = 0; i < outerLinePoints.length - 1; i++) {
+ const point1 = outerLinePoints[i]
+ const point2 = outerLinePoints[i + 1]
+
+ const line = new fabric.Line([point1.x, point1.y, point2.x, point2.y], {
+ stroke: 'black',
+ strokeDashArray: [5, 2],
+ strokeWidth: 1,
+ selectable: false,
+ name: 'originRoofOuterLine',
+ visible: outlineDisplay,
+ })
+
+ canvas.add(line)
+ }
+
+ // 마지막 점과 첫 점을 연결하여 폐곡선 만들기
+ if (outerLinePoints.length > 2) {
+ const lastPoint = outerLinePoints[outerLinePoints.length - 1]
+ const firstPoint = outerLinePoints[0]
+
+ const closingLine = new fabric.Line([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
+ stroke: 'red',
+ strokeWidth: 2,
+ selectable: false,
+ name: 'originRoofOuterLine',
+ })
+
+ canvas.add(closingLine)
+ }
+
+ canvas.renderAll()
+ }
+ }
+
/**
* 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우
*/
diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js
index d991a2cf..cc4ac608 100644
--- a/src/hooks/useCanvasEvent.js
+++ b/src/hooks/useCanvasEvent.js
@@ -402,7 +402,8 @@ export function useCanvasEvent() {
}
} else {
zoom = canvasZoom - 10
- if (zoom < 10) { //50%->10%
+ if (zoom < 10) {
+ //50%->10%
return
}
}
@@ -412,8 +413,33 @@ export function useCanvasEvent() {
const handleZoomClear = () => {
setCanvasZoom(100)
- canvas.set({ zoom: 1 })
- canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
+
+ zoomToAllObjects()
+ canvas.renderAll()
+ }
+
+ const zoomToAllObjects = () => {
+ const objects = canvas.getObjects().filter((obj) => obj.visible)
+ if (objects.length === 0) return
+
+ let minX = Infinity,
+ minY = Infinity
+ let maxX = -Infinity,
+ maxY = -Infinity
+
+ objects.forEach((obj) => {
+ const bounds = obj.getBoundingRect()
+ minX = Math.min(minX, bounds.left)
+ minY = Math.min(minY, bounds.top)
+ maxX = Math.max(maxX, bounds.left + bounds.width)
+ maxY = Math.max(maxY, bounds.top + bounds.height)
+ })
+
+ const centerX = (minX + maxX) / 2
+ const centerY = (minY + maxY) / 2
+ const centerPoint = new fabric.Point(centerX, centerY)
+
+ canvas.zoomToPoint(centerPoint, 1)
canvas.renderAll()
}
diff --git a/src/hooks/useCirCuitTrestle.js b/src/hooks/useCirCuitTrestle.js
index a35d3c8c..83bb0b84 100644
--- a/src/hooks/useCirCuitTrestle.js
+++ b/src/hooks/useCirCuitTrestle.js
@@ -42,7 +42,18 @@ export function useCircuitTrestle(executeEffect = false) {
}
}
// PCS 아이템 목록
- const getPcsItemList = () => {
+ const getPcsItemList = (isMultiModule = false) => {
+ if (isMultiModule) {
+ return models
+ .filter((model) => model.pcsTpCd !== 'INDFCS')
+ .map((model) => {
+ return {
+ itemId: model.itemId,
+ pcsMkrCd: model.pcsMkrCd,
+ pcsSerCd: model.pcsSerCd,
+ }
+ })
+ }
return models.map((model) => {
return {
itemId: model.itemId,
@@ -53,7 +64,18 @@ export function useCircuitTrestle(executeEffect = false) {
}
// 선택된 PCS 아이템 목록
- const getSelectedPcsItemList = () => {
+ const getSelectedPcsItemList = (isMultiModule = false) => {
+ if (isMultiModule) {
+ return selectedModels
+ .filter((model) => model.pcsTpCd !== 'INDFCS')
+ .map((model) => {
+ return {
+ itemId: model.itemId,
+ pcsMkrCd: model.pcsMkrCd,
+ pcsSerCd: model.pcsSerCd,
+ }
+ })
+ }
return selectedModels.map((model) => {
return {
itemId: model.itemId,
@@ -95,6 +117,7 @@ export function useCircuitTrestle(executeEffect = false) {
uniqueId: module.id ? module.id : null,
}
}),
+ roofSurfaceNorthYn: obj.direction === 'north' ? 'Y' : 'N',
}
})
.filter((surface) => surface.moduleList.length > 0)
diff --git a/src/locales/ja.json b/src/locales/ja.json
index db27c2ef..e738ebaa 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -1089,6 +1089,7 @@
"module.circuit.minimun.error": "回路番号は1以上の数値を入力してください。",
"module.already.exist.error": "回路番号が同じで異なるパワーコンディショナのモジュールがあります。 別の回路番号を設定してください。",
"module.circuit.fix.not.same.roof.error": "異なる屋根面のモジュールが選択されています。 モジュールの選択をや直してください。",
+ "module.circuit.indoor.focused.error": "混合モジュールと屋内集中PCSを組み合わせる場合は、手動回路割り当てのみ対応可能です。",
"construction.length.difference": "屋根面工法をすべて選択してください。",
"menu.validation.canvas.roof": "パネルを配置するには、屋根面を入力する必要があります。",
"batch.object.outside.roof": "オブジェクトは屋根に設置する必要があります。",
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 4f6601cd..bed23c44 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -1089,6 +1089,7 @@
"module.circuit.minimun.error": "회로번호는 1 이상입력해주세요.",
"module.already.exist.error": "회로번호가 같은 다른 파워 컨디셔너 모듈이 있습니다. 다른 회로번호를 설정하십시오.",
"module.circuit.fix.not.same.roof.error": "다른 지붕면의 모듈이 선택되어 있습니다. 모듈 선택을 다시 하세요.",
+ "module.circuit.indoor.focused.error": "혼합 모듈과 실내 집중형 PCS를 조합하는 경우, 수동 회로 할당만 가능합니다.",
"construction.length.difference": "지붕면 공법을 모두 선택하십시오.",
"menu.validation.canvas.roof": "패널을 배치하려면 지붕면을 입력해야 합니다.",
"batch.object.outside.roof": "오브젝트는 지붕내에 설치해야 합니다.",
diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss
index c0c4a2cb..472243ed 100644
--- a/src/styles/_reset.scss
+++ b/src/styles/_reset.scss
@@ -460,7 +460,11 @@ button{
}
}
-
+.table-select{
+ height: 20px;
+ color: #fff !important;
+ font-size: 11px !important;
+}
// input
.form-input{
label{
diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js
index 243936da..b2ee57db 100644
--- a/src/util/canvas-util.js
+++ b/src/util/canvas-util.js
@@ -269,7 +269,7 @@ export const getDegreeByChon = (chon) => {
* @returns {number}
*/
export const getChonByDegree = (degree) => {
- return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(1))
+ return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(2))
}
/**
@@ -1036,11 +1036,11 @@ export const getDegreeInOrientation = (degree) => {
{ min: -51, max: -37, value: -45 },
{ min: -36, max: -22, value: -30 },
{ min: -21, max: -7, value: -15 },
- { min: -6, max: 0, value: 0 }
+ { min: -6, max: 0, value: 0 },
]
// 해당 범위에 맞는 값 찾기
- const range = degreeRanges.find(range => degree >= range.min && degree <= range.max)
+ const range = degreeRanges.find((range) => degree >= range.min && degree <= range.max)
return range ? range.value : degree
}