From bf80662f09ad630cc7fb158750d286f334a48316 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 16 Jan 2025 13:54:29 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EC=84=A4=EC=A0=95=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EC=8B=9C=20alert=20=EC=A0=9C=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 배치면 초기 설정 저장시 alert 제거 - 글로벌 설정 저장시 alert 제거 --- src/hooks/option/useCanvasSetting.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 299d7e67..951d9801 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -410,9 +410,10 @@ export function useCanvasSetting() { ], } - await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => { - swalFire({ text: getMessage(res.returnMessage) }) - }) + // await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => { + // swalFire({ text: getMessage(res.returnMessage) }) + // }) + await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }) //Recoil 설정 setCanvasSetting({ ...basicSetting }) @@ -429,7 +430,6 @@ export function useCanvasSetting() { } catch (error) { swalFire({ text: error.message, icon: 'error' }) } - closeAll() } // CanvasSetting 조회 및 초기화 @@ -671,7 +671,7 @@ export function useCanvasSetting() { // HTTP POST 요청 보내기 await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }) .then((res) => { - swalFire({ text: getMessage(res.returnMessage) }) + // swalFire({ text: getMessage(res.returnMessage) }) // Canvas 디스플레이 설정 시 해당 옵션 적용 frontSettings() From f890c8dc5f74fe3da3c554bdecb72344d268f2f7 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Thu, 16 Jan 2025 16:18:25 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EB=B0=8F=20offset=200?= =?UTF-8?q?=20=EC=9D=BC=20=EA=B2=BD=EC=9A=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/useEavesGableEdit.js | 4 +- src/hooks/roofcover/useMovementSetting.js | 12 +- src/hooks/roofcover/useRoofShapeSetting.js | 5 + src/hooks/useMode.js | 20 ++- src/util/qpolygon-utils.js | 172 ++++++++++++++++----- 5 files changed, 164 insertions(+), 49 deletions(-) diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index 27e00a86..7e69941a 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -161,7 +161,7 @@ export function useEavesGableEdit(id) { attributes, }) - const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed) roofBases.forEach((roof) => { roof.innerLines.forEach((line) => { @@ -171,7 +171,7 @@ export function useEavesGableEdit(id) { }) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) - const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText') + const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText' || obj.name === 'lengthText') removeTargets.forEach((obj) => { canvas.remove(obj) }) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 245f1ac6..1da5dcc8 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -322,13 +322,13 @@ export function useMovementSetting(id) { selectable: true, stroke: '#1083E3', strokeWidth: 4, - attributes: { roofId: roof.id, currentRoof: currentObject.attributes.currentRoof }, + attributes: { roofId: roof.id, currentRoofId: currentObject.attributes.currentRoofId }, }) overlapRidges.forEach((line) => - line.attributes.currentRoof.forEach((id) => { - if (!newRidge.attributes.currentRoof.includes(id)) { - newRidge.attributes.currentRoof.push(id) + line.attributes.currentRoofId.forEach((id) => { + if (!newRidge.attributes.currentRoofId.includes(id)) { + newRidge.attributes.currentRoofId.push(id) } }), ) @@ -731,7 +731,7 @@ export function useMovementSetting(id) { name: 'roofPolygon', attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, pitch: currentRoof.attributes.pitch, degree: currentRoof.attributes.degree, direction: currentRoof.direction, @@ -868,7 +868,7 @@ export function useMovementSetting(id) { name: 'roofPolygon', attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, pitch: currentRoof.attributes.pitch, degree: currentRoof.attributes.degree, direction: currentRoof.direction, diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 05f9ae43..ac39d75e 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -411,6 +411,11 @@ export function useRoofShapeSetting(id) { canvas.remove(obj) }) + const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText' || obj.name === 'lengthText') + removeTargets.forEach((obj) => { + canvas.remove(obj) + }) + const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) polygon.lines = [...outerLines] diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 450dd5f6..d1dcc6b7 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1666,7 +1666,7 @@ export function useMode() { const offsetEdges = [] polygon.edges.forEach((edge, i) => { - const offset = lines[i % lines.length].attributes.offset + const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset const dx = edge.outwardNormal.x * offset const dy = edge.outwardNormal.y * offset offsetEdges.push(createOffsetEdge(edge, dx, dy)) @@ -1694,7 +1694,7 @@ export function useMode() { const offsetEdges = [] polygon.edges.forEach((edge, i) => { - const offset = lines[i % lines.length].attributes.offset + const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset const dx = edge.inwardNormal.x * offset const dy = edge.inwardNormal.y * offset offsetEdges.push(createOffsetEdge(edge, dx, dy)) @@ -1760,11 +1760,12 @@ export function useMode() { if (wall.direction) { roof.direction = wall.direction } + if (wall.attributes?.roofId) { + roof.id = wall.attributes.roofId + } roof.name = POLYGON_TYPE.ROOF roof.setWall(wall) - console.log('roof.lines : ', roof.lines) - roof.lines.forEach((line, index) => { const lineLength = Math.sqrt( Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), @@ -1795,7 +1796,7 @@ export function useMode() { Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), ) line.attributes.roofId = roof.id - line.attributes.currentRoof = roof.lines[index].id + line.attributes.currentRoofId = roof.lines[index].id line.attributes.planeSize = lineLength line.attributes.actualSize = lineLength @@ -1827,8 +1828,15 @@ export function useMode() { break } const wallLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + parentId: wall.id, name: 'wallLine', - attributes: { wallId: wall.id, roofId: roof.id, type: line.attributes.type, currentRoof: line.attributes.currentRoof, currentWall: line.id }, + attributes: { + wallId: wall.id, + roofId: roof.id, + type: line.attributes.type, + currentRoofId: line.attributes.currentRoofId, + currentWall: line.id, + }, stroke: wallStroke, strokeWidth: wallStrokeWidth, selectable: false, diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 47a923a3..02140f13 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -368,11 +368,12 @@ export const drawGabledRoof = (roofId, canvas) => { // 마루를 그린다. if (points) { const ridge = new QLine(points, { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 1, name: LINE_TYPE.SUBLINE.RIDGE, - attributes: { roofId: roof.id, currentRoof: [currentRoof.id] }, + attributes: { roofId: roof.id, currentRoofId: [currentRoof.id] }, visible: false, }) const duplicateRidge = ridges.find( @@ -380,7 +381,7 @@ export const drawGabledRoof = (roofId, canvas) => { ) // 중복된 마루는 제외한다. if (duplicateRidge) { - duplicateRidge.attributes.currentRoof.push(currentRoof.id) + duplicateRidge.attributes.currentRoofId.push(currentRoof.id) } else { canvas.add(ridge) canvas.renderAll() @@ -394,7 +395,7 @@ export const drawGabledRoof = (roofId, canvas) => { const index = eave.index, currentRoof = eave.roof const currentWall = wallLines[index] - const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.roof.id)) + const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoofId.includes(eave.roof.id)) let points = [] const signX = Math.sign(currentRoof.x1 - currentRoof.x2) let currentX1 = currentRoof.x1, @@ -536,6 +537,7 @@ export const drawGabledRoof = (roofId, canvas) => { if (sortedPoints.length > 0) { const roofPolygon = new QPolygon(sortedPoints, { + parentId: roof.id, fill: 'transparent', stroke: '#1083E3', strokeWidth: 2, @@ -544,7 +546,7 @@ export const drawGabledRoof = (roofId, canvas) => { name: 'roofPolygon', attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, pitch: currentRoof.attributes.pitch, degree: currentRoof.attributes.degree, direction: currentRoof.direction, @@ -577,7 +579,7 @@ export const drawGabledRoof = (roofId, canvas) => { eaves.forEach((eave) => { const currentRoof = eave.roof - let currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(currentRoof.id)) + let currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoofId.includes(currentRoof.id)) if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) { currentRidges = currentRidges.reduce((farthest, ridge) => { const currentDistance = Math.abs(ridge.x1 - currentRoof.x1) @@ -597,7 +599,8 @@ export const drawGabledRoof = (roofId, canvas) => { const x2 = Math.sign(currentRidges.x1 - currentRidges.x2) === 0 ? currentRoof.x1 : ridgeMidX const y2 = Math.sign(currentRidges.x1 - currentRidges.x2) === 0 ? ridgeMidY : currentRoof.y1 - const centerLine = new QLine([ridgeMidX, ridgeMidY, x2, y2], { + const pitchSizeLine = new QLine([ridgeMidX, ridgeMidY, x2, y2], { + parentId: roof.id, stroke: '#000000', strokeWidth: 2, strokeDashArray: [5, 5], @@ -608,16 +611,17 @@ export const drawGabledRoof = (roofId, canvas) => { type: 'pitchSizeLine', }, }) + canvas.add(pitchSizeLine) + canvas.renderAll() + const adjust = Math.sqrt( - Math.pow(Math.round(Math.abs(centerLine.x1 - centerLine.x2) * 10), 2) + Math.pow(Math.round(Math.abs(centerLine.y1 - centerLine.y2) * 10), 2), + Math.pow(Math.round(Math.abs(pitchSizeLine.x1 - pitchSizeLine.x2) * 10), 2) + + Math.pow(Math.round(Math.abs(pitchSizeLine.y1 - pitchSizeLine.y2) * 10), 2), ) const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree const height = Math.tan(currentDegree * (Math.PI / 180)) * adjust const lengthText = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2))) - canvas.add(centerLine) - canvas.renderAll() - centerLine.setLengthText(lengthText) - roof.innerLines.push(centerLine) + pitchSizeLine.setLengthText(lengthText) } }) } @@ -684,6 +688,7 @@ export const drawShedRoof = (roofId, canvas) => { } points.sort((a, b) => a - b) const line = new QLine([x1, y1, x2, y2], { + parentId: roof.id, stroke: '#000000', strokeWidth: 2, strokeDashArray: [5, 5], @@ -753,6 +758,7 @@ const drawRidge = (roof, canvas) => { // 지붕의 길이가 짧은 순으로 정렬 ridgeRoof.sort((a, b) => a.length - b.length) + console.log('마루 순서 : ', ridgeRoof.map((item) => item.length).join(',')) ridgeRoof.forEach((item) => { if (getMaxRidge(roofLines.length) > roof.ridges.length) { @@ -864,11 +870,12 @@ const drawRidge = (roof, canvas) => { const checkEndYPoint = Math.round(startYPoint + Math.sign(beta) * -1 * (ridgeMaxLength + ridgeBaseLength)) const checkLine = new QLine([startXPoint, startYPoint, checkEndXPoint, checkEndYPoint], { + parentId: roof.id, fontSize: roof.fontSize, stroke: 'red', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, + attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 }, }) const intersectLines = [] roofLines.forEach((line) => { @@ -917,6 +924,7 @@ const drawRidge = (roof, canvas) => { } const ridge = new QLine([startXPoint, startYPoint, endXPoint, endYPoint], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -956,6 +964,7 @@ const drawRidge = (roof, canvas) => { let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) const newRidge = new QLine([x1, y1, x2, y2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -1046,11 +1055,12 @@ const drawHips = (roof, canvas) => { const angle1 = Math.atan2(vectorY1, vectorX1) * (180 / Math.PI) if (Math.abs(Math.round(angle1)) % 45 === 0) { const hip1 = new QLine([currentRoof.x1, currentRoof.y1, ridgeCoordinate.x1, ridgeCoordinate.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, + attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 }, }) canvas.add(hip1) const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 @@ -1068,11 +1078,12 @@ const drawHips = (roof, canvas) => { const angle2 = Math.atan2(vectorY2, vectorX2) * (180 / Math.PI) if (Math.abs(Math.round(angle2)) % 45 === 0) { const hip2 = new QLine([currentRoof.x2, currentRoof.y2, ridgeCoordinate.x1, ridgeCoordinate.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, + attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 }, }) canvas.add(hip2) const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 @@ -1140,11 +1151,12 @@ const drawHips = (roof, canvas) => { if (ridgePoints !== undefined) { const hip = new QLine([currentRoof.x1, currentRoof.y1, ridgePoints.x, ridgePoints.y], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, + attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 }, }) canvas.add(hip) const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10 @@ -1246,6 +1258,7 @@ const connectLinePoint = (polygon) => { missedLine.forEach((p) => { const line = new QLine([p.x1, p.y1, p.x2, p.y2], { + parentId: polygon.id, attributes: { roofId: polygon.id }, fontSize: polygon.fontSize, stroke: '#1083E3', @@ -1522,7 +1535,7 @@ const changeEavesRoof = (currentRoof, canvas) => { .filter( (object) => object.attributes?.roofId === roofId && - object.attributes?.currentRoof === currentRoof.id && + object.attributes?.currentRoofId === currentRoof.id && object.x1 !== undefined && object.x2 !== undefined, ) @@ -1625,6 +1638,7 @@ const changeEavesRoof = (currentRoof, canvas) => { const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree const hip1 = new QLine([currentRoof.x1, currentRoof.y1, hipX2, hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -1644,6 +1658,7 @@ const changeEavesRoof = (currentRoof, canvas) => { } const hip2 = new QLine([currentRoof.x2, currentRoof.y2, hipX2, hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -1674,6 +1689,7 @@ const changeEavesRoof = (currentRoof, canvas) => { const changeGableRoof = (currentRoof, canvas) => { if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.GABLE) { const roofId = currentRoof.attributes.roofId + console.log('roofId', roofId) const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId) let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId) let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId) @@ -1686,7 +1702,7 @@ const changeGableRoof = (currentRoof, canvas) => { .filter( (object) => object.attributes?.roofId === roofId && - object.attributes?.currentRoof === currentRoof.id && + object.attributes?.currentRoofId === currentRoof.id && object.x1 !== undefined && object.x2 !== undefined, ) @@ -1713,6 +1729,7 @@ const changeGableRoof = (currentRoof, canvas) => { roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id) canvas?.remove(line) }) + canvas.renderAll() ridgeLines = ridgeLines.filter( (ridge) => @@ -1722,6 +1739,8 @@ const changeGableRoof = (currentRoof, canvas) => { hipLines = hipLines.filter( (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2), ) + console.log('ridgeLines : ', ridgeLines) + console.log('hipLines : ', hipLines) if (hipLines === undefined || hipLines.length === 0) { hipLines = innerLines.filter( @@ -1794,6 +1813,7 @@ const changeGableRoof = (currentRoof, canvas) => { ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX, midY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -1808,8 +1828,10 @@ const changeGableRoof = (currentRoof, canvas) => { const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180)) hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10 hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2))) + roof.innerLines.push(hip1) let hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX, midY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -1826,10 +1848,7 @@ const changeGableRoof = (currentRoof, canvas) => { const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180)) hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) - hip1.set({ visible: false }) - hip1.setViewLengthText(false) - hip2.set({ visible: false }) - hip2.setViewLengthText(false) + roof.innerLines.push(hip2) canvas?.renderAll() } } @@ -1882,7 +1901,7 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { .filter( (object) => object.attributes?.roofId === roofId && - object.attributes?.currentRoof === currentRoof.id && + object.attributes?.currentRoofId === currentRoof.id && object.x1 !== undefined && object.x2 !== undefined, ) @@ -1985,13 +2004,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { } const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX + hipX2, midY + hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, }, }) const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree @@ -2005,13 +2025,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { roof.innerLines.push(hip1) const hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX + hipX2, midY + hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.HIP, attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, planeSize: currentRoof.length, actualSize: currentRoof.length, }, @@ -2040,13 +2061,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { hipLines.forEach((hip, i) => { const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, name: LINE_TYPE.SUBLINE.GABLE, attributes: { roofId: roof.id, - currentRoof: currentRoof.id, + currentRoofId: currentRoof.id, actualSize: 0, }, }) @@ -2120,7 +2142,7 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { (object) => object.attributes !== undefined && object.attributes.roofId === roofId && - object.attributes.currentRoof === currentRoof.id && + object.attributes.currentRoofId === currentRoof.id && object.x1 !== undefined && object.x2 !== undefined, ) @@ -2231,6 +2253,7 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { let hipY1 = (Math.sign(currentRoof.y1 - midY) * currentRoof.attributes.width) / 2 const gable1 = new QLine([midX + hipX1, midY + hipY1, hipX2, hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2253,6 +2276,7 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { hipY1 = (Math.sign(currentRoof.y2 - midY) * currentRoof.attributes.width) / 2 const gable2 = new QLine([midX + hipX1, midY + hipY1, hipX2, hipY2], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2271,6 +2295,7 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { roof.innerLines.push(gable2) const gable3 = new QLine([gable1.x1, gable1.y1, gable2.x1, gable2.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2287,10 +2312,11 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { // roof.innerLines.push(gable3) const hip1 = new QLine([currentRoof.x1, currentRoof.y1, gable1.x1, gable1.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, - name: LINE_TYPE.SUBLINE.GABLE, + name: LINE_TYPE.SUBLINE.HIP, attributes: { roofId: roof.id, currentRoofId: currentRoof.id, @@ -2305,6 +2331,7 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { // roof.innerLines.push(hip1) const hip2 = new QLine([currentRoof.x2, currentRoof.y2, gable2.x1, gable2.y1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2321,6 +2348,8 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) canvas?.add(hip2) + console.log('currentRoof.id : ', currentRoof.id) + hip1.set({ visible: false }) hip1.setViewLengthText(false) gable3.set({ visible: false }) @@ -2401,7 +2430,7 @@ const changeWallRoof = (currentRoof, canvas) => { .filter( (object) => object.attributes?.roofId === roofId && - object.attributes?.currentRoof === currentRoof.id && + object.attributes?.currentRoofId === currentRoof.id && object.x1 !== undefined && object.x2 !== undefined, ) @@ -2460,6 +2489,7 @@ const changeWallRoof = (currentRoof, canvas) => { }) const addPrevWallLine1 = new QLine([prevX2, prevY2, wallLine.x1 - prevWidthX, wallLine.y1 - prevWidthY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2470,6 +2500,7 @@ const changeWallRoof = (currentRoof, canvas) => { const addPrevWallLine2 = new QLine( [addPrevWallLine1.x2, addPrevWallLine1.y2, addPrevWallLine1.x2 + prevWidthX, addPrevWallLine1.y2 + prevWidthY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2479,6 +2510,7 @@ const changeWallRoof = (currentRoof, canvas) => { ) const addNextWallLine1 = new QLine([wallLine.x2, wallLine.y2, wallLine.x2 + nextWidthX, wallLine.y2 + nextWidthY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2487,6 +2519,7 @@ const changeWallRoof = (currentRoof, canvas) => { }) const addNextWallLine2 = new QLine([addNextWallLine1.x2, addNextWallLine1.y2, nextX1, nextY1], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2532,6 +2565,7 @@ const changeWallRoof = (currentRoof, canvas) => { ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX, wallMidY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2548,6 +2582,7 @@ const changeWallRoof = (currentRoof, canvas) => { hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2))) let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX, wallMidY], { + parentId: roof.id, fontSize: roof.fontSize, stroke: '#1083E3', strokeWidth: 2, @@ -2647,7 +2682,7 @@ export const changeCurrentRoof = (currentRoof, canvas) => { wall.lines.forEach((line, index) => { line.attributes.roofId = newRoof.id - line.attributes.currentRoof = newRoof.lines[index].id + line.attributes.currentRoofId = newRoof.lines[index].id }) canvas?.add(newRoof) canvas?.renderAll() @@ -2709,11 +2744,20 @@ const reDrawPolygon = (polygon, canvas) => { } const drawCenterLine = (roof, canvas) => { + //현재 지붕의 centerLine을 다 지운다. + canvas + .getObjects() + .filter((object) => object.attributes?.roofId === roof.id) + .filter((line) => line.attributes?.type === 'pitchSizeLine') + .forEach((line) => canvas.remove(line)) + const roofLines = roof.lines roofLines.forEach((currentRoof) => { const hips = roof.innerLines.filter( (line) => (currentRoof.x1 === line.x1 && currentRoof.y1 === line.y1) || (currentRoof.x2 === line.x1 && currentRoof.y2 === line.y1), ) + // console.log('hips : ', hips) + const ridge = roof.innerLines .filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE) .filter((line) => { @@ -2738,17 +2782,38 @@ const drawCenterLine = (roof, canvas) => { } return prevDistance < currentDistance ? prev : current }, null) + /*console.log('currentRoof : ', currentRoof.attributes.planeSize, currentRoof) + console.log( + roof.innerLines + .filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE) + .filter((line) => { + if (currentRoof.x1 === currentRoof.x2) { + console.log('currentRoof.x1 === currentRoof.x2', line.x1 === line.x2, line) + if (line.x1 === line.x2) { + return line + } + } else { + console.log('currentRoof.x1 !== currentRoof.x2', line.y1 === line.y2, line) + if (line.y1 === line.y2) { + return line + } + } + }), + ) + console.log('currentRoof.x1 === currentRoof.x2 : ', currentRoof.x1 === currentRoof.x2) + console.log('ridge : ', ridge) + console.log('hips : ', hips)*/ let points = [] if (hips.length === 2 && Math.abs(hips[0].x2 - hips[1].x2) < 1 && Math.abs(hips[0].y2 - hips[1].y2) < 1) { // console.log('삼각') const x1 = (currentRoof.x1 + currentRoof.x2) / 2 const y1 = (currentRoof.y1 + currentRoof.y2) / 2 points.push(x1, y1, hips[0].x2, hips[0].y2) - } else { + } else if (hips.length > 1) { // console.log('삼각 아님') if ( - ((ridge.x1 === hips[0].x2 && ridge.y1 === hips[0].y2) || (ridge.x2 === hips[0].x2 && ridge.y2 === hips[0].y2)) && - ((ridge.x1 === hips[1].x2 && ridge.y1 === hips[1].y2) || (ridge.x2 === hips[1].x2 && ridge.y2 === hips[1].y2)) + ((ridge?.x1 === hips[0].x2 && ridge?.y1 === hips[0].y2) || (ridge?.x2 === hips[0].x2 && ridge?.y2 === hips[0].y2)) && + ((ridge?.x1 === hips[1].x2 && ridge?.y1 === hips[1].y2) || (ridge?.x2 === hips[1].x2 && ridge?.y2 === hips[1].y2)) ) { //사각이면 마루와 현재 라인 사이에 길이를 구한다 // console.log('사각') @@ -2776,7 +2841,7 @@ const drawCenterLine = (roof, canvas) => { } } } else { - //사각 아님 + // console.log('사각 아님') if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) { let xPoints = [] xPoints.push(ridge?.x1, ridge?.x2) @@ -2843,8 +2908,8 @@ const drawCenterLine = (roof, canvas) => { } points.push(oppositeLine.x2, currentRoof.y1, oppositeLine.x2, oppositeLine.y2) } else if (yPoints.length > 1) { - let boolY1 = yPoints.some((y) => y === ridge.y1) - let boolY2 = yPoints.some((y) => y === ridge.y2) + let boolY1 = yPoints.some((y) => y === ridge?.y1) + let boolY2 = yPoints.some((y) => y === ridge?.y2) if (boolY1 && boolY2) { oppositeLine = ridge } @@ -2861,9 +2926,46 @@ const drawCenterLine = (roof, canvas) => { } } } + } else { + // console.log('else : ') + if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD) { + const gables = canvas + .getObjects() + .filter((object) => object.attributes?.currentRoofId === currentRoof.id && object.name === LINE_TYPE.SUBLINE.GABLE) + + let x1, y1, x2, y2 + x1 = (currentRoof.x1 + currentRoof.x2) / 2 + y1 = (currentRoof.y1 + currentRoof.y2) / 2 + + if (currentRoof.x1 === currentRoof.x2) { + const xPoints = [] + gables.forEach((gable) => { + if (gable.x1 !== x1) { + xPoints.push(gable.x1) + } else if (gable.x2 !== x1) { + xPoints.push(gable.x2) + } + }) + x2 = xPoints.reduce((sum, current) => sum + current, 0) / xPoints.length + y2 = y1 + } else { + const yPoints = [] + gables.forEach((gable) => { + if (gable.y1 !== y1) { + yPoints.push(gable.y1) + } else if (gable.y2 !== y1) { + yPoints.push(gable.y2) + } + }) + x2 = x1 + y2 = yPoints.reduce((sum, current) => sum + current, 0) / yPoints.length + } + points.push(x1, y1, x2, y2) + } } if (points !== null) { const pitchSizeLine = new QLine(points, { + parentId: roof.id, stroke: '#000000', strokeWidth: 2, strokeDashArray: [5, 5], From 095f0df646afdb8db085a9127cf283bf61303328 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Thu, 16 Jan 2025 16:19:23 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=EA=B8=B8=EC=9D=B4=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 02140f13..2b37857d 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -726,7 +726,7 @@ export const drawRidgeRoof = (roofId, canvas) => { drawHips(roof, canvas) connectLinePoint(roof, canvas) modifyRidge(roof, canvas) - drawCenterLine(roof, canvas) + // drawCenterLine(roof, canvas) } /** From 63387325c308fff4db1d4553f8627fe026b12a54 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 16 Jan 2025 16:42:33 +0900 Subject: [PATCH 4/5] =?UTF-8?q?customProperties=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/common.js b/src/common/common.js index 903f6fd7..71a075db 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -170,6 +170,9 @@ export const SAVE_KEY = [ 'supFitIntvlPct', 'rackLen', 'trestleDetail', + 'turfPoints', + 'tempIndex', + 'surfaceId', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] From 67a0dae2e2b7ac09915cdb3742066a143af4da43 Mon Sep 17 00:00:00 2001 From: basssy Date: Thu, 16 Jan 2025 16:55:54 +0900 Subject: [PATCH 5/5] =?UTF-8?q?swalFire=20confirm=20=EC=B0=BD=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EC=9D=84=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 8 +-- src/hooks/usePlan.js | 74 ++++++++++++++---------- src/hooks/useSwal.js | 20 +++++-- src/locales/ja.json | 4 +- src/locales/ko.json | 6 +- 5 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 3b3e0003..6d33d474 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -203,9 +203,7 @@ export default function CanvasMenu(props) { setCurrentMenu(menu.title) } - if (pathname !== '/floor-plan' && pathname !== '/floor-plan/estimate/5' && pathname !== '/floor-plan/simulator/6') { - router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`) - } + if (pathname !== '/floor-plan') router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`) } const changeSelectedRoofMaterial = (e) => { @@ -368,7 +366,9 @@ export default function CanvasMenu(props) { */ const handleEstimateLockController = (estimateRecoilState) => { swalFire({ - text: estimateRecoilState.lockFlg === '0' ? getMessage('estimate.detail.lock.alertMsg') : getMessage('estimate.detail.unlock.alertMsg'), + // text: estimateRecoilState.lockFlg === '0' ? getMessage('estimate.detail.lock.alertMsg') : getMessage('estimate.detail.unlock.alertMsg'), + html: estimateRecoilState.lockFlg === '0' ? getMessage('estimate.detail.lock.alertMsg') : getMessage('estimate.detail.unlock.alertMsg'), + confirmButtonText: estimateRecoilState.lockFlg === '1' ? getMessage('estimate.detail.unlock.confirmBtnName') : '', type: 'confirm', confirmFn: async () => { setIsGlobalLoading(true) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index db170dc7..86b6aee3 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -206,42 +206,52 @@ export function usePlan(params = {}) { const handleCurrentPlan = async (newCurrentId) => { const orderingNo = plans?.find((obj) => obj.id === newCurrentId).ordering const objectNo = floorPlanState.objectNo - await promiseGet({ url: `/api/estimate/${objectNo}/${orderingNo}/detail` }) - .then((res) => { - if (res.status === 200) { - const estimateDetail = res.data - if (estimateDetail.docNo) { - res.data.resetFlag = 'N' + //견적서 or 발전시뮬 + if (pathname !== '/floor-plan') { + await promiseGet({ url: `/api/estimate/${objectNo}/${orderingNo}/detail` }) + .then((res) => { + if (res.status === 200) { + const estimateDetail = res.data + if (estimateDetail.docNo) { + res.data.resetFlag = 'N' - if (res.data.itemList.length > 0) { - res.data.itemList.map((item) => { - item.delFlg = '0' - }) - } - if (res.data.pkgAsp === null || res.data.pkgAsp == undefined) { - res.data.pkgAsp = '0.00' - } else { - const number = parseFloat(res.data.pkgAsp) - const roundedNumber = isNaN(number) ? '0.00' : number.toFixed(2) - - res.data.pkgAsp = roundedNumber.toString() - } - setEstimateContextState(res.data) - - if (pathname === '/floor-plan') { - if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { - saveCanvas() + if (res.data.itemList.length > 0) { + res.data.itemList.map((item) => { + item.delFlg = '0' + }) } + if (res.data.pkgAsp === null || res.data.pkgAsp == undefined) { + res.data.pkgAsp = '0.00' + } else { + const number = parseFloat(res.data.pkgAsp) + const roundedNumber = isNaN(number) ? '0.00' : number.toFixed(2) + + res.data.pkgAsp = roundedNumber.toString() + } + setEstimateContextState(res.data) + + if (pathname === '/floor-plan') { + if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { + saveCanvas() + } + } + updateCurrentPlan(newCurrentId) + } else { + swalFire({ text: getMessage('estimate.menu.move.valid1') }) } - updateCurrentPlan(newCurrentId) - } else { - swalFire({ text: getMessage('estimate.menu.move.valid1') }) } - } - }) - .catch((error) => { - swalFire({ text: getMessage('estimate.menu.move.valid1') }) - }) + }) + .catch((error) => { + swalFire({ text: getMessage('estimate.menu.move.valid1') }) + }) + } else { + // if (pathname === '/floor-plan') { + if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { + saveCanvas() + } + // } + updateCurrentPlan(newCurrentId) + } } const updateCurrentPlan = (newCurrentId) => { diff --git a/src/hooks/useSwal.js b/src/hooks/useSwal.js index 018d8f32..f904091d 100644 --- a/src/hooks/useSwal.js +++ b/src/hooks/useSwal.js @@ -1,5 +1,6 @@ import Swal from 'sweetalert2' import withReactContent from 'sweetalert2-react-content' +import { useMessage } from '@/hooks/useMessage' /** * title: 제목 @@ -13,14 +14,25 @@ import withReactContent from 'sweetalert2-react-content' */ export const useSwal = () => { const MySwal = withReactContent(Swal) + const { getMessage } = useMessage() - const swalFire = ({ title = '', text = '', html = '', type = 'alert', icon = '', confirmFn = () => {}, denyFn = () => {} }) => { + const swalFire = ({ + title = '', + text = '', + html = '', + type = 'alert', + icon = '', + confirmButtonText = '', + cancelButtonText = '', + confirmFn = () => {}, + denyFn = () => {}, + }) => { if (type === 'alert') { MySwal.fire({ title, text, icon: icon === '' ? 'success' : icon, - confirmButtonText: '확인', + confirmButtonText: getMessage('common.ok'), }).then(() => { confirmFn() }) @@ -32,8 +44,8 @@ export const useSwal = () => { icon: icon === '' ? 'question' : icon, showCloseButton: true, showCancelButton: true, - confirmButtonText: '확인', - cancelButtonText: '취소', + confirmButtonText: confirmButtonText === '' ? getMessage('common.ok') : confirmButtonText, + cancelButtonText: cancelButtonText === '' ? getMessage('common.cancel') : cancelButtonText, }).then((result) => { if (result.isConfirmed) { confirmFn() diff --git a/src/locales/ja.json b/src/locales/ja.json index a96ff061..900165c5 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -498,6 +498,7 @@ "common.require": "必須", "common.finish": "完了", "common.ok": "確認", + "common.cancel": "キャンセル", "commons.west": "立つ", "commons.east": "ドン", "commons.south": "南", @@ -935,7 +936,8 @@ "estimate.detail.reset.alertMsg": "初期化されました.", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.lock.alertMsg": "見積書を[ロック]すると、変更できません。見積もりを編集するには、ロックを解除してください.", - "estimate.detail.unlock.alertMsg": "見積書を修正して保存", + "estimate.detail.unlock.alertMsg": "[ロック解除]すると、見積書を編集できます.\n 解除しますか?", + "estimate.detail.unlock.confirmBtnName": "解放", "estimate.detail.alert.delFile": "添付ファイルを完全に削除するには[保存]ボタンをクリックしてください", "estimate.detail.alert.selectDelItem": "削除する商品を選択してください.", "estimate.menu.move.valid1": "回路を分割していないため、見積もりを呼び出すことはできません.", diff --git a/src/locales/ko.json b/src/locales/ko.json index 2d049d61..c5ba4658 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -507,6 +507,7 @@ "common.require": "필수", "common.finish": "완료", "common.ok": "확인", + "common.cancel": "취소", "commons.west": "서", "commons.east": "동", "commons.south": "남", @@ -943,8 +944,9 @@ "estimate.detail.save.requiredSalePrice": "단가는 0보다 큰값을 입력해주세요.", "estimate.detail.reset.alertMsg": "초기화 되었습니다.", "estimate.detail.reset.confirmMsg": "수기 변경(저장)한 견적 정보가 초기화되고, 최근 저장된 도면정보가 반영됩니다. 정말로 초기화하시겠습니까?", - "estimate.detail.lock.alertMsg": "견적서를 [잠금]하면, 수정할 수 없습니다. 견적서를 수정하려면 잠금해제를 하십시오", - "estimate.detail.unlock.alertMsg": "견적서를 수정하고, 저장하십시오", + "estimate.detail.lock.alertMsg": "견적서를 [잠금]하면, 수정할 수 없습니다.
견적서를 수정하려면 잠금해제를 하십시오.", + "estimate.detail.unlock.alertMsg": "[잠금해제]하면 견적서를 수정할 수 있습니다.
해제하시겠습니까?", + "estimate.detail.unlock.confirmBtnName": "해제", "estimate.detail.alert.delFile": "첨부파일을 완전히 삭제하려면 [저장]버튼을 클릭하십시오.", "estimate.detail.alert.selectDelItem": "삭제할 제품을 선택하세요.", "estimate.menu.move.valid1": "회로를 나누지 않았기 때문에 견적서를 호출할 수 없습니다.",