From 9c25d3ed66dfc6fd7c92486d9154725d5eaf8f56 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 13 Apr 2026 11:04:59 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[1960]=EC=B6=9C=EB=A0=A5=EB=90=9C=20[?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=8F=84=C2=B7=EA=B3=84=ED=86=B5=EB=8F=84]?= =?UTF-8?q?=EC=97=90=20=EA=B0=80=EB=8C=80=EA=B0=80=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/floorPlan/useImgLoader.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/hooks/floorPlan/useImgLoader.js b/src/hooks/floorPlan/useImgLoader.js index dc272441..47eea944 100644 --- a/src/hooks/floorPlan/useImgLoader.js +++ b/src/hooks/floorPlan/useImgLoader.js @@ -84,15 +84,25 @@ export function useImgLoader() { const originalBg = canvas.backgroundColor canvas.backgroundColor = '#ffffff' - // CORS 대응: 이미지 오브젝트에 crossOrigin 설정 - canvas.getObjects('image').forEach((obj) => { - if (obj.getSrc) { - const img = new Image() - img.crossOrigin = 'anonymous' - img.src = obj.getSrc() - obj.setElement(img) - } - }) + // CORS 대응: 이미지 오브젝트에 crossOrigin 설정 (로드 완료 대기) + const imageObjects = canvas.getObjects('image').filter((obj) => obj.getSrc) + await Promise.all( + imageObjects.map( + (obj) => + new Promise((resolve) => { + const img = new Image() + img.crossOrigin = 'anonymous' + img.onload = () => { + obj.setElement(img) + resolve() + } + img.onerror = () => { + resolve() + } + img.src = obj.getSrc() + }), + ), + ) canvas.renderAll() From d0590f513e241679d544f6886b440bd1fd0c487f Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 13 Apr 2026 16:27:28 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[1491]=EC=8B=A4=EB=82=B4=20=EC=A7=91?= =?UTF-8?q?=EC=A4=91=ED=98=95=20PCS=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=9A=8C=EB=A1=9C=20=EA=B5=AC=EC=84=B1=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=91=EC=86=8D=ED=95=A8=EC=9D=B4=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=82=B0=EC=B6=9C?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/circuitTrestle/CircuitTrestleSetting.jsx | 7 ++----- .../floor-plan/modal/circuitTrestle/step/StepUp.jsx | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index fe7b8e11..83d6f6c3 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -833,12 +833,9 @@ export default function CircuitTrestleSetting({ id }) { pcsItemId: item.itemId, pscOptCd: getPcsOptCd(index), paralQty: serQty.paralQty, + // 접속함 중복 체크: 동일 itemId는 1개만, 다르면 각각 올림 connections: item.connList?.length - ? [ - { - connItemId: item.connList[0].itemId, - }, - ] + ? [...new Map(item.connList.map((conn) => [conn.itemId, { connItemId: conn.itemId, connMaxParalCnt: conn.connMaxParalCnt }])).values()] : [], }) // return { diff --git a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx index 2fc0c38f..14f6aa03 100644 --- a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx @@ -223,7 +223,6 @@ export default function StepUp(props) { useModuleItemList: props.getUseModuleItemList(), pcsItemList: getSelectedPcsItemList(), } - /** PCS 접속함 및 옵션 목록 조회 */ getPcsConnOptionItemList(params).then((res) => { if (res?.result.code === 200 && res?.data) { @@ -550,9 +549,10 @@ export default function StepUp(props) { pcsItemId: serQty.itemId, pcsOptCd: seletedOption, paralQty: serQty.paralQty, - connections: { - connItemId: item.connList[0].itemId, - }, + // 접속함 중복 체크: 동일 itemId는 1개만, 다르면 각각 올림 + connections: item.connList?.length + ? [...new Map(item.connList.map((conn) => [conn.itemId, { connItemId: conn.itemId, connMaxParalCnt: conn.connMaxParalCnt }])).values()] + : [], } }) }) From 55dd3d9d3dd1c2a2f21abb554b0174668329071f Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 13 Apr 2026 18:10:01 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EC=99=B8=EB=B2=BD=EC=84=A0=20=ED=8E=B8?= =?UTF-8?q?=EC=A7=91=20=EB=B0=8F=20=EC=98=A4=ED=94=84=EC=85=8B=20=EA=B8=B8?= =?UTF-8?q?=EC=9D=B4=20=EA=B3=84=EC=82=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useWallLineOffsetSetting.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 31d8f95f..390896eb 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -8,6 +8,7 @@ import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' import Big from 'big.js' import { outerLineFixState } from '@/store/outerLineAtom' +import { calcLinePlaneSize } from '@/util/qpolygon-utils' // 외벽선 편집 및 오프셋 export function useWallLineOffsetSetting(id) { @@ -46,7 +47,13 @@ export function useWallLineOffsetSetting(id) { direction: direction, }) - line.attributes = { ...currentWallLineRef.current.attributes } + // 분할된 라인의 실제 길이로 planeSize/actualSize 재계산 + const newPlaneSize = calcLinePlaneSize({ x1: point1.x, y1: point1.y, x2: point2.x, y2: point2.y }) + line.attributes = { + ...currentWallLineRef.current.attributes, + planeSize: newPlaneSize, + actualSize: newPlaneSize, + } } const TYPES = { From 6cc9201962f9d5aca7c8485ec549f0d72357e40d Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 15 Apr 2026 17:52:22 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useCanvasConfigInitialize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index df4025d8..62f9574b 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -219,7 +219,7 @@ export function useCanvasConfigInitialize() { surface.modules = modules.filter((module) => module.surfaceId === surface.id) }) modules.forEach((obj) => { - console.log(obj) + //console.log(obj) obj.set({ selectable: true, lockMovementX: true, From b9bb4708a87e537030b71527f40417692d0be9ff Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 15 Apr 2026 17:59:28 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[1485]=20=EC=98=A4=EB=B8=8C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=B5=EC=82=AC=EA=B8=B0=EB=8A=A5=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../estimate/popup/EstimateCopyPop.jsx | 53 ++++++++++++++----- src/components/floor-plan/CanvasMenu.jsx | 4 +- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/components/estimate/popup/EstimateCopyPop.jsx b/src/components/estimate/popup/EstimateCopyPop.jsx index 564a1aae..8a21f5c6 100644 --- a/src/components/estimate/popup/EstimateCopyPop.jsx +++ b/src/components/estimate/popup/EstimateCopyPop.jsx @@ -2,6 +2,8 @@ import { useEffect, useState, useContext, useRef } from 'react' import { useMessage } from '@/hooks/useMessage' import { useAxios } from '@/hooks/useAxios' +import { useRecoilValue } from 'recoil' +import { globalLocaleStore } from '@/store/localeAtom' import Select from 'react-select' import { SessionContext } from '@/app/SessionProvider' import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils' @@ -10,24 +12,35 @@ import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateCon export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { const { getMessage } = useMessage() const { get } = useAxios() - + const globalLocale = useRecoilValue(globalLocaleStore) const { handleEstimateCopy, estimateContextState } = useEstimateController(planNo, true) const { session } = useContext(SessionContext) + // storeLvl 기반 동적 라벨 + const lvl = session?.storeLvl || '1' + const nextLvl = Number(lvl) + 1 + const saleStoreLabel = globalLocale === 'ko' + ? `${lvl}차 판매점명 / ID` + : `${lvl}次販売店名/ID` + const otherSaleStoreLabel = globalLocale === 'ko' + ? `${nextLvl}차+하위 판매점명 / ID` + : `${nextLvl}次下位販売店名/ID` + const [saleStoreList, setSaleStoreList] = useState([]) // 판매점 리스트 const [favoriteStoreList, setFavoriteStoreList] = useState([]) //즐겨찾기한 판매점목록 const [showSaleStoreList, setShowSaleStoreList] = useState([]) //보여줄 판매점목록 const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([]) - const [saleStoreId, setSaleStoreId] = useState('') //선택한 1차점 - const [otherSaleStoreId, setOtherSaleStoreId] = useState('') //선택한 1차점 이외 + const [saleStoreId, setSaleStoreId] = useState('') //선택한 상위점 + const [saleStoreName, setSaleStoreName] = useState('') //선택한 상위점명 + const [otherSaleStoreId, setOtherSaleStoreId] = useState('') //선택한 하위점 const [sendPlanNo, setSendPlanNo] = useState('1') const [copyReceiveUser, setCopyReceiveUser] = useState('') - const ref = useRef() //2차점 자동완성 초기화용 + const ref = useRef() //하위점 자동완성 초기화용 useEffect(() => { let url @@ -40,8 +53,9 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { if (session.storeLvl === '1') { url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` } else { - //T01 or 1차점만 복사버튼 노출됨으로 - // url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` + // 2차+ 판매점: 자기 자신이 상위점, 하위점 목록 조회 + setSaleStoreId(session?.storeId) + url = `/api/object/saleStore/${session?.storeId}/childList?storeLvl=${session?.storeLvl}&userId=${session?.userId}` } } @@ -91,7 +105,14 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { setOtherSaleStoreList(otherList) } else { - //T01 or 1차점만 복사버튼 노출됨으로 + // 2차+ 판매점: 자기 자신은 상위점명으로, 나머지는 하위점 선택 목록으로 설정 + const myself = res.find((row) => row.saleStoreId === session?.storeId) + if (myself) { + setSaleStoreName(myself.saleStoreName) + } + otherList = res.filter((row) => row.saleStoreId !== session?.storeId) + setOtherSaleStoreList(otherList) + setOriginOtherSaleStoreList(otherList) } } } @@ -119,7 +140,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { } } - // 1차점 변경 이벤트 + // 상위점 변경 이벤트 const onSelectionChange = (key) => { if (isObjectNotEmpty(key)) { if (key.saleStoreId === saleStoreId) { @@ -154,7 +175,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { } } - // 2차점 변경 이벤트 + // 하위점 변경 이벤트 const onSelectionChange2 = (key) => { if (isObjectNotEmpty(key)) { if (key.saleStoreId === otherSaleStoreId) { @@ -169,7 +190,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) { } } - //2차점 자동완성 초기화 + //하위점 자동완성 초기화 const handleClear = () => { if (ref.current) { ref.current.clearValue() @@ -198,7 +219,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
- {getMessage('estimate.detail.estimateCopyPopup.label.saleStoreId')} * + {saleStoreLabel} *
{session.storeId === 'T01' && (
@@ -247,9 +268,17 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
{saleStoreId}
)} + {session.storeId !== 'T01' && session.storeLvl !== '1' && ( +
+
+ +
+
{saleStoreId}
+
+ )}
-
{getMessage('estimate.detail.estimateCopyPopup.label.otherSaleStoreId')}
+
{otherSaleStoreLabel}